您的位置:首页 > 编程语言 > Java开发

《THINKING IN JAVA》随笔

2016-03-02 16:49 537 查看
第三章  操作符

在最底层,Java的数据库时通过使用操作符来操作的。

第四章  控制执行流程

一些语句   没啥了

第五章 初始化与清理

用类似C++的构造起概念(构造方法)初始化对象。

清理:GC机制会回收由new创建出来的内存区域,对于那些不是new出来的内存区域(非java方法创建的),可以定义finalize(),原理是这样的:一旦GC准备回收存储空间的时候(并不一定发生),将首先调用finalize()方法,并在下一次回收时才会真正释放对象占用的内存。如果一直没有释放,当程序退出的时候这些资源也是会还给系统的。

彻底了解GC:

先了解下其他系统中的垃圾回收机制。

1.引用计数。简单但是速度很慢,每个对象都有一个引用计数器,当有引用连接至对象时,引用计数加1;当引用离开作用域或置为null时,引用计数器减1。

虽然这样的方式管理开销不大,但这项开销会在整个程序的生命周期中持续进行。一旦发现某个对象引用数位0,就回收该对象。在循环中就暴露了问题,循环引用的对象可能会出现对象应该被回收,但引用计数却不为0的情况。对于引用计数的这种机制而言,定位这样的交互自引用的对象组所需要的工作量很大。这种方式一般用来解释说明垃圾回收的工作方式,并不适合真的应用到具体的实现中。

在一些更快的模式中,垃圾回收器并非基于引用计数技术。他们依据的思想是对任何“活”的对象,一定能最终追溯到其存活在堆栈或静态存储区中的引用。这个引用链条可能会穿过数个对象层次(引用传递)。如果从堆栈或静态存储区开始遍历所有的引用,就能找到所有的“活”的对象。对于发现的引用,必须追踪它所引用的对象,然后是此对象所包含的所有引用,如此反复进行,直到“根源于堆栈和静态存储区的引用”所形成的网络全部被访问为止。由于所访问的对象都是“活”的,这样就解决了“交互自引用的对象组”的问题。java虚拟机就采用了一种自适应的垃圾回收技术。如何处理找到的存活的对象取决于不同的java虚拟机的实现。

有种“停止-复制”方式

这意味着先暂停程序的运行(因此不是后台回收模式),然后将所有存活的对象从当前堆栈复制到另一个堆,没有被复制的全部都是垃圾。在新的堆中,所有的对象都是挨着的,紧凑排列,然后就可以直接分配新空间了。搬家的引用必须修正,位于堆栈和静态存储区的引用可以直接修正,单可能还有其他指向这些对象的引用,在遍历堆栈和静态存储区中会被找到。这种方式效率会降低(要暂停程序),还要分配两个堆(来回倒腾)。但是在程序进入稳定后,产生的垃圾较少,这种复制方式仍然会讲所有的内存捣腾到另一处,很浪费,不是吗。

有种“标记-扫清”方式

这种方式是对“停止-复制”方式的改进,虚拟机会检查:如果没有新的垃圾产生,就会进入到另一种工作模式,即“标记-扫清”(自适应模式)

这种方式依据的思想是从堆栈和静态存储区出发,遍历所有的引用,进而找出所有存活的对象,找到“活”的对象就会给对象设一个标记,这个过程不回回收任何对象。只有在全部标记完成时,清理工作才会开始。清理时,没被标记的对象将被释放,剩下的就是不连续的堆空间,不会发生任何复制动作,这种方式也是在程序暂停时运行。

虚拟机中,内存分配以较大的“块”为单位,对象较大时,会占用单独的块。“停止-复制”方式要求在释放旧对象之前必须先把所有的存活对象从旧堆里复制到新堆,这会导致大量的内存复制行为。有了“块”之后,垃圾回收器在回收的时候就可以往废弃的块里拷贝对象了。每个块都用相应的代数来记录它是否还存活。通常,如果块在某处被引用,其代数会增加,,垃圾回收器将对上次回收动作之后新分配的块进行整理,大型的对象仍然不会被复制,小型对象将会被复制整理。虚拟机会进行监视,如果所有的对象都很稳定,垃圾回收器的效率降低的话,就切换到“标记-扫清”工作方式。同样,如果这种模式下,对空间出现很多碎片,就会切换到停止-复制”模式。这就是自适应技术,在“停止-复制”,“标记-扫清”两种模式下自动调整。

此外,java虚拟机中还有许多附加技术用以提升速度。尤其是于加载器操作有关的,称为“just in time,JIT”编译器技术。这种技术可以吧程序全部或部分翻译成本地机器码,运行速度因此提升。当需要装载某个类时,编译器会先找到其.class文件,然后将字节码装入内存,即时编译所有的代码。这样带来的问题是加载动作会散落在整个程序的生命周期中,累加起来要花很多时间,并且会增加可执行代码的长度(如果一只装载的话),导致页面调度,从而降低程序速度。可以采用另一种加载方式,惰性评估,意思是编译器在必要的时候才编译代码(即java
 HOTSPOT技术)。

为什么系统给成员变量初始化了,局部变量必须手动初始化??

问题有点愚蠢。首先java就是这么规定的。其次把,想想就明白啦。成员变量属于这个类的数据,虚拟机在给这个对象分配空间的时候,必须考虑到这个对象里面的数据啊,这就是成员变量嘛,既然新建累的时候都给分配空间了,顺带就初始化咯。局部变量是作用域位于方法体内,这个方法也不一定能够执行,而且有些变量是在运行中执行到某一条件才出来的,这些不一定能够实际用到的变量分配空间并初始化是不是有点多余呢。顺便说一句,成员变量的顺序决定了初始化的顺序,静态成员比非静态成员先初始化,构造方法其实也是静态的方法,即便成员变量散落在代码各个部位,初始化是在构造方法之前被调用,也就是说无法阻止成员变量的初始化了。

枚举类型,看起来像新的数据类型,其实它是一个类:

在java中创建枚举时,编译器会自动添加一些有用的特性,如toString方法(显示实例名字),ordinal方法(用来表示特定enum常量的声明顺序),还有静态的value方法,用来按照enum常量的声明顺序,产生这些常量值构成的数组。跟switch语句很搭。

第六章 访问权限控制

private protected public
没啥了

没声明权限的话,默认是包访问权限,当前包内的类可以访问,包外的不行。

java的编译过程:java源文件从创建到运行需要两大步骤:1.源文件由编译器解释称字节码;2.字节码由java虚拟机解释运行。java既要编译又要解释运行,因此java也叫做半解释程序(semi interpreted language)。如下图:


具体流程是,一,先把源文件编译成.class文件,如果依赖的一个类还没有编译,编译器就会先编译这个依赖的类,然后引用。编译完后的字节码文件分成常量池和方法字节码。

二,运行部分大约也分为两大过程,类的加载和类的执行。虚拟机会在用到类的时候才回去加载这个类,并且只会加载一次,之后都是复用。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: