第九章 构造器与垃圾收集器
堆与栈
实例变量是被声明在类而不是方法里面。它们代表每个独立对象的“字段”(每个实例都能有不同的值)。实例变量存在于所属的对象中。
public class Duck{ int size; //定义在类中的实例变量,每个Duck对象都会有独立的size }
局部变量和方法的参数都是被声明在方法中。它们是暂时的,且生命周期只限于方法被放在栈上的这段期间(也就是方法调用至执行完毕为止)。
public void foo(int x){ //方法接收的参数也是局部变量 int i = x+3; boolean b = true; //定义在方法内部的局部变量 }
方法会被堆在一起。当你调用一个方法时,该方法会放在调用栈的栈顶。实际被堆上栈的是堆栈块,它带有方法的状态,包括执行到哪一行程序以及所有的局部变量的值。
栈顶上的方法是目前正在执行的方法。方法会一直待在这里直到执行完毕为止,如果foo()方法调用bar()方法,则bar()方法会放在foo()方法的上面。
有关对象局部变量:要记得非primitive的变量只是保存对象的引用而已,而不是对象本身。你已经知道对象位于堆内存中。不论对象是否声明或创建,如果局部变量是个对该对象的引用,只有变量本身会放在栈上。
对象本身只会存在于堆上。
如果局部变量生存在栈上,那么实例变量呢?实例变量存在于堆内存的对象中。
构造函数
public Duck(){ //Duck是类名 }写构造函数时要注意:1. 函数名要与类名相同
在我们创建对象时,其实就调用了构造函数:
Duck d = new Duck(); 这里的 Duck()就是在调用Duck类的构造函数构造函数的一项关键特征就是它会在对象能够被赋值给引用之前就执行。这代表你可以有机会在对象被使用之前介入。比如这样:
class Duck{ public Duck(){ System.out.println("Quack"); } } public class UseADuck{ public static void main(String[]args){ Duck d = new Duck(); } } //创建对象时会在屏幕上打印Quack,构造函数让我们有机会可以介入new的过程大部分人都是使用构造函数来初始化对象的状态,也就是说设置给对象的实例变量赋值:
public class Duck{ int size; public Duck(int s){ //构造函数在这里要接收一个int型的参数 size = s; } } public class UseADuck{ public static void main(String[] args){ Duck d = new Duck(43);//需要传值给Duck的构造函数 } }当你在类中自定义了构造函数后,浏览器就不会帮你写空参数的构造函数了。
构造方法与继承
从某个构造函数调用重载版的另一个构造函数
对象的生命周期
只要有活着的引用,对象也就会活着。如果某个对象的引用已经不在它的范围中,但此引用还是活着的,则此对象就会继续活在堆上。如果对对象的唯一引用死了,对象就会从堆中被踢开。引用变量会跟堆栈块一起解散,因此被踢开的对象也就正式的声明出局。
有三种方法可以释放对象的引用:
//1. 引用永久性的离开它的范围 void go(){ Life z = new Life(); } //2. 引用被赋值到其他对象上 Life z = new Life(); z = new Life(); //3. 直接将引用设定为null Life z = new Life(); z = null;