随着计算机革命的发展,“不安全”的编程方式逐渐成为·编程代价高昂的主因之一。
5.1 用构造器确保初始化
class Rock {
Rock() {
System.out.print("Rock");
}
}
public class SimpleConstructor{
public static void main(String[] args){
new Rock();
}
}
new Rock(); 会为对象分配储存空间,并调用相应的构造器。构造器的名称必须与类名相同。每个方法首字母小写的编码风格不适用于构造器。
不接受任何参数的构造器叫做默认构造器,构造器也能带有形式参数,指定如何创建对象。
class Rock2 {
Rock2(int i) {System.out.print("Rock"+i);}}
public class SimpleConstructo2r{
public static void main(String[] args){
new Rock2(110);}}
如果Rock2中Rock2(int i)是唯一的构造器,那么编译器不会允许你以任何其他的方式创建Rock2对象。
构造器是一种特殊的类型方法,因为它没有返回值。这与返回值为空(void)明显不同。对于空返回值,尽管方法本身不会返回什么,但可以选择让它返回别的东西。构造器则不会返回任何东西,你别无选择(new表达式返回了对新建对象的引用,但构造器并没又返回任何值)。
5.2 方法重载
任何程序设计语言具备的一项重要特性就是对名字的运用。当创建一个对象时,也就是给此对象分配的内存空间取了一个名字。所谓方法就是给某个动作去了个名字。通过使用名字你可以引用所有的对象和方法。
- 构造器
假设你要创建一个类,既可以用标准方法初始化,也可以用读取文件信息初始化,这时需要两个构造器,一个默认构造器,另一个取字符串作为形参,该字符串表示初始化所需要的文件名称。由于都是构造器,所以它们都必须有相同的名称即类名。这时必须用到方法重载。
class Tree{
int height;
Tree(){ height = 0 ;}
Tree(int initalHeight){ height = initialHeight; }}
- 区分方法的重载
通过方法的参数列表区分。甚至参数顺序不同也可以区分(但不这么做)
public class OverloadingOrder{
static void f(String s,int i){}
static void f(int i,String s){} }
- 以返回值区分重载方法
void f(){}
int f(){return 1;}
用返回值区分重载方法时,只要编译器可以根据语境来判断语义,是可以区分的,但,有时你并不关心方法的返回值,你想要调用方法的其他效果,这是你忽略了返回值,这时调用无返回值的方法,这样所以行不通。
5.3 默认构造器
如果你写的类没有构造器,编译器会帮你创造一个默认构造器,但是如果已经定义了一个构造器(无论是否有参数),编译器不会帮你自动创建默认构造器。
5.4 this 关键字
this表示对“调用方法对象的引用”
在构造器中调用构造器
可能为一个类写多个构造器,有时可能想在一个构造其中调用另一个构造器,以免重复代码
尽管可以用this调用一个构造器,但却不能调用两个,此外,必须将构造器调用置于起始处,否则编译会报错。除构造器外,编译器禁止在其他任何地方调用构造器。static的含义
主要用途通过类本身调用方法。static方法就是没有this方法,在static方法内部不能调用非静态方法,反过来到是可以的。
5.5 清理:终结处理和垃圾回收
1.对象可能不被垃圾回收
2.垃圾回收并不等于析构
3.垃圾回收只与内存有关
只要程序没有濒临存储空间用完的那一刻,对象占用的的空间就总也得不到释放。如果程序执行结束,并且垃圾回收器一直都没有释放你创建的任何对象的存储空间,则随着程序的退出,那些资源也会交给系统。因为垃圾回收本身也是有开销5.51
不该将finalize()作为通用的处理方法,由于在分配内存是可能采用了类似C语言的做法,而非Java中的通常做法,这种情况主要发生在使用“本地方法的情况下”。比如也许会调用c的malloc()函数分配存储空间,而且除非调用了free()函数,否则存储空间将得不到释放。需要在finalize()中用本地方法调用。5.52 你必须实施清理
无论“垃圾回收”,还是“终结”,都不保证一定会发生。如果Java虚拟机(JVM)并未面临内存消耗尽的情形,它是不会浪费时间执行垃圾回收以恢复内存。5.53 终结条件
finalize()只能存在于程序员很难用到的一些晦涩用法里,5.54 垃圾回收如何工作
垃圾回收器对于提高对象创建速度,却具有明显效果。
Java堆像一个传送带,每分配一个对象,它就往前移动一格,这意味着对象存储空间的分配速度非常快。效率比得上C++。
垃圾回收器它们依据思想:对任何“活”的对象,一定能最终追溯到其存活在堆栈活静态存储区域之间的引用。这个引用链条可能会穿过数个对象层次。由此,如果从堆栈和静态区域开始,遍历所有的引用就能找到所有“活”的对象,对于发现的每个引用,必须追踪它所引用的对象,然后是此对象包含的所有引用,如此反复进行,直到“根源于堆栈和静态区域的引用”所形成的网络全部被访问为止,你所访问的对象都是“活”的。注意,这就解决了“交互自引用的对象组”的问题
5.6 成员初始化
基本类型 有默认值
引用类型 默认值null
局部变量 必须初始化
5.7 构造器初始化
静态初始化、非静态初始化
- 对象创建的过程
1.即使没有显示地使用static关键字,构造器时间上也是静态方法。因此,当首次创建类型Dog对象时(构造器可以看成静态方法),或者Dog类型的静态方法/静态域首次被访问,Java解释器必须查找类路径,定位Dog.class文件。
2.然后载入Dog.class,有关静态初始化的所有动作将会执行,因此,静态初始化只在class文件加载的时候进行一次。
3.当用new Dog()创建对象的时候,首先将在堆上为Dog对象分配足够多的内存空间。
4.这块空间将会被清零,这就自动地将Dog对象中的所有基本类型数据都设置成了默认值。
5.执行所有出现于字段定义的初始化动作。
6.执行构造器。
5.8 数组初始化
- 1
int[] a1 = {1,2,3,4,5};
- 2
int[] a = new int[2];
a[0] = 1;
a[1] = 2;
- 3
int[] a = new int[]{1,2,3}; //与第二种方法不同,这里new不需要指定数组的长度,数组长度由其后的初始化操作确定
2
3 int[] b = new int[]{
4 new Integer(1),
5 new Integer(2),
6 3
7 };
5.9 枚举类型
public enum Spiciness{
NOT,MILD,MEDIUM,HOTF,LAMING
}
当你创建enum时,编译器会自动添加一些有用的特性。例如会创建toString()方法,以便你可以很方便显示某个enumn实例的名字,编译器还会创建ordinalI()方法,用来表示某个特定enum常量的声明顺序,以及static values()方法,用来按照enum常量声明的顺序,产生由这些常量值构成的数组。
enum用作创建数据类型的方式,然后将所得的类型拿来使用。(在switch中的使用)
总结
构造器在Java中占重要地位。大量编程错误源于不正确初始化,并且很难发现,构造器能保证正确初始化。