目录
4.1面向对象程序设计概述
4.1.1 类
4.1.2 对象
- 对象的行为——可以对对象完成哪些操作,或者可以对对象应用哪些方法?
- 对象的状态——当调用那些方法时,对象会如何响应?
- 对象的标识——如何区分具有相同行为和状态的不同对象?
4.1.3 识别类
首先从识别类开始,然后再为各个类添加方法
4.1.4 类之间的关系
在类之间,最常见的关系有:
- 依赖(“uses-a”):如果一个类的方法使用或操纵另一个类的对象,我们就说一个类依赖另一个类。(应尽可能使相互依赖的类减至最少)
- 聚合(“has-a”):类A的对象包含类B的对象
- 继承(“is-a”):表示一个更特殊的类和一个更一般的类之间的关系
4.2 使用预定义类
4.2.1 对象与对象变量
要想使用对象,首先必须构造变量,并指定其初始状态,然后对对象应用方法
在Java程序设计语言中,要使用构造器(构造函数)构造新实例
构造器使一个特殊的方法,用来构造并初始化对象,构造器的名字应与类名相同
对象与对象变量之间存在着重要区别
Date deadline;
//定义了一个对象变量deadline,它可以引用Date类型的变量
//但是deadline不是一个对象!它也没有引用任何对象!
//此时还不能在deadline上使用任何Date方法
//首先必须初始化deadline变量
deadline = new Date();
对象变量并没有实际包含一个变量,它只是引用一个变量
在Java中,任何变量对象的值都是对存储在另外一个地方的某个对象的引用
4.2.2 Java类库中的LocalData类
标准的Java类库中分别包含了两个类:一个用来表示时间点的Date类,一个用日历来表示日期的LocalDate类
4.3 用户自定义类
要构建一个完整的程序,会结合使用多个类,其中只有一个类有main方法
4.3.1 Emloyee类
class Employee{
private String name;
private double salary;
private LocalDate hireDay;
//构造器
public employee(string n,double s,int year,int month,int day){
name = n;
salary = s;
hireDay = LocalDate.of(year,month,day);
}
//a method
public String getName(){
return name;
}
......
}
文件名必须与public类的名字相匹配
在一个文件中,只能有一个公共类,但可以有任意数目的非公共类
4.3.2 多个源文件的使用
将每一个类存放在一个的单独的源文件中,有两种编译方法
如类Emploee和类EmploeeTest
- 使用通配符编译器:javac Employlee*.java
- javac EmployeeTest.java (编译器会自动寻找所包含的其他类的文件)
4.3.3 剖析Employee类
4.3.4 从构造器开始
- 构造器与类同名
- 每个类可以有一个以上的构造器
- 构造器可以有0个、1个或多个参数
- 构造器没有返回值
- 构造器总是伴随着new操作符一起调用
4.3.5 用var声明局部变量
如果可以从变量的初始值推导出它们的类型,那么可以用var关键字声明局部变量
Employee harry = new Employee("harry",50000,1989,10,1);
//等同于
var harry = new Employee("harry",50000,1989,10,1);
4.3.6 使用null引用
4.3.7 隐式参数与显式参数
4.3.8 封装的优点
4.3.9 基于类的访问权限
4.3.10 私有方法
4.3.11 final实例字段
若将实例字段设置为final,这样的字段必须在构造对象时进行初始化,必须确保在每一个构造器执行之后,这个字段的值已经设置,并且以后不能再修改这个字段
final修饰符对于类型为基本类型或者不可变类的字段尤其有用
private final StringBuilder evalutions;
evalutions = new StringBuilder(); //初始化
//final关键字只是表示存储再evalutions变量中的对象引用不会再指示另一个不同的StringBuilder对象
//这个对象可以使用以下方法更改:
public void giveGoldStar(){
evalutions.append(LocalDate.now()+":Gold Star!\n");
}
4.4 静态字段与静态方法
4.4.1 静态字段
如果将一个字段定义为static,每个类就只有一个这样的字段
静态字段属于类,而不属于某个单个的对象
class Employee{
private static int nextId = 1;
private int id;
......
}
4.4.2 静态常量
public class Math{
...
public static final double PI = 3.14159265358979323846;
...
}
//在程序中,可以通过Math.PI来访问这个常量,无需生成对象
4.4.3 静态方法
静态方法是不在对象上执行的方法,没有任何this参数的方法
静态方法不能访问实例字段,因为它不能在对象上执行操作,只能访问静态字段
两种可以使用静态方法的情况:
- 方法不需要访问对象状态,它所需要的参数都通过显式参数提供
- 方法只需要访问类的静态字段
4.4.4 静态工厂方法
静态方法的一种常见的用途,可以用来构造对象
为什么不使用构造器?
- 构造器不能命名,它只能和类名相同,如果希望为构造方法命名来表示构造不同的实例,可以使用静态工厂方法
- 使用构造器时,无法改变所构造的对象的类型,使用静态工厂方法,所能构造的类型更加灵活多样,可以返回子类
4.4.5 main方法
可以调用静态方法而不需要任何对象,main方法也是一个静态方法
main方法不对任何对象进行操作,每一个类都可以有一个main方法
4.5 方法参数
Java程序设计语言总是采用按值调用的,也就是说,方法得到的是所有参数值的一个副本
Java程序设计语言对对象采用的不是按引用调用,实际上,对象引用是按值传递的
Java中对方法参数能做什么,不能做什么
- 方法不能修改基本数据类型的参数(即数值型和布尔型)
- 方法可以改变对象参数的状态
- 方法不能让一个对象参数引用一个新的对象
4.6 对象构造
4.6.1 重载
如果多个方法有相同的名字、不同的参数,便出现了重载
编译器使用各个方法首部中的参数类型与特定方法调用中所使用的值类型进行匹配,来选出正确的方法
Java允许重载任何方法,而不只是构造器方法
若要完整描述一个方法,需要指定方法名以及参数类型,这被称为方法的签名
返回类型不是方法签名的一部分。不能有两个名字相同、参数类型相同,但是不同返回类型的方法
4.6.2 默认字段初始化
如果在构造器中没有显式地为字段设置初值,那么就会被自动地赋为默认值:数值为0、布尔值为false、对象引用为null
4.6.3 无参数的构造器
如果写一个类时没有编写构造器,就会为你提供一个无参数的构造器,这个构造器将所有的实例字段设置为默认值
如果类中提供了至少一个构造器,但是没有提供无参数的构造器,那么构造对象时如果不提供参数就是非法的
仅当类中没有任何其他构造器的时候,你才会得到一个默认的无参数构造器
4.6.4 显式字段初始化
可以在类定义中直接为任何字段赋值
4.6.5 参数名
4.6.6 调用另一个构造器
如果构造器的第一个语句形如 this(...) ,这个构造器将调用同一个类的另一个构造器
public Employee(double s){
this("Employee #"+nextId,s);
nextID++;
}
//当调用 new Employee(60000) 时,Employee(double)构造器将调用 Employee(String,double) 构造器
4.6.7 初始化块
在一个类的声明中,可以包含任意多个代码块,只要构造这个类的对象,这些块就会被执行,这些块被称为初始化块
调用构造器的具体处理步骤:
1、如果构造器的第一行调用了另一个构造器,则基于所提供的参数执行第二个构造器
2、否则,
a)所有数据字段初始化为其默认值(0,false,null)
b)按照在类声明中出现的顺序,执行所有字段初始化方法和初始化块
3、执行构造器主体代码
可以通过提供一个初始值,或者使用一个静态的初始化块来初始化静态字段
在类第一次加载时,将会进行静态字段的初始化。与示例字段一样,处非将静态字段显式的设置为其他值,否则默认的初始值是0、false、null。
所有的静态字段初始化方法以及静态初始化块都将依照类声明中出现的顺序执行
4.6.8 对象析构与finalize方法
4.7 包
4.7.1 包名
使用包名的主要原因是确保包名的唯一性
4.7.2 类的导入
一个类可以使用所属包中的所有类,以及其他包中的公共类
可以使用两种方式访问另一个包中的公共类
- 使用完全限定名,包后面跟着类名
- 使用 import 语句导入一个特定的类或者整个包
java.time.LocalDate today = java.time.LocalDate.now();
import java.time.*;
LocalDate today = LocalDate.now();
在C++中,与包机制类似的是命名空间特性。可以认为Java中的 package 和 import 语句类似于C++中的 namespace 和 using 命令
4.7.3 静态导入
有一种 import 语句允许导入静态方法和静态字段,而不只是类
import static java.lang.System.*;
out.println("hello world!");
4.7.4 在包中增加类
要想将类放入包中,就必须将包的名字放在源文件的开头,即放在定义这个包中的各个类的代码之前。如果没有在源文件中放置 package 语句,这个源文件中的类就属于无名包
4.7.5 包访问
在包中,标记为 public 的部分可以由任意类使用,标记为 private 的部分只能由定义它们的类使用。如果没有指定 private 或 public ,这个部分(类、方法或变量)可以被同一个包中的所有方法访问
4.7.6 类路径
类存储在文件系统的子目录中,类的路径必须与包名匹配。
另外,类文件也可以存储在JAR(Java归档)文件中。在一个JAR文件中,可以包含多个压缩形式的类文件和子目录。
为了使类能够被多个程序共享,只需要做到以下几点:
1、把类文件放在一个目录中
2、将JAR文件放在一个目录中
3、设置类路径(class path)。类路径是所有包含类文件的路径的集合
4.8 JAR文件
Java归档(JAR)文件:在将应用程序打包时,只向用户提供一个单独的文件,而不是一个包含大量类文件的目录结构
JAR文件是压缩的,采用ZIP压缩格式
4.9 文档注释
JDK包含一个很有用的工具,叫做javadoc,它可以由源文件生成一个HTML文档
4.10 类设计技巧
- 一定要保证数据私有
- 一定要对数据进行初始化
- 不要在类中使用过多的基本类型
- 不是所有的字段都需要单独的字段访问器和字段更改器
- 分解有过多职责的类
- 类名与方法名要能够体现它们的职责
- 优先使用不可变的类