Java学习总结

一、一般技术 

1.  Java唯一一种参数传递机制:by value(传值)。

备注:当参数为对象时,以传值方式传递对象的引用。(个人理解:传递指针的拷贝) 


2.  对不变的data和object references使用final

备注:当声明一个对象为final时,不能改变其指向,但能改变其对象的值。

 

3.  缺省情况下所有非私有(non-private)、非静态(non-static)函数都可被覆写。

3.1  如果函数不想被子类覆写,将函数声明为final即可。

3.2  如果类不想被子类覆写,将类声明为final即可,从而禁止覆写该类所有函数。还能提高性能。

 

4.  在数组和Vectors之间慎重选择。

 

数组

Vector

存储数据类型

基本类型、引用类型

引用类型

元素默认值

Yes

No

自动改变大小

固定不变

动态增长

速度快

Yes

No

备注: Vector内部实际是以数组实现的。

 

5.  多态优于instanceof,必要时才使用instanceof。

备注:例如从父类转型为派生类时,需要使用instanceof。

 

6.  一旦不需要的对象引用,就将它设为null。

备注:如果局部变量的引用使用完离函数结束前还需要执行较大代码,可在使用完后设置为null,以便或许能使其在垃圾回收器下次运行时被回收。

二、 对象与相等性

1.  区别  == 和 equals()

备注:请使用 == 测试两个基本类型是否完全相同,或测试两个对象引用是否指向同一个对象;使用equals()比较两个对象的内容是否相等。

 

2.  不要依赖equals()缺省实现

备注:所有Java对象都隐含继承了java.lang.Object,默认的equals()只是检查对象引用是否指向同一个对象。

 

3.  实现equals()建议:

3.1  请检查是否等于this

备注:测试是否指向同一个对象

3.2  优先考虑使用getClass()

备注:getClass()返回某个对象的运行期类(runtime class)。确保只有相同class所产生的对象才有机会被视为相等。范例:

public class Base {

        @Override
        public boolean equals(Object obj) {
            if ( this == obj) {
                 return true;
            }
            if(obj != null && getClass() == obj.getClass()){
                //继续比较相等性或直接返回true
            }
            return false;
        }
    }

3.3  调用super.equals()唤起父类的相关行为

备注:当你为一个派生类撰写equals()时,你必须检查除java.lang.Object之外所有父类,看看它们是否都实现了equals()。

如果有,那么一定要调用super.equals()。      


3.4  在equals()函数中谨慎使用instanceof

备注:如果允许派生类和父类进行比较,可能要采用instanceof(getClass()会恒返回false)。注意instanceof类似于is-a语义,

需要注意位置,即(子类 instanceof 父类 -> 返回true;反之为false)。


三、  异常处理

1.  认识异常流程机制

备注:try区段 -> [catch捕获区段][可选] -> finally终结区段。

 

2.  处理异常

如果异常产生却未被捕获,发生异常的线程将中断。处理异常:

a). 捕获并处理它,防止进一步传播。

b). 捕获并抛出一个新的异常给调用端。

备注:应确保新抛出的异常包含原异常相关信息,以保证不丢失重要信息。

c). 不捕获,任其传播给调用端。

输出错误信息:

a). 日志文件记录曾经发生过的异常

b). 输出异常

输出到标准错误串流:如e.printStackTrace()。

 

3.  防止出现异常覆盖

备注:如果在catch或finally区段又抛出了未捕获的异常,新的异常将覆盖try抛出的异常,只有一个异常可被传播到调用段。


4.  throws子句

备注:提供throws子句的用意在于,提醒函数调用者,告知可能发生的异常。编译器会强迫调用端捕捉这些被列出的异常,所以不要再开发周期的最后才添加。

如果覆写对象(某个父类函数)没有抛出任何异常,那么覆写函数因为增加了代码而引发异常,那么你必须在新函数中捕捉异常并处理。

 

5.  使用finally避免资源泄漏

备注:finally区段代码无论是否发生异常都将执行,尤其适合维护对象内部状态和清理non-memory资源。

 

6.  使用建议:

a). 不要从try区段执行return、break或continue语句离开try区段

