Java基礎知識點總結

Java團長_發表於2018-10-20

前言

本文主要是我之前複習Java基礎原理過程中寫的Java基礎知識點總結。Java的知識點其實非常多,並且有些知識點比較難以理解,有時候我們自以為理解了某些內容,其實可能只是停留在表面上,沒有理解其底層實現原理。


紙上得來終覺淺,絕知此事要躬行。筆者之前對每部分的內容對做了比較深入的學習以及程式碼實現,基本上比較全面地講述了每一個Java基礎知識點,當然可能有些遺漏和錯誤,歡迎讀者指正。

Java基礎知識點總結

每部分內容會重點寫一些常見知識點,方便複習和記憶,但並不是全部內容。

物件導向三大特性

繼承:一般類只能單繼承,內部類實現多繼承,介面可以多繼承

封裝:訪問許可權控制public > protected > 包 > private 內部類也是一種封裝

多型:編譯時多型,體現在向上轉型和向下轉型,通過引用型別判斷呼叫哪個方法(靜態分派)。

執行時多型,體現在同名函式通過不同引數實現多種方法(動態分派)。

基本資料型別

1. 基本型別位數,自動裝箱,常量池。

2. 例如byte型別是1byte也就是8位,可以表示的數字是-128到127,因為還有一個0,加起來一共是256,也就是2的八次方。

32位和64位機器的int是4個位元組也就是32位,char是1個位元組就是8位,float是4個位元組,double是8個位元組,long是8個位元組。

3. 基本資料型別的包裝類只在數字範圍-128到127中用到常量池,會自動拆箱裝箱,其餘數字範圍的包裝類則會新建例項。

String及包裝類

1. String型別是final型別,在堆中分配空間後記憶體地址不可變。

2. 底層是final修飾的char[]陣列,陣列的記憶體地址同樣不可變。

但實際上可以通過修改char[n] = 'a'來進行修改,不會改變String例項的記憶體值,不過在jdk中,使用者無法直接獲取char[],也沒有方法能操作該陣列。

所以String型別的不可變實際上也是理論上的不可變。所以我們在分配String物件以後,如果將其 = "abc",那也只是改變了引用的指向,實際上沒有改變原來的物件。

3. StringBuffer和StringBuilder底層是可變的char[]陣列,繼承父類AbstractStringBuilder的各種成員和方法,實際上的操作都是由父類方法來完成的。

final關鍵字

1. final修飾基本資料型別保證不可變

2. final修飾引用保證引用不能指向別的物件,否則會報錯。

3. final修飾類,類的例項分配空間後地址不可變,子類不能重寫所有父類方法。因此在cglib動態代理中,不能為一個類的final修飾的函式做代理,因為cglib要將被代理的類設定為父類,然後再生成位元組碼。

final修飾方法,子類不能重寫該方法。

抽象類和介面

1. 抽象類可以有方法實現。 抽象類可以有非final成員變數。 抽象方法要用abstract修飾。 抽象類可以有構造方法,但是隻能由子類進行例項化。

2. 介面可以用extends加多個介面實現多繼承。 介面只能有public final型別的成員變數。 介面只能有抽象方法,不能有方法體、 介面不能例項化,但是可以作為引用型別。

程式碼塊和載入順序

假設該類是第一次進行例項化。那麼有如下載入順序 靜態總是比非靜態優先,從早到晚的順序是: 1. 靜態程式碼塊 和 靜態成員變數的順序根據程式碼位置前後來決定。 2. 程式碼塊和成員變數的順序也根據程式碼位置來決定 3. 最後才呼叫構造方法構造方法

包、內部類、外部類

1. Java專案一般從src目錄開始有com...A.java這樣的目錄結構。這就是包結構。所以一般編譯後的結構是跟包結構一模一樣的,這樣的結構保證了import時能找到正確的class引用包訪問許可權就是指同包下的類可見。

import 一般加上全路徑,並且使用.*時只包含當前目錄的所有類檔案,不包括子目錄。

2. 外部類只有public和default兩種修飾,要麼全域性可訪問,要麼包內可訪問。

3. 內部類可以有全部訪問許可權,因為它的概念就是一個成員變數,所以訪問許可權設定與一般的成員變數相同。

非靜態內部類是外部類的一個成員變數,只跟外部類的例項有關。

靜態內部類是獨立於外部類存在的一個類,與外部類例項無關,可以通過外部類.內部類直接獲取Class型別。

異常

1. 異常體系的最上層是Throwable類 子類有Error和Exception Exception的子類又有RuntimeException和其他具體的可檢查異常。

