第五章 初始化与清理
5.2方法重载
- 方法名相同,参数列表不同(可以通过参数列表区分调用的哪个方法)
- 基本类型的重载:如果传入的数据类型(实际参数类型)小于方法中声明的形式参数类型,实际参数类型就会被提升,如果找不到接受char类型的,char会被直接提升为int,其他的基本类型会被转换为上一级类型(如果有对应方法的话),如果传入的参数类型大于形式参数类型,必须通过手动类型装换,否则会报错。
- 无法以返回值区分重在方法
5.4 this关键字
- 只能在方法内部使用,表示调用当前方法的对象的引用。
- 如果方法内部调用同一个类的其他方法,则不必加this。
- 如果方法的形参与类的成员变量重名,可以通过加this来区分。
- 为this加上参数列表将产生符合参数列表的某个构造器的明确表用,只能在构造器方法内使用这种方式,每个构造器内只能使用一次这种方式,并且this必须放在构造器方法第一行,
- static方法是没有this的方法,在static内部不能调用非静态方法(可以通过传递一个对象的引用到静态方法里面实现对非静态方法和非静态数据成员的访问)
5.5 清理:终结处理和垃圾回收
finalize()
- 一旦垃圾回收期准备释放对象占用的内存,就会调用器finalize()方法,并在下一次垃圾回收动作发生时,释放器占用的内存。所以内存不快用完,不会调用finalize()方法。
- finalize()方法并不等于C++中的析构函数
- java中的对象并非总是被回收(1.对象可能不被垃圾回收;2.垃圾回收并不得于“析构”)
- java并未提供和析构函数或相似的概念,如果你想做类似的清理工作,就必须自己手动创建一个执行清理的方法
- 内存不快用完,不会执行垃圾回收操作,当程序执行结束,垃圾回收一直没有释放你占用的空间,随着程序的结束,这部分空间也会交还给操作系统,这个策略是恰当的,垃圾回收本身也有开销,如果不执行垃圾回收,就不用了支付这部分开销。
5.5.1 finalize()的用途何在
- 垃圾回收只与内存有关
- 无论对象如何创建,垃圾回收都会释放其占用的内存,那么当在垃圾回收前调用finalize()释放对象占用的内存就变的毫无意义,但是为什么还要调用finalize()方法呢?是为了处理通过某种创建对象方式以外的方式为对象分配了存储空间,即本地方法,本地方法是一种在java中调用非java代码的方法,即通过非java方式为对象分配了内存,所以垃圾回收不起作用,需要调用finalize()来清楚空间
5.5.2 你必须试试清理
- 因为垃圾回收不一定会发生,所以finalize()不一定会被调用,所以如果你想进行释放存储空间之外的清理工作,你就必须明确调用某个恰当的java方法。
5.5.3 终结条件
- 当对象需要清理时,会调用finalize()方法,所以如果需要验证对象能否被安全的清理,可以在finalize()中添加验证方法,这样可以保证对象在被清理前,进行验证。、
5.5.4垃圾回收器如何工作
- 引用计数:当有引用链接至对象时,该对象引用计数加1,当引用离开作用域或被置为null时,引用计数减1,垃圾回收器会在含有全部对象的列表上遍历,当发现某个对象引用计数为0时,会立即释放其占用的空间,缺陷:如果对象存在循环引用,会出现对象应该被回收,但引用计数不为0。引用计数常用来说明垃圾回收器的工作方式,但未被用于java虚拟机
- 从堆栈和静态存储区开始,遍历所有的引用,对于发现的每个引用必须找到他所引用的对象,然后找到这个对象的所有引用,循环往复,知道所有根源于堆栈和静态存储区的引用所形成的网络全部被访问为止,没有方法访问到的对象就可以清楚,可以解决循环引用这个问题,这种对象根本不会被发现,自然也就清楚了(可能成员变量的引用没有存在堆栈或静态存储区)
- 自适应的垃圾回收计数
- 停止-复制:先停止程序,将所有通过方式2找到的活的对象从一个堆复制到另外一个堆,新堆中的对象时连续的,没有复制的可以清理,这种情况下,这些活的对象的引用必须修正。效率会降低原因:1.首先得有两个堆,然后在两个堆之间复制对象,有些java虚拟机的解决方式是把一个堆中分配几块较大的内存,直接在这些内存块中复制对象;2.复制:程序趋于稳定后只会产生少量的垃圾,如果还是来回复制,就很浪费。有些虚拟机解决方式时,当没有新垃圾产生时会转变成下述工作方式
- 标记-清扫:找到的活的对象会被标记,这个过程不会清理任何对象,当全部标记完成后,清理工作开始。没有标记的对象会被释放,剩下的堆空间不连续,如果想得到连续的空间,虚拟机必须整理剩下的对象。
- 内存分配以较大的块为单位,有了块以后,停止-复制就可以往废弃的块中拷贝对象,每个块都有相应的计数来记录他是否存活,如果这个块在某处被使用,计数就会加1,内含小型对象的块将被复制清理,垃圾回收会对上次清理之后新分配的块进行清理。大型对象不会被复制。java虚拟机将会监视,如果对象都会稳定,就会转变到标记-清扫模式,否则转变到停止-复制模式
- 即时(Just-In-Time,JIT)编译器的计数:1.编译所有代码,2.惰性评估:即时编译器只在必要的时候才编译代码
5.6 成员初始化
- java尽力保证所有的标量在使用前得到初始化。
- 对于方法内的局部变量,如果不初始化会报错
- 自动初始化:类的的成员变量(即字段)是基本类型,会有一个初始值,如果是对象引用,引用会获得一个初始值null。
5.6.1 指定初始化
- 可以为成员变量直接赋值进行初始化,也可以通过调用方法进行初始化
- 向前引用:可以先使用方法,再定义方法,但是不可以使用变量,再定义变量
5.7 构造器初始化
- 可以通过构造器进行初始化
- 对于基本类型和对象引用,会在构造器初始化或指定初始化之前先进行自动初始化。
5.7.2 静态数据的初始化
- 初始化的顺序是先静态对象(如果他们没有因前面的对象创建过程而被初始化),而后是非静态对象
- 当首次创建一个类的对象时,或首次访问属于那个类的静态对象(或静态方法时),执行静态数据的初始化
- 构造器是静态方法
- 当首次创建对象时(构造器为静态方)或者访问该类的静态方法或静态域时,java解释器查找类路径,定位class文件
- 然后载入Dog.class(创建一个class对象),有关静态初始化的动作都会执行,因此,静态初始化只会在Class对象首次加载时进行一次
- 当用new Dog()创建对象时,首先在堆上为Dog对象分配足够的空间。
- 这块存储空间会清零,这就自动将Dog对象中的所有基本数据类型设置为默认值,引用则被设置为null
- 执行所有出现在在字段定义处的初始化
- 执行构造器(执行new Dog()时会先执行一系列操作,然后再执行Dog()构造器方法,所以new Dog()和Dog()不是同一个东西)
5.7.3 显示的静态初始化
- java允许将多个静态初始化动作组织成一个特殊的静态语句(静态块)
public class Spoon{ static int i; static{ i = 47 } }
- 这段代码会在静态数据初始化时发生一次
5.8 数组初始化
- 数组是相同类型的,用一个标识符名称封装到一起的一个对象序列或基本数据类型序列。int[] a1 或 int a1[]
- 编译器不允许指定数组的大小
- int[] a只是数组的一个引用,为了给数组创建相应的存储空间,必须写初始化表达式。int[] a = new int[3]或int[] a = {1,2,3}