1. 类之间的关系
- 依赖("uses-a"):一个类的方法操作另一个类的对象,一般而言,在java中,依赖关系表现为局部变量,方法的形参,或对静态方法的调用。
- 聚合("has-a"):一个类的对象包含另一个类的对象,关联关系的一种,表现为整体与个体的关系,通过成员变量体现。
- 继承("is-a")
2. 对象中的数据称为实例域,对于每个特定的类实例(对象)都有一组特定的实例域值,称为该对象的状态。实现封装的关键在于绝对不能让类中的方法直接访问其他类的实例域。程序仅通过对象的方法与对象数据进行交互。
3. 对象与对象引用
1 Date time = new Date();
表达式 new Date() 构造了一个Date类型的对象,并且它的值是对新创建对象的引用。该引用存储在time中。
访问器方法:只访问对象而不修改对象的方法
更改器方法:修改对象的状态
4. 自定义类
源文件名必须与public类的名字相同。在一个源文件中,只能有一个公共类,可以有任意数目非公共类。该公共类中包含main()方法,作为程序执行的入口。
编译类文件:1. 使用通配符 javac Employee*.java 将会编译所有以Employee开头的.java文件。 2. 编译含有main()方法的主类,即 javac EmployeeTest.java 在编译过程中遇到其他类的全限定名会自动查找对应的class文件,查找失败会进行编译。
5. 构造器
- 构造器与类名相同
- 每个类可以有多个构造器
- 构造器可以有0个或多个参数
- 构造器没有返回值
- 构造器伴随new操作一起调用
(并发编程)在构造器中防止this引用逸出,1. 不要在构造器中启动线程 Thread.start() 2. 不要在构造器中调用可改写的实例方法
6. 显式参数和隐式参数
7. 破坏封装性
class Employee{ private Date hireDay; ... public Date getHireDay(){ return hireDay; // 在公有方法中将私有域发布出去
// return (Date) hireDay.clone();
} ... }
公共方法不要返回可变私有域,应该返回该数据域的拷贝。
8. final 实例域
构建对象时必须初始化,且一旦初始化后不可更改。final修饰符大多用于基本类型域,或不可变类(类中每个方法都不能改变其对象)的域。
(并发编程)不可变的对象和不可变的对象引用:
不可变的引用意为不可指向其他对象,但若指向的是可变对象,对象本身可变。
不可变对象满足:1、创建后其状态不能修改 2、对象的所有域都是final 3、对象是正确创建的(this引用没有逸出)
9. 静态域
每个类中只有一个这样的域。静态域属于类,不属于任何对象。该类的所有对象共享一个静态域。
在虚拟机中,静态域的初始化有两种方法。其一是使用<clinit>()方法(类加载的初始化阶段),另外就是在ConstantValue属性中赋值(类加载的准备阶段)。
10. 静态方法
静态方法不能访问非静态域。
11. 工厂方法
使用工厂方法而不是构造器格式化对象:
- 无法命名构造器
- 当使用构造器时,无法改变所构造的对象类型。
12. 方法参数
按值调用:方法接收的是调用者提供的值
按引用调用:方法接收的是调用者提供的变量地址
一个方法可以修改传递的引用所对应的变量值,而不能修改传递的值调用所对应的变量值,java是按值调用,方法得到的是所有参数值的一个拷贝,方法不能修改传递给它的任何参数变量的内容。修改的仅仅是拷贝而不是值本身。退出方法后,虚拟机栈中的当前栈帧(包含局部变量表,操作数栈,动态连接, 方法出口等)退栈,局部变量(该拷贝)消失。
既然java总是按值传递,那为什么例如参数为数组的时候又可以修改数组内的元素呢?
因为,方法参数共有两种类型:
- 基本数据类型(数字、布尔值)
- 对象引用
一个方法不可能修改一个基本数据类型的参数,但是可以修改对象引用所指的对象状态。按值调用在这里体现的是:我们无法改变引用本身的值也就是指向的是哪一个对象。首先,进入方法时,我们得到引用的一个拷贝,引用和拷贝都指向同一个对象。我们在方法内部,若将该拷贝指向其他对象,方法结束后,原引用仍然不变。其原理类似于不可变的对象引用。
13. 对象构造
重载(静态分派的应用):方法名相同,参数列表不同。静态(多)分派:根据方法调用者(和方法参数)的静态类型决定将符号引用具体化为哪个直接引用,即决定调用哪个方法,静态分派发生在编译期,由编译器完成操作。
关于静态分派和动态分派,请参见《JVM虚拟机高级特性》。
方法的签名:方法名和参数类型。注:返回类型不属于方法签名。
局部变量必须初始化。静态变量在类加载的连接(验证,准备,解析)的准备阶段赋逻辑零值(这时还不是程序里的初始值)。成员变量在new操作下在构造器中赋值,若构造器没有显式赋值,则会给予默认初始值。
方法参数的命名技巧:
public Employee(String aName, double aSalary){ name = aName; salary = aSalary; }
this: 1、方法内部代表该方法所属的对象的引用 2、构造器中代表同一个类的另一个构造器
14. 初始化块
在构造类对象时,初始化块会执行且只执行一次。 静态初始化块对对象的静态域初始化。在类第一次加载时,进行初始化。所有静态初始化块语句和静态初始化块都按照类定义的顺序执行。在静态初始化块之后定义的类变量,在初始化块中可以赋值,但不能访问。
... static { i = 10; system.out.print(i); //报错 } static int i;
15. 类的导入
一个类可以使用所属包中的所有类,以及其他包中的公有类。有两种方式访问另一个包中的公有类。
1、在每个类之前添加完整的类名
2、import 导入特定的类或者整个包
import 不仅可以导入类,还增加了导入静态方法和静态域的功能。
16. 包的作用域
标记为 public 的部分可以被任意的类使用,标记为 private 的部分只能被定义它们的类使用。如果没有指定(defult)将可以被包中的所有方法访问。
访问权限 | 类 | 包 | 子类 | 其他包 |
public | ∨ | ∨ | ∨ | ∨ |
protect | ∨ | ∨ | ∨ | × |
default | ∨ | ∨ | × | × |
private | ∨ | × | × | × |
17. 类设计技巧
- 保证数据私有
- 对数据初始化
- 对职责过多的类分解
- 类名和方法名能够体现它们的职责
- 优先使用不可变类