01.Java基礎問題

目錄介紹

  • 1.0.0.1 請手寫equal方法【String類】,講講具體的原理?Object類的equla方法是怎樣的?
  • 1.0.0.2 請說下String與StringBuffer區別,StringBuffer底部如何實現?String類能夠被繼承嗎,爲何?
  • 1.0.0.3 String a=""和String a=new String("")的的關係和異同?String的建立機制如何理解?
  • 1.0.0.4 爲何 Java 中的 String 是不可變的(Immutable)?字符串設計和實現考量?String不可變的好處?
  • 1.0.0.5 static關鍵字能夠修飾什麼?static使用的注意事項有哪些?static關鍵字的特色?使用static存在什麼問題?
  • 1.0.0.6 static變量存儲位置是哪裏?靜態變量的生命週期?靜態變量什麼時候銷燬?靜態引用的對象回收如何理解?
  • 1.0.0.7 訪問修飾符public,private,protected,以及不寫(默認)時的區別?訪問修飾符底層怎麼實現訪問權限管理?
  • 1.0.0.8 靜態變量和實例變量的區別?成員變量與局部變量的區別有那些?外部類和內部類有何區別,生命週期是怎樣的?
  • 1.0.0.9 如何實現對象克隆?克隆有哪些方式?深克隆和淺克隆有何區別?深克隆和淺克隆分別說的是什麼意思?
  • 1.0.1.0 int和Integer的區別?裝箱、拆箱什麼含義?何時裝箱/拆箱?裝箱和拆箱是如何實現的?
  • 1.0.1.1 Object有哪些公有方法?Object類toString()返回的是什麼?爲何說類必定要實現Cloneable接口才能夠克隆?
  • 1.0.1.2 final,finally,finalize有什麼不一樣?finally什麼狀況下不會被執行?java.lang 包下爲何要設置final?
  • 1.0.1.3 爲何要用通配符?上界通配符和下界通配符注意要點?什麼是無界通配符?如何理解泛型編譯器類型檢查?
  • 1.0.1.4 什麼是泛型擦除,可否經過開發中實際案例說下?如何獲取泛型的具體的類型【反射】?
  • 1.0.1.5 如何驗證int類型是否線程安全?那些類型是線程安全的?舉一個線程安全的例子【AtomicInteger】?
  • 1.0.1.6 Java序列話中若是有些字段不想進行序列化怎麼辦?Java序列化機制底層實現原理是怎樣的?
  • 1.0.1.8 原始數據類型和引用類型侷限性?爲什麼要引用基本數據包裝類?基本數據類型必定存儲在棧中嗎?
  • 1.0.1.9 new Integer(123) 與 Integer.valueOf(123)有何區別,請從底層實現分析二者區別?
  • 1.0.2.0 instanceof它的做用是什麼?在使用過程當中注意事項有哪些?它底層原理是如何實現的,說說你的理解?

好消息

  • 博客筆記大彙總【15年10月到至今】,包括Java基礎及深刻知識點,Android技術博客,Python學習筆記等等,還包括平時開發中遇到的bug彙總,固然也在工做之餘收集了大量的面試題,長期更新維護而且修正,持續完善……開源的文件是markdown格式的!同時也開源了生活博客,從12年起,積累共計500篇[近100萬字],將會陸續發表到網上,轉載請註明出處,謝謝!
  • 連接地址:https://github.com/yangchong211/YCBlogs
  • 若是以爲好,能夠star一下,謝謝!固然也歡迎提出建議,萬事起於忽微,量變引發質變!全部博客將陸續開源到GitHub!

