您的位置:首页 > 其它

10.构造器与垃圾收集器(对象的开始和结束)

2018-01-18 16:52 232 查看
目录

概述

栈与堆 内存中的两种区域 变量和对象的生存空间 变量的有效范围

对象的创建 构造函数 构造器
父子类 构造函数之间的关系

同一类中 在某个构造函数中调用重载版的另一个构造函数

对象的生命周期 垃圾收集器GC

概述

我们决定对象何时创建,如何创建,何时声明放弃对象。当对象被放弃后,垃圾收集器GC会销毁对象,回收所占用的内存空间,如果不释放,可能会出现内存不足的问题。

下面会讨论 对象如何创建存在何处如何保存和抛弃更有效率。(包括堆,栈,范围,构造器,超级构造器,空引用等要点 )

栈与堆 (内存中的两种区域, 变量和对象的生存空间, 变量的有效范围)

栈(stack):也称 堆栈方法调用变量 的生存空间。

堆(heap)对象 的生存空间。

JVM启动时,它会从底层的操作系统取得一块内存,以此区段来执行Java程序。至于有多少内存,以及你是否能够调整它,都要视JVM和平台的版本而定。

所有对象都存活于可垃圾回收的堆heap上。

所有局部变量都存在于 栈stack 上相对应的 堆栈块 中。

变量的生存空间要看它是哪一种(这里不是指它的类型)变量而定:

实例变量:是被声明在类里,代表每个独立对象的字段。即 实例变量存在于所属的对象中。

对象创建时,会在堆上开辟一个空间,空间大小足以存放该对象的所有实例变量

①如果实例变量全都是主数据类型的,则Java会依据主数据类型的大小为该实例变量留下空间,int32位,long64位等。

②如果实例变量是个对象,则Java只会保该对象的引用量至于这个对象是否在堆上带有所属空间,就要看声明的时候有没有赋值,没有赋值就是null,没有空间。

*

局部变量栈变量):局部变量参数 是被声明在方法中,它们是暂时的,且生命周期只限于方法被放在 上的这段期间(也就是方法调用至执行完毕为止)

当调用一个方法时,带有该方法状态(包括执行到哪一行程序所有的局部变量的值)的 堆栈块 会被放在调用栈的栈顶。方法执行完成后,它的堆栈块就会被释放掉。

对象局部变量(即对象引用变量 / 非主数据类型变量)保存的是对象的引用而已,是存放在栈stack上( 主数据类型变量 和 对象引用变量 都是放在栈上的)。而对象本身只会存在堆heap上。

了解 堆栈机制 的原因:为了学习 变量的有效范围对象的建立内存管理,线程和异常处理(这两点后面的小节会讨论)。

对象的创建 (构造函数 (构造器))

声明对象和赋值的3个步骤:声明引用变量,创建对象,连接对象和引用。

Duck myDuck = new Duck();


第二个步骤:创建对象 过程的详解:

new Duck() 看起来像是在调用”Duck()”这个方法,其实它内部是在调用Duck类的 构造函数 Duck()。

构造函数

构造函数 Duck() 看起来很像方法,但它并不是方法。它带有 new 的时候会执行程序代码,即这段程序代码会在你初始化一个对象的时候先执行

如果你完全没有写任何一个构造函数,编译器会帮你自动写一个无 参的构造函数

如: public Duck(){ } //编译器自己写的

构造函数方法 相比,它没有返回值函数名一定要和类名相同。区分点在 有没有返回值,因为Java允许有和类同名的方法。

④关键特征之一:构造函数会在 对象被赋值给 引用 之前就执行。即 让你有机会介入new 的过程。

构造函数的意义:就是用来初始化对象的状态(实例变量)

利用构造函数初始化状态:
如果某种对象不应该在状态被初始化之前就使用,就别让任何人能够在没有初始化的情况下取得该种对象。
最好的方法就是把初始化的程序代码放在构造函数中,然后把构造函数设置成需要参数的。

class Duck{
int size;
Duck(){  //建议无参构造函数要有。指定默认值。
size = 22;   //给出状态初始化值。
}

Duck(int duckSize){ //使用参数进行状态初始化操作。当自己写了有参构造函数后,JVM就不会帮你写无参的构造函数了。
//但是建议无参的构造函数要有。
size = duckSize;
}
}

class Test{
public static void main(String[] args){
Duck d= new Duck(22);    //初始化赋值
System.out.println(d.size);
}
}


构造函数不会被继承

