java解惑知識點總結

  1. 判斷是不是奇數的方法:i % 2 != 0 或者 (i & 1) != 0,不能用i % 2 == 1,因爲存在負數
  2. 精確計算的時候不要用小數計算,例如“錢”,乘以100用整數代替小數計算。
  3. long aa = 24 * 60 * 60 * 1000 * 1000會造成精度損失,是因爲等號右邊本身是按照int進行計算的,完成之後纔將值轉成long,解決辦法只需要將其中一個常亮後面加L使其成爲長整型。
  4. 多重轉型:
    • 如果最初的數值是有符號的,那麼就執行符號擴展,
    • 如果是char類型,那麼不管被轉成什麼類型,都執行0擴展
    • 如果目標小於源長度,則直接截取就行了
  5. 謎題9:不要將複合操作符用於byte,short,char上,當用於Int時 ,要確保右側不是double,float,long類型,避免強制轉換,導致精度損失。
  6. +號操作符當且僅當它的操作數中至少有一個是String類型時,才執行字符串鏈接操作。
  7. 謎題13,只要是常量表達式組成的字符串相等,hello world = hello + world,那麼引用相等,但是如果其中一個是變量就不行了,因爲靜態常量在編譯期就轉換了,而如果帶了變量,就只有在運行期才知道該變量是什麼東西了。
  8. new String(char[])時,儘量都指明字符集,不要依賴默認的字符集
  9. j = j++相當於temp = j;j=j+1;j = temp;
  10. for(int i = start;i<= start + 1;i++){}startInteger.max_value時,可以無限循環
  11. while(i == i + 1)i = Double.POSITIVE_INFINITY,也就是無窮大時,可以無限循環
  12. while(i != i),當i = Double.NaN(不是數字的數量)時,爲true
  13. while(i != i + 0)i爲一個字符串時,就可以true,無限循環
  14. 移位運算符只能是整數
  15. 謎題31,所有的算術操作都會對short,byte,char類型先提升爲int類型。
  16. while(i<=j && j<=i&& i!=j){},當i,j等於同一個數的包裝類時滿足爲true,包裝類在比較運算符時自動拆包了,但是== ,!=這些時是引用。
  17. int有31位精度,而float只有24位精度
  18. 謎題37,不能隨便拋出try中不存在的受檢查異常,也就是基本上不能隨便加cache但是可以隨便捕獲Exception異常
  19. 一個方法可以拋出的被檢查異常集合是它繼承的父類中方法拋出異常的交集,而不是合集
  20. 要確定一個程序是否可以不止一次的對一個空final類型進行賦值是一個很困難的問題,比如,第一次通過方法賦值,出現錯誤了,那麼第二次是否還可以再賦值?所以編輯器採用了保守方法:一個空final變量只有在它是明確未賦過值得地方纔可以被賦值,也就是隻有第一次,就算失敗了,第二次也不能賦值了。
  21. 當調用一個構造器時,實例變量的初始化操作(也就是屬性)將先於構造器的程序體運行。
  22. 對於一個對象包含與它自己類相同的實例時,例如,鏈表列表節點,樹節點和圖節點,需要避免造成棧滿異常(stackOverfloatError),儘量就不要屬性是該類,除非是單例,通過static修飾一下
  23. 實例初始化操作拋出的任何異常都會傳播給構造器。
  24. 令人混淆的構造器案例:
    1. Java的重載解析過程是以兩個階段運行的,第一個階段選取所有可獲得並且可以應用的方法或者構造器;第二個階段是在第一個階段選取的方法或構造器中選取最精確的一個。如果第一個的參數類型可以傳遞給第二個,那麼就說,第一個精確點,eg:double[]可以傳遞給Object,所以double[]精確。
    public class Confusing{
        private Confusing(Object o){
            System.out.println("Object");
        }
        private Confusing(double[] dArray){
            System.out.println("array");
        }
        public static void main(String[] args){
            new Confusing(null);// array
        }
    }
  1. 啊呀!我的貓變成狗了
    1. 非靜態方法可以直接訪問靜態方法,不加類名。
    2. final類不可繼承;final方法能繼承,但不能被子類重寫;final變量是常量
    3. 靜態方法被繼承到子類中,那麼也可以直接通過SubClass.staticMethod()調用;
  2. 我所得到的都是靜態的
    class Dog{
        public static void bark(){System.out.println("woof");}
    }
    class BaseJi extends Dog{
        public static void bark(){} 
    }
    public static viod main(String[] args){
        Dog woofer = new Dog();
        Dog nipper = new BaseJi();
        woofer.bark();//woof
        nipper.bark();//woof    
    }
  1. 對靜態方法的調用不存在任何動態的分派機制,在編譯時刻已經確定,因爲他們都被聲明爲Dog類型的,所以都會調用Dog類型的靜態方法,輸出woof;
  2. 更改方法:去掉static,因爲靜態的東西是在編譯期就定了,去掉的話,就是重寫,而不是隱藏了。
  3. 謎題49:比生命更大
    public class Elvis{
        public static final Elvis INSTANCE = new Elvis();
        private final int beltSize;
        private static final int current_year = Calendar.getInstance().get(Calendar.YEAR);
        private Elvis(){
            beltSize = current_year - 1930; 
        }   
        public int beltSize(){
            return beltSize;
        }
        public static void main(String[] args){
            System.out.println(INSTANCE.beltSize());//-1930
        }
    }
    //1. new Elvls時候,會觸發類被初始化,但是該類已經在初始化了,所以會被忽略掉;
    //2.類初始化:先執行所有靜態域,並給初始值,然後再執行靜態域右邊的表達式,來給靜態域初始值;
  1. 謎題50,不是你的類型:
    1. null對於每一個引用類型來說都是子類型,但是當instanceof操作符左側爲null時返回false
    2. new Type() instanceof String instanceof操作符要求:如果兩個操作數的類型都是類,其中一個必須是另一個子類型,會在編譯器就檢測出來。
    3. Type type = (Type) new Object()會在運行期報錯,但是可以編譯通過。
  2. 不要在構造器中調用被子類重寫過的方法。
  3. 助手方法(靜態方法)通常都優於靜態代碼塊,因爲方法可以命名。
  4. Null和Void
    public class Type{
        public static void greet(){
            System.out.println("hello word");
        }
        public static void main(String[] args){
            ((Type)null).greet();//輸出了hello word
        }
    }
    //1.靜態方法調用在編譯器已經確定,
    //2.靜態方法的調用前用類名做限定,不要用表達式
  1. Java語言規範不允許本地變量聲明語句作爲一條語句在for,while,do循環中while Type type = new Type()需要加{}括起來。
  2. BigInteger,BigDecimal,包裝類,String都是不可變的,改動之後需要用新的接受String aa = "aa"; aa = aa + "bb";
  3. equal方法重寫,可以先用instanceof判斷一下類型,相同不,再比較值。比較兩個實例是否相等時,可以先判斷引用是否相等
  4. split接受的是一個正則表達式;
  5. 對於陌生的數組,可以使用Arrays.deepToString()打印出來
  6. Math.abs(Integer.MIN_VALUE)Integer.MIN_VALUE
  7. 謎題66:一件私事:重寫(方法)和隱藏(屬性)的區別,重寫除非在子類中通過super可以調用到父類的方法,否則調不到了,而隱藏可以轉型到父類,就可以訪問到了;當聲明“屬性”,“靜態方法”,“內部類”時,如果和父類的名稱相同了,會發生隱藏。
  8. final修飾方法時,對於實例方法表示不能“重寫”,對於靜態方法來說,不能“隱藏”
  9. 隱藏一個成員將阻止其被繼承。
  10. 謎題77:搞亂鎖的妖怪:
