本文主要是我最近複習Java基礎原理過程當中寫的Java基礎學習總結。Java的知識點其實很是多,而且有些知識點比較難以理解,有時候咱們自覺得理解了某些內容,其實可能只是停留在表面上,沒有理解其底層實現原理。前端
紙上得來終覺淺,絕知此事要躬行。筆者以前對每部分的內容 對作了比較深刻的學習以及代碼實現,基本上比較全面地講述了每個Java基礎知識點,固然可能有些遺漏和錯誤,還請讀者指正。java
更多關於Java後端學習的內容請到個人CSDN博客上查看:
Java基礎學習總結
每部份內容會重點寫一些常見知識點,方便複習和記憶,可是並非所有內容,詳細的內容請參見具體的文章地址。
面向對象三大特性
繼承:通常類只能單繼承,內部類實現多繼承,接口能夠多繼承
封裝:訪問權限控制public > protected > 包 > private 內部類也是一種封裝
多態:編譯時多態,體如今向上轉型和向下轉型,經過引用類型判斷調用哪一個方法(靜態分派)。
運行時多態,體如今同名函數經過不一樣參數實現多種方法(動態分派)。
基本數據類型
基本類型位數,自動裝箱,常量池
例如byte類型是1byte也就是8位,能夠表示的數字是-128到127,由於還有一個0,加起來一共是256,也就是2的八次方。
32位和64位機器的int是4個字節也就是32位,char是1個字節就是8位,float是4個字節,double是8個字節,long是8個字節。
因此它們佔有字節數是相同的,這樣的話兩個版本才能夠更好地兼容。(應該)
基本數據類型的包裝類只在數字範圍-128到127中用到常量池,會自動拆箱裝箱,其他數字範圍的包裝類則會新建實例
String及包裝類
String類型是final類型,在堆中分配空間後內存地址不可變。
底層是final修飾的char[]數組,數組的內存地址一樣不可變。
但實際上能夠經過修改char[n] = 'a'來進行修改,不會改變String實例的內存值,不過在jdk中,用戶沒法直接獲取char[],也沒有方法能操做該數組。
因此String類型的不可變實際上也是理論上的不可變。因此咱們在分配String對象之後,若是將其 = "abc",那也只是改變了引用的指向,實際上沒有改變原來的對象。
StringBuffer和StringBuilder底層是可變的char[]數組,繼承父類AbstractStringBuilder的各類成員和方法,實際上的操做都是由父類方法來完成的。
final關鍵字
final修飾基本數據類型保證不可變
final修飾引用保證引用不能指向別的對象,不然會報錯。
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中,在工做中必不可少。
泛型
Java中的泛型是僞泛型,只在編譯期生效,運行期自動進行泛型擦除,將泛型替換爲實際上傳入的類型。
泛型類用class <T> A {
}這樣的形式表示,裏面的方法和成員變量均可以用T來表示類型。泛型接口也是相似的,不過泛型類實現泛型接口時能夠選擇注入實際類型或者是繼續使用泛型。
泛型方法能夠自帶泛型好比void <E> E go();
泛型可使用?通配符進行泛化 Object<?>能夠接受任何類型
也可使用 <? extends Number> <? super Integer>這種方式進行上下邊界的限制。
Class類和Object類
Java反射的基礎是Class類,該類封裝全部其餘類的類型信息,而且在每一個類加載後在堆區生成每一個類的一個Class<類名>實例,用於該類的實例化。
Java中能夠經過多種方式獲取Class類型,好比A.class,new A().getClass()方法以及Class.forName("com.?.?.A")方法。
Object是全部類的父類,有着本身的一些私有方法,以及被全部類繼承的9大方法。
有人討論Object和Class類型誰先加載誰後加載,由於每一個類都要繼承Object,可是又得先被加載到堆區,事實上,這個問題在JVM初始化時就解決了,不必多想。
javac和java
javac 是編譯一個java文件的基本命令,經過不一樣參數能夠完成各類配置,好比導入其餘類,指定編譯路徑等。
java是執行一個java文件的基本命令,經過參數配置能夠以不一樣方式執行一個java程序或者是一個jar包。
javap是一個class文件的反編譯程序,能夠獲取class文件的反編譯結果,甚至是jvm執行程序的每一步代碼實現。
反射
Java反射包reflection提供對Class,Method,field,constructor等信息的封裝類型。
經過這些api能夠輕易得到一個類的各類信息而且能夠進行實例化,方法調用等。
類中的private參數能夠經過setaccessible方法強制獲取。
反射的做用可謂是博大精深,JDK動態代理生成代理類的字節碼後,首先把這個類經過defineclass定義成一個類,而後用class.for(name)會把該類加載到jvm,以後咱們就能夠經過,A.class.GetMethod()獲取其方法,而後經過invoke調用其方法,在調用這個方法時,實際上會經過被代理類的引用再去調用原方法。
枚舉類
枚舉類繼承Enum而且每一個枚舉類的實例都是惟一的。
枚舉類能夠用於封裝一組常量,取值從這組常量中取,好比一週的七天,一年的十二個月。
枚舉類的底層實現實際上是語法糖,每一個實例能夠被轉化成內部類。而且使用靜態代碼塊進行初始化,同時保證內部成員變量不可變。
序列化
序列化的類要實現serializable接口
transient修飾符能夠保證某個成員變量不被序列化
readObject和writeOject來實現實例的寫入和讀取。
待更新。
事實上,一些擁有數組變量的類都會把數組設爲transient修飾,這樣的話不會對整個數組進行序列化,而是利用專門的方法將有數據的數組範圍進行序列化,以便節省空間。
動態代理
jdk自帶的動態代理能夠代理一個已經實現接口的類。
cglib代理能夠代理一個普通的類。
動態代理的基本實現原理都是經過字節碼框架動態生成字節碼,而且在用defineclass加載類後,獲取代理類的實例。
通常須要實現一個代理處理器,用來處理被代理類的前置操做和後置操做。在JDK動態代理中,這個類叫作invocationHandler。
JDK動態代理首先獲取被代理類的方法,而且只獲取在接口中聲明的方法,生成代理類的字節碼後,首先把這個類經過defineclass定義成一個類,而後把該類加載到jvm,以後咱們就能夠經過,A.class.GetMethod()獲取其方法,而後經過invoke調用其方法,在調用這個方法時,實際上會經過被代理類的引用再去調用原方法。
而對於cglib動態代理,通常會把被代理類設爲代理類的父類,而後獲取被代理類中全部非final的方法,經過asm字節碼框架生成代理類的字節碼,這個代理類很神奇,他會保留原來的方法以及代理後的方法,經過方法數組的形式保存。
cglib的動態代理須要實現一個enhancer和一個interceptor,在interceptor中配置咱們須要的代理內容。若是沒有配置interceptor,那麼代理類會調用被代理類本身的方法,若是配置了interceptor,則會使用代理類修飾過的方法。
多線程
這裏先不講juc包裏的多線程類。juc相關內容會在Java併發專題講解。
線程的實現能夠經過繼承Thread類和實現Runable接口
也可使用線程池。callable配合future能夠實現線程中的數據獲取。
Java中的線程有7種狀態,new runable running blocked waiting time_waiting terminate
blocked是線程等待其餘線程鎖釋放。
waiting是wait之後線程無限等待其餘線程使用notify喚醒
time_wating是有限時間地等待被喚醒,也多是sleep固定時間。
Thread的join是實例方法,好比a.join(b),則說明a線程要等b線程運行完纔會運行。
o.wait方法會讓持有該對象o的線程釋放鎖而且進入阻塞狀態,notify則是持有o鎖對象的線程通知其餘等待鎖的線程獲取鎖。notify方法並不會釋放鎖。注意這兩個方法都只能在synchronized同步方法或同步塊裏使用。
synchronized方法底層使用系統調用的mutex鎖,開銷較大,jvm會爲每一個鎖對象維護一個等待隊列,讓等待該對象鎖的線程在這個隊列中等待。當線程獲取不到鎖時則讓線程阻塞,而其餘檢查notify之後則會通知任意一個線程,因此這個鎖時非公平鎖。
Thread.sleep(),Thread.interrupt()等方法都是類方法,表示當前調用該方法的線程的操做。
一個線程實例連續start兩次會拋異常,這是由於線程start後會設置標識,若是再次start則判斷爲錯誤。
IO流
IO流也是Java中比較重要的一塊,Java中主要有字節流,字符流,文件等。其中文件也是經過流的方式打開,讀取和寫入的。
IO流的不少接口都使用了裝飾者模式,即將原類型經過傳入裝飾類構造函數的方式,加強原類型,以此得到像帶有緩衝區的字節流,或者將字節流封裝成字符流等等,其中須要注意的是編碼問題,後者打印出來的結果多是亂碼哦。
IO流與網絡編程息息相關,一個socket接入後,咱們能夠獲取它的輸入流和輸出流,以獲取TCP數據包的內容,而且能夠往數據報裏寫入內容,由於TCP協議也是按照流的方式進行傳輸的,實際上TCP會將這些數據進行分包處理,而且經過差錯檢驗,超時重傳,滑動窗口協議等方式,保證了TCP數據包的高效和可靠傳輸。
網絡編程
承接IO流的內容
IO流與網絡編程息息相關,一個socket接入後,咱們能夠獲取它的輸入流和輸出流,以獲取TCP數據包的內容,而且能夠往數據報裏寫入內容,由於TCP協議也是按照流的方式進行傳輸的,實際上TCP會將這些數據進行分包處理,而且經過差錯檢驗,超時重傳,滑動窗口協議等方式,保證了TCP數據包的高效和可靠傳輸。
除了使用socket來獲取TCP數據包外,還可使用UDP的DatagramPacket來封裝UDP數據包,由於UDP數據包的大小是肯定的,因此不是使用流方式處理,而是須要事先定義他的長度,源端口和目標端口等信息。
爲了方便網絡編程,Java提供了一系列類型來支持網絡編程的api,好比URL類,InetAddress類等。
後續文章會帶來NIO相關的內容,敬請期待。
Java8
接口中的默認方法,接口終於能夠有方法實現了,使用註解便可標識出默認方法。
lambda表達式實現了函數式編程,經過註解能夠聲明一個函數式接口,該接口中只能有一個方法,這個方法正是使用lambda表達式時會調用到的接口。
Option類實現了非空檢驗
新的日期API
各類api的更新,包括chm,hashmap的實現等
Stream流概念,實現了集合類的流式訪問,能夠基於此使用map和reduce並行計算。
關注微信公衆號「黃小斜」後回覆「Java」、「Python」、「C++」、「大數據」、【機器學習】、「算法」、[AI]、「Android」、「前端」、「iOS」、「BAT」、「校招」、「筆試」、「面試」、「計算機基礎」、[LeetCode] 等關鍵字能夠獲取對應的免費學習資料。 程序員
![](http://static.javashuo.com/static/loading.gif)