这是Java编程思想之读书笔记系列第三篇,主要讲述第四五章的相关内容,尤其是第五章初始化与清理,个人感觉看下来还是受益匪浅的。好了,下面是具体内容:
- Math.random()产生0和1之间(包括0,但不包括1)的一个double值
- C要求所有的变量都在一个块的开头定义,以便在创建这个块的时候,可以为那些变量分配空间;而在Java和C++中,则可在整个块的范围分散变量声明,在真正需要的地方才加以定义
- 逗号操作符(只在for循环的控制表达式中使用)和逗号分隔符
- 无穷循环:while(true)和for(;;)
- goto是Java中的一个保留字
- 标签是后面跟有冒号的标识符,在Java中,标签起作用的唯一地方刚好是迭代开始之前,即标签和迭代之间不能置入任何语句
- 在Java中使用标签的唯一理由就是因为有循环嵌套存在,而且想从多层嵌套中break或continue
- switch里是一个整数选择因子,只能是整型或者可以转成整型的数值类型,long和string是不能作用在switch上的
- 方法重载的时候,参数顺序不同也足以区分
- 基本数据类型的方法重载:
- 如果传入的数据类型(实际参数类型)小于方法中声明的形式参数类型,实际数据类型就会被提升;char如果无法找到恰好接受char参数的方法,就会把char直接提升至int型
- 如果传入的数据类型大于方法中声明的形式参数类型,必须进行显示的窄化转换
- this,表示对当前调用方法的那个对象的引用
- 可以用this调用一个构造器,但却不能调用两个,即在一个构造器里不能连续调用另外两个构造器;此外,必须将构造器调用置于最初始处,否则会报错
- 在静态方法里传入一个对象的引用,通过这个对象就可以调用非静态方法和访问非静态数据成员;静态方法有全局函数的语义,而Java是禁止使用全局函数的,所以static方法是不是面向对象的,有待商榷
- 垃圾回收器只知道释放那些经由new分配的内存,但是一个对象可能通过new以外的方式获得特殊的内存,此时垃圾回收器就不知道该如何释放这块特殊的内存,此时可以通过定义finalize()方法进行处理。工作原理如下:一旦垃圾回收器准备好释放对象占用的存储空间,会首先调用finalize()方法,并且在下一次垃圾回收动作发生时,才会真正回收对象占用的内存。这里有几点需要注意:(获得特殊内存的实例???)
- 对象可能不被垃圾回收
- 垃圾回收不等于“析构”
- 垃圾回收只与内存有关
- finalize方法的需求只有一种特殊情况:通过某种创建对象以外的方式为对象分配了存储空间
- 本地方法和局部对象(???)
- 本地方法是一种在Java中调用非Java代码的方式,本地方法目前只支持C和C++
- Java不允许创建局部对象(???),必须使用new创建
- 垃圾回收器对于提高对象的创建速度有明显的效果
- 通过垃圾回收器对对象重新排列,实现了一种高速的、有无限空间可供分配的堆模型
- 垃圾回收器技术
- 引用计数:如果对象之间存在循环引用,可能会出现“对象应该被回收,但是引用计数不为0”的情况。简单,但是速度很慢
- 对任何“活”的对象,一定能最终追溯到其存活在堆栈或静态存储区之中的引用
- 自适应的、分代的、停止-复制(stop-and-copy)、标记-清扫(mark-and-sweep)式垃圾回收器
- 自适应:Java虚拟机会进行监视,如果所有对象都很稳定,垃圾回收器的效率降低,就切换到“标记-清扫”方式;同样,Java虚拟机会跟踪“标记-清扫”的效果,要是堆空间出现很多碎片,就会切换回“停止-复制”方式
- 分代:内存分配以较大的“块”为单位,每个块都会用相应的代数(generation count)来记录它是否存活
- 停止-复制:先暂停程序的运行,然后将所有存活的对象从当前堆复制到另一个堆,没有被复制的全部都是垃圾;新堆里的对象紧密排列
- 标记-清扫:每当找到一个活的对象,设一个标记,全部标记工作完成时,开始清理动作
- 即时编译器(Just-In-Time,JIT)
- 惰性评估(lazy evaluation):即时编译器只要必要的时候才编译代码
- 静态初始化只有在必要的时刻才会进行,而且只会初始化一次(在class对象首次加载的时候)
- 构造器可以看成静态方法???
- 对象创建过程(以Dog为例):P136
- 当程序第一次新建Dog对象,或者程序调用Dog的静态属性或方法时,解释器会去查找Dog.class所在路径
- 载入Dog.class,有关静态初始化的所有动作都会执行。因此,静态初始化只在Class对象首次加载的时候进行一次
- 当用new Dog()创建对象的时候,首先在堆上为Dog对象分配足够多的存储空间
- 将分配的存储空间清零,这就自动地将Dog对象中的所有基本类型数据都设置成了默认值
- 执行所有出现于字段定义处的初始化动作
- 执行构造器
- Java允许将多个静态初始化动作组织成一个特殊的“静态子句”,也叫“静态块”,实例:
static
Cup cup1;
static
Cup cup2;
static
{
cup1 =
new
Cup(
1
);
cup2 =
new
Cup(
2
);
}
Java中也有被称为实例初始化的类似语法,用来初始化每一个对象的非静态变量,实例:
Mug mug1;
Mug mug2;
{
mug1 =
new
Mug(
1
);
mug2 =
new
Mug(
2
);
}
与静态初始化子句相比,就是少了static关键字。这种语法对于匿名内部类的初始化是必要的,而且它也可以保证无论调用哪个显示构造器,某些操作都会发生。
- Arrays.toString将产生一维数组的可打印版本
- 数据定义和初始化:
- 定义:
- int[] a;
- int a[];
- 初始化:
- Integer[] a = new Integer[4];(后续赋值)
- Integer[] a = { new Integer(1), new Integer(2), 3,(最后一个逗号可选) };
- Integer[] a = new Integer[]{ new Integer(1), new Integer(2), 3 };
- 定义:
- 标准Java库中的类能够打印出有意义的内容,我们自己新建的类没有重写toString()方法的话,打印出的内容是:类名+@+多个十六进制数字
- 可变参数列表:type...,实例比如:String...,Character...。可以使用任何类型的参数列表,包括基本类型;将0个参数传递给可变参数列表是可行的
- getClass方法获得对应对象的类,并且在打印类时,可以看到该类类型的编码字符串。如果前导有[,表示该对象是紧随的类的数组。比如class [Ljava.lang.Character,表示该对象是Character类型的一个数组,Character类型对应的编码字符串是L
- 应该总是在重载方法的一个版本上使用可变参数列表,或者压根不使用。下面这个实例里,g('a','b')会提示编译错误(character和int的共通性):
public
class TestGetClass {
static
void f(
float
i, String... args){
System.
out
.println(
"first"
);
}
static
void f(String... args){
System.
out
.println(
"second"
);
}
static
void g(
float
i,
Character
... args){
System.
out
.println(
"first"
);
}
static
void g(
Character
... args){
System.
out
.println(
"second"
);
}
public
static
void main(String[] args) {
f(1,
"a"
);
f(
"a"
,
"b"
);
g(1,
'a'
);
g(
'a'
,
'b'
);
}
}
- 在你创建enum时,编译器会自动添加一些有用的特性。
- toString()方法,以便可以很方便地显示某个enum实例的名字;
- ordinal()方法,用来表示某个特定enum常量的声明顺序;
- static values()方法,用来按照enum常量的声明顺序,产生由这些常量值构成的数组
- enum也可以看成是一个类型,里面的每一个值就相当于该类型的一个实例,通过values()这一静态方法可以获得该类型的所有实例构成的数组,每个实例可以通过ordinal()方法获得对应的下标
- 一个类加载的时候,有三个部分需要加载:
- 静态变量
- 静态方法
- 静态初始化块
- 对象实例创建:
- 成员变量引入
- 实例初始化块
- 构造方法
以上就是第四五章的笔记内容,如有错误,敬请指出,谢谢!