1.Java
静态绑定和动态绑定
1.1
静态绑定:
静态绑定就是所谓的程序编译时绑定,
java
中的变量都是静态绑定的
,
方法只有
static
和
final(
所有
private
默认是
final
的,子类不可能修改
父类的私有方法
)
是静态绑定的。编译时已经确切知道程序所要执行
的是哪一个类的哪一个方法,不存在重载等问题。
例如:
static
、
private
修饰的方法或者变量,对于方法的调用或者变
量的使用是不存在歧义的,所以在编译时即可确定。
1.2
动态绑定:
动态绑定运行时绑定,
编译时不知道程序所引用的对象只有在程序执
行时才能确定。
例如:
Parent p=new Child(),
父类中的
public
方法,如果在子类中进行
了重载,
声明的父类引用实际上绑定的是子类的对象,
在使用父类中
被重载的方法时,
其实指向的是子类中的方法,
这一点只能在程序被
运行到才能确定。
1.3
向上转型、向下转型:
·
Java
向上转型是安全的,而向下转型是不安全的。
·对于向上转型(
Java
多态的体现)
,给父类的引用绑定上子类的对
象,父类中的非
private
属性以及方法在子类中都得到了继承。在向
上转型的过程中并不会丢失父类的属性(
static/private
的属性或者方
法在声明对父类的引用时已经确定)
,所以这个过程是安全的。当程
序执行时,
父类被重载的方法会动态绑定为子类的方法
(若子类中未
进行重载,执行的将是父类的方法)
。由于向上转型声明的是对父类
型的引用,
所以在向上转型会丢失掉子类定义的非重载的方法
(其实
是隐藏了,可以通过
instanceof
进行向下转型的检查,进行
cast
来重
新获取对子类特有方法的调用)
。
·对于向下转型是不安全的,这一点很显然,声明子类的引用却动态
绑定上了父类对象
(因为是动态绑定,
所以这一类型转换错误只会在
执行时发现)
,子类中定义的变量或函数,父类中是不可能拥有的。
但是对于向上转型的逆过程这一安全性问题又可以得到解决,
因为堆
里保存的对象本身就是子类对象。
Parent
p=new Child();
if(a instanceOf Child)
(Child) p;//
向下转型安全
注:
Java
是
oop
语言,不同于
C++
(为了兼容
C
)
,
java
的所有类都
继承了
Object
。
Java
中的某些容器存储的对象也是通用类型
Object
,
由于
Java
继承的单根结构,所以容器可以存储所有类型的对象(向
上转型)
。但是从容器中取出对象时需要进行向下转型(除非想取到
Object
对象)
,这个过程就需要用到向下转型,而这里存在了不安全
因素,
程序会进行向下转型的检查,
会造成额外开销,
所以养成良好
的编程习惯,在使用容器时指定容器存储的对象类型。
2.GC
垃圾回收
2.1
对象存储
Java
不同于
C++
,
C++
对象通常需要通过调用析构函数进行对象
的释放,这与
C++
采用的堆栈方式存储对象机制相关(当然
C++
还提
供了其他的方式)
,对象的存储与释放对应堆栈的
push/pop
,采用这
种方式对象的创建以及释放会很快速,然而却限制了程序的灵活性,
因为对象内存分配得在程序执行前就得确定,
并且如果忽略了对象的
释放常常会导致内存泄漏。
Java
的对象存储是利用的堆机制,
Java
程序在编译时会创建对象
的引用(对象引用可以是堆栈存储)
,在程序执行时如果需要创建对
象,
则在程序所分配的堆中创建这样一个对象。
Java
堆类似于一个池,
而且对象的存放是紧凑的
(这样对于内存分配以及是释放都有好处)
,
当创建一个对象时,
会根据当前的堆
“指针”
查找到堆中的空闲区域,
然后向后分配给这个对象内存空间,分配后修改“指针”位置。从这
个角度上讲
Java
对象的创建和
C++
的堆栈存储对象效率上并没什么区
别,但是
Java
并不要求程序员人为的完成对对象的释放,而堆的空
间是有限的如何合理的分配堆释放那些无用的对象就需要使用到
Java
的垃圾回收了。
2.2
垃圾回收
2.2.1Java
的
GC
是一种“停止——复制、标记——清理、分代、自适
应”的垃圾回收。
2.2.2GC
原理
:
首先
GC
发生的时间,
一般有两种一种是内存不足、
另一种是空闲时,
因为垃圾回收需要暂停程序执行,
需要系统开销,
所以不到一定时候
不会进行垃圾回收。
对于垃圾回收原理,
当程序的
heap
空间不足时,
gc
发生,
需要对那些不再被引用的“死”对象进行释放,
进行垃圾回
收首先需要跟踪对象的引用以确定哪些对象可以被释放哪些不可以
被释放。
JVM
会根据堆栈中存放的对象引用,到
heap
中查找响应的
对象,因为对象的创建是在
new
的时候发生,我们无法事先知道一
个对象的生命周期,
但是我们可以找到对对象的所有引用,
当对该对
象的所有引用都结束后,
对象其实就可以进行释放。
对于对象内部引
用对象的情况,只需要按照“引用
—
>
对象
—
>
引用
…
”的方式一直找
下去即可完成对所有对象的跟踪。这样完成了对“垃圾”的查找。
2.2.3GC
方式
完成对“垃圾”的查找,如何对“垃圾进行清理”?垃圾清理之前,
程序需要被迫暂停,
因为
gc
不仅仅完成的是对
heap
中无用对象的清
理,同时也对对象的存储进行了整理。
2.2.3.1
垃圾清理首先采用的方式是“停止——复制”方法:
将堆中所有的“活对象”复制到另外一个堆中,原有的堆内所有对象
即可全部释放。对象被复制到另外的堆中,并且进行紧凑的排列,这
样程序在创建新对象时依赖对指针查找空闲空间即可。但是这样的
“停止——复制”
方式带来一个问题,
需要额外的多分配一倍的空间,
而且来回的在内存页中切换复制需要耗费时间,有的
JVM
采用的解
决方法是给堆分配几块大的内存页,
这样复制只需要在少数的几个内
存页中进行。
2.2.3.2
“标记——清扫”方法:
可以看出,
“停止——复制”的处理办法其实完成的是将活对象复制
保留,将整个堆内所有对象释放(无论对象是否被引用)
,然而对于
垃圾较少的情况下这种方式很浪费。
“标记——清扫”方法适用在程
序执行稳定垃圾较少的情况下,比如在完成“停止——复制”之后,