建议要有无参的构造函数(无论是否需要指定默认值),这样创建对象的方式更全面。因为你无法通过单一的构造函数就能很清楚地达到这个目的。

有很少的时候是不需要无参的构造函数,这时有默认值的无参构造函数存在是不合理的

例:Color这个类,它是用来设定字型或GUI按钮的颜色,没有无参构造函数。得使用 3个int来代表RGB三色 的构造函数。没有给参数会报错:错误:没有这样的构造函数。

Color c = new Color(3,45,200);

*

⑧如果一个类有一个以上的构造函数,这代表它们是重载的。它们参数的顺序类型必须不同,否则无法通过编译。

⑨将构造函数 设定为 私有private 的,代表 该类以外 不能存取(即创建对象),但是类中还是可以创建本类对象的。(这里涉及到一个Java设计模式,工厂模式,下一节会讨论)

[b]父子类 构造函数之间的关系[/b]

在创建某个对象时(通过new),对象会取得 所有实例变量(父子类所有的) 所需的 空间,包括一路继承下来的东西(即使是不能直接访问的父类的私有变量)。

父类 私有的 包装过的 实例变量 虽然不能被子类直接调用,但是它们在创建子类对象时,仍然会开辟空间创建它们(但不算是真正的对象),并存储在子类对象(堆)中。因为子类可能会用到某些继承下来的方法需要读取父类的实例变量。即子类继承的方法可能会使用父类的状态。

在继承里,创建子类对象也代表创建了父类和Object类的“实例”,但是从始至终都只有子类这一个对象。

构造函数链:在创建新对象时,构造函数在执行的时候,首先会执行父类的构造函数,会一直发生连锁反应到 Object 这个类。

调用父类构造函数的唯一方法是调用 super();。在子类 所有的 构造函数(构造函数如果没写编译器会自动添加一个无参构造函数)的第一行,如果你没有写 super();,则编译器会自动加上super();。(这里自动添加的指的是父类的无参构造函数)。若调用父类 有参 的构造函数,则使用 super(参数,…) 即可。

super()(无论 无参 或者 有参) 的调用必须是 第一行

抽象类 也是有构造函数的,虽然不能直接new出对象,但抽象的类还是父类,它的构造函数会在子类创建对象时执行。

[b]同一类中 在某个构造函数中调用重载版的另一个构造函数[/b]

在同一类中的构造函数里,在 第一行,使用 this()this(参数,…) 来调用重载的另一个构造函数。

this()(无论 无参 或者 有参) 的调用必须是 第一行

this 表示当前对象的引用this() 只能用在构造函数中。

每个构造函数可以选择调用 super()this() ,但是 不能同时调用

对象的生命周期 (垃圾收集器GC)

对象的生命周期 取决于它的 引用变量的存活,而引用变量的存活又看它是 局部变量 还是 实例变量

局部变量 的生命周期只在 声明该变量的方法中使用范围是在本方法中,本方法在调用其他方法时,局部变量还存活,但不在使用范围。

实例变量 的生命周期和 对象 相同,对象还活着,实例变量就也活着。 使用范围是在本类中。

public class Life{
int size;   //实例变量,本类可用

public void setSize(int s){   //s是局部变量,本方法可用
size = s;
}
}


4 . 局部变量 生命周期Life范围Scope 的区别:

Life : 只要 变量堆栈块 还存在于 堆栈 上,局部变量就还活着,直到方法出栈。

Scope : 局部变量的范围 只限于声明它的方法之内,如果在方法中调用了其他方法,那么不在范围内,但是局部变量还活着。

5 . 变量的生命周期对象生命周期 的影响:

①只要 有活着的 引用,对象也就活着。即就算 引用的范围 不在该对象之中(比如对象正在调用其他类的方法),只要引用还活着,对象就活着。

②如果对象的 唯一 引用死了,引用变量会和堆栈块一起解散。对象会从堆中被踢开,被正式声明为 可被垃圾回收器回收 状态。如果程序内存不足,GC就会去歼灭 部分 或 全部的 可回收对象

注意: 当对象用完了你就要抛弃,这样才能让垃圾回收器有东西可回收。

*

释放对象引用 的3种方法:

1. 将引用定义在方法中,方法结束时,引用会消失。

2.将引用赋值到其他对象上。

3.直接将引用设置为 null

*

关于 null :

null 代表 “空” 的字节组合(实际上是什么只有JVM才知道)。对 null 使用 圆点运算符 会在执行期遇到 NullPointerException 空指针异常。

如果有错误,欢迎指出。

如果对你有帮助,可以顶顶。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: