【Java入门系列】Java基础入门 --- 安装,数据类型,类

Overview

一、Java环境安装

Java是一门强大的编程语言,首先需要在您的计算机上安装Java开发环境。以下是安装Java环境的步骤:

1. 选择Java版本

对于初学者,建议选择Java 8或Java 11版本。您可以从Oracle官方网站下载这些版本。

2. 下载示例代码

您可以下载示例代码,以便学习和实践Java编程。示例代码通常包含了一些基础的Java程序,可以帮助您快速入门。

示例代码下载链接(以wget为例):

1$ wget https://horstmann.com/corejava/corejava11.zip

3. 配置环境变量

将Java主目录添加到系统的PATH环境变量中,这样您就可以在任何位置运行Java命令。您可以使用以下命令来编辑配置文件:

1$ vim ~/.zshrc

在配置文件中添加以下行:

1export PATH=/your/java/installation/path/bin:$PATH

确保将/your/java/installation/path替换为您的Java安装路径, 比如"/Users/simingzhang/siming/jdk-11.0.17/bin"。

4. 验证安装

corejava实例代码和javasrc内置库代码都放到java主目录下:

 1$ tree -L 1
 2.
 3├── README.html
 4├── bin
 5├── conf
 6├── corejava
 7├── include
 8├── javasrc
 9├── jmods
10├── legal
11├── lib
12├── man
13└── release

使用以下命令验证您的Java安装是否成功:

1$ javac --version
2javac 11.0.17
3
4$ java --version
5java 11.0.17 2022-10-18 LTS
6Java(TM) SE Runtime Environment 18.9 (build 11.0.17+10-LTS-269)
7Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.17+10-LTS-269, mixed mode)

5. 运行Welcome程序

您可以尝试运行一个简单的Java程序,比如"Welcome"程序。以下是示例代码和运行步骤:

 1$ cat Welcome.java
 2/**
 3 * This program displays a greeting for the reader.
 4 * @version 1.30 2014-02-27
 5 * @author Cay Horstmann
 6 */
 7public class Welcome
 8{
 9   public static void main(String[] args)
10   {
11      String greeting = "Welcome to Core Java!";
12      System.out.println(greeting);
13      for (int i = 0; i < greeting.length(); i++)
14         System.out.print("=");
15      System.out.println();
16   }
17}
18
19$ ls
20Welcome.java
21$ javac Welcome.java
22$ ls
23Welcome.class Welcome.java
24$ java Welcome
25Welcome to Core Java!
26=====================

这个示例演示了一个简单的Java程序,欢迎您进入Java编程的世界。

二、基础数据类型

Java具有丰富的数据类型,让我们首先了解一些基本的概念:

2.1 一个简单的Java应用程序

在Java中,一个简单的应用程序通常由以下几个要素组成:

  • 使用关键字public作为访问修饰符。
  • 区分大小写。
  • 类名必须以字母开头,遵循大驼峰命名规则。
  • 源代码文件名必须与公共类的名字相同。

运行已编译的程序时,Java虚拟机总是从指定类的main方法开始执行。

2.2 注释

在Java中,注释是非常重要的。它们用于提供关于代码的说明和文档。

Java注释示例:

1// This is a single-line comment.
2
3/*
4 * This is a multi-line comment.
5 * This is the second line.
6 */

2.3 数据类型

Java是一种强类型语言,每个变量都必须声明一个数据类型。

整型

Java提供了不同大小的整数类型,例如intshortlongbyte。每种类型都有不同的取值范围。

  • int:4字节,取值范围为-2147483648到2147483647。
  • short:2字节,取值范围为-32768到32767。
  • long:8字节,取值范围为-9223372036854775808到9223372036854775807。
  • byte:1字节,取值范围为-128到127。

浮点类型

Java提供了floatdouble两种浮点类型。double类型的精度是float的两倍。

浮点类型还有特殊值,如正无穷大、负无穷大和NaN(非数值)。

char类型

char类型用于表示字符,字符字面值需要用单引号括起来。

Unicode和char类型

Java中的char类型使用Unicode编码来表示字符。

boolean类型

boolean类型只有两个值:truefalse。它主要用于条件判断。

2.4 变量与常量

在Java中,变量用于存储数据,常量用于存储不可变的值。

2.5 运算符

Java提供了各种运算符,包括算术运算符、比较运算符、逻辑运算符等。

