宏:编译器预处理,将变量替换为常量,提高程序运行效率。
在Java中,final变量就是一个“宏变量”,编译器会把程序中所有用到该变量的地方替换成该变量的值。书中“堆栈”(stack)是指“栈”,“堆”(heap)是指内存池或叫内存堆。
说堆栈容易让人混淆,有必要澄清一下。矢量(Vector)和列表(List)的区别
Vector的底层数据结构是数组,是存储空间连续的存储结构;
List,书中指的是像LinkedList这类链表,是不连续的存储结构。
所以说,Vector的元素取值、修改效率较高,增加、删除效率较低;链表则相反。Java的单根结构
所有的类默认继承Object,Object有默认的方法,例如finalize()。利用单根结构可以方便地实现垃圾收集器,利用finalize()方法。
当垃圾回收器认为一个对象没有存在意义时,会调用该对象的finalize()方法,释放该对象在堆中占用的内存。Java规定了基本数据类型的大小。
基本数据类型的大小,即占用空间大小,并不随着机器结构的变化而变化。这种大小的不可更改是Java 程序具有很强移植能力的原因之一。成员变量的默认值
如果成员变量是基本数据类型,Java会分配默认值;
如果局部变量是基本数据类型,则不会分配默认值,需要程序初始化。移位运算符
左移运算符<<,低位补0;
右移运算符>>是有符号移位运算,若值为正,则在高位补0;若值为负,则在高位补1;
无符号右移运算符>>>,无论正负,都在高位补0。float类型初始化时,数字常量要追加f,否则与double混淆,编译报错。
switch选择因子的类型可以是int, char, String等常量。
书中说选择因子必须是int或char的整型。因为byte,short会向上转为int,所以也支持byte,short。这种说法有误,在Java 1.7以后的版本已经支持更多类型了。默认构造方法
如果一个类没有定义构造方法,则会生成一个无参、方法体为空的默认构造器。
如果定义了构造方法,则不会生成这个无参、方法体为空的默认构造器,除非程序显式定义。构造方法的使用
构造方法只能在构造方法内被调用;
一个构造方法内只能调用一个构造方法;
构造方法的调用必须是方法体中的第一条语句。Java创建一个源码文件的时候,它通常叫作一个“编辑单元”。
每个编译单元都必须有一个以.java 结尾的名字。而且在编译单元的内部,可以有一个公共(public)类,它必须拥有与文件相同的名字;
每个编译单元内都只能有一个public 类,也可以没有。区分组合与继承的使用场景
用组合表达“包含”关系,用继承表达“属于”关系。声明时未初始化的final成员变量
如果一个finial成员变量在声明时未进行初始化,则需要在构造方法中将其初始化。final方法的意义
可禁止继承类修改;
编译器可以将final方法嵌入调用方法内,避免方法调用时的系统开销,提升效率;
不是final的方法,则动态绑定,即在程序运行时才知道具体调用哪个方法(结合Java多态理解);
private的方法默认为final方法,即自动编译为final。final类中的所有方法默认为final。
每个对象的代码都存在于独立的文件中。除非真的需要,否则那个文件不会载入。
抽象类不能实例化。
接口的方法默认是public,可以不明确声明为public,但是不可以声明为其他可见形式。
类只能单继承某个类,可以实现多个接口;接口与接口之间可以多继承。
用抽象类还是接口?
如果事先知道某个类会成为基础类,那么第一个选择就是把它变成一个接口。只有在必须使用方法定义或者成员变量的时候,才应考虑采用抽象类。注意根据Java 命名规则,static final修饰的基本数据类型(亦即编译期常数)应全部采用大写字母(用下划线分隔单个标识符里的多个单词)。
字段的初始化时机是在类加载时,也就是在访问字段之前就初始化了。
static字段和代码块会先于非static字段和代码块加载。静态变量和静态代码块的加载顺序由编写先后决定。内部类因为有外部类的引用,可以直接访问外部类的成员变量,private也可以。这是编译器帮我们实现的。
由于C++的vector不进行范围检查,所以访问速度较快——在Java 中,由于对数组和集合都要进行范围检查,所以对性能有一定的影响。
自动转换字符串技术
一旦编译器碰到一个字串,后面跟随一个“+”,就希望后面又跟随一个字串。这里编译器对“+”的处理是创建一个StringBuilder对象,用append()方法拼接两个字符串。
如果+的后面是一个对象,那么自动调用append(Object obj),底层方法是obj.toString();如果+的后面是基本数据类型,就调用append(相应数据类型)。TreeSet是用红黑树排序的有序Set。
创建不可修改的集合对象。
Collection c = new ArrayList();
...
c = Collections.unmodifiableCollection(c);
Map m = new HashMap();
...
m = Collections.unmodifiableMap(m);
- 创建线程安全的集合对象
Collection c = Collections.synchronizedCollection(new ArrayList());
List list = Collections.synchronizedList(new ArrayList());
Set s = Collections.synchronizedSet(new HashSet());
Map m = Collections.synchronizedMap(new HashMap());
实现线程安全的原理是,方法用了synchronized关键字。
Throwable 对象有两种常规子类。其中,Error 代表编译期和系统错误,我们一般不必特意捕获它们(除在特殊情况以外);Exception 是可以从任何标准Java 库的类方法中抛出的基本类型。
自定义异常类
class MyException extends Exception {
public MyException() {}
public MyException(String msg) {
super(msg);
}
}
- 对象序列化可将对象转换成一系列字节,并可完全恢复回原来的样子。这一过程亦可通过网络进行。这意味着序列化机制能自动补偿操作系统间的差异。
为防止对象的敏感部分被序列化,一个办法是将类实现Externalizable。这样,没有任何东西可以自动序列化,只能在writeExternal()明确序列化那些需要的部分;
若操作的是一个Serializable 对象,所有序列化操作都会自动进行。为解决这个问题,可以用transient逐个字段地关闭序列化。
Class的newInstance()方法是克隆clone()一个对象的另一种手段。
两者是有区别是,利用newInstance()可在没有现成对象供“克隆”的情况下新建一个对象。protected修饰的成员或方法的可见性,要分子类和超类是否在同一个包中。
与基类不在同一个包中的子类,只能访问自身从基类继承而来的受保护成员,而不能访问基类实例本身的受保护成员。在相同包时,protected和public是一样的。实现Cloneable接口的意义
一个类的对象能调用clone()方法的前提是实现了Cloneable接口,然而Cloneable接口没有定义方法。实现Cloneable接口的意义是:
可用instanceof关键字判断对象是否能够克隆;
if(myHandle instanceof Cloneable)
对于不想被克隆的对象可以抛出CloneNotSupportedException异常。
protected native Object clone() throws CloneNotSupportedException;
String是final类
String一旦修改,就返回一个新的String对象。若内容没有改变,则只返回指向原来那个String的引用。
声明为final是不想被修改,作为工具类一来效率会提升,再者提高了安全性。因为底层代码调用了本地方法,与操作系统交互,如果可以随意改,势必降低安全性。线程的四个状态:创建、阻塞、运行、死亡。
线程的优先级意味着线程的“重要程度”。
若线程的优先级较低,只不过表示它被准许运行的机会小一些而已。线程组中的线程可以修改组内的其他线程;我们只能降低一个线程组的最大优先级,而不能提高它;一个子组的最大优先级在任何时候都只能低于或等于它的父组的最大优先级。