【Java入门系列】Java基础入门 --- 安装,数据类型,类
Overview
一、Java环境安装
Java是一门强大的编程语言,首先需要在您的计算机上安装Java开发环境。以下是安装Java环境的步骤:
1. 选择Java版本
对于初学者,建议选择Java 8或Java 11版本。您可以从Oracle官方网站下载这些版本。
- Java 8下载链接:https://www.oracle.com/java/technologies/javase/jdk8-archive-downloads.html
- Java 11下载链接:https://www.oracle.com/java/technologies/javase/jdk11-archive-downloads.html
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提供了不同大小的整数类型,例如int
、short
、long
和byte
。每种类型都有不同的取值范围。
int
:4字节,取值范围为-2147483648到2147483647。short
:2字节,取值范围为-32768到32767。long
:8字节,取值范围为-9223372036854775808到9223372036854775807。byte
:1字节,取值范围为-128到127。
浮点类型
Java提供了float
和double
两种浮点类型。double
类型的精度是float
的两倍。
浮点类型还有特殊值,如正无穷大、负无穷大和NaN(非数值)。
char类型
char
类型用于表示字符,字符字面值需要用单引号括起来。
Unicode和char类型
Java中的char
类型使用Unicode编码来表示字符。
boolean类型
boolean
类型只有两个值:true
和false
。它主要用于条件判断。
2.4 变量与常量
在Java中,变量用于存储数据,常量用于存储不可变的值。
2.5 运算符
Java提供了各种运算符,包括算术运算符、比较运算符、逻辑运算符等。
整数被0除会产生一个异常,浮点数被0除会得到无穷大或者NaN结果; 而且在使用运算符时要小心,避免出现除以零等异常情况。 几个小tips:
- 不要在boolean类型与任何数值类型之间进行强制类型转换
- 可以在赋值中使用二元运算符,这是一种很简便的简写形式; 比如
x += 1
等价于x = x + 1
- 建议不要在表达式中使用++,因为这样的代码很容易让人困惑,而且会带来烦人的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中,数组的长度是固定的。
多维数组允许您创建表格或矩阵等数据结构。
注意:
- 长度为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 面向对象程序设计概述
面向对象程序设计是一种重要的编程范式,它强调程序的结构是由对象和对象之间的交互所定义的。
类是构造对象的模板,对象是类的实例。类包含数据字段和方法,用于描述对象的属性和行为。
注意:
- Java编写的所有代码都位于某个类里面
- 对象的数据称为实例字段,操作数据的过程称为方法
- 程序只能通过对象的方法与对象进行数据交互,封装给对象赋予了"黑盒"特征
- 所有的类都有一个共同的超类Object,通过扩展一个类来建立另一个类的过程称为继承
对象的三个主要特性:
- 对象的行为:可以对对象完成哪些操作,或者可以对对象应用哪些方法
- 对象的状态:当调用那些方法时,对象会如何响应
- 对象的标识:如何区分具有相同行为与状态的不同对象
类之间最常见的关系:
- 依赖
- 聚合
- 继承
3.2 使用预定义类
Java提供了许多预定义类,您可以直接使用它们来完成各种任务。例如,LocalDate
类用于处理日期。
在使用预定义类时,通常需要创建类的实例,并调用其方法来执行操作。
注意:
- Java中,使用构造器(或称构造函数)构造新实例,构造器的名字应该与类名相同,构造出来的对象需要存到一个变量中
- 在Java中,任何对象变量的值都是对存储在另外一个地方的某个对象的引用,new操作符的返回值也是一个引用; null表示这个对象变量目前没有引用任何对象, 比如Date deadline = new Date;
- 下面的程序使用预定义类"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 }
关于构造器:
- 构造器与类同名
- 每个类可以有一个以上的构造器
- 构造器可以有0个、1个或者多个参数
- 构造器没有返回值
- 构造器总是伴随着new操作符一起调用
注意:
- 关键字public意味着任何类的任何方法都可以调用这些方法; private确保只有Employee类自身的方法能够访问这些实例字段
- 不要在构造器中定义与实例字段同名的局部变量
- 不要编写返回可变对象引用的访问器方法,因为如果是引用的话,在外界也可以更改,就违背了封装的设计原则(这个对象只能在方法中修改返回), 会让程序变得不可控
- final修饰符(不可修改,并且确保每一个构造器执行之后这个字段已经设置):如果一个类不希望被继承,就用final修饰符来修饰这个类,如果一个方法不希望被重写,就用final修饰符来修饰这个方法,如果一个变量不希望被修改,就用final修饰符来修饰这个变量
- 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中对方法参数能做什么和不能做什么
- 方法不能修改基本数据类型的参数
- 方法可以改变对象参数的状态
- 方法不能让一个对象参数引用一个新的对象
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}
注意:
- 由于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.*;
注意:
- 如果没有在源文件中放置package语句,这个源文件中的类就属于无名包
- 编译器处理文件(带有文件分隔符和扩展名.java的文件), 而Java解释器(虚拟机)执行类(带有点号和.class扩展名的文件)
- 标记为public的部分可以由任意类使用,标记为private的部分只能由定义他们的类使用;如果没有指定访问修饰符,那么这个类只能被同一个包中的其他类访问
- 类存储在文件系统的子目录中,类的路径必须与包名匹配
- 类文件也可以存储在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}
方法注释:每一个方法注释必须放在所描述的方法之前:
- @param:参数的名字和说明
- @return:返回值的说明
- @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;
通用注释:
- @version:版本号
- @since:自从哪个版本
- @see:参考
- @deprecated:不建议使用
3.10 类设计技巧
设计良好的类是编程的基础,以下是一些类设计的技巧:
- 保证数据私有性。
- 对数据进行初始化。
- 不要过多使用基本数据类型: 如果一个类中有很多基本类型,那么就应该考虑将它分解成多个类
- 不要为每个字段都提供独立的访问器和更改器。
- 将职责过多的类进行分解。
- 类名和方法名应该能够清晰地反映其职责。