整数被0除会产生一个异常,浮点数被0除会得到无穷大或者NaN结果; 而且在使用运算符时要小心,避免出现除以零等异常情况。 几个小tips:

  1. 不要在boolean类型与任何数值类型之间进行强制类型转换
  2. 可以在赋值中使用二元运算符,这是一种很简便的简写形式; 比如x += 1等价于x = x + 1
  3. 建议不要在表达式中使用++,因为这样的代码很容易让人困惑,而且会带来烦人的bug
  1public class Variable {
  2    // const 需要全大写
  3    public static final double CM_PER_INCH = 2.331;
  4
  5    // 枚举类型在java 11必须要local不能public
  6    enum Size {SMALL, MEDIUM, LARGE, EXTRA_LARGE}
  7
  8    public static void main(String[] args) {
  9        // 定义变量并且打印变量
 10        double salary;
 11        int vacationDays;
 12        long earthPopulation;
 13        boolean done;
 14        int i, j;
 15        salary = 1.2;
 16        vacationDays = 10;
 17        earthPopulation = 20;
 18        done = true;
 19        double money = 1.15;
 20        var test = 'Z';
 21        System.out.println(salary);
 22        System.out.println(vacationDays);
 23        System.out.println(earthPopulation);
 24        System.out.println(done);
 25        System.out.println(money);
 26        System.out.println(test);
 27
 28        System.out.println(CM_PER_INCH);
 29
 30        Size s = Size.LARGE;
 31        System.out.println(s);
 32        /*
 33        1.2
 34        10
 35        20
 36        true
 37        1.15
 38        Z
 39        2.331
 40        LARGE
 41         */
 42
 43        // 变量的运算符
 44        System.out.println(salary * vacationDays);
 45        System.out.println(money + vacationDays);
 46        System.out.println(money % vacationDays);
 47        /*
 48        12.0
 49        11.15
 50        1.15
 51         */
 52
 53        test();
 54        convert();
 55        rela_operator();
 56    }
 57
 58    public static void test() {
 59        // 变量的数学计算
 60        System.out.println("This is test part");
 61        double x = 4;
 62        double y = Math.sqrt(x);
 63        System.out.println(y);
 64        System.out.println(y * Math.PI);
 65        /*
 66        This is test part
 67        2.0
 68        6.283185307179586
 69         */
 70    }
 71
 72    public static void convert() {
 73        // 变量的类型转换
 74        System.out.println("This is convert part");
 75        int n = 1289;
 76        float y = n;
 77        System.out.println(y);
 78        double x = 9.997;
 79        int nx = (int) Math.round(x);
 80        System.out.println(nx);
 81        /*
 82        This is convert part
 83        1289.0
 84        10
 85         */
 86    }
 87
 88    public static void rela_operator() {
 89        // 变量的关系运算符
 90        System.out.println("This is relation operator part");
 91        int n = 1;
 92        n += 4;
 93        n++;
 94        System.out.println(n);
 95
 96        // 前缀形式会先完成+1,而后缀形式会使用变量原来的值, 所以下面的答案都是14(++只是作用于相连的变量上)
 97        int a = 2 * ++n;
 98        int b = 2 * n++;
 99        System.out.println(n);
100        System.out.println(a);
101        System.out.println(b);
102        System.out.println(n);
103
104        int x = 2;
105        int y = 3;
106        boolean b1 = x != 0 && x > 1;
107        if (b1) {
108            // 三元运算符
109            System.out.println(x < y ? x : y);
110        }
111        /*
112        This is relation operator part
113        6
114        8
115        14
116        14
117        8
118        2
119         */
120    }
121}

2.6 字符串

Java中的字符串是不可变的,意味着一旦创建,就不能修改。字符串类具有许多有用的方法,如equals()isEmpty()等。

 1public class Character {
 2    public static void main(String[] args) {
 3        // 定义字符串
 4        String greeting = "Hello";
 5        // 截取字符串,左闭右开
 6        String s = greeting.substring(0, 3);
 7        System.out.println(s);
 8
 9        // 拼接字符串
10        String expletive = "Expletive";
11        String sum = expletive + s;
12        System.out.println(sum);
13        System.out.println(sum.repeat(3));
14        /*
15        Hel
16        ExpletiveHel
17        ExpletiveHelExpletiveHelExpletiveHel
18         */
19
20        // 一定不要使用 == 运算符检测两个字符串是否相等,因为这个运算符只能确定两个字符串内存是否相同
21        if (s.equals("Hel")) {
22            System.out.println("s is equal to Hel");
23        }
24
25        // 空串和 null
26        if (s != null && s.length() != 0) {
27            System.out.println("s is not empty");
28        }
29
30        // 字符串构建
31        StringBuilder builder = new StringBuilder();
32        builder.append(s);
33        builder.append("siminghahaha");
34        String result = builder.toString();
35        System.out.println(result);
36        /*
37        s is equal to Hel
38        s is not empty
39        Helsiminghahaha
40         */
41    }
42}