2. Error是jvm完全無法處理的系統錯誤,只能終止執行。

執行時異常指的是編譯正確但執行錯誤的異常,如陣列越界異常,一般是人為失誤導致的,這種異常不用try catch,而是需要程式設計師自己檢查。

可檢查異常一般是jvm處理不了的一些異常,但是又經常會發生,比如Ioexception,Sqlexception等,是外部實現帶來的異常。

3. 多執行緒的異常流程是獨立的,互不影響。 大型模組的子模組異常一般需要重新封裝成外部異常再次丟擲,否則只能看到最外層異常資訊,難以進行除錯。

日誌框架是異常報告的最好幫手,log4j,slf4j中,在工作中必不可少。

泛型

1. Java中的泛型是偽泛型,只在編譯期生效,執行期自動進行泛型擦除,將泛型替換為實際上傳入的型別。

泛型類用classA {

}

2. 這樣的形式表示,裡面的方法和成員變數都可以用T來表示型別。泛型介面也是類似的,不過泛型類實現泛型介面時可以選擇注入實際型別或者是繼續使用泛型。

3. 泛型方法可以自帶泛型比如void <E> go();

泛型可以使用?萬用字元進行泛化 Object可以接受任何型別

也可以使用 這種方式進行上下邊界的限制。

Class類和Object類

1. Java反射的基礎是Class類,該類封裝所有其他類的型別資訊,並且在每個類載入後在堆區生成每個類的一個Class<類名>例項,用於該類的例項化。

2. Java中可以通過多種方式獲取Class型別,比如A.class,new A().getClass()方法以及Class.forName("com.?.?.A")方法。

3. Object是所有類的父類,有著自己的一些私有方法,以及被所有類繼承的9大方法。

知乎上有人討論Object和Class型別誰先載入誰後載入,因為每個類都要繼承Object,但是又得先被載入到堆區,事實上,這個問題在JVM初始化時就解決了,沒必要多想。

javac和java

1. javac 是編譯一個java檔案的基本命令,通過不同引數可以完成各種配置,比如匯入其他類,指定編譯路徑等。

2. java是執行一個java檔案的基本命令,通過引數配置可以以不同方式執行一個java程式或者是一個jar包。

3. javap是一個class檔案的反編譯程式,可以獲取class檔案的反編譯結果,甚至是jvm執行程式的每一步程式碼實現。

反射

1. Java反射包reflection提供對Class,Method,field,constructor1 等資訊的封裝型別。

2. 通過這些api可以輕易獲得一個類的各種資訊並且可以進行例項化,方法呼叫等。

類中的private引數可以通過setaccessible方法強制獲取。

3. 反射的作用可謂是博大精深,JDK動態代理生成代理類的位元組碼後,首先把這個類通過defineclass定義成一個類,然後用class.for(name)會把該類載入到jvm,之後我們就可以通過,A.class.GetMethod()獲取其方法,然後通過invoke呼叫其方法,在呼叫這個方法時,實際上會通過被代理類的引用再去呼叫原方法。

列舉類

1. 列舉類繼承Enum並且每個列舉類的例項都是唯一的。

2. 列舉類可以用於封裝一組常量,取值從這組常量中取,比如一週的七天,一年的十二個月。

3. 列舉類的底層實現其實是語法糖,每個例項可以被轉化成內部類。並且使用靜態程式碼塊進行初始化,同時保證內部成員變數不可變。

序列化

1. 序列化的類要實現serializable介面

transient修飾符可以保證某個成員變數不被序列化

readObject和writeOject來實現例項的寫入和讀取。

2. 事實上,一些擁有陣列變數的類都會把陣列設為transient修飾,這樣的話不會對整個陣列進行序列化,而是利用專門的方法將有資料的陣列範圍進行序列化,以便節省空間。

動態代理

1. jdk自帶的動態代理可以代理一個已經實現介面的類。

2. cglib代理可以代理一個普通的類。

3. 動態代理的基本實現原理都是通過位元組碼框架動態生成位元組碼,並且在用defineclass載入類後,獲取代理類的例項。

一般需要實現一個代理處理器,用來處理被代理類的前置操作和後置操作。在JDK動態代理中,這個類叫做invocationHandler。

4. JDK動態代理首先獲取被代理類的方法,並且只獲取在介面中宣告的方法,生成代理類的位元組碼後,首先把這個類通過defineclass定義成一個類,然後把該類載入到jvm,之後我們就可以通過,A.class.GetMethod()獲取其方法,然後通過invoke呼叫其方法,在呼叫這個方法時,實際上會通過被代理類的引用再去呼叫原方法。