备注:如果try区段和finally区段都return 数据,将返回finally区段return的数据。

b). 将try/catch区段置于循环之外

备注:异常对代码性能产生负面影响

c). 不要将异常用于流程控制

d). 在构造函数中抛出异常

e). 抛出异常之前将对象恢复为有效状态

备注:考虑下次执行这段代码时会发生什么事情,代码是否还能正常运行。


四、性能

1. 先把焦点放在设计、数据结构和算法身上

备注:良好的设计、明智的选择数据结构和算法可能比高效代码更重要。

 

2.  不要依赖编译器优化技术

 

3.  理解运行时(runtime)代码优化

备注:JIT将bytecode于运行时转换为本地二进制码,从而提高性能。因此编译后代码被执行次数越多,本机代码生成代价就越合算。

 

4.  连接字符串使用StringBuffer要比String快,尤其是大量字符串拼接

 

5.  将对象创建成本降至最小

备注:复用既有对象,不要创建非必要的对象,只在需要的时候才创建它们。

 

6.  将同步化(synchronization)降至最低

备注:如果synchronized函数抛出异常,则在异常离开函数之前,锁会自动释放。如果整个函数都需要被同步化,

为了产生体积较小且执行速度较快的代码,请优先使用函数修饰符,而不是在函数内使用synchronized代码块。

 

7.  尽可能使用stack变量

备注:如果在函数中频繁访问成员变量、静态变量,可以用本地(local)变量替代,最后操作完后再赋值给成员/静态变量。

 

8.  尽可能使用static、final和private函数

备注:此类函数可以在编译期间被静态决议(statically resolved),而不需要动态议决(dynamic resolved)。(子类无法覆写)

 

9.  类的成员变量、静态变量都有缺省值,无须重复初始化

备注:记住,本地变量没有缺省值(例如函数内定义的变量)。

 

10.  尽可能使用基本数据类型

备注:如int、short、char、boolean,使得代码更快更小。

 

11.  不要使用枚举器(Enumeration)和迭代器(Iterator)来遍历Vector

备注:使用for循环+get()

 

12.  使用System.arraycopy()来复制数组

备注:使用System.arraycopy()代替for循环,可以产生更快的代码。如:

  public  void copyArray( int[] src,  int[] dest) {
       int size = src.length;
      System.arraycopy(src, 0, dest, 0, size);
}

System.arraycopy()是以native method实现的,可以直接、高效的移动原始数组到目标数组,因此它执行速度更快。

 

13.  优先使用数组,然后才考虑Vector和ArrayList,理由:

a).  Vector的get()是同步的

b).  ArrayList基本上就是一个非线程同步的Vector,比Vector要快

c).  ArrayList和Vector添加元素或移除元素都需要重新整理数组。

备注:不要仅仅因为手上有个数不定的数据需要存储,就毫无选择的使用Vector或ArrayList。可以考虑创建一个足够大的数组,

通常这样可能会浪费内存,但性能上的收益可能超过内存方面的代价。

 

14.  手工优化代码

a).  剔除空白函数和无用代码

b).  削减强度

备注:以更高效的操作替换成本昂贵的操作。一个常见的优化手法是使用复式复制操作符(如+=、-=)。

c).  合并常量

备注:将变量声明为final,使得操作在编译时就进行。

d).  删减相同的子表达式

备注:可用一个临时变量代替重复的表达式。

e).  展开循环

备注:如循环次数少且已知循环次数,可展开去掉循环结构,直接访问数组元素。缺点是会产生更多代码。

f).  简化代数

备注:使用数学技巧来简化表达式。(例如从1+..+100的问题)

g).  搬移循环内的不变式

备注:循环内不变化的表达式可用移至循环外,不必重复计算表达式。

 

15.  编译为本机代码

备注:将程序的某部分编译为本机二进制代码,然后通过JNI访问。

 

五、多线程

1.  对于实例(instance)函数,同步机制锁定的是对象,而不是函数和代码块。

备注:函数或代码块被声明为synchronized并非意味它在同一时刻只能有一个线程执行(同一对象不同线程调用会阻塞)。

Java语言不允许将构造函数声明为synchronized。

 

