初始化
构造器(constructor):就是C++中的构造函数,不写也会有一个默认的无参构造函数,会给所有成员变量进行一个初始化 重载:方法名字相同,但是参数不同(最开始是为了实现构造器而设置的,后来发现可以帮助多态的实现)。
为什么重载不能根据返回值的不同来确定?因为存在歧义,比如void f(); int f();
同时存在,然后主函数写了main(){f();}
,此时编译器无法知道这个f()
指的是哪一个函数。 涉及基本类型的函数重载:基本类型能够升级,比如void f(float a)
可以通过f(5)
进行调用,即整数5
会在调用的时候因为找不到int
参数类型的函数而升级为float
类型,但是类型是不能降级的。 this 关键字
this
其实就是对象本身,一般对象调用方法的时候,会隐式地讲第一个参数设置为对象本身。不可以出现在static
修饰的静态函数中,因为静态方法可以通过类名直接调用,而不需要通过对象,因此无法传入对象。 this 关键字可以作为返回值,从而进行类方法的级联调用this 关键字可以在构造器中调用构造器,从而减少初始化代码,但是只能调用一个构造器,而且绝不能在其他类方法中调用
清理:终结处理与垃圾清理
终结处理
finalize()
:并不等价于析构函数。一旦GC准备事放对象占用的存储空间,将首先调用finalize()
方法,并且在下一次GC发生的时候,才会回收对象占用的内存。finalize()
和析构函数相同点在于,它也会默认存在于一个类中,它是在对象即将被清理的情况下才可能会被调用,用于检查在垃圾回收前可能会出现的逻辑问题
class Book {
boolean checkOut = false ;
Book ( boolean checkout) {
checkOut = checkout;
}
void checkIn ( ) {
checkOut = false ;
}
protected void finalize ( ) {
if ( checkOut) System. out. println ( "Error: checked out" ) ;
super . finalize ( ) ;
}
}
public class TerminationCondition {
public static void main ( ) {
Book novel = new Book ( true ) ;
novel. checkIn ( ) ;
new Book ( true ) ;
System. gc ( ) ;
}
}
在gc
的时候,会调用finalize
函数。如果有一个Book对象在销毁前没有被checkIn
那么会触发finalize()
中的警告。
垃圾清理
一个有趣的现象:java
的垃圾清理器提高了对象在堆上分配的效率,甚至可以媲美其他语言在栈上分配的效率。在某些JVM
中,堆的实现就想传送带,每分配一个对象,就会向前移动一个位置,这就使得存储空间分配速度很快,因为java的堆指针只需要简单的往后移动到未分配空间的区域即可,而不用和C++一样,找到一块足够大的空间进行分配。 为什么可以像传送带一样呢,这不会导致内存溢出吗?因为java的垃圾回收会在适当的时候进行内存回收,举个例子
class mem {
public void f ( ) {
while ( true ) {
int [ ] a = new int [ 10000 ] ;
}
}
}
这段代码是不断分配内存,换成C++程序,执行一段时间之后,就会直接崩溃,而在java中,我们会发现,内存持续增长到某个阈值的时候,就会上下起伏。 所以垃圾清理(Garbage Collector, GC)是如何工作的?
引用计数:计算每个对象的引用数量,如果引用数量为0了,说明该对象已经没有用处了,可以直接清理,一旦有其他引用指向了这个对象,那么引用数量+1。引用计数方法无法解决环形引用问题,如A a = new A(); A b =a; A c = b; a = c;
这样,a,b,c
都是垃圾,因为都没有指向有意义的地方,但是每个对象的引用都是1,导致引用计数失效。这种方法极少被JVM使用。 抛弃引用计数,如何找到存活对象:从堆和静态存储区中寻找对象,如果存在对象且存在对应的引用,那么就以这些引用根,遍历找到所有相关的引用。 标记-清理:每找到一个存活对象,就进行一个标记。标记完所有对象之后,对没有标记的对象进行内存的释放。好处在于不需要复制存活对象,但是剩下的堆空间是不连续的,如果想要得到连续对象,仍然需要重新分配内存。 停止-复制(非后台清理):先暂停程序运行,将所有活着的对象从当前堆复制到另一个堆,然后清理当前堆的所有对象。然后,由于每个对象都换了位置,所有的引用都需要更新自己指向的地址,位于堆和静态存储区的直接引用可以直接修改 (这句话什么意思,GC直接修改吗),其它非直接引用则需要在遍历的过程中才能找到对象的新地址 (意思是需要手动更新?好像通过二级引用的方法可以实现不更新这个引用,但是速度相对慢) java的GC能够自适应的切换停止-复制
与标记-清理
两种方法, 当JVM看到每个对象都很稳定,停止-拷贝
存在大量拷贝的情况下,会切换成标记-清理
的情况,反之,当堆中存在大量碎片的情况下,又会切换成停止-拷贝
的方法
参考