Thinking in Java 4th Edition
Chap 1 对象入门
略
Chap 2 一切都是对象
1. Java中对象的操作都是传址。需要注意的是String类型,String类型的操作都会产生新的对象。
public class Test01 { public String str = "Hello"; public char[] c = { 'a', 'b', 'c' };
public void change(String s, char[] c) { s = "Ok"; c[0] = 'A'; System.out.println(s == this.str);//false,String赋值操作会产生新的对象 System.out.println(c == this.c);//true,还是原来的地址 } public void change(String s) { this.str = "Ok"; }
public static void main(String[] args) { Test01 t1 = new Test01(); t1.change(t1.str, t1.c); System.out.println(t1.str+","+String.valueOf(t1.c));//Hello,Abc t1.change(t1.str); System.out.println(t1.str);//Ok } } |
2. 对象通过new创建。保存对象的位置:
a) 寄存器:最快,地址由编译器分配,没有直接控制权。
b) 栈:位于RAM,访问速度仅次于寄存器,先入后出,存放对象“句柄”,不存放new创建的对象。
c) 堆:位于RAM,存放java对象,不用关注存放对象的生存时间和对象的大小。
d) 静态存储:static关键字定义对象存储的位置,程序运行期间,静态存储中的数据可以随时调动,Java对象不会存储于静态存储区。位于RAM。
e) 常数存储:直接置于程序代码内部,不可修改,位于ROM。
f) 非RAM存储:各种流对象。
3. 基本类型(9种):
a) boolean: 1bit
b) byte: 8bit
c) char: 16bit
d) short:16bit
e) int: 32bit
f) long: 64bit
g) float: 64bit
h) double: 64bit
i) void
4. 变量的作用域:不同于C/C++,如下代码在Java中不允许:
int area = 10; { int area = 20; } |
5. 类实例化的不同对象含有相同的方法,各自维护不用的数据成员存储空间。类的基本类型数据成员会有默认值,局部变量没有默认值。
6. 类文档标记:
a) @see:查看其它类
b) @version:版本信息
c) @author:作者
方法标记:
a) @param:参数
b) @return:返回值说明
c) @exception:异常信息
d) @deprecated:不推荐使用
Chap 3 控制程序流程
1. 关系运算符==对于基本类型比较的是值,对于对象数据类型,比较的是对象的地址。类方法equals默认使用==比较对象引用地址,可以重写equals以达到想要的内容比较。
public class Test02 { private int id;
@Override public boolean equals(Object obj) { if (null == obj) { return false; } if (this == obj) { return true; } if (obj.getClass() != this.getClass()) { return false; }
return this.id == ((Test02) obj).id; }
public static void main(String[] args) { Test02 t1 = new Test02(); Test02 t2 = new Test02(); System.out.println(t1.equals(t2));// true } } |
2. 逻辑运算符||、&&的短路特性会使后面的表达式、函数不执行。如:false==true&&1/0,能够编译通过,后面的1/0不会执行。
3. 移位运算符:<<左移(相当于*2),有符号右移>>(相当于/2,值正:高位补0;值负:高位补1),无符号右移>>>(无论正负,高位补0)。char、byte和short右移时会自动转换成int,因此有符号右移>>可能不正确,无符号右移动没有这种问题。
4. Java没有sizeof,因为java是平台无关的。
5. 比int小的类型(char/byte/short)在算数或者按位运算是会转型为int。小数的字面常量是double类型,因此float f = 1.3;语句是不正确的。
6. goto是java的关键字,但是没有使用。java支持标签,通过break label和continue label进行标签跳转。
Chap 4 初始化和清除
1. 构造函数在对象创建时自动调用,构造函数的名字必须和类名相同。
2. 返回值不能用于判断方法重载,因为方法的调用有时并不关心返回值。
3. this关键字用于获取当前对象的句柄。构造方法中通过this调用其他构造函数只能在最开始的代码调用,并且只能调用一次。
4. static方法不能调用非static方法,非static方法可以调用static方法(没有什么意义)。
5. finalize方法垃圾收集器回收对象时调用的方法,执行finalize并不一定会触发垃圾回收,垃圾回收只跟内存有关。finalize的用途是调用其他语言(如C++)申请内存时,垃圾回收机制并不能有效的回收内存,此时需要调用finalize覆盖默认的方法来实现对这部分内存的释放和回收。
6. System.gc()告诉垃圾收集器打算进行垃圾收集,而垃圾收集器进不进行收集是不确定的;System.runFinalization()强制调用已经失去引用的对象的finalize方法;
PS:runFinalizersOnExit因为线程安全问题不再使用,此外还有:
a) Thread.stop:这个方法会解除被加锁的对象的锁,因而可能造成这些对象处于不一致的状态,而且这个方法造成的ThreadDeath异常不像其他的检查期异常一样被捕获。可以使用interrupt方法代替。事实上,如果一个方法不能被interrupt,那stop方法也不会起作用。
b) Thread.suspend, Thread.resume:这俩方法有造成死锁的危险。使用suspend时,并不会释放锁;而如果我想先获取该锁,再进行resume,就会造成死锁。可以使用object的wait和notify方法代替。wait方法会释放持有的锁。
c) Runtime.runFinalizersOnExit:这个方法本身就是不安全的。它可能导致终结器(finallizers)在活动对象上被调用,而其他线程正在并发操作这些对象。而且,这个调用不是“线程安全”的,因为它设置了一个VM全局标志。
7. 所有成员变量的初始化优先于构造函数进行赋值,静态代码块、静态变量优先于所有成员变量初始化,且静态代码块、静态变量只初始化一次。非静态代码块执行顺序优先级和成员变量一致,按照代码书写的顺序执行。
class MyInteger { MyInteger(int i) { System.out.println("MyInteger constructed."); } }
public class Test02 { MyInteger i = new MyInteger(1); { int a = 100; System.out.println("non-static block initialized."); }
Test02() { System.out.println("Test02 constructed."); }
MyInteger j = new MyInteger(2);
static { System.out.println("Static block initialized."); }
public static void main(String[] args) { System.out.println(new Test02().i); } } |
输出: Static block initialized. MyInteger constructed. non-static block initialized. MyInteger constructed. Test02 constructed. com.conanswp.chap04.MyInteger@15db9742
|
8. Java支持数组类型的直接赋值,其效果是对象引用的赋值。
public class Test03 { public static void main(String[] args) { int[] a1 = { 1, 2, 3, 4, 5 }; int[] a2; a2 = a1; for (int i = 0; i < a2.length; i++) a2[i]++; for (int i = 0; i < a1.length; i++) prt("a1[" + i + "] = " + a1[i]); }
static void prt(String s) { System.out.println(s); } } |
输出: a1[0] = 2 a1[1] = 3 a1[2] = 4 a1[3] = 5 a1[4] = 6 |
9. 基本类型数组会根据数据类型有默认值,非基本类型数组初始化为null。
public class Test04 { public static void main(String[] args) { int[] primary_array = new int[5]; for (int i : primary_array) System.out.println(i);// 输出0
Integer[] object_array = new Integer[5]; for (Integer o : object_array) System.out.println(o);// 输出null Integer[] object_array1 = new Integer[] { new Integer(1), new Integer(2) }; for (Integer o : object_array1) System.out.println(o);// 输出1/2 } } |
Chap 5 隐藏实施过程
1. 包:库单元。一个java源码文件称之为一个编译单元,一个编译单元只能有一个public类且编译单元文件的名字必须与public类的名字完全相同,包括大小写。Package包必须作为编译单元的第一个非注释语句出现。
2. 访问指示符号:默认不写是包内友好的访问权限。Public公共访问,protected继承访问,private私有访问。
Chap 6 类再生
1. 类中非primative对象的初始化可以在对象定义、构造器和紧挨对象使用前进行初始化。
2. 衍生类的构建起会自动插入对基础类构建起的调用,因此衍生类构建时会首先调用基础类的构造方法。还可以通过在衍生类构造函数第一样通过super调用基础类的构造方法。
3. final关键字:
|-修饰变量->常数,编译期间不会变化。
|-空白final:java1.1允许,必须在定义字段时使用表达式或者在构造函数中对final变量进行初始化。
|-final自变量:方法中的参数可以通过final修饰,只能读取,不能改变。
|-final方法:防止继承改变,并能够提高执行效率(传统方法调用:自变量入堆栈->跳到方法并执行->跳回来->清除堆栈自变量->返回值处理。Final方法调用:用实际代码的副本进行替换。要求代码不能太长。)。类内所有private方法都自动成为final方法。
|-final类:final class XXX不允许继承,内部变量的值可以改变,除非其值是final修饰的。
4. 继承初始化:先执行基础类+衍生类的static属性和静态代码块,然后是基础类的成员变量初始化+构造函数,最后是衍生类的成员变量初始化+构造函数。
package com.conanswp.chap06;
class Base { private int a = 10; private static int b = 20;
Base() { System.out.println("Base Constructed."); say(); }
private int c = 30; private static int d = 40;
public void say() { System.out.println("Hi from Base, a=" + a + ", b="+b+"."); } }
class Derive extends Base { private int a = 50; private static int b = 60; Derive() { System.out.println("Derive constructed."); } public void say() { System.out.println("Hi from Derive, a="+a+", b="+b+"."); } private int e = 70; private static int f = 80; }
public class Test {
public static void main(String[] args) { new Derive().say(); }
} |
输出: Base Constructed. Hi from Derive, a=0, b=60. Derive constructed. Hi from Derive, a=50, b=60. |