2.7 输入输出

Java提供了多种方式进行输入和输出操作。标准输入输出流是常用的方式之一。
不过需要注意,因为输入是可见的,所以不适合从控制台读取密码,密码要放入字符数组中,而不是字符串中;比如Console cons = System.console(); char[] passwd = cons.readPassword();

 1import java.io.*;
 2import java.nio.charset.StandardCharsets;
 3import java.nio.file.Path;
 4import java.util.*;
 5
 6public class FiFOPart {
 7    public static void main(String[] args) throws IOException {
 8        // 获取输入
 9        Scanner in = new Scanner(System.in);
10        System.out.println("What's your name?");
11        String name = in.nextLine();
12
13        Scanner ain = new Scanner(System.in);
14        System.out.println("Your age?");
15        int age = ain.nextInt();
16
17        System.out.println(name + " is " + age + " years old.");
18
19        // 打印输出
20        System.out.printf("%s is %d years old.\n", name, age);
21        /*
22        What's your name?
23        ZhangSiming
24        Your age?
25        26
26        ZhangSiming is 26 years old.
27        ZhangSiming is 26 years old.
28         */
29
30        // 读写文件
31        write_file();
32        read_file();
33    }
34
35    public static void read_file() throws IOException {
36        Scanner in = new Scanner(Path.of("test.txt"), StandardCharsets.UTF_8);
37        while (in.hasNext()) {
38            System.out.println(in.nextLine());
39        }
40    }
41
42    public static void write_file() throws IOException {
43        PrintWriter out = new PrintWriter("test.txt", StandardCharsets.UTF_8);
44        out.println("Hello, world!");
45        out.close();
46    }
47}

2.8 控制流程

控制流程用于控制程序的执行顺序。Java支持条件语句、循环语句、switch语句等。
请注意,在循环中使用for语句时要小心循环变量的范围。
块作用域:在块内部定义的变量只能在块内部使用,块外部不能使用块内部定义的变量。

 1public class Blockcontrol {
 2    public static void main(String[] args) {
 3        // if条件判断
 4        int x = 10;
 5        if (x < 20) {
 6            System.out.println("This is if statement");
 7        } else {
 8            System.out.println("This is else statement");
 9        }
10        // This is if statement
11
12        // while循环
13        int y = 20;
14        while (y < 25) {
15            System.out.println("value of y : " + y);
16            y++;
17        }
18        /*
19        value of y : 20
20        value of y : 21
21        value of y : 22
22        value of y : 23
23        value of y : 24
24         */
25
26        // do和while组合表示, 先执行一次do, 然后判断while条件是否成立, 如果成立则继续执行do, 否则退出循环
27        int z = 30;
28        do {
29            System.out.println("value of z : " + z);
30            z++;
31        } while (z < 25);
32        // value of z : 30
33
34        // for循环
35        for (int i = 0; i < 10; i++) {
36            System.out.println("value of i : " + i);
37        }
38        /*
39        value of i : 0
40        value of i : 1
41        value of i : 2
42        value of i : 3
43        value of i : 4
44        value of i : 5
45        value of i : 6
46        value of i : 7
47        value of i : 8
48        value of i : 9
49         */
50
51        // switch语句,如果没有break语句,会一直执行到break语句或者switch语句结束
52        int a = 6;
53        switch (a) {
54            case 1:
55                System.out.println("value is 1");
56                break;
57            case 2:
58                System.out.println("value is 2");
59                break;
60            case 3:
61                System.out.println("value is 3");
62                break;
63            default:
64                System.out.println("value is not 1, 2 or 3");
65        }
66        // value is not 1, 2 or 3
67
68        // goto语句,跳转到指定的标签处
69        int gt = 20;
70        goto_exam:
71        while (gt < 25) {
72            // 跳过22
73            if (gt == 22) {
74                gt++;
75                continue;
76            }
77            if (gt == 24) {
78                for (int i = 0; i < 10; i++) {
79                    if (i == 5) {
80                        break goto_exam;
81                    }
82                    System.out.println("value of i : " + i);
83                }
84            }
85            System.out.println("value of gt : " + gt);
86            gt++;
87        }
88        /*
89        value of gt : 20
90        value of gt : 21
91        value of gt : 23
92        value of i : 0
93        value of i : 1
94        value of i : 2
95        value of i : 3
96        value of i : 4
97         */
98    }
99}

