现在的位置: 首页 > 综合 > 正文

《Java编程思想》学习笔记1——面向对象和JVM基础

2014年11月22日 ⁄ 综合 ⁄ 共 5407字 ⁄ 字号 评论关闭

1.java中的4种访问制权限:

(1).public:最大访问控制权限,对所有的类都可见。

(2).protect:同一包可见,不在同一个包的所有子类也可见。

(3).default:包访问权限,即同一个包中的类可以可见。默认不显式指定访问控制权限时就是default包访问控制权限。

(4).private:最严格俄访问控制权限,仅该类本身可见,对外一切类都不可以访问(反射机制可以访问)。

2.面向对象编程中两种对象组合方式——is-a 和 has-a:

(1).is-a组合:一个类继承具有相似功能的另一个类,根据需要在所继承的类基础上进行扩展。

优点:具有共同属性和方法的类可以将共享信息抽象到父类中,增强代码复用性,同时也是多态的基础。

缺点:子类中扩展的部分对父类不可见,另外如果共性比较少的时候使用继承会增加冗余代码。

(2).has-a组合:has-a组合是在一个类中引用另一个类作为其成员变量。

优点:可扩展性和灵活性高。在对象组合关系中应优先考虑has-a组合关系。

缺点:具有共性的类之间看不到派生关系。

3.多态:

在面向对象编程中,子类中拥有和父类相同方法签名的方法称为子类方法覆盖父类方法,当调用子类方法的某个操作时,不必明确知道子类的具体类型,只需要将子类类型看作是父类的引用调用其操作方法,在运行时,JVM会根据引用对象的具体子类类型而调用应该的方法,这就是多态。

多态的基础是java面向对象编程的晚绑定机制。编程中有如下两种绑定机制:

(1).早绑定:一般在非面向对象编程语言中使用,在程序编译时即计算出具体调用方法体的内存地址。

(2).晚绑定:面向对象编程语言中经常使用,在程序编译时无法计算出具体调用方法体的内存地址,只进行方法参数类型和返回值类型的校验,在运行时才能确定具体要调用方法体的内存地址。

4.java单继承的优点:

相比于C++的多继承,java只支持类的单继承,java中的所有类的共同基类是Object类,Object类java类树的唯一根节点,这种单继承有以下好处:

(1).单继承可以确保所有的对象拥有某种共同的特性,这样对于JVM虚拟机对所有的类进行系统级的操作将提供方便,所有的java对象可以方便地在内存堆栈中创建,传递参数也变的更加方便简单。

(2).java的单继承使得实现垃圾回收器功能更加容易,因为可以确保JVM知道所有对象的类型信息。

5.选择容器对象两个原则:

(1).容器所能提供不同的类型的接口和外部行为是否能够满足需求。

(2).不同容器针对不同的操作效率不同。

6.类型转换:

Java中有两种常见的类型转换:向上类型转换(upcast)和向下类型转换(downcast):

(1).向上类型转换(upcast):

向上类型转换是将子类对象强制类型转换为父类类型,经典用法是面向对象的多态特性。向上类型转换时,子类对象的特性将不可见,只有子类从父类继承的特性仍然保持可见,向上类型转换时编译器会自动检查是否类型兼容,通常是安全的。

(2).向下类型转换:

向下类型转换是将父类类型强制转换为子类类型,转换过后父类中不可见的子类特性又恢复可见性,向下类型转换时,编译器无法自动检测是否类型兼容,往往会产生类型转换错误的运行时异常,通常不安全。

7.java中5个存放数据的地方:

(1).寄存器(Registers):位于CPU内部,是速度最快的存储区,但是数量和容量有限。在java中不能直接操作寄存器。

(2).栈(Stack):栈位于通用随机访问存储器 (General random-access memory,RAM,内存) 中,通过处理器的栈指针访问,栈指针从栈顶向栈底分配内存,从栈底向栈顶释放内存。栈是仅次于寄存器的速度第二快的存储器,在java程序中,一般的8种基本类型数据和对象的引用通常存放在栈内存中,不通过new关键字的字符串对象也是存放在栈的字符串池中。栈的优势是,存取速度比堆要快,仅次于寄存器,栈数据可以共享。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。

