@7788999z
2014-05-09T08:32:54.000000Z
字数 3885
阅读 677
Java核心技术卷1
4.1.1 类
类(class)是构造对象的模板或蓝图。
4.1.2 对象
对象的三个主要特性:
- 对象的行为(behavior)——可以对对象施加哪些操作,或者说可以对对象施加哪些方法。
- 对象的状态(state)——当施加那些方法时,对象如何响应?
- 对象的标识(identity)——如何辨别具有相同行为与状态的不同对象?
4.1.3 识别类
识别类的简单规则是:在分析问题的过程中寻找名词,而方法对应着动词。
当然,所谓“名词与动词”原则只是一种粗略的方法,在创建类的时候,哪些名词和动词是重要的完全取决于个人的经验。
4.1.4 类之间的关系
在类之间,最常见的关系有:
- 依赖(“uses-a”)
- 聚合(“has-a”)
- 继承(“is-a”)
依赖(dependence),即“use-a”的关系,是一种最明显的、最常见的关系。通常,如果一个类的方法操纵另一个类的对象,我们就说一个类依赖与另一个类。比如,类A的一个方法的参数中有类B的一个实例,我们就说类A依赖于类B。
应该尽可能地将相互依赖的类减至最少。如果类A不知道类B的存在,它就不会关心类B的任何改变(这意味着类B的改变不会导致类A产生任何bug)。用软件工程的术语来说,就是让类之间的耦合度最小。
聚合(aggregation),即“has-a”的关系,是一种具体且易于理解的关系。例如,一个Order对象包含一些Item对象。聚合关系意味着类A的对象包含着类B的对象。
继承(inheritance),即“is-a”的关系,是一种用于表示特殊与一般关系的。
4.2.1 对象与对象变量
在Java中,使用构造器(constructor)构造新对象(实例)。构造器是一种特殊的方法,用来构造并初始化对象。
构造器的名字应该与类名相同。
对象变量仅仅引用了对象。实际上,对象变量如Date today = new Date()
中,today为对象变量,today表示在内存中的栈里面的一个内存地址,这个内存地址为一个数值,这个数值指向了内存中堆里面的具体的对象new Date()
。可以把对象变量想象成门牌号,对象则表示具体的内容。如果光有门牌号,而没有指向具体的内容,则我们不能对这个门牌号进行任何具体的操作(比如关门、开门等)。
4.2.2 Java类库中的GregorianCalendar类
4.2.3 更改器方法(set)和访问器方法(get)
4.3.1 一个Employee类
4.3.2 多个源文件的使用
编译类A的时候,如果类A中使用了类B的话,会自动找到类B并编译它。更重要的是,如果类B的java版本比class版本新,Java编译器会重新编译类B.java。
4.3.3 解析Employee类
4.3.4 从构造器开始
4.3.5 隐式参数和显示参数
在每一个方法中,关键词this表示隐式参数。
4.3.6 封装的优点
- 可以改变内部实现,除了该类的方法外,不会影响其他代码。
- 更改器方法可以执行错误检查,然而直接对域进行赋值的话不会进行这些处理。
警告:注意不要编写返回引用可变对象的访问器方法。如:
class Employee{
private Date hireDay;
public Date getHireDay(){
return this.hireDay;
}
}
这样做会破坏封装性!请看下面这段代码:
Employee harry = ...;
Date d = harry.getHireDay();
double tenYearsInMilliSeconds = 10 * 365 * 24 * 60 * 60 * 1000;
d.setTime(d.getTime - (long)tenYearsInMilliSeconds);
出错的原因很微妙,d和harry.getHeriDay()引用了同一个对象。对d调用更改器方法就可以自动地改变这个雇员对象的私有状态!
如果需要返回一个可变对象的引用,应该首先对它克隆(clone)。如:
class Employee{
...
public Date getHeriDay(){
return (Date) hireDay.clone();
}
...
}
4.3.7 基于类的访问权限
方法出了可以访问它的调用对象的私有数据,还可以访问所属类的所有对象的私有数据。例如:
class Employee{
...
boolean equals(Employee other){
return this.name.equals(other.name);
}
}
4.3.8 私有方法
4.3.9 Final实例域
构建对象时必须初始化这样的域,并且在后面的操作中,都不能被修改。
static修饰静态域和静态方法。
4.4.1 静态域
属于类,不属于任何独立的对象。
4.4.2 静态常量
4.4.3 静态方法
4.4.4 Factory方法
4.4.5 main方法
程序设计语言中有关参数传递给方法的一些专业术语:
- 值调用(call by value),表示方法接收的是调用者提供的值。
- 引用调用(call by reference),表示方法接受的是调用者提供的变量地址。
一个方法可以修改传递引用调用所对应的变量值,而不能修改传递值调用所对应的变量值。
Java程序设计语言总是采用值调用。也就是说,方法得到的是所有参数值的一个拷贝,特别是,方法不能修改传递给它的任何参数变量的内容。对于基础数据类型(如int、double)是值不改变,而对于对象类型是引用对象不改变(引用对象的域可改变)。
例如:
public static void tripleValue(double x){
x = 3 * x;
}
public static void main(String[] args){
double percent = 10;
tripleValue(percent);
System.out.println(percent);
}
可以看到,无论怎样,调用这个方法之后,percent的值还是10。下面看一下具体的执行过程:
- x被初始化为percent值的一个拷贝(10)。
- x被乘以3后等于30.但是percent仍然是10.
- 这个方法结束后,参数变量x不再使用。
再看一个对象引用的例子,可以改变对象引用的域,如下,将一个雇员的薪金提高两倍的操作:
public static void tripleSalary(Employee x){
x.raiseSalary(200);
}
public static void main(String[] args){
harry = new Employee(...);
tripleSalary(harry);
}
具体的执行过程为:
- x被初始化为harry值的拷贝,这里是一个对象的引用。
- raiseSalary方法应用于这个对象的引用。x和harry同时引用的那个Employee对象的薪金提高两倍。
- 方法结束后,参数变量x不再使用。当然,对象变量harry继续引用那个薪金增至3倍的雇员对象。
通过上面的例子,有些程序员可能会认为Java对对象参数采用的是引用调用,实际上是误解。再看一个反例:
public static void swap(Employee x,Employee y){
Employee temp = x;
x = y;
y = temp;
}
public static void main(String[] args){
Employee a = new Employee("bob",...);
Employee b = new Employee("jim",..,);
swap(a,b);
System.out.println(a.getName());// bob
System.out.println(b.getName());// jim
}
如果是引用传递的话,a和b应该进行了置换,而实际上并没有,a和b的引用没有发生改变,因为x和y只是a和b的引用的一个拷贝,在方法结束后就丢弃了。
总结,在Java中,方法参数的使用情况:
- 一个方法不能修改一个基本数据类型的参数(即数值型和布尔类型)。
- 一个方法可以改变一个参数对象的状态(域)。
- 一个方法不能实现让对象参数引用一个新的对象。
4.6.1 重载
如果一个类有多个方法有相同的名字,不同的参数(包含个数和顺序的不同),便产生了方法重载。
4.6.2 默认域初始化
如果在构造器中没有显示地给域赋初始值,则会自动地被赋上默认值:数值为0、布尔值为false、对象引用为null。
4.6.3 默认构造器
如果不显示地定义构造器的话,系统会提供一个不带参数的默认构造器。
4.6.4 显示域初始化
初始值不一定是常量,可以调用方法对域进行初始化。
4.6.5 参数名
命名有意义。
4.6.6 调用另一个构造器
public Employee(double s){
this("jim",s);
}
4.6.7 初始化块
初始化数据域的方法:
- 在构造器中赋值。
- 在声明中赋值。
- 初始化块
class Employee{
private static int nextId;
private int id;
private String name;
private double salary;
{
id = nextId;
nextId++;
}
}
只要构造类的对象,这些初始化块就会执行。相应的,有静态初始化块static{}
。
4.6.8 对象析构与finalize方法
finalize方法将在垃圾回收器清除对象之前调用。在实际应用中,由于不知道垃圾回收器在何时清除对象,也就不知道何时会调用finalize方法,所以不要依赖与finalize方法回收任何短缺的资源。