2.9 大数

对于大数计算,Java提供了BigInteger类,可以处理大整数; 特别大的数比如彩票中奖的几率。

 1import java.math.*;
 2import java.util.*;
 3public class BigIntegerTest {
 4    public static void main(String[] args) {
 5        // 这个程序演示了如何使用大数值BigInteger类, 以及如何使用大数值BigInteger类来计算概率
 6        // 通俗点说就是计算概率的时候, 用int或者long都会溢出, 所以用BigInteger类来计算
 7        Scanner in = new Scanner(System.in);
 8
 9        System.out.println("How many numbers do you need to draw?");
10        int k = in.nextInt();
11
12        System.out.println("What is the highest number you can draw?");
13        int n = in.nextInt();
14
15        BigInteger lotteryOdds = BigInteger.valueOf(1);
16        for (int i = 1; i <= k; i++) {
17            lotteryOdds = lotteryOdds.multiply(BigInteger.valueOf(n - i + 1)).divide(BigInteger.valueOf(i));
18        }
19        System.out.println("Your odds are 1 in " + lotteryOdds + ". Good luck!");
20        /*
21        How many numbers do you need to draw?
22        111
23        What is the highest number you can draw?
24        1111
25        Your odds are 1 in 228162770660494339523962629246420402033509870004236362946558722063262768528532360822215561441512138917100676811940334385612061873122251943150491920143450176. Good luck!
26         */
27    }
28}

2.10 数组

数组是一种用于存储多个相同类型的元素的数据结构。Java中,数组的长度是固定的。
多维数组允许您创建表格或矩阵等数据结构。

注意:

  1. 长度为0的数组和null并不相同
  1import java.util.Arrays;
  2
  3public class ArrayTest {
  4    public static void main(String[] args) {
  5        // 定义并初始化数组的几种方法
  6        int[] a;
  7        a = new int[100];
  8        int[] b = new int[100];
  9        int[] c = {1, 2, 3, 4, 5,};
 10        int[] d = {};
 11
 12        // 打印数组的长度
 13        System.out.println("a's length is " + a.length);
 14        System.out.println("b's length is " + b.length);
 15        // 数组的长度是固定的,不能改变
 16        // c[5] = 6;
 17        System.out.println("c's length is " + c.length);
 18        System.out.println("d's length is " + d.length);
 19        /*
 20        a's length is 100
 21        b's length is 100
 22        c's length is 5
 23        d's length is 0
 24         */
 25
 26        // 访问数组元素的方式1
 27        System.out.println("c[0] is " + c[0]);
 28
 29        for (int i = 0; i < c.length; i++) {
 30            System.out.println("c[" + i + "] is " + c[i]);
 31        }
 32        // 访问数组元素的方式2
 33        for (int i : c) {
 34            System.out.println("i is " + i);
 35        }
 36        // 打印数组元素最快的方式
 37        System.out.println(java.util.Arrays.toString(c));
 38        /*
 39        a's length is 100
 40        b's length is 100
 41        c's length is 5
 42        d's length is 0
 43        c[0] is 1
 44        c[0] is 1
 45        c[1] is 2
 46        c[2] is 3
 47        c[3] is 4
 48        c[4] is 5
 49        i is 1
 50        i is 2
 51        i is 3
 52        i is 4
 53        i is 5
 54        [1, 2, 3, 4, 5]
 55         */
 56
 57        // 数组是引用类型,c和cc指向同一个数组
 58        int[] cc = c;
 59        cc[0] = 9999;
 60        System.out.println("c[0] is " + c[0]);
 61
 62        // 数组的拷贝
 63        int[] cc_long = Arrays.copyOf(c, c.length + 10);
 64        System.out.println("cc_long's length is " + cc_long.length);
 65        System.out.println(java.util.Arrays.toString(c));
 66        System.out.println("cc_long[14] is " + cc_long[14]);
 67
 68        // 快排
 69        Arrays.sort(c);
 70        System.out.println(java.util.Arrays.toString(c));
 71        /*
 72        c[0] is 9999
 73        cc_long's length is 15
 74        [9999, 2, 3, 4, 5]
 75        cc_long[14] is 0
 76        [2, 3, 4, 5, 9999]
 77         */
 78
 79        MultiArray();
 80    }
 81
 82    public static void MultiArray() {
 83        // define a two-dimensional array
 84        double[][] balances;
 85        balances = new double[10][10];
 86        balances[0][0] = 1.0;
 87        balances[0][1] = 2.0;
 88        balances[1][0] = 3.0;
 89        balances[1][1] = 4.0;
 90
 91        double[][] bb = {
 92                {1, 2, 3, 4, 5},
 93                {1, 2, 3, 4, 5}
 94        };
 95
 96        // 打印二维数组
 97        for (double[] row : bb) {
 98            for (double value : row) {
 99                // Print with column and row line
100                System.out.printf("%10.2f", value);
101            }
102        }
103
104        // 或者直接用deepToString打印多维数组
105        System.out.println(Arrays.deepToString(balances));
106        /*
107              1.00      2.00      3.00      4.00      5.00      1.00      2.00      3.00      4.00      5.00[[1.0, 2.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [3.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]]
108         */
109
110        // 打印三角数组
111        int[][] triang = triangular();
112        for (int[] row : triang) {
113            for (int value : row) {
114                // Print with column and row line
115                System.out.printf("%2d", value);
116            }
117            System.out.println();
118        }
119        /*
120         1
121         1 1
122         1 2 1
123         1 3 3 1
124         1 4 6 4 1
125         1 51010 5 1
126         1 6152015 6 1
127         1 721353521 7 1
128         1 82856705628 8 1
129         1 936841261268436 9 1
130         110451202102522101204510 1
131         */
132    }
133
134    public static int[][] triangular() {
135        // 打印杨辉三角形
136        // 杨辉三角形:第n行有n个元素,第n行的第k个元素等于第n-1行的第k-1个元素和第k个元素之和
137        final int NMAX = 10;
138        int[][] odds = new int[NMAX + 1][];
139        for (int n = 0; n <= NMAX; n++) {
140            odds[n] = new int[n + 1];
141        }
142
143        for (int n = 0; n < odds.length; n++) {
144            for (int k = 0; k < odds[n].length; k++) {
145                int lotteryOdds = 1;
146                for (int i = 1; i <= k; i++) {
147                    lotteryOdds = lotteryOdds * (n - i + 1) / i;
148                }
149
150                odds[n][k] = lotteryOdds;
151            }
152        }
153        return odds;
154    }
155}

三、对象与类

算法+数据结构=程序, 算法是第一位的,数据结构是第二位的,这就明确地表述了程序员的工作方式。

3.1 面向对象程序设计概述

面向对象程序设计是一种重要的编程范式,它强调程序的结构是由对象和对象之间的交互所定义的。
类是构造对象的模板,对象是类的实例。类包含数据字段和方法,用于描述对象的属性和行为。

注意:

  1. Java编写的所有代码都位于某个类里面
  2. 对象的数据称为实例字段,操作数据的过程称为方法
  3. 程序只能通过对象的方法与对象进行数据交互,封装给对象赋予了"黑盒"特征
  4. 所有的类都有一个共同的超类Object,通过扩展一个类来建立另一个类的过程称为继承

对象的三个主要特性:

  1. 对象的行为:可以对对象完成哪些操作,或者可以对对象应用哪些方法
  2. 对象的状态:当调用那些方法时,对象会如何响应
  3. 对象的标识:如何区分具有相同行为与状态的不同对象

类之间最常见的关系:

  1. 依赖
  2. 聚合
  3. 继承

3.2 使用预定义类

Java提供了许多预定义类,您可以直接使用它们来完成各种任务。例如,LocalDate类用于处理日期。
在使用预定义类时,通常需要创建类的实例,并调用其方法来执行操作。

注意:

  1. Java中,使用构造器(或称构造函数)构造新实例,构造器的名字应该与类名相同,构造出来的对象需要存到一个变量中
  2. 在Java中,任何对象变量的值都是对存储在另外一个地方的某个对象的引用,new操作符的返回值也是一个引用; null表示这个对象变量目前没有引用任何对象, 比如Date deadline = new Date;
  3. 下面的程序使用预定义类"LocalDate"打印了当月的日历,并且用星号标识了所在的具体某一天(Java类库中的LocalDate类:Date类使用距离一个固定时间点的毫秒数表示,这个时间点就是所谓的纪元(ephch), 他是UTC时间1970年1月1日00:00:00)。
 1/*
 2Mon Tue Wed Thu Fri Sat Sun
 3      1   2   3   4   5   6
 4  7   8   9  10  11  12  13
 5 14  15  16  17  18  19  20
 6 21* 22  23  24  25  26  27
 7 28  29  30  31
 8 */
 9
10import java.time.*;
11
12public class CalendarTest {
13    public static void main(String[] args) {
14        // java.time.LocalDate
15        LocalDate date = LocalDate.now();
16        int month = date.getMonthValue();
17        int today = date.getDayOfMonth();
18        // 由当前日期算出月初第一天日期
19        date = date.minusDays(today - 1);
20        DayOfWeek weekday = date.getDayOfWeek();
21        int value = weekday.getValue(); // 1 = Monday, ... 7 = Sunday
22        System.out.println("Mon Tue Wed Thu Fri Sat Sun");
23
24        // 月初打印多少个空格取决于月初第一天是星期几
25        for (int i = 1; i < value; i++)
26            System.out.print("    ");
27
28        // 打印每一天,直到月末,根据是否为周一判断是否换行;并且在当前日期打出*标识
29        while (date.getMonthValue() == month) {
30            System.out.printf("%3d", date.getDayOfMonth());
31            if (date.getDayOfMonth() == today)
32                System.out.print("*");
33            else
34                System.out.print(" ");
35            date = date.plusDays(1);
36            if (date.getDayOfWeek().getValue() == 1)
37                System.out.println();
38        }
39        if (date.getDayOfWeek().getValue() != 1)
40            System.out.println();
41    }
42}

3.3 用户自定义类

您可以创建自己的类来建模和解决特定问题。自定义类通常包括字段、构造器和方法。
在类中,使用private关键字来保护数据字段,同时提供公共的访问器和更改器方法。
构造器用于初始化对象的状态,可以有多个构造器以适应不同的需求。

Java中最简单的类定义形式为:

 1   class ClassName
 2   {
 3       field1;
 4       field2;
 5       ...
 6       constructor1;
 7       constructor2;
 8       ...
 9       method1;
10       method2;
11       ...
12   }

关于构造器:

  1. 构造器与类同名
  2. 每个类可以有一个以上的构造器
  3. 构造器可以有0个、1个或者多个参数
  4. 构造器没有返回值
  5. 构造器总是伴随着new操作符一起调用

注意:

  1. 关键字public意味着任何类的任何方法都可以调用这些方法; private确保只有Employee类自身的方法能够访问这些实例字段
  2. 不要在构造器中定义与实例字段同名的局部变量
  3. 不要编写返回可变对象引用的访问器方法,因为如果是引用的话,在外界也可以更改,就违背了封装的设计原则(这个对象只能在方法中修改返回), 会让程序变得不可控
  4. final修饰符(不可修改,并且确保每一个构造器执行之后这个字段已经设置):如果一个类不希望被继承,就用final修饰符来修饰这个类,如果一个方法不希望被重写,就用final修饰符来修饰这个方法,如果一个变量不希望被修改,就用final修饰符来修饰这个变量
  5. var声明局部变量,Java10以后才会有
 1/*
 2name=Carl Cracker,salary=78750.0,hireDay=1987-12-15
 3name=Harry Hacker,salary=52500.0,hireDay=1989-10-01
 4name=Tony Tester,salary=42000.0,hireDay=1990-03-15
 5 */
 6
 7import java.time.*;
 8
 9public class EmployeeTest {
10    public static void main(String[] args) {
11        // 创建三个Employee对象
12        Employee[] staff = new Employee[3];
13        staff[0] = new Employee("Carl Cracker", 75000, 1987, 12, 15);
14        staff[1] = new Employee("Harry Hacker", 50000, 1989, 10, 1);
15        staff[2] = new Employee("Tony Tester", 40000, 1990, 3, 15);
16        // 给每一个Employee对象加薪5%
17        for (Employee e : staff)
18            e.raiseSalary2(5);
19        // 打印出每一个Employee对象的信息
20        for (Employee e : staff)
21            System.out.println("name=" + e.getName() + ",salary=" + e.getSalary() + ",hireDay=" + e.getHireDay());
22    }
23}
24
25class Employee {
26    private final String name;
27    private double salary;
28    private LocalDate hireDay;
29
30    public Employee(String n, double s, int year, int month, int day) {
31        name = n;
32        salary = s;
33        hireDay = LocalDate.of(year, month, day);
34    }
35
36    public String getName() {
37        return name;
38    }
39
40    public double getSalary() {
41        return salary;
42    }
43
44    public LocalDate getHireDay() {
45        return hireDay;
46    }
47
48    public void raiseSalary(double byPercent) {
49        double raise = salary * byPercent / 100;
50        // 不能这样操作,因为name被设置成了final:name += "test";
51        salary += raise;
52    }
53
54    public void raiseSalary2(double byPercent) {
55        double raise = this.salary * byPercent / 100;
56        this.salary += raise;
57    }
58}

3.4 静态域与静态方法