5. 而對於cglib動態代理,一般會把被代理類設為代理類的父類,然後獲取被代理類中所有非final的方法,通過asm位元組碼框架生成代理類的位元組碼,這個代理類很神奇,他會保留原來的方法以及代理後的方法,通過方法陣列的形式儲存。

cglib的動態代理需要實現一個enhancer和一個interceptor,在interceptor中配置我們需要的代理內容。如果沒有配置interceptor,那麼代理類會呼叫被代理類自己的方法,如果配置了interceptor,則會使用代理類修飾過的方法。

多執行緒

這裡先不講juc包裡的多執行緒類。juc相關內容會在Java併發專題講解。

1. 執行緒的實現可以通過繼承Thread類和實現Runable介面 也可以使用執行緒池。callable配合future可以實現執行緒中的資料獲取。

2. Java中的執行緒有7種狀態,new runable running blocked waiting timewaiting terminate

blocked是執行緒等待其他執行緒鎖釋放。 waiting是wait以後執行緒無限等待其他執行緒使用notify喚醒 timewating是有限時間地等待被喚醒,也可能是sleep固定時間。

3. Thread的join是例項方法,比如a.join(b),則說明a執行緒要等b執行緒執行完才會執行。

4. o.wait方法會讓持有該物件o的執行緒釋放鎖並且進入阻塞狀態,notify則是持有o鎖物件的執行緒通知其他等待鎖的執行緒獲取鎖。notify方法並不會釋放鎖。注意這兩個方法都只能在synchronized同步方法或同步塊裡使用。

5. synchronized方法底層使用系統呼叫的mutex鎖,開銷較大,jvm會為每個鎖物件維護一個等待佇列,讓等待該物件鎖的執行緒在這個佇列中等待。當執行緒獲取不到鎖時則讓執行緒阻塞,而其他檢查notify以後則會通知任意一個執行緒,所以這個鎖時非公平鎖。

6. Thread.sleep(),Thread.interrupt()等方法都是類方法,表示當前呼叫該方法的執行緒的操作。

一個執行緒例項連續start兩次會拋異常,這是因為執行緒start後會設定標識,如果再次start則判斷為錯誤。

IO流

1. IO流也是Java中比較重要的一塊,Java中主要有位元組流,字元流,檔案等。其中檔案也是通過流的方式開啟,讀取和寫入的。

2. IO流的很多介面都使用了裝飾者模式,即將原型別通過傳入裝飾類建構函式的方式,增強原型別,以此獲得像帶有緩衝區的位元組流,或者將位元組流封裝成字元流等等,其中需要注意的是編碼問題,後者列印出來的結果可能是亂碼哦。

3. IO流與網路程式設計息息相關,一個socket接入後,我們可以獲取它的輸入流和輸出流,以獲取TCP資料包的內容,並且可以往資料包裡寫入內容,因為TCP協議也是按照流的方式進行傳輸的,實際上TCP會將這些資料進行分包處理,並且通過差錯檢驗,超時重傳,滑動視窗協議等方式,保證了TCP資料包的高效和可靠傳輸。

網路程式設計

承接IO流的內容

1. IO流與網路程式設計息息相關,一個socket接入後,我們可以獲取它的輸入流和輸出流,以獲取TCP資料包的內容,並且可以往資料包裡寫入內容,因為TCP協議也是按照流的方式進行傳輸的,實際上TCP會將這些資料進行分包處理,並且通過差錯檢驗,超時重傳,滑動視窗協議等方式,保證了TCP資料包的高效和可靠傳輸。

2. 除了使用socket來獲取TCP資料包外,還可以使用UDP的DatagramPacket來封裝UDP資料包,因為UDP資料包的大小是確定的,所以不是使用流方式處理,而是需要事先定義他的長度,源埠和目標埠等資訊。

3. 為了方便網路程式設計,Java提供了一系列型別來支援網路程式設計的api,比如URL類,InetAddress類等。

Java8

1. 介面中的預設方法,介面終於可以有方法實現了,使用註解即可標識出預設方法。

2. lambda表示式實現了函數語言程式設計,通過註解可以宣告一個函式式介面,該介面中只能有一個方法,這個方法正是使用lambda表示式時會呼叫到的介面。

3. Option類實現了非空檢驗

4. 各種api的更新,包括chm,hashmap的實現等

5. Stream流概念,實現了集合類的流式訪問,可以基於此使用map和reduce平行計算。

PS:如果覺得我的分享不錯,歡迎大家隨手點贊、轉發。

(完)


640?

Java團長

專注於Java乾貨分享

640

掃描上方二維碼獲取更多Java乾貨

相關文章