public class Worker extends Thread{
    private volatile boolean flag = false;
    public void run(){
        while(!flag){
            pretendToWork();
        }
        System.out.print("beer is good");
    }
    private void pretendToWork(){
        try{
             Thread.sleep(300);
        }catch(InterruptedException e){}
    }
    synchronized void quit(){
        flag = true;
        join();//內部是通過wait實現;這裏釋放的是主線程的cup和鎖,也就是誰當前擁有該對象的鎖;
        //join:將限定符線程加入到當前線程之前,這裏是this,也就是worker線程
    }
    synchronized void keepWorking(){
        flag = false;
    }
    public static void main(String[] args){
        Worker worker = new Worker();
        worker.start();
        Timer t = new Timer(true);
        t.schedule(new TimerTask(){
            public void run(){worker.keepWorking()}
        },500);
        Threed.sleep(400);
        worker.quit();
    }
}
//首先,worder開始工作,在300s是看一下還沒有下班,然後接着睡覺工作,
//等到400s的時候,主線程設置了下班了,並等待worker線程執行完成,釋放了鎖和cpu;
//等到500s的時候,Timer線程又開始工作了。
//等到600s的時候,工人查看,還是工作時間;所以就這樣一直在工作;不會有輸出;
//以上的問題出在  主線程 下班的時候,釋放了 worker對象的鎖,導致Timer可以進入,所以只需要在分開造兩個鎖就可以了。
private final Object lock = new Object();
public void quit(){
    synchronized(lock){//keepWorking也同步這個鎖
        flag = true;
        join();//這裏釋放就是當前對象的鎖,但 不是  lock這個鎖;
    } 
} 
  1. 謎題85,:不要在類初始化的時候啓動任何線程,也就是靜態代碼塊中
  2. 謎題89:泛型迷藥
    1. 非靜態成員類可以訪問外部類的泛型,所以該類是不需要自己的泛型的;
    2. 靜態成員類需要自己的泛型;
    3. 優先考慮用靜態成員類代替非靜態成員類;
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章