Java基礎知識點梳理(詳細)

  1. 基本數據類型

    Java內置8種基本類型 -> 6種數字類型 -> 4種整數類型: byte, short, int, long
    -> 2種浮點類型: float, double
    -> 1種布爾類型: boolean
    -> 1種字符類型: char

    (類型大小寫注意,比如boolean是原始數據類型,Boolean是對應的封裝對象)

    • 數據類型特徵表

      類型 位數 最小值 最大值 默認值 其他
      byte 8 -128(-2^7) 127(2^7-1) 0 有符號、二進制補碼錶示
      short 16 -32768(-2^15) 32767(2^15-1) 0 有符號、二進制補碼錶示
      int 32 -2^31 2^31-1 0 有符號、二進制補碼錶示
      long 64 -2^63 2^63-1 0L(0l) 有符號、二進制補碼錶示
      float 32 2^(-149) 2^128-1 0.0f 單精度、IEEE754標準
      double 64 2^(-1074) 2^1024-1 0.0d 雙精度、IEEE754標準
      char 16 \u0000(0) \uffff(65535) \u0000(0) 單一的、Unicode字符
    • 浮點數內存結構

      類型 位數 符號位 指數位 尾數位
      float 32 1 8 23
      double 64 1 11 52
    • 原始數據類型對應的封裝對象

      • (byte, Byte), (short, Short), (long, Long), (float,Float), (double, Double), (boolean, Boolean)
      • (int, Integer), (char, Character)

      小題

      Integer i = null;
      int j = i.intValue();
      

      編譯通過,但運行時報錯NullPointerException。因爲調用了null.somemethod()。

    • 自動裝箱和拆箱

      Integer i = 100;  //自動裝箱,編譯器執行Integer.valueOf(100)
      int j = i;        //自動拆箱,編譯器執行i.intValue()
      

      小題

       Integer i1 =200;  
       Integer i2 =200;          
       System.out.println("i1==i2: "+(i1==i2));                   
       Integer i3 =100;  
       Integer i4 =100;  
       System.out.println("i3==i4: "+(i3==i4));
      

      運行結果爲false,true.

      首先,==和equals()的區別:

      • ==比較的是兩個對象的引用是否相同,或者是比較原始數據類型是否相等;
      • equals()比較的是兩個對象的內容是否相同。

      其次,-128~127的Integer值可以從緩存中取得。其他情況要重新創建。

      public static Integer valueOf(int i) {
          if (i >= IntegerCache.low && i <= IntegerCache.high)
              return IntegerCache.cache[i + (-IntegerCache.low)];
          return new Integer(i);
      }
      

      小題

      String str1 ="abc";
      String str2 ="abc";
      System.out.println(str2==str1); 
      System.out.println(str2.equals(str1));  
      String str3 =new String("abc");
      String str4 =new String("abc"); 
      System.out.println(str3==str4);  
      System.out.println(str3.equals(str4));  
      

      結果是true,true, false, true.前兩個String對象從String池中獲取,後兩個對象是新創建的,內容相同但引用不同。

      小題

      String d ="2"; 
      String e ="23";
      e = e.substring(0, 1);
      System.out.println(e.equals(d)); 
      System.out.println(e==d);
      

      結果是true,false.直接上源碼:

      public String substring(int beginIndex, int endIndex) {
          if (beginIndex < 0) {
              throw new StringIndexOutOfBoundsException(beginIndex);
          }
          if (endIndex > value.length) {
              throw new StringIndexOutOfBoundsException(endIndex);
          }
          int subLen = endIndex - beginIndex;
          if (subLen < 0) {
              throw new StringIndexOutOfBoundsException(subLen);
          }
          return ((beginIndex == 0) && (endIndex == value.length)) ? this
                  : new String(value, beginIndex, subLen);
      }
      
    • 關於基本數據類型的幾點補充

      1. short s1=1; s1=s1+1;

        這一句編譯錯誤,因爲執行s1+1返回的結果是int類型(執行隱式類型轉換)。修改的話要強制轉換爲short型纔可以。

        short s1=1; s1+=4;

        這一句沒有任何問題。

      2. switch語句不能作用於long類型中,可以作用於char, byte, short, int, Character, Byte, Short, Integer, String or an enum.

      小題

     public static void main(String[] args) throws Throwable {
         int j=0;
         for(int i=0;i<1000;i++) {
             j=j++;
         }
         System.out.println(j);
     }
    

    運行結果爲0

    解釋一(未十分確信):Java使用中間緩存變量機制,j=j++語句會執行爲:

    temp=j;
    j=j+1;
    j=temp;
    

    解釋二(靠譜):使用javap反彙編命令進行反彙編,其中j=j++對應的結果是(j對應的變量編號是1):

       11: iload_1                   //將局部變量j的值放到棧頂:0
       12: iinc          1, 1        //將局部變量j的值加1,j=1
       15: istore_1                  //將棧頂的值放到局部變量j中,j=0
    

    所以從底層實現看,j=j++這一句中的自增操作只是對局部變量的操作,局部變量變化後沒有存儲到棧頂,反而被之前棧頂的值覆蓋了,所以相當於不起作用。

  2. 關鍵字

    • final:
      • 修飾變量時,用以定義常量;
      • 修飾方法時,方法不能被重寫(Override);
      • 修飾類時,類不能被繼承。
  3. 數組

    • 3種創建方式

      int[] arr1 = {1,2,3,4};             //正確
      int[] arr2 = new int[4];            //正確
      int[] arr3 = new int[]{1,2,3,4};    //正確
      int[] arr4 = new int[4]{1,2,3,4};s  //錯誤,編譯不通過
      
    • 數組越界,拋出ArrayIndexOutOfBoundsException

    • 數組具有length屬性

    • 如果不對數組指定初值,默認初始化爲相應數據類型的默認值。

    • 多維數組,嵌套中括號即可。

    • 數組是一種特殊的結構,在數組對象的對象頭中需要記錄數組長度的信息

  4. String

    • 不屬於基本類型,內部實際值是一個char[]數組

      JDK1.6之前,方法區包括運行時常量池在永久代中;
      JDK1.7,方法區和運行時常量池在永久代,字符串常量池在堆中;
      JDK1.8,永久代被廢棄,方法區在元空間,運行時常量池和字符串常量池在堆中。原文

    • 創建

      String s1="ss";                 //先將"ss"存儲在池中,然後返回引用
      String s2=new String("ss");     //創建新對象,使用的"ss"已經在池中
      String s3="Prog"+"gramming";    //創建3個對象,均存儲在池中
      
    • 字符串常量池和不可變(Immutable)字符串

      字符串的分配,和其他的對象分配一樣,耗費高昂的時間與空間代價。JVM爲了提高性能和減少內存開銷,在實例化字符串常量的時候進行了一些優化。爲了減少在JVM中創建的字符串的數量,字符串類維護了一個字符串池,每當代碼創建字符串常量時,JVM會首先檢查字符串常量池。如果字符串已經存在池中,就返回池中的實例引用。如果字符串不在池中,就會實例化一個字符串並放到池中。Java能夠進行這樣的優化是因爲字符串是不可變的,可以不用擔心數據衝突進行共享。原文

      查看源碼可以發現,String的定義是public final class String implements java.io.Serializable, Comparable<String>, CharSequence,經過final修飾,無法被繼承。
      擴展點:封裝類型Short, Integer均被final修飾,繼承自Number類。
      擴展點2: 封裝類型Short, Integer等也是不可變類型,內部實際值是對應基本類型的名爲value的final變量。

      小題

      String str1 = "str";
      String str2 = "ing";
      String str3 = "str" + "ing";
      String str4 = str1 + str2;
      System.out.println(str3 == str4);
      String str5 = "string";
      System.out.println(str3 == str5);
      

      結果是false,true。後一個結果應該很好理解,因爲第3行代碼執行時已經將"string"存儲在常量池中,第6行代碼返回的是常量池中同一字符串"string"的引用,所以結果爲true。前一個結果可能有點疑惑,後來把這段代碼編譯反彙編後發現執行第3行代碼時是直接生成的String對象,內容爲"string";而執行第四行代碼時是藉助new StringBuilder().append(“str”).append(“ing”).toString(),關鍵在於StringBuilder中定義的toString()方法,返回的是一個重新創建的String對象,並沒有存在池中,所以前一個結果爲false。

      //StringBuilder.toString()源代碼
      public String toString() {
          // Create a copy, don't share the array
          return new String(value, 0, count);
      }
      
    • 由字符串解析爲基本數據類型,基本數據類型封裝類的parse方法可能會報NumberFormatException,比如Integer.parseInt("era");
      擴展點:

       Integer a=Integer.parseInt("");    //NumberFormatException
       JSONObject jsonObject=new JSONObject();
       jsonObject.put("num", "");
       Integer b = jsonObject.getInteger("num");   //null
       int bV = jsonObject.getIntValue("num");   //0
      
    • intern()方法:若由equals()判斷池中包含相同的對象則返回池中該對象的引用,否則將新對象加入池中並返回引用。注意不管是怎麼創建的,都先從池裏找

    • 對於字符串拼接,考慮性能,如果循環進行拼接操作,生成的字符串都會存在池裏,並且每次拼接都要重新構造StringBuilder對象,影響性能。因此可以使用StringBuilder優化:

      StringBuilder builder...append(String.valueOf("fsdasf"));
      
    • StringBuilder/StringBuffer

      • StringBuilder線程不安全,但效率高;
      • StringBuffer線程安全,但效率低。

      線程安全:某個函數、函數庫在多線程環境中被調用時,能夠正確地處理多個線程之間的共享變量,使程序功能正確完成

  5. 集合
    在這裏插入圖片描述

    • 類型:List, Set, Map,都是接口而非具體實現。

    • List

      • 特點是有序並且按線性方式存儲,可以根據元素的下標進行精準的控制。

      • ArrayList是順序表的體現,底層用數組實現。

        數組在線性表中的分類屬於順序表,即:順序表中的數據元素存儲是連續的,內存劃分的區域也是連續的。

      • LinkedList是鏈表的體現,使用雙向鏈表實現。(鏈表的三種形式:單向鏈表、雙向鏈表、循環鏈表)

        鏈表在物理存儲上通常是非連續、非順序的方式存儲的,但是數據元素的邏輯順序是連續的,實現方式是通過鏈表中的引用來實現。

      • Stack是Vector的子類,而Vector實現了List<E>接口;是棧結構的代表。

        棧和隊列是特殊的線性表,或者說是受到限制的線性表,其限制是僅允許在線性表的尾部進行添加和刪除操作,這一端被稱爲棧頂,另一端稱爲棧底。

    • Queue

      • 隊列Queue直接繼承自Collection接口,是隊列結構的代表,使用鏈表結構實現。

        Queue接口是隊列的體現,在實現上是基於鏈表實現的,但是具體的實現類是LinkindList,也就是說,java通過Queue接口收窄了LinkedList的訪問權限,只提供從隊尾,隊頭等的操作,從而實現了對列。

      (注:Queue繼承自Collection接口,而LinkedList實現了Deque接口,Deque接口繼承自Queue接口,即實現了Queue接口的所有方法

      • Queue接口的主要方法:add, offer(添加元素), poll(返回並刪除隊列頭部元素)。

        根據offer()方法的官方註解,更加推薦使用offer()方法:

        /** 
        * Inserts the specified element into this queue if it is possible to do
        * so immediately without violating capacity restrictions.
        * When using a capacity-restricted queue, this method is generally
        * preferable to {@link #add}, which can fail to insert an element only
        * by throwing an exception.
        * ...
        * @return {@code true} if the element was added to this queue, else
        * {@code false}
        * ...
        **/ 
        
    • Set

      • 直接繼承自Collection接口,HashSet內部使用HashMap實現;
      • 特點是無序但是不包含重複的元素,可以用來去重
      • 元素存放方式爲散列存放,即通過計算hash值存放元素。
    • Map

      • Map單獨爲一個接口,HashMap是基於哈希表的對Map接口的實現,而哈希表的底層數據結構是數組和鏈表;
      • 特點是能夠根據key快速查找value;鍵必須唯一,put一個鍵相同的值時該鍵會被更新,即同一個鍵只能映射到一個值。
      • 鍵值對以Entry類型的對象實例存在
  6. 面向對象

    • 面向對象三大特徵:封裝、繼承和多態

    • 構造函數:創建對象時調用。若未顯式定義構造函數,系統自動生成無參構造函數。

    • 重載:發生在一個類中,函數名相同,參數列表不同(類型/個數)

    • 重寫:發生在兩個類中,函數名相同,參數列表相同。

    • 繼承:初始化子類時先初始化父類,即調用構造函數時隱式執行父類構造函數。

    • 單繼承性:Java允許一個類只能有一個父類。

    • super關鍵字:既可以作爲父類對象的引用調用父類方法,也可以作爲父類構造函數名顯式調用父類構造函數。

    • 垃圾回收:對象被回收時會調用finalize()方法。

      垃圾回收機制:當垃圾回收器(Garbage Collector)認定對象沒有任何引用時會將其回收,在回收前調用finalize方法。但是《Java編程思想》中還提到了這樣一句話:

      記住,無論是“垃圾回收”還是“終結”,都不保證一定會發生。如果Java虛擬機(JVM)並未面臨內存耗盡的情形,它是不會浪費時間去執行垃圾回收以恢復內存的。

      看起來Java的垃圾清理機制似乎並不是那麼完美。調用System.gc()方法可以強制進行垃圾回收。

      附一篇講Java垃圾回收機制比較好的文章

    • 多態:

      Employee e=new Manager();

      運行期間JVM動態推定對象的實際類型爲Manager。

    • 封裝:

      訪問修飾權限:private, default, public, protected

      修飾符 當前類 同一包內 子孫類 其他包 其他包子孫類
      public Y Y Y Y Y
      protected Y Y Y N Y/N(說明
      default Y Y N N N
      private Y N N N N

      protected關鍵字特別說明:

      • 子類與基類在同一包中:被聲明爲 protected 的變量、方法和構造器能被同一個包中的任何其他類訪問;
      • 子類與基類不在同一包中:那麼在子類中,子類實例可以訪問其從基類繼承而來的 protected 方法,而不能訪問基類實例的protected方法
  7. 抽象類和接口

    • 抽象類不能被實例化,實例化由子類完成;但是抽象類也是類,可以有構造方法!

    • 抽象類不一定要有抽象方法;抽象方法所在的類一定是抽象類;

    • abstract和final不能同時修飾一個類,否則編譯不通過。因爲final聲明的類不能被繼承,如果同時修飾意味着這個類永遠是抽象類。

    • abstract不能與private, static(?), final, native(?)的同時修飾一個方法,否則編譯不通過。

    • 模板方法模式

      抽象類體現的就是一種模板設計模式,抽象類作爲多個子類通用的模板,子類在抽象類的基礎上進行擴展,這種模式解決的就是一部分功能不確定,就把不確定的功能部分暴露出去,讓子類自己去實現。

    • 接口是更高級別的抽象,接口中的方法必須都是抽象方法;接口中聲明的方法都是沒有方法主體的;

    • 接口中的屬性默認修飾符public static final,方法默認修飾符public abstract,這些都可以省略。

    • 一個類可以實現多個接口,但只能繼承一個父類。

      擴展點:接口和抽象類的主要區別

      • 抽象類是一種對事物的抽象,而接口是一種對行爲的抽象;

      • 抽象類是對整個類整體進行抽象,包括屬性、行爲,但是接口卻是對類局部(行爲)進行抽象。

      • 抽象類是一種模板式設計,而接口是一種行爲規範,是一種輻射式設計。

        模板式設計的含義是:如果B和C都使用了公共模板A,如果他們的公共部分需要改動,那麼只改動A就可以了;

        輻射式設計的含義是:如果B和C都實現了公共接口A,如果現在要向A中添加新的方法,那麼B和C都必須進行相應改動。

    • 匿名內部類:繼承自抽象類或接口的不具有類名的內部類。例如Runnable是一個抽象接口,內部只有一個run方法。

      Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("THread");
            }
        });
        thread.start();
      

      擴展點:jdk1.8引入的lambda表達式,只有一個方法的接口情形下,可以使用lambda表達式簡化代碼。

      Thread thread = new Thread(()->System.out.println("Thread"));
      

      擴展點2:查閱源碼可發現Runnable上帶有註解@FuntionalInterface,該註解自jdk1.8引入,表示是一個函數,只有一個抽象方法;編譯器會在編譯期檢查函數定義。@FuntionalInterface可以註解在類,接口或枚舉類型上。

  8. 異常處理

    請一定要記住這張圖

    • Error和Exception, checked異常和unchecked異常

      • Error(unchecked異常)

        是程序無法處理的錯誤,通常發生於虛擬機自身,表示運行應用程序中較嚴重問題。例如,Java虛擬機運行錯誤(Virtual MachineError),當 JVM 不再有繼續執行操作所需的內存資源時,將出現 OutOfMemoryError。這些異常發生時,Java 虛擬機(JVM)一般會選擇線程終止。

      • Exception(checked異常)

        必須處理的異常:Checked異常是Java特有的,在java設計哲學中,Checked異常被認爲是可以被處理或者修復的異常,所以Java程序必須顯式處理Checked異常,當我們使用或者出現Checked異常類的時候,程序中要麼顯式try- catch捕獲該異常並修復,要麼顯式聲明拋出該異常,否則程序無法通過編譯。

        (注:以上內容引用他人文章,原作者聲明:作者:胖先森鏈接:https://juejin.im/post/5a81941ff265da4e976e9004來源:掘金著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。)

    • try-catch-finally

      可以用多個catch子句處理不同異常,級別由低到高。

      try {
         doSomethint();
      } catch (Exception1 e) {
         doSomethingCatch1();
      } catch (Exception2 e) {
        doSomethingCatch2();
      } finally {
        finallyDoSomething();
      }
      

      擴展點:在Java SE 7或者更高版本中,一個catch塊可以同時處理多種異常類型,有助於減少重複代碼。

      //...
      } catch (ClassNotFoundException | SQLException e) {
          e.printStackTrace();
      }
      

      注意當catch語句要捕獲多種異常類型時, 參數(在這裏是e)隱式地成爲final變量。
      擴展點2:try子句後,catch和finally不是必須得,有一個出現就可以。

    • 方法聲明異常的關鍵字是throws,拋出異常throw。

    • try-with-resources子句(Java SE 7引入)

      援引官網的介紹吧:

      The try-with-resources statement is a try statement that declares one or more resources. A resource is an object that must be closed after the program is finished with it. The try-with-resources statement ensures that each resource is closed at the end of the statement. Any object that implements java.lang.AutoCloseable, which includes all objects which implement java.io.Closeable, can be used as a resource.

  9. 線程

    • 線程是操作系統調度的最小單元,也叫輕量級進程。同一進程可以創建多個線程,而他們都擁有各自的計算器、堆棧和局部變量等屬性,並且能夠訪問共享的內存變量。

    • 線程的5個狀態

      • 創建(New):使用new關鍵字創建一個線程
      • 就緒(Ready):調用start方法,等待CPU調度
      • 運行(Running):執行run方法
      • 阻塞(Blocked):由於一些原因放棄CPU使用權,暫時停止執行
      • 死亡(Dead):run方法執行完畢或者執行時產生異常
    • 幾個重要的名詞區分

      • 同步和異步

        同步方法調用後必須等待方法返回才能執行後續行爲;異步方法調用後可以立刻執行後續行爲。

      • 併發和並行

        並行是真正意義上的多個任務同時執行;併發是支持處理多個任務,不一定要同時,多個任務可能是串行的,但每個任務只能獲取CPU很短的佔用時間,多個任務在很短的時間內交替執行。

        我相信你已經能夠得出結論——“並行”概念是“併發”概念的一個子集。也就是說,你可以編寫一個擁有多個線程或者進程的併發程序,但如果沒有多核處理器來執行這個程序,那麼就不能以並行方式來運行代碼。因此,凡是在求解單個問題時涉及多個執行流程的編程模式或者執行行爲,都屬於併發編程的範疇。

      • 阻塞和非阻塞

        阻塞是指某一線程訪問一公共資源時其他線程必須等待該線程釋放資源纔可以使用,否則就要掛起線程等待;非阻塞是指線程之間不會發生資源爭奪。

      • 原子性

        原子性是指一個操作是不可被中斷的,即使多個線程是同時執行的。

      • 可見性

        可見性是指當某個線程修改了共享變量的值,其他線程能否立刻知曉。

      • 有序性

        **Java內存模型中的程序天然有序性可以總結爲一句話:如果在本線程內觀察,所有操作都是有序的;如果在一個線程中觀察另一個線程,所有操作都是無序的。**前半句是指“線程內表現爲串行語義”,後半句是指“指令重排序”現象和“工作內存和主內存同步延遲”現象。

    • volatile和synchronized

      Java中提供了關鍵字volatile和synchronized關鍵字來保證線程之間操作的有序性。volatile包含了禁止指令重排序的語義,並保證不同線程對同一變量操作時的可見性;synchronized關鍵字對同一時刻同一變量只允許一個線程對其進行lock操作。
      volatile保證可見性、有序性,不保證原子性。

    • 線程創建

      • 繼承Thread類重寫run()方法
      • 匿名內部類,即在new Thread()後跟具體的定義體,其中重寫了run()方法
      • 實現Runnable接口,重寫run()方法
    • 開啓線程的run()和start()方法區別

      • run()方法不能新建一個線程,而是在當前線程中調用run()方法;
      • start()方法新建一個線程並調用其run()方法。
    • 終止線程不要使用stop()

      一般情況下,線程在執行完畢後就會結束,無需手工關閉,但是我們也經常會創建無限循環的後臺進程以持續提供某項服務,所以就需要手動關閉這些線程。
      在JDK中也有終止線程的API,例如stop()方法,但是極度不推薦這個方法,因爲stop()方法得到調用後,會強行把執行到一半的線程終止,可能會引起數據不一致問題。

      但是想要終止一個無限循環的線程應該怎麼做?

      我們推薦的做法是在類中添加一個isStop的布爾值屬性,判斷isStop爲true則跳出循環體,線程執行完畢自動終止,就避免了數據不一致的問題。

    • wait() 和notify()

      這兩個方法不是Thread類特有的,而是所有類的父類Object中的。

      當一個對象調用了wait方法後,如:objectA.wait(),當前線程就會在這個對象上等待,會釋放該對象的鎖,直到其他線程調用了objectA.notify()方法爲止。
      需要注意的是,wait和notify方法都必須獲得對象的監視器(鎖),在同步代碼得到執行後也會釋放對象的鎖,所以必須被包含在對象的synchronzied語句中。

      (注:以上內容援引他人文章,原作者聲明:作者:kk_miles鏈接:https://juejin.im/post/5a30a7466fb9a0450f21ec5c來源:掘金著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。)

    • 小題

      public class WaitNotifyDemo {
        final static Object object = new Object();
        public static class Thread1 extends Thread{
            @Override
            public void run() {
                synchronized (object){
                    System.out.println(System.currentTimeMillis()+" 線程1開啓。。");
                    try {
                        object.wait();      //1.先獲取object的鎖,然後開始等待,並再次釋放object的鎖。
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(System.currentTimeMillis()+" 線程1結束。。");    //4f. 兩秒後,線程2執行結束,線程結束,重新獲得了object的鎖,此句猜得到執行
                }
            }
        }
        public static class Thread2 extends Thread{
            @Override
            public void run() {
                synchronized (object){
                    System.out.println(System.currentTimeMillis()+" 線程2開啓,並執行notify通知線程1 。。");
                    object.notify();    //2.獲取object的鎖成功,通知object的其他線程(即線程1),這裏還未釋放object的鎖!
                    System.out.println(System.currentTimeMillis()+" 線程2執行notify結束。。");
                    try {
                        Thread.sleep(2000);    //3. 使線程2暫停2秒,即2秒後線程2才能執行結束,才能把object的鎖釋放。
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(System.currentTimeMillis()+" 線程2結束。。");
                }
            }
        }
      
        public static void main(String[] args) {
            Thread thread1 = new Thread1();
            Thread thread2 = new Thread2();
            thread1.start();
            thread2.start();
        }
      }
      

      以上代碼是一個關於wait和notify的示意代碼,聲明一個靜態對象和兩個靜態內部類表示兩個線程,連個線程分別在對象object的同步塊中調用wait和notify方法,在線程1中調用了object的wait方法前需要先獲取object的鎖,然後進入線程等待,並釋放鎖;然後線程2的notify方法執行前需要獲取object的鎖,然後通知線程1。但是此時線程1仍然無法執行wait方法後面的代碼,原因是線程2Thread.sleep(2000)使得線程2在2秒之後才能退出並且釋放object的鎖,也即線程1必須等待線程2的object同步代碼塊執行結束後才能獲得鎖,去繼續執行下面的代碼,具體的輸出日誌如下:

        1510300350884 線程1開啓。。
        1510300350884 線程2開啓,並執行notify通知線程1 。。
        1510300350884 線程2執行notify結束。。
        1510300352884 線程2結束。。      //2秒中之後線程2結束,釋放object鎖
        1510300352885 線程1結束。。      //線程2釋放鎖之後,線程1獲得鎖,結束等待狀態,繼續向下執行。
      
    • 生產者消費者問題(線程同步,互斥)

  10. I/O操作

  • 流是一組有順序的, 有起點和終點的字節集合,是對數據傳輸的總稱或抽象。

  • I/O流的分類

    • 根據處理數據類型劃分爲:字符流和字節流
    • 根據數據流向劃分爲:輸入流和輸出流
  • 字符流和字節流

    字符流本質上就是基於字節流,讀取時查詢相應的碼錶。

    區別:

    • 讀寫單位:字節流以字節爲單位;字符流以字符爲單位,一次可能讀多個字節;

    • 處理對象:字節流能處理所有類型的數據(圖片、視頻等),而字符流只能處理字符類型的數據。

      (文本數據優先考慮字符流,其他情況都用字節流)

  • Java 基本I/O操作可以參考這篇文章

  1. 網絡編程

    • Java建立TCP連接的步驟:
      1. 服務器實例化一個ServerSocket對象,通過服務器的特定端口通信;
      2. 服務器調用ServerSocket的accept()方法,一直等待直到客戶端連接到服務器的端口爲止;
      3. 服務器等待時,客戶端實例化一個Socket對象,指定服務器地址和端口號請求連接;
      4. 客戶端的Socket構造函數嘗試連接到服務器指定端口,如果成功連接,在客戶端創建一個Socket對象使得可以與服務器通信;
      5. 服務器端accept()方法返回一個新的socket引用,使得可以連接到客戶端。
  2. 反射

    Java的反射機制是指程序可以訪問、檢測並修改本身的狀態或行爲的一種能力,並能根據自身行爲的狀態和結果調整或修改應用所描述行爲的狀態和相關的語義。

    通過反射機制,可以訪問Java對象的屬性、方法、構造方法、關鍵字、返回值等等。

    實例操作:

    1. 獲取Class對象

      • Class c = Class.forName("A");
      • Class c = A.class;
      • Class c = new A().getClass();
    2. 利用Class對象獲取新實例

      • Object o = c.newInstance();
    3. 進行相關操作,比如獲取屬性、方法等等,例如

      • Field fs = c.getDeclaredFields()[0];
  3. 註解
    註解現在用到的地方也比較多了,Spring Boot中大量應用註解,改變了傳統基於xml方式配置的代碼風格,讓配置可以通過代碼完成。註解部分的內容可以參考Java中的註解



關於Java基礎的內容還是挺多,本文會階段性更新內容,歡迎大家交流討論。

參考鏈接:

  1. Java到底是傳值還是傳引用
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章