Java编程思想——第五章:初始化与清理
Java采用构造器,创建对象时自动调用。对于不再使用的内存资源,使用垃圾回收器自动将其释放
1.用构造器确保初始化
-
创建对象时,如果其类具有构造器,Java就会在用户有能力操作对象之前自动调用相应的构造器,从而保证了初始化的进行。
-
构造器采用与类相同的名称,构造器是一种特殊类型的方法,没有返回值。
-
不接受任何参数的构造器叫默认构造器(无参构造器)。
2.方法重载
为了让方法名相同而形式参数不同的构造器存在,必须用到方法重载
2.1 区分方法重载
- 每个重载的方法都必须由一个独一无二的参数类型列表
2.2 涉及基本类型的重载
- 基本类型能从一个较小的类型自动提升至一个较大的类型
2.3 以返回值区分重载方法
-
根据方法的返回值区分重载方法是 行不通的。
//方法重载 void f(){} int f(){return 1;} //调用,这样的情况就不知道是在调用哪个方法 f()
3.默认构造器
- 类中没有明确的构造器,编译器会自动帮你创建一个默认构造器。但是自己明确地创建了构造器。则编译器不会帮你创建默认构造器。
4.this关键字
this关键字只能在方法内部使用,表示对“调用方法的那个对象”的引用。
class Banana{
void peel(int i){}
}
public class BananaPeel{
public static void main(String[] args){
Banana a = new Banana();
a.peel();
}
}
/**
* 上述的Banana对象在调用peel方法时,实际上有一个参数this
* a.peel(this);
*/
4.1 构造器中调用构造器
- 为避免代码重复,可以使用this在一个构造器中调用另一个构造器。
4.2 static的含义
- static方法是没有this的方法。在static方法的内部不能调用非静态方法,反过来可以。在没有创建对象的情况下,可以通过类名调用static方法。
5.清理:终结处理和垃圾回收
假定你的对象(并非使用new)获得一块特殊的内存区域,由于垃圾回收器只知道释放那些经有new分配的内存,所以它不知道该如何释放该对象的这块特殊的内存。为了应对这种情况,Java允许在类中定义一个名为finalize()的方法。
它的工作原理假定是这样的:一旦垃圾回收器准备好释放对象占用的存储空间,将首先调用其finalize()方法,并在下一次垃圾回收动作发生时,才会真正回收对象占用的内存。
只要程序没有濒临存储空间用完的那一刻,对象占用的空间就总也得不到释放。如果程序执行结束,并且垃圾回收器一直没有释放你创建的任何对象的存储空间,则随着程序退出,那些资源也会全部交换给操作系统
5.1 finalize()的用途何在
- 垃圾回收器使用的原因是:为了回收程序不再适用的内存。所以对于与垃圾回收有关的任何行为来说(尤其finalize()方法),它们也必须通内存及其回收有关。
5.2 你必须实施清理
- 无论是垃圾回收还是终结,都不保证一定会发生。如果JVM并未面临内存耗尽的情形,它是不会浪费时间去执行垃圾回收以回复内存的。
5.3 终结条件
5.4 垃圾回收器如何工作
-
垃圾回收器对于提高对象的创建速度,却具有明显的效果。
- 分配新对象的时候,Java的堆指针只是简单地移动到尚未分配的区域。
- gc工作的时候,将一面回收空间,一面使堆中的对象 紧凑排列 。
-
垃圾回收技术
-
引用记数(从未被应用)
每个对象都有一个引用计数器,当有引用连接至对象时,引用计数加1.当引用离开作用域或被置为null的时候,引用计数减1,虽然管理引用记数的开销不大,但会持续发生。gc会在含有全部对象的列表上遍历,当发现某个对象的引用记数为0时,就释放其占用的空间。
-
自适应的垃圾回收技术
对于如何处理找到的存活对象,取决于不同的虚拟机实现
-
停止-复制(stop-and-copy)
先暂停程序的运行,然后将所有存活的对象从当前堆复制到另一个堆,没有被复制的都是垃圾。对象被复制到新堆的时候就会摆出紧凑排列。但是这样的方式效率会降低。
-
标记-清扫(mark-and-sweep)
程序进入稳定后,可能只会产生少量垃圾,如果还是复制到新堆中,就会很浪费。为避免这种情形,一些JVM会进行检查:如果没有新垃圾产生,就会转到另一种工作模式--标记清扫。对于一般用途而言,标记-清扫速度很慢,但是在垃圾很少的情况下,它的速度很快。
工作思路:从堆栈和静态存储区出发,遍历所有的引用,进而找出所有存活的对象。每找到一个存活的对象,就会给对象设一个标记,这个过程不会回收任何对象。只有全部标记工作完成的时候,清理工作才会开始。在清理过程中,没有标记的对象将被释放,不会发生任何复制动作。所以剩下的堆空间是不连续的,gc要是希望空间连续的话,就得重新整理剩下的对象。
-
-
6.成员初始化
-
Java尽力保证:所有变量在使用前都能得到恰当的初始化。
-
类的每个基本数据成员保证都会有一个初始值。如果不将对象引用初始化,会默认为null。
-
如果但对于方法的局部变量,Java以编译是错误形式来贯彻这种保证。因此强制程序员提供一个初始值,往往能够帮助找出程序的缺陷。
7.构造器初始化
在运行时刻,可以调用方法或执行某些动作来确定初值。但要牢记:无法阻止自动初始化的进行,它将在构造器被调用之前发生。
7.1 初始化顺序
- 在类的内部,变量定义的先后顺序决定了初始化的顺序。即使变量定义散布于方法定义之间,它们仍旧会在任何方法(包括构造器)被调用之前得到初始化。
7.2 静态数据的初始化
-
无论创建多少个对象,静态数据都只占用一份存储区域。static关键字不能应用于局部变量,因此它只能作用于域。如果一个域是静态的基本类型域,且也没有对它进行初始化,那么它就会获得基本类型的标准初值;如果它是一个对象引用,那么它的默认初始化值就是null。
-
静态初始化只有在必要时刻才会进行。当对象被创建的时候,初始化才会开始。
-
初始化的顺序,先是静态对象,而后是非静态对象。
7.3 显式地静态初始化
- Java允许将对个静态初始化动作组织成一个特殊的静态子句(静态块),这段代码只执行一次。
7.4 非静态示例初始化
- 实例初始化,用来初始化每一个对象的非静态变量。
8.数组初始化
-
将一个数组复制给另一个数组,只是复制了一个引用
int a1 = {1,2,3}; int a2 = a1; for(int i = 0; i < a2.length; i ++){ a2[i] = a2[i] + 1; } for(int i = 0; i < a1.length; i ++){ print(a1[i]); } //234
9.枚举类型
- 当创建enum时,编译器会自动添加一些有用的特性。例如,它会自动创建
toString()
方法,以便你可以方便地显示某个enum实例的名字。还会创建ordinal()
方法,用来表示某个特定enum常量的声明顺序,以及static value()
方法,用来按照enum方法的声明顺序,产生有这些常量值构成的数组。
31/05/2019 :created
01/06/2019 :add 6,7,8,9