(3).堆(Heap):也是位于通用随机访问存储器 (General random-access memory,RAM,内存) 中的共享内存池。Java的堆是一个运行时数据区,类的对象从中分配空间,凡是通过new关键字创建的对象都存放在堆内存中,它们不需要程序代码来显式的释放。堆是由垃圾回收来负责的,堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态分配内存,存取速度较慢。

(4).常量存储器(Constant storage):java中的常量是存放在系统内嵌的只读存储器中(read-only memory,ROM)的。

(5).非随机存储器(Non-RAM storage):对于流对象和持久化对象,通常存放在程序外的存储器,如硬盘。

8.移位运算:

左移运算符<<:将比特位左移指定位数,右边部分补0,左移一位相当于乘2。

右移运算符>>:将比特位右移指定位数,如果是正数,左边第一位(符号位)补0,其余位补0,如果是负数,左边第一位补1,其余位补0。右移一位相当于除2。

无符号右移运算符>>>:将比特位右移指定位数,不论是正数或者负数,左边移除位统统补0。

9.java中,比int类型小的原始类型(char、byte、short)进行数学运算或者位运算时,数据类型首先转换成int类型,然后进行相应的运算。

10.方法重载(overloading):方法同名,参数列表不同称为方法重载,注意方法的返回值类型不同不能作为方法重载。

11.java中的析构函数:

Java中没有像C/C++的析构函数,用来销毁不用的对象是否内存空间,只有以下三个方法用于通知垃圾回收器回收对象。

(1).finalize( )只是通知JVM的垃圾收集器当前的对象不再使用可以被回收了,但是垃圾回收器根据内存使用状况来决定是否回收。

finalize()最有用的地方是在JNI调用本地方法时(C/C++方法),调用本地方法的析构函数消耗对象释放函数。

(2). System.gc()是强制析构,显式通知垃圾回收器释放内存,但是垃圾回收器也不一定会立即执行,垃圾回收器根据当前内存使用状况和对象的生命周期自行决定是否回收。

(3).RunTime.getRunTime().gc()System.gc()类似。

注意:这三个函数都不能保证垃圾回收器立即执行,推荐不要频繁使用。

12.垃圾回收器原理:

(1).引用计数(ReferenceCounting)垃圾回收算法

一种简单但是速度较慢的垃圾回收算法,每个对象拥有一个引用计数器(Reference Counter),当每次引用附加到这个对象时,对象的引用计数器加1。当每次引用超出作用范围或者被设置为null时,对象的引用计数器减1。垃圾回收器遍历整个对象列表,当发现一个对象的引用计数器为0时,将该对象移出内存释放。

引用计数算法的缺点是,当对象环状相互引用时,对象的引用计数器总不为0,要想回收这些对象需要额外的处理。

引用计数算法只是用来解释垃圾回收器的工作原理,没有JVM使用它实现垃圾回收器。

引用计数的改进算法:

任何存活的对象必须被在静态存储区或者栈(Stack)中的引用所引用,因此当遍历全部静态存储区或栈中的引用时,即可以确定所有存活的对象。每当遍历一个引用时,检查该引用所指向的对象,同时检查该对象上的所有引用,没有引用指向的对象和相互自引用的对象将被垃圾回收器回收。

(2).暂停复制(stop-and-copy)算法:

垃圾回收器的收集机制基于:任何一个存活的对象必须要被一个存储在栈或者静态存储区的引用所引用。

暂停复制的算法是:程序在运行过程中首先暂停执行,把每个存活的对象从一个堆复制到另一个堆中,已经不再被使用的对象被回收而不再复制。

暂停复制算法有两个问题:

a.必须要同时维护分离的两个堆,需要程序运行所需两倍的内存空间。JVM的解决办法是在内存块中分配堆空间,复制时简单地从一个内存块复制到另一个内存块。

b.第二个问题是复制过程的本身处理,当程序运行稳定以后,只会产生很少的垃圾对象需要回收,如果垃圾回收器还是频繁地复制存活对象是非常低性能的。JVM的解决方法是使用一种新的垃圾回收算法——标记清除(mark-and-sweep)。

一般来说标记清除算法在正常的使用场景中速度比较慢,但是当程序只产生很少的垃圾对象需要回收时,该算法就非常的高效。

(3).标记清除(mark-and-sweep)算法:

和暂停复制的逻辑类似,标记清除算法从栈和静态存储区开始追踪所有引用寻找存活的对象,当每次找到一个存活的对象时,对象被设置一个标记并且不被回收,当标记过程完成后,清除不用的死对象,释放内存空间。

标记清除算法不需要复制对象,所有的标记和清除工作在一个内存堆中完成。

注意:SUN的文档中说JVM的垃圾回收器是一个后台运行的低优先级进程,但是在早期版本的JVM中并不是这样实现的,当内存不够用时,垃圾回收器先暂停程序运行,然后进行垃圾回收。

(4).分代复制(generation-copy)算法:

一种对暂停复制算法的改进,JVM分配内存是按块分配的,当创建一个大对象时,需要占用一块内存空间,严格的暂停复制算法在释放老内存堆之前要求把每个存活的对象从源堆拷贝到新堆,这样做非常的消耗内存。

通过内存堆,垃圾回收器可以将对象拷贝到回收对象的内存堆中,每个内存块拥有一个世代计数(generation count)用于标记对象是否存活。每个内存块通过对象被引用获得世代计数,一般情况下只有当最老的内存块被回收时才会创建新的内存块,这主要用于处理大量的短存活周期临时对象回收问题。一次完整的清理过程中,内存块中的大对象不会被复制,只是根据引用重新获得世代计数。

JVM监控垃圾回收器的效率,当发现所有的对象都是长时间存活时,JVM将垃圾回收器的收集算法调整为标记清除,当内存堆变得零散碎片时,JVM又重新将垃圾回收器的算法切换会暂停复制,这就是JVM的自适应分代暂停复制标记清除垃圾回收算法的思想。

13.java即时编译技术(JIT):

Java的JIT是just-in-timecomplier技术,JIT技术是java代码部分地或全部转换成本地机器码程序,不再需要JVM解释,执行速度更快。

当一个”.class”的类文件被找到时,类文件的字节码被调入内存中,这时JIT编译器编译字节码代码。

JIT有两个不足:

(1).JIT编译转换需要花费一些时间,这些时间贯穿于程序的整个生命周期。

(2).JIT增加了可执行代码的size,相比于压缩的字节码,JIT代码扩展了代码的size,这有可能引起内存分页,进而降低程序执行速度。

对JIT不足的一种改进技术是延迟评估(lazy evaluation):其基本原理是字节码并不立即进行JIT编译除非必要,在最近的JDK中采用了一种类似延迟JIT的HotSpot方法对每次执行的代码进行优化,代码执行次数越多,速度越快。

14.非内部类的访问控制权限只能是默认的包访问权限或者是public的,不能是protected和private的。内部类的访问控制权限可以是protected和private。

15.Java中的高精度数值类型:

BigInteger和BigDecimal是java中的高精度数值类型,由于它们是用于包装java的基本数据类型,因此这两个高精度数值类型没有对应的原始类型。

(1).BigInteger支持任意精度的整数,即使用BigInteger可以表示任意长度的整数值而在运算中不会因为范围溢出丢失信息。

(2).BigDecimal支持任意精度的固定位数浮点数,可以用来精确计算货币等数值。

普通的float和double型浮点数因为受到小数点位数限制,在运算时不能准确比较,只能以误差范围确定是否相等,而BigDecimal就可以支持固定位数的浮点数并进行精确计算。

16.Java只处理public和protected访问控制权限成员的文档注释,private和默认的包访问控制权限成员的文档注释将被忽略。

17.Java中赋值运算:

Java中赋值运算是把赋值运算符”=”右边的值简称右值拷贝到赋值运算符左边的变量,如a=b,即把b代表的变量或常量值复制给变量a,切记a只能是变量,不能说常量值。

(1).原始类型赋值运算:

Java中8种原始数据类型赋值运算是将赋值运算符右边的值拷贝到赋值运算符左边的变量中。

原始类型赋值运算后,无论改变赋值运算符那一边的值,都不会影响赋值运算符另一边的值。

(2).引用类型的赋值运算:

Java中除了8中原始数据类型外,所有的数据类型都是对象类型,对象类型的赋值运算是操作引用,如a=b,把b引用赋值给a引用,即原本b引用指向的对象现在由a和b引用同时指向。

引用赋值运算符又叫别名运算符,即它相当于给引用对象取了一个别名,其实引用的还是同一个对象。

引用类型的赋值运算,如果赋值运算符任意一边的引用改变了被引用对象的值,赋值运算符另一边的引用也会受影响,因为两个引用指向的是同一个被引用的对象。

抱歉!评论已关闭.