1.缺省情况下, classes 拥有的任何 non-private 、 non-static 函数都允许被subclasses 覆写( overridden )。 class 设计者如果希望阻止 subclasses 覆写( 修改)某个函数。则必须采取明确动作,也就是将该函数声明为 final 。
2.当新建一个数组时, 请记住,每一个元素都将依据其自身型别而被赋予缺省值。如果数组的元素是object references时, Java便不会调用default构造函数,而是将其初值设为null。
3.一旦你的程序不再参考(或说引用)某个对象,垃圾回收器就会回收这个对象所拥有的内存。当不再需要某对象时,你可以将其 references 设为 null ,以协助垃圾回收器取回内存。如果你的程序执行于一个内存受限环境中,这可能很有益处。
4.总体而言, equals() 的最佳实现方式就是搭配 getClass() ,后者可确保只有相同 class 所产生的对象才有机会被视为相等。此外, equals() 应当查看参与 比较的对象是否为同一个对象。 equals() 不必对每一个属性( attributes )进行比较,只有那些攸关 [ 相等性 ] 的属性才需要比较。
5.在equals()函数中使用getClass(),这种做法使得只有隶属于同一个class的对象才能被视为相等。如果希望derived class对象与其base class对象也可被视为相等。
6.实现 equals() 时需要遵循某些规则:
1)如果某个 class 的两个对象即使占据不同的内存空间, 也可被视为 「 逻辑上相等」的话,那么你得为这个 class 提供一个 equals() 。
2)请检查是否等于 this。
3)比较这个 class 中的相关属性(值域, fields ),以判断两个对象是否相等。
4)如果有 java.lang.Object 以外的任何 base class 实现了 equals() , 那么就应该调用super.equals()。
在 equals() 函数中面对 getClass() 和 instanceof 进行取舍时,你要仔细斟酌以下问题:
1)如果只允许同一个 class 所产生的对象被视为相等,则通常使用getClass()。
2)只有在不得不「对 derived classes 对象与 base classes 对象进行比较」 的场合中,才使用 instanceof ,而且你应该明白这样做带来的可能问题和复杂性。
3)如果使用 instanseof ,而且 derived class 和 base class 都实现有 equals() ,则一定要知道,这种比较不会展现出所谓的「对称相等性」(symmetric equality )。
7.将try/catch区段置于循环之外,这样有助于性能的提高,当不用JIT来优化的时候。
8.不要将异常用于流程控制。否则,会使得性能低下,含义模糊,而且难以维护。
9.对象构建过程中,下面事件一定会以固定顺序发生:
1)从 heap 之中分配内存,用以存放全部的instance 变量以及这个对象连同其superclasses 的实现专属数据 (implementation-specific data) 。所谓 [ 实现专属数据 ] 包括指向“class and method data ”的指针。
2)对象的instance 变量被初始化为其相应的缺省值。
3)调用 most derived class( 最深层派生类 ) 的构造函数 (constructor) 。构造函数做的第一件事就是调用superclass 的构造函数。这个程序一直反复持续到java.1ang.object 构造函数被调用为止。一定 要记住,java.1ang.object 是一切 Java 对象的base class 。
4)在构造函数本体执行之前,所有instance 变量的初始值设定式 (initializers) 和初始化区段(initialization blocks) 先获得执行,然后才执行构造函数本体。于是 base class 的构造函数最先执行, most derived class 的构造函数最后执行。这使得任何 class 的构造函数都能放心大胆地使用其任何 superclasses 的 instance 变量。
10.声明 synchronized 函数,或声明[含有 synchronized 区段]的函数,会大大降低执行性能。无论你使用 synchronized 作为修饰符,还是在函数中使用 synchronized 区段,都意味着体积增大。只有当你得代码要求同步化,而且你理解那么做的代价之后,才使用synchronized 函数。如果整个函数都需要被同步化,则为了产生体积较小且执行速度较快的代码,请优先使用函数修饰符,而不是在函数内使用synchronized 区段。
11.Java 对于instance 变量、 static 变量和 arrays 都进行了缺省的初始化动作(default Initialization) ,我们不应该因为[ 重复将这些变量和arrays初始化为缺省值] 而非必要的低效性能上的好处。
12.当synchronized被当做函数修饰符的时候,它所取得的lock将被交给函数调用者(某对象)。如果synchronized用于object reference,则取得的lock将被交给该reference所指对象。
13.对synchronized的最后一点说明: Java 语言不允许你将构造函数声明为, synchronized( 那么做会发生编译错误 ) 。原因是当两个线程并发调用同一个构造函数时,它们各自操控的是同一个 class 的两个不同实体 ( 对象 ) 的内存 ( 也就是说 synchronized 是画蛇添足之举 ) 。然而如果在这些构造函数内包含了彼此竞争共享资源的代码,则必须同步控制那些资源以回避冲突。
14.关键词synchronized可用于static函数和class literals(类名称字面常量,eg. Foo.class)身上,当你调用一个synchronized static 函数时,获得的lock将与[定义该函数]之class的Class 对象相关联(而不是与调用函数的那个对象相关联)。当你对一个class literal 调用其synchronized 区段时,获得的也是同样那个lock,也就是[与特定Class对象相关联]的lock。
15.Volatile or synchronized ? 这取决于多个因素。如果并发性(concurrency)很重要,而且你不需要更新很多变量,则可以考虑使用 volatile 。如果你需要更新许多变量,使用volatile 可能要比使用 synchronization 降低执行速度。记住 , 一旦变量被声明为volatile ,在每次访问它们时,它们就与主内存进行一致化。但如果使用 synchronized ,只有在取得 lock 和释放 lock 的时候,才会对变量和主内存进行一致化。如果你要更新许多变量,而且不情愿为每次访问都付出[一致化]代价,或是你因为其他什么原因打算消除并发性 (concurrency) ,可考虑使用 synchronized 。
技术 | 优点 | 缺点 |
Synchronized | 取得和释放lock时,进行私有专用副本与主内存正本的一致化 | 消除了并发性的可能 |
volatile | 允许并发行 | 每次访问变量,就进行稀有专用内存与对应之主内存的一致化 |
16.要实现一个immutable class(不可变类)时, 请遵循下列规则:
1)声明这个 class 为 final 。
2)声明所有数据为 private 。
3)只提供取值函数 (getter) ,不提供设值函数 (setter) 。
4)在构造函数中设置所有 instance 数据。
5)如果函数返回 reference to mutable objects ,请克隆那些 mutable objects 。
6)如果函数接受 reference to mutable objects ,请克隆那些 mutable objects 。
7)如果缺省之浅层克隆 ( shallow clone ) 不能符合 immutable object 的正常行为,请实现出深层克隆 ( deep clone ) 。
17.immutability (不变性)技术
技术 | 优点 | 缺点 |
Immutable inference 不可变(恒常)接口 | 简单易行。无需付出性能上的代价 | 有破绽,可被破坏 |
Common interface or base calss 公共接口或基类 | 没有破绽。清晰地将mutable object 和immutable objects 分离 | 需实现更多的classes,完成更深的classes 继承系 |
Immutable delegation class不可变(恒常)委托类 | 没有破绽。当你无法修改既有的mutable object 源码时,此法可用 | 需付出性能上的代价 |
18.在构造函数中调用non-final函数,要谨慎。如果这个函数在derived class中被override了的话,可能子类构造的时候会调用子类override后的这个函数。
后记: 今天一天把这本薄书(电子版)翻完了,都是一些基础的知识,但很有用。做了一些笔记,留着以后温故而知新。:)