10.构造器与垃圾收集器(对象的开始和结束)
2018-01-18 16:52
232 查看
目录
概述
栈与堆 内存中的两种区域 变量和对象的生存空间 变量的有效范围
对象的创建 构造函数 构造器
父子类 构造函数之间的关系
同一类中 在某个构造函数中调用重载版的另一个构造函数
对象的生命周期 垃圾收集器GC
下面会讨论 对象如何创建,存在何处,如何保存和抛弃更有效率。(包括堆,栈,范围,构造器,超级构造器,空引用等要点 )
堆(heap):对象 的生存空间。
JVM启动时,它会从底层的操作系统取得一块内存,以此区段来执行Java程序。至于有多少内存,以及你是否能够调整它,都要视JVM和平台的版本而定。
所有对象都存活于可垃圾回收的堆heap上。
所有局部变量都存在于 栈stack 上相对应的 堆栈块 中。
变量的生存空间要看它是哪一种(这里不是指它的类型)变量而定:
实例变量:是被声明在类里,代表每个独立对象的字段。即 实例变量存在于所属的对象中。
对象创建时,会在堆上开辟一个空间,空间大小足以存放该对象的所有实例变量。
①如果实例变量全都是主数据类型的,则Java会依据主数据类型的大小为该实例变量留下空间,int32位,long64位等。
②如果实例变量是个对象,则Java只会保该对象的引用量。至于这个对象是否在堆上带有所属空间,就要看声明的时候有没有赋值,没有赋值就是null,没有空间。
*
局部变量(栈变量):局部变量 和 参数 是被声明在方法中,它们是暂时的,且生命周期只限于方法被放在 栈 上的这段期间(也就是方法调用至执行完毕为止)
当调用一个方法时,带有该方法状态(包括执行到哪一行程序 和 所有的局部变量的值)的 堆栈块 会被放在调用栈的栈顶。方法执行完成后,它的堆栈块就会被释放掉。
对象局部变量(即对象引用变量 / 非主数据类型变量)保存的是对象的引用而已,是存放在栈stack上( 主数据类型变量 和 对象引用变量 都是放在栈上的)。而对象本身只会存在堆heap上。
了解 堆栈机制 的原因:为了学习 变量的有效范围,对象的建立,内存管理,线程和异常处理(这两点后面的小节会讨论)。
第二个步骤:创建对象 过程的详解:
new Duck() 看起来像是在调用”Duck()”这个方法,其实它内部是在调用Duck类的 构造函数 Duck()。
构造函数:
① 构造函数 Duck() 看起来很像方法,但它并不是方法。它带有 new 的时候会执行程序代码,即这段程序代码会在你初始化一个对象的时候先执行。
②如果你完全没有写任何一个构造函数,编译器会帮你自动写一个无 参的构造函数。
如: public Duck(){ } //编译器自己写的
③构造函数 和 方法 相比,它没有返回值,函数名一定要和类名相同。区分点在 有没有返回值,因为Java允许有和类同名的方法。
④关键特征之一:构造函数会在 对象被赋值给 引用 之前就执行。即 让你有机会介入new 的过程。
⑤构造函数的意义:就是用来初始化对象的状态(实例变量)。
⑥构造函数不会被继承。
⑦建议要有无参的构造函数(无论是否需要指定默认值),这样创建对象的方式更全面。因为你无法通过单一的构造函数就能很清楚地达到这个目的。
有很少的时候是不需要无参的构造函数,这时有默认值的无参构造函数存在是不合理的:
例: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() ,但是 不能同时调用。
局部变量 的生命周期只在 声明该变量的方法中。 使用范围是在本方法中,本方法在调用其他方法时,局部变量还存活,但不在使用范围。
实例变量 的生命周期和 对象 相同,对象还活着,实例变量就也活着。 使用范围是在本类中。
4 . 局部变量 生命周期Life 和 范围Scope 的区别:
Life : 只要 变量堆栈块 还存在于 堆栈 上,局部变量就还活着,直到方法出栈。
Scope : 局部变量的范围 只限于声明它的方法之内,如果在方法中调用了其他方法,那么不在范围内,但是局部变量还活着。
5 . 变量的生命周期 对 对象生命周期 的影响:
①只要 有活着的 引用,对象也就活着。即就算 引用的范围 不在该对象之中(比如对象正在调用其他类的方法),只要引用还活着,对象就活着。
②如果对象的 唯一 引用死了,引用变量会和堆栈块一起解散。对象会从堆中被踢开,被正式声明为 可被垃圾回收器回收 状态。如果程序内存不足,GC就会去歼灭 部分 或 全部的 可回收对象。
注意: 当对象用完了你就要抛弃,这样才能让垃圾回收器有东西可回收。
*
释放对象引用 的3种方法:
1. 将引用定义在方法中,方法结束时,引用会消失。
2.将引用赋值到其他对象上。
3.直接将引用设置为 null。
*
关于 null :
null 代表 “空” 的字节组合(实际上是什么只有JVM才知道)。对 null 使用 圆点运算符 会在执行期遇到 NullPointerException 空指针异常。
如果有错误,欢迎指出。
如果对你有帮助,可以顶顶。
概述
栈与堆 内存中的两种区域 变量和对象的生存空间 变量的有效范围
对象的创建 构造函数 构造器
父子类 构造函数之间的关系
同一类中 在某个构造函数中调用重载版的另一个构造函数
对象的生命周期 垃圾收集器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 空指针异常。
如果有错误,欢迎指出。
如果对你有帮助,可以顶顶。
相关文章推荐
- 第九章 构造器与垃圾收集器---对象的前世今生
- 第九章:构造器与垃圾收集器-对象的前世今生
- 对象引用是怎样严重影响垃圾收集器的
- Java垃圾收集——对象已死?
- 读 - 深入理解java虚拟机 - 笔记(六-1) - 垃圾收集器和内存分配策略(3章)-对象已死吗
- 看Java中对象引用如何严重影响垃圾收集器(2)
- 对象引用是怎样严重影响垃圾收集器
- 对象引用及垃圾收集算法
- 深入理解java虚拟机(四):对象存活判定算法和垃圾收集算法
- jvm垃圾收集器回收什么样的对象以及各种引用
- 1.3垃圾回收——内存分配策略、垃圾收集器(G1)、GC算法、GC参数、对象存活的判定
- java学习笔记之对象清除、垃圾收集
- JAVA对象引用与垃圾收集
- 垃圾收集与对象生命拯救(读书笔记)
- 深入理解java虚拟机(四):对象存活判定算法和垃圾收集算法
- 使用java的Calendar对象获得当前日期的上几个度开始、结束时间
- Java 垃圾收集器与内存分配策略(一):对象“死亡判断”和四种引用
- 对象引用是怎样严重影响垃圾收集器的
- Java垃圾收集--对象的finalize()方法
- 《深入理解JAVA虚拟机》学习笔记(二)JAVA垃圾收集之对象存活判定算法