静态域和静态方法属于类而不是对象,它们可以被所有对象共享; 换言之如果将一个字段定义为static,每个类只有一个这样的字段, 并且从这个类实例化出来的每一个对象都共享这个字段的值。
静态常量: 是不可修改的值,通常使用public static final修饰, 比如public static final double PI = 3.1415926;
静态方法: 是没有隐式参数(是没有this参数的方法)的方法,可以直接通过类名调用, 无需实例化对象,比如Math.pow(2, 3);
main方法也是静态方法,实际上启动程序的时候还没有任何对象,静态的main方法将执行并构造程序所需要的对象

 1/*
 2name=Tom,id=1,salary=40000.0
 3name=Dick,id=2,salary=60000.0
 4name=Harry,id=3,salary=65000.0
 5Next available id=4
 6 */
 7public class StaticTest {
 8    public static void main(String[] args) {
 9        EmployeeNew[] staff = new EmployeeNew[3];
10        staff[0] = new EmployeeNew("Tom", 40000);
11        staff[1] = new EmployeeNew("Dick", 60000);
12        staff[2] = new EmployeeNew("Harry", 65000);
13
14        for (EmployeeNew e : staff) {
15            e.setId();
16            System.out.println("name=" + e.getName() + ",id=" + e.getId() + ",salary=" + e.getSalary());
17        }
18        int n = EmployeeNew.getNextId();
19        System.out.println("Next available id=" + n);
20    }
21}
22
23class EmployeeNew {
24    // 所有这个类实例化出来的对象都共享这个nextId
25    private static int nextId = 1;
26    private String name;
27    private double salary;
28    private int id;
29
30    public EmployeeNew(String n, double s) {
31        name = n;
32        salary = s;
33        id = 0;
34    }
35
36    public String getName() {
37        return name;
38    }
39
40    public double getSalary() {
41        return salary;
42    }
43
44    public int getId() {
45        return id;
46    }
47
48    public void setId() {
49        id = nextId;
50        nextId++;
51    }
52
53    public static int getNextId() {
54        return nextId;
55    }
56
57    public static void main(String[] args) {
58        EmployeeNew e = new EmployeeNew("Harry", 50000);
59        System.out.println(e.getName() + " " + e.getSalary());
60    }
61}

3.5 方法参数

程序语言设计汇总关于将参数传递给方法:按值调用表示方法接收的是调用者提供的值,而按引用调用表示方法接收的是调用者提供的变量地址
Java程序设计语言总是采用按值调用,也就是说,方法得到的是所有参数值的一个拷贝,也就是说,方法不能修改传递给它的任何参数变量的内容

Java中对方法参数能做什么和不能做什么

  1. 方法不能修改基本数据类型的参数
  2. 方法可以改变对象参数的状态
  3. 方法不能让一个对象参数引用一个新的对象

3.6 对象构造

重载:有些类有多个构造器,这种情况下,编译器会挑选出一个"最近匹配"的构造器,如果没有找到任何匹配的构造器,编译器就会报错
默认字段初始化:如果再构造器中没有显示地给字段赋值,那么就会自动地将他们初始化为0、false或者null
显式字段初始化:比如

1private String name = "";
2private double salary;
3private LocalDate hireDay;

调用另一个构造器:关键字this只是一个方法的隐式参数,当然还可以在一个构造器中用this调用另一个构造器,这个调用必须是构造器的第一个语句,例如:

1public Employee(double s)
2{
3    this("Employee #" + nextId, s);
4}

注意:

  1. 由于Java会完成自动的垃圾回收,不需要人工回收内存,所以Java不支持析构器;警告不要使用finalize方法来完成清理
 1/*
 2name=Harry,id=9794,salary=40000.0
 3name=Employee #9795,id=9795,salary=60000.0
 4name=,id=9796,salary=0.0
 5 */
 6
 7import java.util.*;
 8
 9public class ConstructorTest {
10    public static void main(String[] args) {
11        // 创建一个长度为3的EmployeeCt数组
12        EmployeeCt[] staff = new EmployeeCt[3];
13        // 重载构造器,创建三个EmployeeCt对象
14        staff[0] = new EmployeeCt("Harry", 40000);
15        staff[1] = new EmployeeCt(60000);
16        staff[2] = new EmployeeCt();
17
18        // 打印对象信息
19        for (EmployeeCt e : staff) {
20            System.out.println("name=" + e.getName() + ",id=" + e.getId() + ",salary=" + e.getSalary());
21        }
22    }
23}
24
25class EmployeeCt {
26    private static int nextId;
27    private int id;
28    private String name = ""; // instance field initialization
29    private double salary;
30
31    // static initialization block
32    // 所有的类,不管是不是实例化,都只执行最开始的一次
33    static {
34        Random generator = new Random();
35        // set nextId to a random number between 0 and 9999
36        nextId = generator.nextInt(10000);
37    }
38
39    // object initialization block
40    {
41        id = nextId;
42        nextId++;
43    }
44
45    // 下面是三个重载的构造器
46    public EmployeeCt(String n, double s) {
47        name = n;
48        salary = s;
49    }
50
51    public EmployeeCt(double s) {
52        // calls the Employee(String, double) constructor
53        this("Employee #" + nextId, s);
54    }
55
56    public EmployeeCt() {
57        // name initialized to "" --see above
58        // salary not explicitly set--initialized to 0
59        // id initialized in initialization block
60    }
61
62    public String getName() {
63        return name;
64    }
65
66    public double getSalary() {
67        return salary;
68    }
69
70    public int getId() {
71        return id;
72    }
73}