1.0.0.1 請手寫equal方法【String類】,講講具體的原理?Object類的equla方法是怎樣的?

  • 代碼以下所示,若是是手寫代碼,必定要弄清楚邏輯思路!
    public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String) anObject;
            int n = count;
            if (n == anotherString.count) {
                int i = 0;
                while (n-- != 0) {
                    if (charAt(i) != anotherString.charAt(i))
                            return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }
    複製代碼
  • Object類的equla方法是怎樣的?
    public boolean equals(Object obj) {
        return (this == obj);
    }
    複製代碼

1.0.0.2 請說下String與StringBuffer區別,StringBuffer底部如何實現?String類能夠被繼承嗎,爲何?

  • String類的特色
    • String的特色是一旦被建立,就不能被改變。注意是地址不能改變。StringBuffer底層是可變的字節序列……
  • String類能夠被繼承嗎
    • 看String源碼可知,String類被final關鍵字修飾了,因此不能被繼承。這個地方能夠說下final關鍵字做用。
  • String、StringBuffer和StringBuilder的區別?
    • String是字符串常量,而StringBuffer、StringBuilder都是字符串變量,即String對象一建立後不可更改,然後二者的對象是可更改的:
    • StringBuffer是線程安全的,而StringBuilder是非線程安全的,這是因爲StringBuffer對方法加了同步鎖或者對調用的方法加了同步鎖
    • String更適用於少許的字符串操做的狀況,StringBuilder適用於單線程下在字符緩衝區進行大量操做的狀況,StringBuffer適用於多線程下在字符緩衝區進行大量操做的狀況
    • 技術博客大總結

1.0.0.3 String a=""和String a=new String("")的的關係和異同?String的建立機制如何理解?

  • 區別
    • 經過String a=""直接賦值的方式獲得的是一個字符串常量,存在於常量池;注意,相同內容的字符串在常量池中只有一個,即若是池已包含內容相等的字符串會返回池中的字符串,反之會將該字符串放入池中
    • 經過new String("")建立的字符串不是常量是實例對象,會在堆內存開闢空間並存放數據,且每一個實例對象都有本身的地址空間
  • String的建立機制
    • 因爲String在Java世界中使用過於頻繁,Java爲了不在一個系統中產生大量的String對象,引入了字符串常量池。其運行機制是:建立一個字符串時,首先檢查池中是否有值相同的字符串對象,若是有則不須要建立直接從池中剛查找到的對象引用;若是沒有則新建字符串對象,返回對象引用,而且將新建立的對象放入池中。可是,經過new方法建立的String對象是不檢查字符串池的,而是直接在堆區或棧區建立一個新的對象,也不會把對象放入池中。上述原則只適用於經過直接量給String對象引用賦值的狀況。

1.0.0.4 爲何Java中的 String 是不可變的(Immutable)?字符串設計和實現考量?String不可變的好處?

  • 不可變類String的緣由
    • String主要的三個成員變量 char value[], int offset, int count均是private,final的,而且沒有對應的 getter/setter;
    • String 對象一旦初始化完成,上述三個成員變量就不可修改;而且其所提供的接口任何對這些域的修改都將返回一個新對象;
    • 技術博客大總結
    • 是典型的 Immutable 類,被聲明成爲 final class,全部屬性也都是final的。也因爲它的不可變,相似拼接、裁剪字符串等動做,都會產生新的 String 對象。
  • 字符串設計和實現考量?
    • String 是 Immutable 類的典型實現,原生的保證了基礎線程安全,由於你沒法對它內部數據進行任何修改,這種便利甚至體如今拷貝構造函數中,因爲不可變,Immutable 對象在拷貝時不須要額外複製數據。
    • 爲了實現修改字符序列的目的,StringBuffer 和 StringBuilder 底層都是利用可修改的(char,JDK 9 之後是 byte)數組,兩者都繼承了 AbstractStringBuilder,裏面包含了基本操做,區別僅在於最終的方法是否加了 synchronized。
    • 這個內部數組應該建立成多大的呢?若是過小,拼接的時候可能要從新建立足夠大的數組;若是太大,又會浪費空間。目前的實現是,構建時初始字符串長度加 16(這意味着,若是沒有構建對象時輸入最初的字符串,那麼初始值就是 16)。咱們若是肯定拼接會發生很是屢次,並且大概是可預計的,那麼就能夠指定合適的大小,避免不少次擴容的開銷。擴容會產生多重開銷,由於要拋棄原有數組,建立新的(能夠簡單認爲是倍數)數組,還要進行arraycopy。
  • String不可變的好處?
    • 能夠緩存 hash 值
      • 由於 String 的 hash 值常常被使用,例如 String 用作 HashMap 的 key。不可變的特性可使得 hash 值也不可變,所以只須要進行一次計算。
    • String Pool 的須要
      • 若是一個String對象已經被建立過了,那麼就會從 String Pool 中取得引用。只有 String 是不可變的,纔可能使用 String Pool。
    • 安全性 技術博客大總結
      • String 常常做爲參數,String 不可變性能夠保證參數不可變。例如在做爲網絡鏈接參數的狀況下若是 String 是可變的,那麼在網絡鏈接過程當中,String 被改變,改變 String 對象的那一方覺得如今鏈接的是其它主機,而實際狀況卻不必定是。
    • 線程安全
      • String 不可變性天生具有線程安全,能夠在多個線程中安全地使用。

1.0.0.5 static關鍵字能夠修飾什麼?static使用的注意事項有哪些?static關鍵字的特色?使用static存在什麼問題?

  • 能夠用來修飾:成員變量,成員方法,代碼塊,內部類等。具體以下所示
    • 修飾成員變量和成員方法
      • 被 static 修飾的成員屬於類,不屬於單個這個類的某個對象,被類中全部對象共享,能夠而且建議經過類名調用。
      • 被static 聲明的成員變量屬於靜態成員變量,靜態變量存放在Java內存區域的方法區。
    • 靜態代碼塊
      • 靜態代碼塊定義在類中方法外,靜態代碼塊在非靜態代碼塊以前執行(靜態代碼塊—>非靜態代碼塊—>構造方法)
      • 該類無論建立多少對象,靜態代碼塊只執行一次.
    • 靜態內部類(static修飾類的話只能修飾內部類)
    • 靜態內部類與非靜態內部類之間存在一個最大的區別:
      • 非靜態內部類在編譯完成以後會隱含地保存着一個引用,該引用是指向建立它的外圍內,可是靜態內部類卻沒有。沒有這個引用就意味着:1.它的建立是不須要依賴外圍類的建立。2.它不能使用任何外圍類的非static成員變量和方法。
    • 靜態導包(用來導入類中的靜態資源,1.5以後的新特性):
      • 這兩個關鍵字連用能夠指定導入某個類中的指定靜態資源,而且不須要使用類名調用類中靜態成員,能夠直接使用類中靜態成員變量和成員方法。
  • static使用的注意事項有哪些?
    • 在靜態方法中是沒有this關鍵字的
      • 靜態是隨着類的加載而加載,this是隨着對象的建立而存在。
      • 靜態比對象先存在。
    • 靜態方法只能訪問靜態的成員變量和靜態的成員方法【靜態只能訪問靜態,非靜態能夠訪問靜態的也能夠訪問非靜態的】
  • static關鍵字的特色?
    • 隨着類的加載而加載
    • 優先於對象存在
    • 被類的全部對象共享
    • 能夠經過類名調用【靜態修飾的內容通常咱們稱其爲:與類相關的,類成員】
  • 使用static存在什麼問題?
    • 1.佔用內存,而且內存通常不會釋放;
    • 2.在系統不夠內存狀況下會自動回收靜態內存,這樣就會引發訪問全局靜態錯誤。
    • 3.在Android中不能將activity做爲static靜態對象,這樣使activity的全部組件對象都存入全局內存中,而且不會被回收;

1.0.0.6 static變量存儲位置是哪裏?靜態變量的生命週期?靜態變量什麼時候銷燬?靜態引用的對象回收如何理解?

  • static變量存儲位置
    • 注意是:存儲在JVM的方法區中
    • static變量在類加載時被初始化,存儲在JVM的方法區中,整個內存中只有一個static變量的拷貝,可使用類名直接訪問,也能夠經過類的實例化對象訪問,通常不推薦經過實例化對象訪問,通俗的講static變量屬於類,不屬於對象,任何實例化的對象訪問的都是同一個static變量,任何地放均可以經過類名來訪問static變量。
  • 靜態變量的生命週期
    • 類在何時被加載?技術博客大總結
    • 當咱們啓動一個app的時候,系統會建立一個進程,此進程會加載一個Dalvik VM的實例,而後代碼就運行在DVM之上,類的加載和卸載,垃圾回收等事情都由DVM負責。也就是說在進程啓動的時候,類被加載,靜態變量被分配內存。
  • 靜態變量什麼時候銷燬
    • 類在何時被卸載?在進程結束的時候。
    • 說明:通常狀況下,全部的類都是默認的ClassLoader加載的,只要ClassLoader存在,類就不會被卸載,而默認的ClassLoader生命週期是與進程一致的
  • 靜態引用的對象回收
    • 只要靜態變量沒有被銷燬也沒有置null,其對象一直被保持引用,也即引用計數不多是0,所以不會被垃圾回收。所以,單例對象在運行時不會被回收

1.0.0.7 訪問修飾符public,private,protected,以及不寫(默認)時的區別?訪問修飾符底層是怎麼實現訪問權限管理的?

  • 訪問修飾符public,private,protected,以及不寫(默認)時的區別?
    • 公有,使用public修飾,聲明爲公有表示可供全部其餘的任何類使用,例如main方法前面就有public修飾。使用了public修飾意味着訪問權限是最大的。
    • 私有,使用private修飾,聲明爲私有表示僅在本類中可見。私有的方法和屬性不能被其餘類使用,能夠起到信息隱藏的做用,是封裝的主要方式。同時使用private修飾會影響繼承,private修飾的類不能被繼承,private修飾的方法不能被重寫。全部剛好與public相反,private修飾的訪問權限最低,所以需謹慎使用。
    • 默認,什麼類,變量,方法時能夠不使用任何訪問修飾符,此時表示默認修飾。對同一個包中的類是能夠進行訪問和修改的,可是不能跨包使用,這種狀況使用的相對較少。
    • 受保護的,使用protected修飾,與默認的修飾不一樣,受保護的修飾訪問權限要大於默認修飾的,由於protected修飾的除了能夠在同一個包中使用外,在其餘包中的子類也是可使用的,所以他的訪問權限是大於默認的而小於公有的。
    區別以下:
    做用域          當前類      同包    子類     其餘
    public            √         √       √      √
    protected         √         √       √      ×
    default           √         √       ×      ×
    private           √         ×       ×      ×
    複製代碼

1.0.0.8 靜態變量和實例變量的區別?成員變量與局部變量的區別有那些?外部類和內部類有何區別,生命週期是怎樣的?

  • 靜態變量和實例變量的區別
    • 靜態變量是被static修飾符修飾的變量,也稱爲類變量,它屬於類,不屬於類的任何一個對象,一個類無論建立多少個對象,靜態變量在內存中有且僅有一個拷貝。靜態變量能夠實現讓多個對象共享內存。在Java開發中,上下文類和工具類中一般會有大量的靜態成員。
    • 實例變量必須依存於某一實例,須要先建立對象而後經過對象才能訪問到它
  • 成員變量與局部變量的區別
    • 1.從語法形式上,當作員變量是屬於類的,而局部變量是在方法中定義的變量或是方法的參數;成員變量能夠被 public,private,static 等修飾符所修飾,而局部變量不能被訪問控制修飾符及 static 所修飾;可是,成員變量和局部變量都能被 final 所修飾;
    • 2.從變量在內存中的存儲方式來看,成員變量是對象的一部分,而對象存在於堆內存,局部變量存在於棧內存
    • 3.從變量在內存中的生存時間上看,成員變量是對象的一部分,它隨着對象的建立而存在,而局部變量隨着方法的調用而自動消失。
    • 4.成員變量若是沒有被賦初值,則會自動以類型的默認值而賦值(一種狀況例外被 final 修飾但沒有被 static 修飾的成員變量必須顯示地賦值);而局部變量則不會自動賦值。
  • 外部類和內部類有何區別,生命週期是怎樣的?
    • Java中的內部類共分爲四種:
      • 靜態內部類static inner class (also called nested class)
      • 成員內部類member inner class
      • 局部內部類local inner class
      • 匿名內部類anonymous inner class
    • 內部類就至關於一個外部類的成員變量,因此能夠直接訪問外部變量,外部類不能直接訪問內部類變量,必須經過建立內部類實例的方法訪問。
      • new InnerClass(32).m就是建立內部類實例訪問內部類成員變量。你想不通的確定是指內部類的私有變量怎麼能夠被外部類訪問吧,按常規,私有變量m只能在InnerClass裏被訪問,

1.0.0.9 如何實現對象克隆?克隆有哪些方式?深克隆和淺克隆有何區別?深克隆和淺克隆分別說的是什麼意思?

  • 如何實現對象克隆,有兩種方式:
    • 1.實現Cloneable接口並重寫Object類中的clone()方法;
    • 2.實現Serializable接口,經過對象的序列化和反序列化實現克隆,能夠實現真正的深度克隆,代碼以下。
    • 注意:若是不知足上面兩個條件之一,那麼就不能進行克隆的!
  • 克隆有哪些方式?
    • 克隆(複製)在Java中是一種常見的操做,目的是快速獲取一個對象副本。克隆分爲深克隆和淺克隆。
    • 淺克隆:建立一個新對象,新對象的屬性和原來對象徹底相同,對於非基本類型屬性,仍指向原有屬性所指向的對象的內存地址。
    • 深克隆:建立一個新對象,屬性中引用的其餘對象也會被克隆,再也不指向原有對象地址。
  • 深克隆和淺克隆有何區別?技術博客大總結
    • 深淺克隆都會在堆中新分配一塊區域,區別在於對象屬性引用的對象是否須要進行克隆(遞歸性的)。
  • Java的clone()方法
    • 1.clone方法將對象複製了一份並返回給調用者。通常而言,clone()方法知足
    • 2.對任何的對象x,都有x.clone() !=x 由於克隆對象與原對象不是同一個對象
    • 3.對任何的對象x,都有x.clone().getClass()= =x.getClass()//克隆對象與原對象的類型同樣
    • 4.若是對象x的equals()方法定義恰當,那麼x.clone().equals(x)應該成立
  • 注意問題:
    • 基於序列化和反序列化實現的克隆不只僅是深度克隆,更重要的是經過泛型限定,能夠檢查出要克隆的對象是否支持序列化,這項檢查是編譯器完成的,不是在運行時拋出異常,這種是方案明顯優於使用Object類的clone方法克隆對象。
  • 代碼以下所示
    • 若是對象實現Cloneable並重寫clone方法不進行任何操做時,調用clone是進行的淺克隆。而使用對象流將對象寫入流而後再讀出是進行的深克隆。
    public class MyUtil {  
      
        private MyUtil() {  
            throw new AssertionError();  
        }  
      
        public static <T> T clone(T obj) throws Exception {  
            ByteArrayOutputStream bout = new ByteArrayOutputStream();  
            ObjectOutputStream oos = new ObjectOutputStream(bout);  
            oos.writeObject(obj);  
      
            ByteArrayInputStream bin = new ByteArrayInputStream(bout.toByteArray());  
            ObjectInputStream ois = new ObjectInputStream(bin);  
            return (T) ois.readObject();  
              
            // 說明:調用ByteArrayInputStream或ByteArrayOutputStream對象的close方法沒有任何意義  
            // 這兩個基於內存的流只要垃圾回收器清理對象就可以釋放資源  
        }  
    } 
    
    
    class CloneTest {  
        public static void main(String[] args) {  
            try {  
                Person p1 = new Person("Hao LUO", 33, new Car("Benz", 300));  
                p1.clone();//淺克隆
                Person p2 = MyUtil.clone(p1);   // 深度克隆  
                p2.getCar().setBrand("BYD");  
                // 修改克隆的Person對象p2關聯的汽車對象的品牌屬性  
                // 原來的Person對象p1關聯的汽車不會受到任何影響  
                // 由於在克隆Person對象時其關聯的汽車對象也被克隆了  
                System.out.println(p1);  
            } catch (Exception e) {  
                e.printStackTrace();  
            }  
        }  
    }
    複製代碼

1.0.1.0 int和Integer的區別?裝箱、拆箱什麼含義?何時裝箱和拆箱?裝箱和拆箱是如何實現的?

  • int和Integer的區別:基本數據類型、引用類型
    • Integer是int的包裝類,int則是java的一種基本數據類型
    • Integer變量必須實例化後才能使用,而int變量不須要
    • Integer實際是對象的引用,當new一個Integer時,其實是生成一個指針指向此對象;而int則是直接存儲數據值
    • Integer的默認值是null,int的默認值是0
  • 裝箱、拆箱技術博客大總結
    • 裝箱就是自動將基本數據類型轉換爲包裝器類型
    • 拆箱就是自動將包裝器類型轉換爲基本數據類型
    int a = 10;
    //裝箱操做
    Integer integer1 = Integer.valueOf(a);
    
    //拆箱操做
    Integer integer2 = new Integer(5);
    int i2 = integer2.intValue();
    複製代碼
  • jdk中如何操做裝箱、拆箱
    • 在JDK中,裝箱過程是經過調用包裝器的valueOf方法實現的,而拆箱過程是經過調用包裝器的xxxValue方法實現的(xxx表明對應的基本數據類型)。
    • Integer、Short、Byte、Character、Long 這幾個類的valueOf方法的實現是相似的,有限可列舉,共享[-128,127];
    • Double、Float的valueOf方法的實現是相似的,無限不可列舉,不共享;
    • Boolean的valueOf方法的實現不一樣於以上的整型和浮點型,只有兩個值,有限可列舉,共享;
  • 何時裝箱/拆箱?
    • 何時拆箱主要取決於:在當前場景下,你須要的是引用類型仍是原生類型。若須要引用類型,但傳進來的值是原生類型,則自動裝箱(例如,使用equals方法時傳進來原生類型的值);若須要的是原生類型,但傳進來的值是引用類型,則自動拆箱(例如,使用運算符進行運算時,操做數是包裝類型)。
  • 裝箱和拆箱是如何實現的
    • 以Interger類爲例,下面看一段代碼來了解裝箱和拆箱的實現
    public class Main {
        public static void main(String[] args) {
            Integer y = 10;
            int c = i;
        }
    }
    複製代碼
    • 而後來編譯一下:
      • 從反編譯獲得的字節碼內容能夠看出,在裝箱的時候自動調用的是Integer的valueOf(int)方法。而在拆箱的時候自動調用的是Integer的intValue方法。
      • 所以能夠用一句話總結裝箱和拆箱的實現過程:裝箱過程是經過調用包裝器的valueOf方法實現的,而拆箱過程是經過調用包裝器的 xxxValue方法實現的。(xxx表明對應的基本數據類型)。

1.0.1.1 Object有哪些公有方法?Object類toString()返回的是什麼?爲何說類必定要實現Cloneable接口才能夠克隆?

  • 經常使用方法
    • equals(): 和==做用類似技術博客大總結
    • hashCode():用於哈希查找,重寫了equals()通常都要重寫該方法
    • getClass(): 獲取Class對象
    • wait():讓當前線程進入等待狀態,並釋放它所持有的鎖
    • notify()&notifyAll(): 喚醒一個(全部)正處於等待狀態的線程
    • toString():轉換成字符串
  • Android的Object類toString()返回的是什麼?
    • 返回的是類名和hashcode的組合字符串
    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
    複製代碼
  • 爲何說類必定要實現Cloneable接口才能夠克隆?具體看下Android中Object類代碼
    • 看一下代碼便可知道,對象須要instanceof判斷該對象是不是Cloneable的實例
    protected Object clone() throws CloneNotSupportedException {
        if (!(this instanceof Cloneable)) {
            throw new CloneNotSupportedException("Class " + getClass().getName() +
                                                 " doesn't implement Cloneable");
        }
    
        return internalClone();
    }
    複製代碼

1.0.1.2 final,finally,finalize有什麼不一樣?finally什麼狀況下不會被執行?java.lang 包下爲何要設置final?

  • final能夠修飾類,方法,變量
    • final修飾類表明類不能夠繼承拓展
    • final修飾變量表示變量不能夠修改
    • final修飾方法表示方法不能夠被重寫
  • finally則是Java保證重點代碼必定要被執行的一種機制
    • 可使用 try-finally 或者 try-catch-finally 來進行相似關閉 JDBC鏈接、保證 unlock 鎖等動做。
  • finalize 是基礎類 java.lang.Object的一個方法
    • 它的設計目的是保證對象在被垃圾收集前完成特定資源的回收。finalize 機制如今已經不推薦使用,而且在 JDK 9開始被標記爲 deprecated。
  • final 關鍵字深刻理解技術博客大總結
    • 能夠將方法或者類聲明爲 final,這樣就能夠明確告知別人,這些行爲是不準修改的。
    • 若是你關注過 Java 核心類庫的定義或源碼, 有沒有發現java.lang 包下面的不少類,至關一部分都被聲明成爲final class?在第三方類庫的一些基礎類中一樣如此,這能夠有效避免 API 使用者更改基礎功能,某種程度上,這是保證平臺安全的必要手段。
  • 在如下4種特殊狀況下,finally塊不會被執行:
    • 1.在finally語句塊中發生了異常。
    • 2.在前面的代碼中用了System.exit()退出程序。
    • 3.程序所在的線程死亡。
    • 4.關閉CPU。
  • java.lang 包下爲何要設置final?
    • final 變量產生了某種程度的不可變(immutable)的效果,因此,能夠用於保護只讀數據,尤爲是在併發編程中,由於明確地不能再賦值 final 變量,有利於減小額外的同步開銷,也能夠省去一些防護性拷貝的必要。
    • 使用 final 修飾參數或者變量,也能夠清楚地避免意外賦值致使的編程錯誤,甚至,有人明確推薦將全部方法參數、本地變量、成員變量聲明成 final。

1.0.1.3 爲何要用通配符?上界通配符和下界通配符注意要點?什麼是無界通配符?如何理解泛型編譯器類型檢查?

  • 爲何要使用通配符
    • 通配符的設計存在必定的場景,例如在使用泛型後,首先聲明瞭一個Animal的類,然後聲明瞭一個繼承Animal類的Cat類,顯然Cat類是Animal類的子類,可是List卻不是List的子類型,而在程序中每每須要表達這樣的邏輯關係。爲了解決這種相似的場景,在泛型的參數類型的基礎上新增了通配符的用法。
  • 上界通配符 - 上界通配符顧名思義,表示的是類型的上界【 **包含自身**】,所以通配的參數化類型多是T或T的子類。正由於沒法肯定具體的類型是什麼,add方法受限(能夠添加null,由於null表示任何類型),但能夠從列表中獲取元素後賦值給父類型。如上圖中的第一個例子,第三個add()操做會受限,緣由在於List和List是List的子類型。
  • 下界通配符 - 下界通配符表示的是參數化類型是T的超類型(**包含自身**),層層至上,直至Object,編譯器無從判斷get()返回的對象的類型是什麼,所以get()方法受限。可是能夠進行add()方法,add()方法能夠添加T類型和T類型的子類型,如第二個例子中首先添加了一個Cat類型對象,而後添加了兩個Cat子類類型的對象,這種方法是可行的,可是若是添加一個Animal類型的對象,顯然將繼承的關係弄反了,是不可行的。
  • 無界通配符
    • 任意類型,若是沒有明確,那麼就是Object以及任意的Java類了
    • 無界通配符用<?>表示,?表明了任何的一種類型,能表明任何一種類型的只有null(Object自己也算是一種類型,但卻不能表明任何一種類型,因此List和List的含義是不一樣的,前者類型是Object,也就是繼承樹的最上層,然後者的類型徹底是未知的)。
    • 技術博客大總結
    • 如何理解編譯器類型檢查
      • 看一個網上的案例
        • image
      • 在引入泛型以後,經過將代碼中的「public class Box」更改成「public class Box」來建立泛型類型的聲明,而這個聲明的背後實質上是引入了能夠在類中任何地方使用的類型變量T。如實例4中所示:能夠看到,除了新增的泛型類型聲明外,全部在原來代碼中出現的Object都被類型變量T所替換。
      • 乍一看類型變量這個詞,感受有點晦澀難懂,但其實若是仔細思量一番會發現它其實並不難理解,上面的實例4能夠理解爲「在使用泛型時,能夠將類型參數T傳遞給Box類型自己」,結合Oracle給出的官方定義「泛型的本質是類型參數化」會有更深的理解。
      • 在實例5中,在對象聲明和初始化的時候,都指定了類型參數T,在場景一種,T爲String;在場景二中,T爲Integer。這樣,在場景二中向IntegerBox中傳入String類型的數據「aaaaa」時,程序會報錯。實例6中的泛型集合對象的操做也與之相似,在聲明瞭一個List的boxes對象以後,若是向boxes中傳入Integer對象11111,程序會報錯。
      • 能夠看到,經過對於泛型的使用,以前的多業務場景中的問題都獲得瞭解決,由於如今在編譯階段就能夠解決以前類型不匹配的問題,而不用等到運行時才暴露問題,只要合理使用泛型,就能在很大程度上規避此類風險。對於泛型的使用,這種參數化類型的做用表面上看是聲明,背後實際上是約定。
    • 1.0.1.4 什麼是泛型擦除,可否經過開發中實際案例說下?如何獲取泛型的具體的類型【反射】?

      • 開發中的泛型擦除案例php

        • 泛型是提供給javac編譯器使用的,限定集合的輸入類型,編譯器編譯帶類型說明的集合時會去掉「類型」信息。
        public class GenericTest {
            public static void main(String[] args) {
                new GenericTest().testType();
            }
            public void testType(){
                ArrayList<Integer> collection1 = new ArrayList<Integer>();
                ArrayList<String> collection2= new ArrayList<String>();
                System.out.println(collection1.getClass()==collection2.getClass());
                //二者class類型同樣,即字節碼一致
                System.out.println(collection2.getClass().getName());
                //class均爲java.util.ArrayList,並沒有實際類型參數信息
            }
            
            //輸出結果
            //true
            //java.util.ArrayList
        }
        複製代碼
      • 如何獲取泛型的具體的類型?技術博客大總結java

        • 使用反射可跳過編譯器,往某個泛型集合加入其它類型數據。
        • 只有引用類型才能做爲泛型方法的實際參數,具體案例以下所示
        public class GenericTest {
            public static void main(String[] args) {
                swap(new String[]{"111","222"},0,1);//編譯經過
                
                //swap(new int[]{1,2},0,1);
                //編譯不經過,由於int不是引用類型
                
                swap(new Integer[]{1,2},0,1);//編譯經過
            }
            
            /*交換數組a 的第i個和第j個元素*/
            public static <T> void swap(T[]a,int i,int j){
                T temp = a[i];
                a[i] = a[j];
                a[j] = temp;
            }
        }
        複製代碼
        • 但注意基本類型有時能夠做爲實參,由於有自動裝箱拆箱。下面例子(編譯經過了):
        public class GenericTest {
            public static void main(String[] args) {
                new GenericTest().testType();
                int a = biggerOne(3,5);
                //int 和 double,取交爲Number
                Number b = biggerOne(3,5.5);
                //String和int 取交爲Object
                Object c = biggerOne("1",2);
            }
            //從x,y中返回y
            public static <T> T biggerOne(T x,T y){
                return y;
            }
        }
        複製代碼
        • 同時,該例還代表,當實參不一致時,T取交集,即第一個共同的父類。
        • 另外,若是用Number b = biggerOne(3,5.5);改成String c = biggerOne(3,5.5);則編譯報錯:
        Error:(17, 29) java: 不兼容的類型: 推斷類型不符合上限
            推斷: java.lang.Number&java.lang.Comparable<? extends java.lang.Number&java.lang.Comparable<?>>
            上限: java.lang.String,java.lang.Object
        複製代碼

      1.0.1.5 如何驗證int類型是否線程安全?那些類型是線程安全的?舉一個線程安全的例子【AtomicInteger】?

      • 如何驗證int類型是否線程安全
        • 200個線程,每一個線程對共享變量 count 進行 50 次 ++ 操做
        • int 做爲基本類型,直接存儲在內存棧,且對其進行+,-操做以及++,–操做都不是原子操做,都有可能被其餘線程搶斷,因此不是線程安全。int 用於單線程變量存取,開銷小,速度快
        • 技術博客大總結
        int count = 0;
        private void startThread() {
            for (int i = 0;i < 200; i++){
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        for (int k = 0; k < 50; k++){
                            count++;
                        }
                    }
                }).start();
            }
            // 休眠10秒,以確保線程都已啓動
            try {
                Thread.sleep(1000*10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                Log.e("打印日誌----",count+"");
            }
        }
        
        //指望輸出10000,最後輸出的是9818
        //注意:打印日誌----: 9818
        複製代碼
      • 那些類型是線程安全的
        • Java自帶的線程安全的基本類型包括: AtomicInteger, AtomicLong, AtomicBoolean, AtomicIntegerArray,AtomicLongArray等
      • AtomicInteger線程安全版
        • AtomicInteger類中有有一個變量valueOffset,用來描述AtomicInteger類中value的內存位置 。
        • 當須要變量的值改變的時候,先經過get()獲得valueOffset位置的值,也即當前value的值.給該值進行增長,並賦給next
        • compareAndSet()比較以前取到的value的值當前有沒有改變,若沒有改變的話,就將next的值賦給value,假若和以前的值相比的話發生變化的話,則從新一次循環,直到存取成功,經過這樣的方式可以保證該變量是線程安全的
        • value使用了volatile關鍵字,使得多個線程能夠共享變量,使用volatile將使得VM優化失去做用,在線程數特別大時,效率會較低。技術博客大總結
        private static AtomicInteger atomicInteger = new AtomicInteger(1);
        static Integer count1 = Integer.valueOf(0);
        private void startThread1() {
            for (int i = 0;i < 200; i++){
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        for (int k = 0; k < 50; k++){
                            // getAndIncrement: 先得到值,再自增1,返回值爲自增前的值
                            count1 = atomicInteger.getAndIncrement();
                        }
                    }
                }).start();
            }
            // 休眠10秒,以確保線程都已啓動
            try {
                Thread.sleep(1000*10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                Log.e("打印日誌----",count1+"");
            }
        }
        
        //指望輸出10000,最後輸出的是10000
        //注意:打印日誌----: 10000
        
        //AtomicInteger使用了volatile關鍵字進行修飾,使得該類能夠知足線程安全。
        private volatile int value;
        public AtomicInteger(int initialValue) {
            value = initialValue;
        }
        複製代碼

      1.0.1.6 Java序列話中若是有些字段不想進行序列化怎麼辦?Java序列化機制底層實現原理是怎樣的?

      • 對於不想進行序列化的變量,使用transient關鍵字修飾。
      • transient關鍵字的做用是:阻止實例中那些用此關鍵字修飾的的變量序列化;當對象被反序列化時,被transient修飾的變量值不會被持久化和恢復。transient只能修飾變量,不能修飾類和方法。

      1.0.1.8 原始數據類型和引用類型侷限性?爲什麼要引用基本數據包裝類?基本數據類型必定存儲在棧中嗎?

      • 原始數據類型和引用類型侷限性
        • 原始數據類型和 Java 泛型並不能配合使用
        • Java 的泛型某種程度上能夠算做僞泛型,它徹底是一種編譯期的技巧,Java 編譯期會自動將類型轉換爲對應的特定類型,這就決定了使用泛型,必須保證相應類型能夠轉換爲Object。
      • 爲什麼要引用基本數據包裝類技術博客大總結
        • 就好比,咱們使用泛型,須要用到基本數據類型的包裝類。
        • Java 的對象都是引用類型,若是是一個原始數據類型數組,它在內存裏是一段連續的內存,而對象數組則否則,數據存儲的是引用,對象每每是分散地存儲在堆的不一樣位置。這種設計雖然帶來了極大靈活性,可是也致使了數據操做的低效,尤爲是沒法充分利用現代 CPU 緩存機制。
        • Java 爲對象內建了各類多態、線程安全等方面的支持,但這不是全部場合的需求,尤爲是數據處理重要性日益提升,更加高密度的值類型是很是現實的需求。
      • 基本數據類型必定存儲在棧中嗎?
        • 首先說明,"java中的基本數據類型必定存儲在棧中的嗎?」這句話確定是錯誤的。
        • 基本數據類型是放在棧中仍是放在堆中,這取決於基本類型在何處聲明,下面對數據類型在內存中的存儲問題來解釋一下:
        • 一:在方法中聲明的變量,即該變量是局部變量,每當程序調用方法時,系統都會爲該方法創建一個方法棧,其所在方法中聲明的變量就放在方法棧中,當方法結束系統會釋放方法棧,其對應在該方法中聲明的變量隨着棧的銷燬而結束,這就局部變量只能在方法中有效的緣由
          • 在方法中聲明的變量能夠是基本類型的變量,也能夠是引用類型的變量。當聲明是基本類型的變量的時,其變量名及值(變量名及值是兩個概念)是放在JAVA虛擬機棧中。當聲明的是引用變量時,所聲明的變量(該變量其實是在方法中存儲的是內存地址值)是放在JAVA虛擬機的棧中,該變量所指向的對象是放在堆類存中的。
        • 二:在類中聲明的變量是成員變量,也叫全局變量,放在堆中的(由於全局變量不會隨着某個方法執行結束而銷燬)。一樣在類中聲明的變量便可是基本類型的變量,也但是引用類型的變量
          • 當聲明的是基本類型的變量其變量名及其值放在堆內存中的。引用類型時,其聲明的變量仍然會存儲一個內存地址值,該內存地址值指向所引用的對象。引用變量名和對應的對象仍然存儲在相應的堆中

      1.0.1.9 new Integer(123) 與 Integer.valueOf(123)有何區別,請從底層實現分析二者區別?

      • new Integer(123) 與 Integer.valueOf(123) 的區別在於:
        • new Integer(123) 每次都會新建一個對象;技術博客大總結
        • Integer.valueOf(123) 會使用緩存池中的對象,屢次調用會取得同一個對象的引用。
        Integer x = new Integer(123);
        Integer y = new Integer(123);
        System.out.println(x == y);    // false
        Integer z = Integer.valueOf(123);
        Integer k = Integer.valueOf(123);
        System.out.println(z == k);   // true
        複製代碼
      • valueOf() 方法的實現比較簡單,就是先判斷值是否在緩存池中,若是在的話就直接返回緩存池的內容。
        public static Integer valueOf(int i) {
            if (i >= IntegerCache.low && i <= IntegerCache.high)
                return IntegerCache.cache[i + (-IntegerCache.low)];
            return new Integer(i);
        }
        複製代碼
      • 在 Java 8 中,Integer 緩存池的大小默認爲 -128~127。
        static final int low = -128;
        static final int high;
        static final Integer cache[];
        
        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;
        
            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);
        
            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }
        複製代碼
      • 編譯器會在自動裝箱過程調用 valueOf() 方法,所以多個Integer實例使用自動裝箱來建立而且值相同,那麼就會引用相同的對象。
        Integer m = 123;
        Integer n = 123;
        System.out.println(m == n); // true
        複製代碼
      • 基本類型對應的緩衝池以下:
        • boolean values true and false
        • all byte values
        • short values between -128 and 127
        • int values between -128 and 127
        • char in the range \u0000 to \u007F
      • 在使用這些基本類型對應的包裝類型時,就能夠直接使用緩衝池中的對象。

      1.0.2.0 instanceof它的做用是什麼?在使用過程當中注意事項有哪些?它底層原理是如何實現的,說說你的理解?

      • 它的做用是什麼?
        • instanceof是Java的一個二元操做符,和==,>,<是同一類東西。因爲它是由字母組成的,因此也是Java的保留關鍵字。它的做用是測試它左邊的對象是不是它右邊的類的實例,返回boolean類型的數據。
      • 使用過程當中注意事項有哪些?技術博客大總結
        • 類的實例包含自己的實例,以及全部直接或間接子類的實例
        • instanceof左邊顯式聲明的類型與右邊操做元必須是同種類或存在繼承關係,也就是說須要位於同一個繼承樹,不然會編譯錯誤
        //好比下面就會編譯錯誤
        String s = null;
        s instanceof null
        s instanceof Integer
        複製代碼

      其餘介紹

      01.關於博客彙總連接

      02.關於個人博客

      • 個人我的站點:www.yczbj.org,www.ycbjie.cn
      • github:https://github.com/yangchong211
      • 知乎:https://www.zhihu.com/people/yang-chong-69-24/pins/posts
      • 簡書:http://www.jianshu.com/u/b7b2c6ed9284
      • csdn:http://my.csdn.net/m0_37700275
      • 喜馬拉雅聽書:http://www.ximalaya.com/zhubo/71989305/
      • 開源中國:https://my.oschina.net/zbj1618/blog
      • 泡在網上的日子:http://www.jcodecraeer.com/member/content_list.php?channelid=1
      • 郵箱:yangchong211@163.com
      • 阿里雲博客:https://yq.aliyun.com/users/article?spm=5176.100- 239.headeruserinfo.3.dT4bcV
      • segmentfault頭條:https://segmentfault.com/u/xiangjianyu/articles
      • 掘金:https://juejin.im/user/5939433efe88c2006afa0c6e
相關文章
相關標籤/搜索