5.1 用构造器确保初始化
- 构造器与类名相同
- 编译器自动调用构造器
不接受任何参数的构造器称为默认构造器或者无参构造器 - 构造器是特殊的方法,没有返回值,也不返回空
Tree t = new Tree(12)
如果Tree(int)是唯一定义的构造器,则不允许以其他任何方式创建对象,也不能用无参构造器创建对象
5.2 方法重载
不同参数列表能够区分重载的方法,不仅是类型不同,参数的顺序不同也视为不同的方法,不能以返回值的类型来区分不同的方法
static void f(String s,int i) {}
static void f(int i,String s) {}
obj.f("aaa",1);
obj.f(1,"aaa");
//不同参数顺序,视为两个不同方法
void f() {}
int f() { return 1; }
//执行f(); 无法区分两个方法
- 如果传入的参数类型小于方法中声明的形式参数类型,实际数据类型就会被提升
- 如果传入参数类型大于方法中声明的形式参数类型,编译器就必须执行类型窄化
5.3 默认构造器
如果没有构造器,编译器会帮你自动调用默认构造器,如果有自定义的构造器,则编译器不会为你调用默认构造器
Class A {
A(int a) {}
}
A x = new A(); //非法
A y = new A(1);
5.4 this关键字
- 希望在方法的内部得到调用它的那个对象,使用 this 对象
- 在方法内部调用同一个类的另一个方法,并不需要使用 this
public class A {
void pick() {}
void pit() { pick(); }
}
- 只有需要明确指出对当前对象的引用时,才需要使用 this
public class A {
int i = 0;
A increment() {
i++;
return this;
}
}
- this 也可以用于将当前兑现传递给其他的方法
- 使用this关键字可以在构造器中调用构造器
public class Flower {
String s = "a";
int i = 1;
Flower(String ss) {
s = ss;
}
Flower(int ii) {
i = ii;
}
Flower(String ss,int ii) {
this(ss); //使用 this 作为构造器
// !this(ii); 只能用this 调用1个构造器,不能调用两个
i = ii;
}
}
- this 关键字能解决类成员与方法参数重名的问题
this.s = s;
- static 的方法即是没有this对象的方法,故在 static 方法的内部不能调用非静态的方法
5.5 清理:终结处理和垃圾回收
- finalize() 方法,用于回收那些不是new 得到的对象
- 对象可能不被垃圾回收
- 垃圾回收不等于析构
- 垃圾回收只与内存有关
-
finalize() 方法实际上不是清理 java 中创建的对象,而是 java 代码中调用了其他语言代码的时候, 采取了其他创建对象的方式, 这时创建的对象必须要手动回收
-
垃圾回收器使得 Java 从堆分配空间的速度可以和其他语言从堆栈分配空间的速度匹配
-
垃圾回收器的思想是,对于一切活的对象,一定能追溯到它在堆栈或者静态存储区之中的引用链条,这个链条可能穿越数个对象层次,如果从堆栈和静态存储区开始,遍历所有的引用,就能找到所有活的对象
-
有一种名叫停止-复制机制,先暂停程序的运行,然后将所有存活的对象从当前堆复制到另一个堆,没有被复制的对象全部是垃圾,当对象从一个堆到另一个堆,所有指向它的引用都要被修正
-
为了防止垃圾很少的时候垃圾回收器也执行停止-复制,垃圾回收器会转换到另一种模式,标记-清扫模式,,遍历所有的引用,进而找出所有存活的对象,每当找到一个对象,就会给对象一个标记,这个过程不会回收任何对象,只有全部标记过程完成时,才会开始清理工作,在清理过程中,没有被标记的对象会被释放,不会发生任何复制动作
-
自适应的,分代的,停止-复制,标记-清扫式垃圾回收器
5.6 成员初始化
- 尽力保证所有变量都在使用前得到恰当的初始化
- 局部变量如果声明时不赋初始值,编译器会报错
- 类的成员会自动获得初始值,一个对象如果没有赋初始值,则会被赋为 null
class Depth() {}
public class Measurement() {
Depth d = new Depth();
}
//如果没有为 d 赋予初始值就使用它,就会出现运行时错误
public class MethodInit2 {
int i = f(); //可以用方法来赋初始值
int j = g(i); //方法中也可以带参数,但参数必须是已初始化的
int f() { return 1; }
int g(int n) { return n * 10; }
}
构造器的初始化
- 初始化顺序,变量定义的顺序决定了初始化的顺序,它们会在任何方法(包括构造器)被调用之前得到初始化
- 静态成员的初始化,初始化的值与其他成员一样,只有在类的第一个对象被创建的时候才去初始化静态成员,先初始化静态成员,再初始化非静态成员,静态成员只初始化一次,下一次再创建对象不会再次初始化静态成员
- 对象的创建过程
- 创建一个 Dog 类对象,即使没有使用 static 关键字,构造器也是一种静态方法,首次创建 Dog 类对象时,Java 解释器必须先查找类路径,以定位 Dog.class 文件
- 然后载入 Dog.class 有关静态初始化的操作都会执行,因此,静态成员只在 Class 对象首次加载的时候进行一次初始化
- 当用 new Dog() 的时候,首先在堆上为 Dog 对象分配足够的空间
- 这块存储空间会被清零,自动将 Dog 对象的所有基本类型数据都设置了默认值,引用则被设置成 null
- 执行所有字段定义处的初始化
- 执行构造器
- 显式的静态初始化
public class Spoon {
static int i;
static {
i = 47;
}
//static {} 代码块只在首次创建类对象或者首次访问静态数据成员的时候执行一次
}
- 显式的非静态初始化
public class Mugs {
Mug mug1;
{
mug1 = new Mug();
} //非静态成员的初始化也在构造器之前
Mugs() {}
}
5.8 数组初始化
- 复制引用
int[] a1 = {1, 2, 3, 4, 5};
int a2 = a2;
//这里仅仅复制了一个引用,实际上是同一个数组
- 用 new 初始化数组
int[] a;
Random rand = new Random();
a = new int[rand.nextInt(2)]; // 直接用new int[]初始化数组
- 花括号初始化数组
Integer[] a = {
new Integer(1),
new Integer(2)
} //仅能用于数组定义时
Integer[] b = new Integer[] {
new Integer(1),
new Integer(2)
}
- 参数传递初始化数组
public static void main(String[] args) {
other.main(new String[] { "aaa", "bbb" });
//在方法传递参数中初始化数组
- 可变参数列表
static void print(Object[] args) {
...
}
print(new Object[] {
new Integer(1),
new Float(2)
});
//用Object做参数
static void print(Object... args) {
for(Object obj : args)
...
}
print(new Integer(47), new Float(3.14), new Double(3.44));
//可变参数列表
static void print(int i,String... args);
print(1,"aa","bb");
print(2,"aa");
print(1); //可变参数列表可以传入0个参数
- 枚举类型
public enum Spiciness {
NOT, MILD, MEDIUM, HOT, FLAMING
}
Spiciness howhot = Spiciness.MEDIUM;
print(howhot); //MEDIUM
print(howhot.ordinal()); //2
//ordinal 编号