Java札记


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

“标记——清扫”方法:

 

可以看出,

“停止——复制”的处理办法其实完成的是将活对象复制

保留,将整个堆内所有对象释放(无论对象是否被引用)

,然而对于

垃圾较少的情况下这种方式很浪费。

“标记——清扫”方法适用在程

序执行稳定垃圾较少的情况下,比如在完成“停止——复制”之后,

你可能感兴趣的:(Java札记)