读后笔记 -- Java核心技术(第11版 卷I ) Chapter4 对象与类
4.1 OOP 概述
4.1.1. 几个重要概念
- 类 class:构造对象的模板或蓝图
- 实例 instance:类构造(construct)的对象
- 封装 encapsulation:将数据和行为组合在一个包中,并对对象的使用者隐藏了数据的实现方式
- 实例域 instance field:对象中的数据
- 方法 method:操纵数据的过程
4.1.2. 对象的三个主要特征
- 行为(behavior) — 对对象完成哪些操作 或 应用哪些方法?
- 状态(state ) — 当调用那些方法时,对象如何响应?
- 标识(identity ) — 如何区分具有相同行为与状态的不同对象?
4.1.4. 类之间的关系
- 依赖("uses-a"):类 A的方法 使用 / 操纵 类 B 的对象。应尽可能减少类之间的耦合
- 聚合("has-a"): 类 A的对象 包含 类 B 的对象
- 继承("is-a"): 如类 B 继承 类 A,则 类 B 包含了 A 的所有方法,还有一些额外的功能
4.2 使用预定义类
4.2.1. 对象与对象变量
![](https://img2022.cnblogs.com/blog/1251574/202204/1251574-20220427170808815-467779465.png)
Date deadline = new Date(); // 有两个部分: 1) new Date() 构造了一个 Date 类型的对象, 并且它的值是对新创建对象的引用; 2) 这个引用存储在变量 deadline 中;
- 可以显式地将对象设置为 null; 表明这个对象变量没有引用任何对象;
- 值为 null 的对象不能应用于方法上,会产生错误。 如:Date birthday = null; String s = birthday.toString(); // runtime error!
- 局部变量不会自动地初始化为 null,必须通过调用 new 或将他们设置为 null 进行初始化;
Date deadline = null;
4.2.1/2. Date 类和 LocalDate 类
- Date 类: 表示时间点
- LocalDate:日历表示法
4.2.3. 访问器方法 和 构造器方法
// 1. Date 类: 表示时间点 Date birthday = new Date(); // Tue May 03 15:51:22 CST 2022 System.out.println(birthday.toString()); // 2. LocalDate 类 (Use factory methods to create instances): 日历表示法,创建日期的2个方法:
// 2.1 : 静态工厂方法 LocalDate.now();
// 2.2 : 指定某个日期,如 LocalDate curDate = LocateDate.of(2022, 5, 3) LocalDate curDate = LocalDate.now(); int year = curDate.getYear(); int month = curDate.getMonthValue(); int day = curDate.getDayOfMonth(); // 2022,5,3 System.out.println(year + "," + month + "," + day); // 2.3. plusDays() 重新生成一个新的 LocalDate 对象,然后把新对象赋给 newDate 变量 《==== 访问器方法
// LocalDate methods: LocalDate newDate = curDate.plusDays(100); int newYear = newDate.getYear(); int newMonth = newDate.getMonthValue(); int newDay = newDate.getDayOfMonth(); // 2022,8,11 System.out.println(newYear + "," + newMonth + "," + newDay); // 4. 更改器方法:someDay 的值在 add 方法后,随即被更改。其 月份的 0 对应 1月,依次类推。该方法现很少使用 => 被 java.time 类代替 GregorianCalendar someDay = new GregorianCalendar(2022, 5, 3); someDay.add(Calendar.DAY_OF_MONTH, 100); // 《==== 更改器方法 int year2 = someDay.get(Calendar.YEAR); int month2 = someDay.get(Calendar.MONTH); int day2 = someDay.get(Calendar.DAY_OF_MONTH); // 2022,8,11 System.out.println(year2 + "," + month2 + "," + day2);
- LocalDate 类:类的所有方法都是 访问器方法
- GregorianCalender 类: GregorianCalender.add() => 更改器方法; GregorianCanlender.get() => 访问器方法
4.3 用户自定义类
# 最简单的类定义形式: class ClassName { field1 ... constructor1 ... method1 ... }
4.3.4 构造器(构造函数)
- 与类同名
- 每个类可以有一个以上的构造器(也叫 重载)
- 可以有 0~N 个参数
- 没有返回值
- 总是伴随着 new 操作一起调用。不能对一个已经存在的对象调用构造器来重设实例字段
- !不要在构造器中定义与实例字段重名的局部变量
4.3.5 var 声明局部变量
- var 关键字只能用于方法中的局部变量。参数和字段的类型必须声明;
- 不会对数值类型使用 var,如 int, long 或 double;
4.3.7 隐式参数 与 显式参数
- 隐式参数:调用这个方法的对象
- 显式参数:方法的()内的参数
// case1: Employee[] staff = new Employee[2]; staff[0] = new Employee("Carl Cracker", 75000, 1987, 12, 5); staff[1] = new Employee("Harry Hacker", 50000, 1989, 10, 1); for (Employee e : staff) { e.raiseSalary(5); // raiseSalary 有两个参数:1)隐式参数:方法名前的类对象 e; 2)显式参数:5(数值); } // case2: public void raiseSalary(double byPercent) { double raise = salary * byPercent / 100; // 这里的 salary 完整的是 this.salary,即 double raise = this.salary * byPercent / 100; 每一个方法中,关键字 this 表示隐式参数 salary += raise; }
4.3.8. 封装的优点
一般情况下,获得或设置实例字段的值,需要提供三项内容:
- 一个私有的数据域 private String name;
- 一个公有的域访问器方法 public String getName();
- 一个公有的域更改器方法 public String setName();
** 如果需要返回一个可变数据字段的副本,应使用 clone()
4.3.9. 基于类的访问权限
- 一个方法可以访问所属类的所有对象的私有数据
class Employee { ... public boolean equals(Employee other) { return name.equals(other.name); } } // 调用如下: if(harry.equals(boss))... // 该方法可以访问 harry 的私有字段,还可以访问 boss 的私有字段。原因:boss 也是 Employee 类型的对象
4.3.10 私有方法
1)数据域:所有的 private;
2)方法的类型:
- (1)大部分都是 public
- (2)私有方法 private:
- (1)辅助方法的应用场景:与当前的实现机制非常紧密、或需要一个特别的协议 或 调用次序;
- (2)好处:当改用其他的方法来实现时,私有方法即可删除,而公有方法可能被其他代码依赖;
4.3.11. final 实例字段
- 1)不可变的类(如 String) 声明的变量 是不可变的,如 private final String name;
- 2)可变类(如 StringBuilder)声明的变量 还是会变的,如:
---- 不可变类 声明的 final 变量 ------ private final String user = "root"; private final String password = "jun19931129"; private final String driver = "com.mysql.jdbc.Driver"; private final String url = "jdbc:mysql://localhost:3306/ant";
---- 可变类 声明的 final 变量 ----- private final StringBuilder evaluations; // final 不表示这个变量指示的对象是不可变,只是 final 对象不会替换为另一个不同的对象。evaluations 在下面的第三行被改变了内容,但其对象引用没有变化 public Employee() { evaluations = new StringBuilder(); ... } public void giveGoldStar() {evaluations.append("Gold star!\n"); }
4.4 静态字段与静态方法
4.4.1. 静态字段 static(一般 IDE 中,用斜体表示)
class Employee {
// 静态字段 nextId(大多数OOP程序中,也称类字段) private static int nextld = 1;
// 实例字段 private int id; .... } public void setld() { id = nextld; nextld++; } Empolyee harry = new Employee(); harry.setld(); // => 实际的操作是: // harry.id = Employee.nextld; // Employee.nextId++; Empolyee james = new Employee(); james.setld(); // => 实际的操作是: // james.id = Employee.nextld; // Employee.nextId++;
- 针对每一个字段,每个类仅一个 static,即该类所有实例 共享 这个字段。它属于类,不属于任何独立的对象;
- 每个对象对于所有的实例字段,都有自己的一份拷贝。即如果有 100 个 Employee 类的对象,就有 100个实例字段 id;
// 1) 静态常量(一般 大写),共享常量 public static final double PI = 3.14; // 2)另一个静态常量 System.out,它在 System 类中声明 public final class System { ... public static final PrintStream out = null; ... }
4.4.3. 静态方法
- 不在对象上执行的方法,如 Math.pow(x, a)。【使用场景:只使用传入方法的参数,而不处理任何对象】
- 静态方法是没有 this 参数的方法
- (下例)静态方法仅能访问静态字段。所以,可访问静态字段 nextId,不能访问 实例字段 id
public class Employee { private static int nextId =1; private int id; public Employee(int id) { this.id = id; } public static int getNextId() { return nextId; } public static void setNextId(int nextId) { Employee.nextId = nextId; } public int getId() { return id; } public void setId(int id) { this.id = id; } }
// (接上例)访问静态方法:类.静态方法 int n = Employee.getNextId(); // 如果省略静态方法的 static,则需要下面方式访问 => 不建议,因为 getNextId() 与 harry 没有任何关系 Employee harry = new Employee("Harry", 50000); System.out.println(harry.getNextId());
两种情况下使用静态方法:
- 方法不需要访问对象状态,其所需参数都是通过显式参数提供(如:Math.pow)
- 方法只需要访问类的静态字段(如:Employee.getNextld)
4.4.4. (静态方法的另外一个用途)工厂方法
1)如 LocalDate(LocalDate.now(), LocalDate.of()),NumberFormat 的类 使用静态工厂方法来构造对象;
2)main 方法是个静态方法,无需使用对象调用;
4.5 方法参数
1. 常见的参数传递给方法的方式:
- 1)按值调用(C、C++、Pascal、Java):方法接收的是调用者提供的值的副本,不能修改变量的内容;
- 2)按引用调用(C++、Pascal):方法接收的是调用者提供的地址;
2. 方法参数有两种类型:
- 基本数据类型:值不能被修改;
- 对象引用: 对象引用作为参数。方法得到的是对象引用的拷贝,对象引用及其他的拷贝同时引用同一个对象
// 1) 值传递 public static void tripleValue(double x) { x = 3 * x; } // 调用: double percent = 10; tripleValue(percent); // 结果: 1)在方法执行中 x = 30; 2)执行完后,percent 还是 10; // 2) 对象引用传递 => 副本和参数引用同一对象,修改后,两者都发生改变 public static void tripleSalary(Employee x) { x.raiseSalary(200); } // 调用: harry = new Employee("Harry Hacker", 30000); tripleSalary(harry); // 结果: harry 的 salary 修改成 90000 // 3)对象引用传递并互换 => 参数未发生改变,仅副本 swap public static void swap(Employee x, Employee y) { Employee temp = x; x = y; y = temp; } // 调用: Employee a = new Employee("Alice", 30000); Employee b = new Employee("Bob", 40000); swap(a, b); // 结果: a, b 还是引用之前的对象
总结:
- 1)方法 不能修改 一个基本数据类型的参数(即数值型或布尔型)【1)的意思是:引用的对象没有改变,而且是基本类型,所以其对象的值也不会改变】
- 2)方法 可以改变 一个对象参数的状态(对象引用)
- 3)方法 不能 让对象参数引用一个新的对象(交换对象)【2)、3)的意思是:对象引用时,引用的对象没有改变,但可用改变原对象的值】
所以,最终可以说,数字 或 对象(object reference are passed by value. If objects were passed by reference, you could swap them, but actually not) 都是按值传递的。
4.6 对象构造
A class can have more than one constructor:
StringBuilder messages = new StringBuilder(); // 无参构造器 StringBuilder todoList = new StringBuilder("To do:\n"); // String 类型参数的构造器
4.6.1. 重载
- 重载(overloading) :在一个类里面,方法名字相同,参数不同。返回类型可相同也可不同;
- 每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表; Name + parameter types = Method Signature;
- the return type is not a part of method signature. 即, 不能有两个名字相同、参数相同但不同返回类型的方法;
- 最常用的是 构造器的重载;
** 重写(Overwrite)与重载(Overload)的区别,更多详细解释参看:https://www.runoob.com/java/java-override-overload.html
4.6.2. 默认字段初始化
- 类中的字段:一般,构造器需要显式初始化,否则取默认值:数值:0,boolean:false,对象引用:null(建议还是显式初始化);
- 局部变量:必须初始化;
4.6.3. 无参数的构造器
- 仅当类中没有任何构造器时, 系统才会提供一个无参数构造器。且所有的实例字段设置为默认值(If a class has at least one constructor, the no-argument constructor is not provided.)
- If a class has at least one constructor, the no-argument constructor is not provided.
- 如类中提供了至少一个构造器,但没有无参数构造器,构造对象时如果不提供参数,则不合法。
// 1. 无参数构造函数:仅当类中无构造器时,系统提供如下构造器 public Employee() { name = ""; salary = 0; hireDay = LocalDate.now(); } // case 1:编写类,但无构造函数 => 此时系统将加载上述无参构造器 public class Employee { private String name; private double salary; // 此处省略构造函数 public String getName() { return name; } ..... } // case 2:类,1)提供了构造函数,但没有无参构造器 2)构造对象时未提供参数 public class Employee { private String name; private double salary; public Employee(String name, double salary)
public String getName() { return name; } ... } // 下面调用时报错 Employee test = new Employee();
- 当一个类的所有构造器都希望把相同的值赋予某个特定的实例字段时。处理方法:在执行构造器之前,先执行赋值操作。
class Employee { private String name = ""; // 所有构造器的实例字段 name 都将被初始化为 "" ... }
4.6.5 参数名
单字母的参数一般很难理解其意思,下面2种方式可以较好的处理:
// case 1:参数变量 同 构造器的变量 名相同,构造器中使用 this(隐式参数)访问实例字段。IdeaJ 默认使用该方式 public Employee(String name, double salary) { this.name = name; // 前面的 this.name 指实例字段,后面的 name 是参数 this.salary= salary; } // case 2:构造器参数加一前缀,如“a” public Employee(String aName, double aSalary) { name = aName; salary= aSalary; }
将公共的初始化块抽取出来,有2种方式:
- 1. 公共的部分放在一个公共的构造器,其他构造器通过 this() 调用 -- 4.6.6
- 2. 公共的部分放在初始化块,这样每个构造对象时都会调用 -- 4.6.7
4.6.6. 调用另一个构造器
如果构造器的第一个语句形如 this(...), 这个构造器将调用同一个类的另一个构造器。
public Employee(double s) { // calls Employee(String, double) in 4.6.5 this("Employee #" + nextld, s); // 调用另外一个构造器,必须在构造器的第一个语句 nextld++; }
采用这种方式使用 this 关键字非常有用, 这样对公共的构造器代码部分只编写一次即可。
4.6.7. 初始化块
1)初始化数据字段的方法:
- (1)构造器中设置值;
- (2)声明中赋值;
- (3)初始化块(不常见),通常将公共的初始化代码放在构造器中。【初始化块=>对象构造时执行, 静态初始化块=> 类加载时执行】;
2)调用构造器的具体处理步骤:
- (1)所有数据域被初始化为默认值(0、false 或 null);
- (2)按在类声明中出现的次序, 依次执行所有域初始化语句和初始化块;
- (3)如果构造器第一行调用了第二个构造器,则执行第二个构造器主体;
- (4)执行这个构造器的主体;
3)如果对类的静态变量进行初始化的代码比较复杂,那么可以使用静态的初始化块。
----- Employee.java -------- package constructtest; import java.util.Random; public class Employee { private static int nextId; private int id; private String name = ""; // instance field initialization 初始化方式二(实例字段初始化):声明中赋值:private double salary; static { // static initialization block 静态初始化块(static {}):=> 类第一次加载时,将进行静态字段的初始化 Random generator = new Random(); nextId = generator.nextInt(1000); } { // object initialization block。初始化方式三:初始化块({}) => 对象构造时执行 id = nextId; nextId++; } public Employee(String n, double s) { // three overloaded constructors。初始化方式一:构造器 name = n; salary = s; } public Employee(double s) { this("Empolyee #" + nextId, s); // 4) calls the Employee(String, double) constructor } public Employee(){ // the default constructor } public int getId() { return id; } public String getName() { return name; } public double getSalary() { return salary; } }
4.6.8. 对象析构 与 finalize()
- Java 有自动的垃圾回收器,所以不支持析构器;
- 当使用了内存以外的资源,就显得有必要;对象用完后,可调用 close(),具体看 7.2.5节;
- !! 不要 finalize() 来清理,该方法已废弃。它要在垃圾回收器清除对象之前调用,但不知道什么时候才调用;
4.7 包
4.7.2. 类的导入 import
// 1)使用完整包名: java.time.LocalDate today = java.time.LocalDate.now(); // 2)使用 import 方式(最常用); import java.util.*; LocalDate today = LocalDate.now();
* 如果要使用的类来自2个包,需要指明导入具体的包;Avoids name conflict: java.util.Date ≠ java.sql.Date
import java.time.LocalDate; import java.util.*; import java.sql.*; import java.util.Date; // 1) add special import public class PackageTest { public static void main(String[] args) { LocalDate today = LocalDate.now(); // 引用 java.time.LocalDate System.out.println(today); Date deadline = new Date(); System.out.println(deadline); // 2) add full package name before the class java.util.Date deadline2 = new java.util.Date(); java.sql.Date today2 = new java.sql.Date(2021, 12, 25); System.out.println("deadline2: " + deadline2 + ", today2: " + today2); } }
4.7.3. 静态导入 import static(不是很建议)
// import 除了可以导入类,还可以导入 静态方法 和 静态变量,这样引用时就不必加类名前缀。但实际上不是很清晰 import static java.lang.System.*; out.println("Hello, World!") // i.e, System.out exit(0); //i.e, System.exit // Can be handy for mathematical functions: import static java.lang.Math.*; ... r = sqrt(pow(x, 2) + pow(y, 2));
4.7.4. 在包中增加类
如: package com.hostman.corejava; // package 语句必须在 源文件的开头,即该类属这个包
不放在包里的类,属于 无名包
> javac com/hostman/corejava/PackageTest.java // 编译时指定完整的路径,用 / > java com.hostman.corejava.PackageTest // 运行时指定完整的包名,用 .
4.7.5. 包访问
- 标记为 public:可被任意类使用;
- 标记为 private:只能被定义它们的类使用;
- 没有指定 public 或 private :其 (类、方法或变量)可被同一个包中的所有方法访问
- 类可以这样定义,但变量必须显式定义为 private(一般来说)
一般 coding 习惯:
- 字段:private
- 方法:public
- 要继承的字段或方法:protected
- 没有任何访问修饰符:不建议
4.7.6 类路径
- 类存储在文件系统的子目录中,且类的目录必须与包名匹配;
- 类文件也可存储在 JAR 文件中。一个 JAR 文件可以包含多个压缩形式的类文件和子目录【如 JDK的 /jre/lib/rt.jar 有数千个库文件】
- 1 ) 把类放到一个目录中, 如 /home/user/classdir。如增加 com.horstmann.corejava.Employee 类,Employee.class 须位于子目录 /home/user/classdir/com/horstmann/corejava 中;
- 2 ) 将 JAR 文件放在一个目录中,例如:/home/user/archives;
- 3 ) 设置类路径(classpath)。类路径是所有包含类文件的路径的集合;
- UNIX: 冒号(:)分隔:/home/user/classdir:.:/home/user/archives/archive.jar
- Windows:分号(;)分割:c:\classdir;.;c:\archives\archive.jar
上述路径拆分成如下3个部分:
- 基目录:/home/user/classdir 或 c:\classdir
- 当前目录: .
- JAR 文件 /home/user/achives.jar 或 c:\achives\achive.jar
2. 另外,从 Java 6 开始,可以在 JAR 文件目录中指定通配符, 如 /home/user/classdir:.:/home/user/archives/'*' 或 c:\classdir;.;c:\archives\*
- 如没有设置类路径, 也不会产生什么问题, 默认的类路径包含目录 ;
- 如果设置了类路径却忘了包含 "." (即当前)目录, 则程序仍然可以通过编译, 但不能运行; [ 即: 可以不设置,但如果设置了必须包含 ]
4. 1) 虚拟机 查找的 com.horstmann.corejava.Employee 类文件的顺序:
- (1)java API 类:jre/lib 和 jre/lib/ext 目录下的归档文件中所存放的系统类文件;
- (2)通过 类路径,查找如下文件:
- /home/user/classdir/com/horstmann/corejava/Employee.class
- com/horstmann/corejava/Employee.class 从当前目录开始
- com/horstmann/corejava/Employee.class in /home/user/archives/archive.jar
import java.util.*; import com.horstmann.corejava.*; // 源代码引用了 Employee 类
4.7.7. 设置类路径(现在基本都通过 IDE 开发,无需设置)
1)使用 -classpath / -cp / --class-path (Java 9):
java -classpath /home/user/classdir:.:/home/user/archives/archive.jar MyProg (Linux,用“:”分割) java -classpath c:\classdir;.;c:\archives\archive.jar MyProg (Windows,用“;”分割)
2)通过配置 ClASSPATH 环境变量(该方法逐渐少用),如 shell 脚本
export CLASSPATH=/home/user/classdir:.:/home/user/archives/archive.jar (Linux)
set CLASSPATH=c:\classdir;.;c:\archives\archive.jar (Windows)
4.8 JAR 文件
4.8.1 创建 JAR 文件jar cvf jarFileName file1 file2 ...
4.8.2 清单文件
清单文件 MANIFEST.MF 位于 \META-INF 路径下,具体格式如下:
Manifest-Version: 1.0 // 主节(main section),作用于整个 JAR 文件 lines describing this archive Name: Woozle.class // 节,每个节 以"Name"开头,节之间用空行分开 lines describing this file Name: com/mycompany/mypkg/ // 节 line describing this package
// 创建包含清单文件的 JAR jar cfm MyArchive.jar manifest.mf com/mycompany/mypkg.*.class // 更新清单文件,如下例,更新的内容放在 mainifest-additions.mf jar ufm MyArchive.jar mainifest-additions.mf
4.8.3 可执行 JAR 文件
// 方式一:通过参数 e 指定入口 jar cvfe MyProgram.jar com.mycompany.mypkg.MainAppClass files to add // 方式二:清单文件中指定主类 Main-Class: com.mycompany.mypkg.MainAppClass // 然后简单命令启动 java -jar MyProgram.jar
4.9 文档注释
4.9.1. javadoc
javadoc 从下面几处抽取信息:
- 模块;
- 包;
- 公共类与接口;
- 公共的和受保护的字段;
- 公共的和受保护的构造器及方法;
4.9.2-6. 各种注释
注释类型 | 标识 | 说明 |
类注释 |
/** * */ |
必须在 import 之后,类定义之前 |
方法注释 | 1. @param 变量描述 | 一个方法的所有 @param 标记必须放在一起 |
2. @return 返回描述 | ||
3. @throws 异常描述 | ||
字段注释 |
/** * */ |
只需要对公共字段(通常指的是静态常量)建立文档,如
/**
* The "Hearts" card suit
*/
public static final int HEARTS = 1;
|
通用注释
|
1. @author 姓名 |
可使用多个 @author 标记,每个 @author 标记对应一个作者
|
2. @version 当前版本 | ||
3. @since 引入特性的版本描述 | ||
4. @see 引用,有三种形式: 1)package.class#feature label 2)<a href="...">label</a> 3)"text"
具体实例: 1)@see com.horstmann.corejava.Employee#raiseSalary(double) 2)@see <a href="www.horstmann.com/corejava. html">The Core ]ava home page</a> 3)@see "Core Java 2 volume 2" |
1. @see reference 在 “see also” 部分增加一个超链接。可用于类、方法中 2. 可为一个特性添加多个 @see,但必须放在一起
1)建立一个链接到 com.horstmann.corejava.Employee 类的 raiseSalary(double)
方法的超链接。一定要用 “#” 分隔
2)@see 后面是 “<” 时,必须是 超链接
3)@see 后面是 "" 时,文本就会显示在 “see also” 部分
|
|
包与概述注释 |
1. 包注释:在每一个包目录中添加一个单独的文件 Option1: package.html 文件,在标记 <body> ... </body> 间的所有文本会被抽取 Option2: package-info.java 文件, 必须包含一个初始的以 /** 和 */ 界定的 javadoc 注释,后面是一个 package 语句。 不能包含更多的代码或注释 2. 概述注释:overview.html 文件,放在包含所有源文件的父目录中, 在标记 <body> ... </body> 间的所有文本会被抽取 |
4.9.7. 生成 Javadoc
1)命令
- 多个包: javadoc -d docDirectory nameOfPackage1 nameOfPackage2
- 文件在无名包:javadoc -d docDirectory *.java
其他选项:
- -author / -version:添加 @author / @version 标记(默认被省略)
- -link:为标准类添加超链接(所有标准类库会自动链接到 Oracle 网站的文档),如 javadoc -link http://docs.oracle.com/javase/17/docs/api *.java
2)IdeaJ 生成 javadoc : https://blog.csdn.net/qq_42498502/article/details/90078972
4.10 类设计技巧
- 1. 数据私有; --- 最重要
- 2. 显式地初始化所有的数据:提供默认值 或 通过构造器设置默认值;
- 3. 不要在类中使用过多的基本类型;
- 4. 不是所有的字段都需要单独的字段访问器和字段更改器;
- 5. 分解职责过多的类;
- 6. 类名和方法名 能体现它们的职责:
1)类名可以清楚地理解其含义(Order, RushOrder, BillingAddress); 2)访问器用 get 开头(getSalary); 3)更改器用 set 开头(setSalary)
- 7. 优先使用不可变的类。
更改对象的问题在于,如多个线程同时更新一个对象,就会发生并发变更,结果不可预料。 如类不可变,则安全地在线程间共享其对象。