2.  同步实例函数和同步静态函数争取的是不同的locks。

备注:两者均非多线程安全,可以使用实例变量进行同步控制,如(byte[] lock = new byte[0]),比其他任何对象都经济。

 

3.  对于synchronized函数中可被修改的数据,应使之成为private,并根据需要提供访问函数。如果访问函数返回的是可变对象,那么可以先克隆该对象。

 

4.  避免无谓的同步控制

备注:过度的同步控制可能导致代码死锁或执行缓慢。再次提醒,当一个函数声明为synchronized,所获得的lock是隶属于调用此函数的那个对象。

 

5.  访问共享变量时请使用synchronized或volatile

备注:如果并发性很重要,而且不需要更新很多变量,则可以考虑使用volatile。一旦变量被声明为volatile,在每次访问它们时,它们就与主内存进行一致化。

如果使用synchronized,只在取得lock和释放lock时候才一致化。

 

6.  在单一操作(single operation)中锁定所有用到的对象

备注:如果某个同步函数调用了某个非同步实例函数来修改对象,它是线程安全的。

使用同步控制时,一定要对关键字synchronized所作所为牢记在心。它锁定的是对象而非函数或代码。

 

7.  以固定而全局性的顺序取得多个locks(机制)以避免死锁。

备注:嵌入[锁定顺序]需要额外的一些工作、内存和执行时间。

 

8.  优先使用notifyAll()而非notify()

备注:notify()和notifyAll()用以唤醒处以等待状态的线程,wait()则让线程进入等待状态。notify()仅仅唤醒一个线程。

 

9.  针对wait()和notifyAll()使用旋转锁(spin locks)

备注:旋转锁模式(spin-lock pattern)简洁、廉价,而且能确保等待着某个条件变量的代码能循规蹈矩。

 

10.  使用wait()和notifyAll()替代轮询(polling loops)

备注:调用wait()时会释放同步对象锁,暂停(虚悬,suspend)此线程。被暂停的线程不会占用CPU时间,直到被唤醒。如:

            public void run()
            {
                int data;
                while(true){
                    synchronized (pipe) {
                        while((data = pipe.getDate()) == 0){
                            try{
                                pipe.waite();
                            }
                            catch(InterruptedException e){}
                        }
                    }
                    
                    //Process Data
                }
            }


11.  不要对已锁定对象的对象引用重新赋值。

 

12.  不要调用stop()和suspend()

备注:stop()中止一个线程时,会释放线程持有的所有locks,有搅乱内部数据的风险;suspend()暂时悬挂起一个线程,但不会释放持有的locks,

可能带来死锁的风险。两种都会引发不可预测的行为和不正确的行为。

当线程的run()结束时,线程就中止了运行。可以用轮询+变量来控制,如下代码:

            private volatile boolean stop;
            
            public void stopThread()
            {
                stop = true;
            }
    
            public void run()
            {
                while(!stop){
                    //Process Data
                }
            }

注意:这里使用了关键字volatile,由于Java允许线程在其私有专用内存 中保留主内存变量的副本(可以优化),

 线程1对线程2调用了stopThread(),但线程2可能不会及时察觉到stop主内存变量已变化,导致不能及时中止线程。

 

六、类与接口 

1.  实现一个final类(immutable class 不可变类)时,请遵循下列规则:

a). 声明所有数据为private

b).  只提供取值函数(getter),不提供赋值函数(setter)

c).  在构造函数中设置实例数据

d).  如果函数返回、接受引用final对象,请克隆这个对象。

e).  区别浅层拷贝和深层拷贝应用场景。如拷贝Vector需要使用深层拷贝。

 

2.  实现clone()时记得调用super.clone()

备注:不管是浅层拷贝还是深层拷贝都需要调用super.clone()。

 

3.  别只依赖finalize()清理内存以外的资源

备注:finalize()函数只有在垃圾回收器释放对象占用的空间之前才会被调用,回收时可能并非所有符合回收条件的对象都被回收,

 也无法保证是否被调用、何时调用。实现finalize()方法时记得调用super.finalize()。

 

4.  在构造函数内应避免调用非final函数,以免被覆写而改变初衷。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值