3.7 包

使用包的主要原因是确保类名的唯一性;类的导入示例:

1import java.util.*;
2import java.sql.*;

有一种import语句允许导入静态方法和静态字段,而不只是类:

1import static java.lang.System.*;

注意:

  1. 如果没有在源文件中放置package语句,这个源文件中的类就属于无名包
  2. 编译器处理文件(带有文件分隔符和扩展名.java的文件), 而Java解释器(虚拟机)执行类(带有点号和.class扩展名的文件)
  3. 标记为public的部分可以由任意类使用,标记为private的部分只能由定义他们的类使用;如果没有指定访问修饰符,那么这个类只能被同一个包中的其他类访问
  4. 类存储在文件系统的子目录中,类的路径必须与包名匹配
  5. 类文件也可以存储在JAR(Java归档)文件中,在一个JAR文件中,可以包含多个压缩形式的类文件和子目录
 1$ tree .
 2.
 3├── TestPackage.java
 4├── com
 5│   └── horstmann
 6│       └── corejava
 7│           └── TestClass.java
 8
 9$ cat TestPackage.java
10import com.horstmann.corejava.*;
11
12public class TestPackage {
13    public static void main(String[] args) {
14        // 不需要使用全限定名,因为有import语句
15        TestClass.main(args);
16    }
17}
18
19$ cat com/horstmann/corejava/TestClass.java
20package com.horstmann.corejava;
21
22public class TestClass {
23    public static void main(String[] args) {
24        System.out.println("Hello, World!");
25    }
26}

3.8 JAR文件

JAR文件是Java应用程序或库的打包方式,它将所有相关的类和资源打包到一个文件中,方便分发和部署。
JAR的清单文件:清单文件是一个包含应用程序或者库的主清单属性的特殊文件,清单文件的名字是META-INF/MANIFEST.MF,清单文件的第一行必须是Manifest-Version: 1.0,清单文件中的每一行都是一个属性-值对,属性名和值之间用冒号分隔,属性名区分大小写,属性值前后可以有空格,属性值可以跨越多行,如果以空行开头,那么它就是前一个属性值的一部分,清单文件的最后一行必须以换行符结束

3.9 文档注释

类注释:类注释必须放在import语句之后,类定义之前,比如:

1/**
2* @version 1.01 2004-02-21
3* @author
4*/
5public class ClassName
6{
7   . . .
8}

方法注释:每一个方法注释必须放在所描述的方法之前:

  1. @param:参数的名字和说明
  2. @return:返回值的说明
  3. @exception:抛出的异常

比如:

 1/**
 2* Computes the square root of a number.
 3* @param x a nonnegative number
 4* @return the square root of x
 5* @throws IllegalArgumentException if x is negative
 6*/
 7public static double sqrt(double x)
 8{
 9   if (x < 0) throw new IllegalArgumentException("x must be nonnegative");
10   return Math.sqrt(x);
11}

字段注释:只需要对公共字段建立

1/**
2* The name of the employee.
3*/
4private String name;

通用注释:

  1. @version:版本号
  2. @since:自从哪个版本
  3. @see:参考
  4. @deprecated:不建议使用

3.10 类设计技巧

设计良好的类是编程的基础,以下是一些类设计的技巧:

  • 保证数据私有性。
  • 对数据进行初始化。
  • 不要过多使用基本数据类型: 如果一个类中有很多基本类型,那么就应该考虑将它分解成多个类
  • 不要为每个字段都提供独立的访问器和更改器。
  • 将职责过多的类进行分解。
  • 类名和方法名应该能够清晰地反映其职责。