夯實基礎系列一:Java 基礎總結
阿新 • • 發佈:2018-12-10
前言
大學期間接觸 Java 的時間也不短了,不論學習還是實習,都讓我發覺基礎的重要性。網際網路發展太快了,各種框架各種技術更新迭代的速度非常快,可能你剛好掌握了一門技術的應用,它卻已經走在淘汰的邊緣了。
而學習新技術總要付出一定的時間成本,那麼怎麼降低時間成本呢?那就是打好基礎,技術再怎麼革新,底層的東西也很少會變動,牢固的基礎會幫助你在各種新技術的學習中游刃有餘,快速上手。
因為我選擇的方向是後臺開發,所以談談我認為的基礎有哪些。其他方向肯定也有自己的體系,從低層到高層,可以自己摸索。後臺的話,我覺得網路知識,各種協議,web 知識,資料庫知識,Linux 基本操作以及自己選擇的後臺語言知識,這些是最基礎最需要牢固掌握的。
所以從今天起,會出一系列與後臺基礎相關的博文,一是對自己過去學習的一個總結,二是分享出來,希望可以幫助到需要的人。
概要
Java 基礎我做了 10 個方面的總結,包括基本概念,面向物件,關鍵字,基本型別與運算,字串與陣列,異常處理,Java 平臺與記憶體管理,分散式 Java 應用,多執行緒,IO。以下對這些內容做一些簡單的總結,同時我也有完整的思維導圖,部落格上不方便展示,若有需要,聯絡我。
細節
1. 基本概念
1.1 語言特點
- 純面向物件
- 平臺無關性
- 內建類庫
- 支援web
- 安全性
- 防止程式碼攻擊
- 健壯性
- 強型別機制
- 垃圾回收器
- 異常處理
- 安全檢查機制
- 去除C++中難以理解易混淆的特性
1.2 與C++比較
- 解釋編譯混合型語言,執行速度慢,跨平臺
- 純面向物件,只有類,不存在全域性變數或全域性函式
- 無指標,無多繼承,可多實現
- 垃圾回收器自動管理記憶體
1.3 main函式知識
- Java程式入口方法
- 可由final,synchronized修飾,不能用abstract
1.4 Java程式初始化順序
- 靜態優於非靜態
- 父類優於子類
- 按照成員變數的定義順序
- 總共10個
1.5 作用域與可見性
- 靜態變數屬於類
- 區域性變數屬於花括號
- 成員變數看下一條
- public、protected、default、private 可見性依次遞減
1.6 建構函式
- 與類名相同,無返回值
- 可過載,不能被繼承,即不能被覆蓋
- 引數個數任意
- 伴隨new 一起呼叫,為系統呼叫
- 完成物件的初始化工作
- 子類可通過super顯式呼叫父類。父類沒有提供無參,子類必須顯式呼叫
- 未定義,預設無參,修飾符取決於類修飾符
1.7 標識介面
- 無任何方法宣告
- 表示實現它的類屬於一個特定的型別
1.8 clone 方法
- 實現Cloneable介面
- 重寫Object類中的clone()
- clone()中呼叫super.clone()
- 把淺複製引用指向新的克隆體
1.9 反射
- 定義:允許程式在執行時進行自我檢查,也允許對其內部成員進行操作
- 功能
- 得到一個物件所屬的類
- 獲取一個類的所有成員和方法
- 執行時建立物件
- 在執行時呼叫物件的方法
- 獲取類的方式
- class.forName("類路徑")
- 類名.class
- 例項.getClass()
1.10 建立物件的四種方式
- new
- 反射機制
- clone()
- 反序列化
1.11 package 作用
- 提供多層名稱空間,解決命名衝突
- 對類按功能進行分類,使專案組織更加清晰
2. 面向物件
2.1 與面向過程區別
- 層次邏輯關係不同。
- 面向物件是通過類的層次結構來體現類之間的繼承與發展
- 面向過程是通過模組的層次結構概括模組與模組間的關係與功能
- 資料處理方式不同與控制程式方式不同
- 面向物件是資料與操作封裝成一個整體,通過事件驅動來啟用和執行程式
- 面向過程是資料單獨儲存,控制程式方式上按照設計呼叫或返回程式
2.2 特性
- 抽象
- 繼承
- 多型
- 封裝
2.3 這種開發方式優點
- 開發效率高。程式碼重用
- 保證軟體的魯棒性。經過長期測試的已有程式碼
- 保證軟體的高可維護性。設計模式成熟
2.4 繼承
- 單繼承
- 只能繼承父類的非私有成員變數和方法
- 同名成員變數,子類覆蓋,不會繼承
- 相同函式簽名,子類覆蓋,不會繼承
2.5 組合和繼承區別
- 組合:在新類中建立原有類的物件。has a
- 繼承是 is a
2.6 多型
- 方法過載
- 編譯時多型
- 方法覆蓋
- 執行時多型
- 成員變數無多型概念
2.7 覆蓋和過載區別
- 子父類關係,垂直;同類方法間關係,水平
- 一對方法發生關係;多個方法發生關係
- 引數列表相同;引數列表不同
- 呼叫的方法根據物件的型別決定;根據呼叫時的實參表決定方法體
2.8 抽象類與介面異同
同
- 不能被例項化
- 介面的實現類實現了介面,抽象類的子類實現了方法,才能被例項化
異
- 介面只能定義方法,不能實現;抽象類可以有定義和實現
- 介面需要被實現;抽象類需要被繼承
- 介面強調特定功能的實現;抽象類強調所屬關係
- 介面成員變數預設為 public static final,成員方法 public abstract
- 抽象類變數預設default,方法不能用 private、static、synchronized、native 修飾
2.9 內部類
- 靜態內部類
- static 修飾
- 只能訪問外部類中的static資料
- 成員內部類
- 與例項繫結
- 不可定義靜態屬性和方法
- 外部例項化後,該內部類才能被例項化
- 區域性內部類
- 程式碼塊內
- 不能被public、protected、private以及static修飾
- 只能訪問final 區域性變數
- 匿名內部類
- 無類名
- 無建構函式,必須繼承或實現其他類
- 原則
- 無建構函式
- 無靜態成員,方法和類
- 不能是public、protected、private、static
- 只能建立匿名內部類的一個例項
- new 後面有繼承或實現
- 特殊的區域性內部類
2.10 如何獲取父類類名
- 利用反射:obj.getClass().getSuperClass().getName()
- 不使用super.getClass()原因:該方法在 Object中為final與native,子類不能覆蓋,返回此Object執行時類
2.11 this
- 指向當前例項物件
- 區分成員變數與方法形參
2.12 super
- 訪問父類成員變數或方法
- 子類同名會覆蓋,訪問父類只能通過super
- 子類建構函式需顯示呼叫父類建構函式時,super()必須為建構函式的第一條語句
3. 關鍵字
3.1 變數命名
- 英文字母
- 數字
- _和$
- 不能包含空白字元
- 首字元不能為數字
- 保留字不能做識別符號
- 區分大小寫
3.2 assert
- 軟體除錯
- 執行時開啟 -ea
3.3 static
- 特定類的統一儲存空間,類繫結
- 成員變數:屬於類,記憶體中只有一個複製
- 成員方法:調靜態資料。可實現單例模式
- 程式碼塊:初始化靜態變數,只被執行一次
- 內部類:不能與外部類重名,只能訪問外部類靜態資料(包括私有)
3.4 switch
- 多分支選擇
- 整型或字元型別變數或整數表示式
- Java 7 開始支援 String。原理是String的hashCode()返回的int型別值匹配
3.5 volatile
- 保證執行緒間的可見性
- 從記憶體中取資料,而不是快取
- 不保證原子性
3.6 instanceof
- 二元運算子
- 判斷一個引用型別的變數所指向的物件是否是一個類的例項
- 即左邊物件是否是右邊類的例項
3.7 strictfp
- 精確浮點
- 確保浮點運算的準確性
- 若不指定,結果依賴於虛擬機器平臺
- 指定後依賴於統一標準,保證各平臺的一致性
3.8 null
- 不是合法的Object例項
- 無記憶體
- 表明該引用目前沒有指向任何物件
4. 基本型別與運算
4.1 基本資料型別
- int長度
- byte(8 bit)
- short(16 bit)
- int(32 bit)
- long(64 bit)
- float長度
- 單精度(32 bit float)
- 雙精度(64 bit double)
- boolean 型別變數的取值
- true
- false
- char資料型別:Unicode字元(16 bit)
- void:java.lang.Void 無法直接對其進行操作
4.2 不可變類
- 例項建立後,值不可變
- 所有的基本型別的包裝類+String
- 優點
- 使用簡單
- 執行緒安全
- 節省記憶體
- 缺點:會因為值的不同而產生新的物件,導致無法預料的問題
4.3 型別轉換
- 隱式型別轉換
- 低精度到高精度
- byte->short->char->int->long->float->double
- 顯式型別轉換
- 反之
- 可能會損失精度
- 型別自動轉換
- 低到高
- char型別會轉換為其對應的ASCII碼
- byte、char、short參與運算自動轉為int,但"+=",不轉
- 基本資料型別與boolean不能相互轉換
- 多種型別混合運算,自動轉成容量最大型別
運算子優先順序
點 () [] +(正) -(負) ++ -- ~ ! * / % +(加) -(減) << >> >>> < <= > >= instanceof == != & | ^ && || ?: = += -= *= /= %= &= |= ^= ~= <<= >>= >>>=
5. 字串與陣列
5.1 字串建立與儲存機制
- 堆
- 常量池
- new String("abc")建立1個或2個物件
5.2 ==、equals和hashCode區別
- == 比較引用,記憶體
- 未覆蓋,同==;比較內容
- hashCode鑑定物件是否相等,返回整數
5.3 String,StringBuffer,StringBuilder
- String:不可變,執行效率最低
- StringBuffer:可修改,執行緒安全,效率較高
- StringBuilder:可修改,執行緒不安全,效率最高
5.4 其他
- 陣列初始化方式
- length屬性和length()方法
6. 異常處理
6.1 finally塊執行時機
- 若try中有return,在return前
- 若try-finally或catch-finally中都有return,finally會覆蓋
6.2 finally程式碼塊不是一定會被執行
- 程式進入try之前出現異常
- try中呼叫System.exit(0)
6.3 Error
嚴重錯誤,不可恢復
6.4 Exception
- 可恢復,編譯器可捕捉
- 檢查性異常
- IO
- SQL
- 執行時異常
- JVM處理
- NullPointException
- ClassCastException
- ArrayIndexOutOfBoundsException
- 出現異常後,一直往上層拋,直到遇到處理程式碼或最上層
- 多型。若先捕獲基類,再捕獲子類。子類處理程式碼將永遠不會得到執行
7. Java平臺與記憶體管理
7.1 Java平臺與其他語言平臺的區別
- 純軟體,包括JVM與JAVA API
- JVM虛擬,不跨平臺
7.2 JAVA程式碼的執行
- 程式碼編譯為class:sun jdk 中javac
- 裝載class:ClassLoader
- 執行class
- 解釋執行
- 編譯執行
- client compiler
- server compiler
7.3 java原始碼編譯機制
- 詞法分析器元件:Token流
- 語法分析器元件:語法樹
- 語義分析器元件:註解語法樹
- 將語法樹中的名字、表示式等元素與變數、方法、型別等聯絡到一起
- 檢查變數使用前是否已宣告
- 推導泛型方法的型別引數
- 檢查型別匹配性
- 進行常量摺疊
- 檢查所有語句都可到達
- 檢查變數的確定性賦值
- 解除語法糖
- 將泛型JAVA轉成普通Java
- 檢查所有checked exception都被捕獲或丟擲
- 將含語法糖的語法樹轉成簡單語法樹eg:foreach,自動摺疊
- 程式碼生成器元件:位元組碼
7.4 類載入機制
- 裝載:全限定名+類載入器載入類
- 連結
- 校驗
- 格式不符,拋VerifyError
- 載入引用的類失敗:拋NoClassDefFoundError
- 準備:靜態變數預設初始化
- 解析:屬性、方法驗證(可選)
- 校驗
- 初始化(不是類載入必須觸發的)
- 靜態初始化程式碼
- 構造器程式碼
- 靜態屬性初始化
- 觸發時機
- 呼叫了new
- 反射呼叫了類中的方法
- 子類呼叫了初始化
- JVM啟動過程中指定的初始化類
- Bootstrap Class Loader:$JAVA_HOME/jre/lib/rt.jar
- Extension Class Loader:$JAVA_HOME/jre/lib/ext/*.jar
- System Class Loader:$CLASSPATH
- User Defined Class Loader
7.5 類執行機制
- 解釋執行
- JVM位元組碼為中間程式碼,由JVM在執行期對其解釋並執行
- invokestatic
- invokevirtual
- invokeinterface
- invokespecial
- 基於棧
- 程式碼緊湊,體積小
- 執行緒建立後,產生PC和Stack
- 指令解釋執行
- 棧頂快取:棧頂值快取在暫存器上
- 部分棧幀共享
- JVM位元組碼為中間程式碼,由JVM在執行期對其解釋並執行
- 編譯執行
- client compiler
- 輕量級,佔記憶體少
- 方法內聯
- 去虛擬化
- 冗餘消除
- server compiler
- 重量級,佔記憶體多
- 逃逸分析是C2進行很多優化的基礎
- 標量替換:用標量替換聚合量
- 棧上分配
- 若物件未逃逸,C2會選擇在棧上直接建立Point物件例項,而不是在堆上
- 棧上分配更快速,物件易回收
- 同步消除:如果發現同步的物件未逃逸,那也沒有同步的必要。C2會去掉同步程式碼塊
- client compiler
7.6 記憶體空間
- 方法區:類資訊,執行緒共享
- 堆
- 物件例項+陣列
- 分代管理
- 新生代
- 舊生代
- 本地方法棧:支援native方法,Sun JDK的實現中本地方法棧和JVM方法棧是同一個
- PC暫存器:執行緒私有
- JVM方法棧:執行緒私有
7.7 記憶體分配
- Java物件,堆上分配,分配需加鎖,開銷大
- 當堆上空間不足-->GC-->仍不足-->拋OutOfMemory
- Sun JDK 為新建立的執行緒在Eden上分配TLAB
- 多個小物件比大物件分配更高效
- 基於逃逸分析直接從棧上分配
7.8 記憶體回收
- 收集器
- 引用計數收集器
- 計數器增減有消耗
- 不適合迴圈引用
- 跟蹤收集器
- 集中式管理
- 全域性記錄資料的引用狀態
- 從根集合掃描物件,可能會造成應用程式暫停
- 三種實現演算法
- 複製
- 適用於回收空間中存活物件較少
- 缺點:需要增加一塊空的記憶體空間及進行物件的移動
- 標記-清除:會產生記憶體碎片
- 標記-壓縮:不產生記憶體碎片
- 複製
- 引用計數收集器
- Sun JDK中可用GC
- 新生代
- 序列GC(Serial GC):複製演算法
- Minor GC
- 強軟弱虛
- 並行回收GC(Parrallel Scavenge):掃描複製多執行緒
- 並行 GC(ParNew):配合舊生代 CMS
- 序列GC(Serial GC):複製演算法
- 舊生代和持久代可用GC
- 序列:標記壓縮+清除
- 並行:標記壓縮
- 併發:CMS
- 標記:暫停
- 併發標記:恢復,輪詢著色物件,以標記它們
- 重新標記:暫停
- 併發收集:恢復
- CMS記憶體回收易產生碎片,但是它提供了整理碎片的功能
- 浮動垃圾:CMS回收時產生應該回收但要等到下次CMS才能被回收掉的物件
- 新生代
- Full GC
- 對新生代舊生代及持久代都進行的GC
- 觸發的四種情況
- 舊生代空間不足
- 持久代空間滿
- CMS GC出現promotion failed和concurrent mode failure
- 統計得到的Minor GC晉升到舊生代的平均大小大於舊生代的剩餘空間
7.9 記憶體洩露
- 一個不再被程式使用的物件或變數還在記憶體中佔有儲存空間
- 符合垃圾回收標準
- 物件賦空值null
- 給物件賦予新值,重新分配了記憶體空間
- 洩露的兩種情況
- 堆中申請的空間沒有被釋放
- 物件不再被使用,但仍然存活在記憶體中
- 洩露原因
- 靜態集合類
- 各種連線
- 監聽器
- 變數不合理的作用域
- 單例模式
8. 分散式Java應用
8.1 基於訊息方式實現系統間的通訊
- TCP/IP+BIO
- socket.setSoTimeOut()設定等待響應的超時時間
- 一連線一執行緒
- 缺點:無論連線是否真實,都要建立執行緒
- BIO下伺服器端所能支撐的連線數目有限
- TCP/IP+NIO
- Channel
- SocketChannel:建立連線,監聽事件,操作讀寫
- ServerSocketChannel:監聽埠,監聽連線事件
- Selector:獲取是否要處理的事件
- Buffer:存放處理的資料
- NIO Reactor模式,通過註冊感興趣的事件及掃描是否有感興趣的事件發生,從而做出相應的動作
- 多個請求,連線複用
- 只有在有真實的請求時,才會建立執行緒
- 一請求一執行緒
- Channel
- UDP/IP+BIO
- DatagramSocket:負責監聽埠,讀寫資料
- DatagramPacket:作為資料流物件進行傳輸
- UDP/IP+NIO
- DatagramChannel:監聽埠,進行讀寫
- ByteBuffer:資料流傳輸
- NIO好處:只在有流要讀取或可寫入流時才做出相應的IO操作,而不像BIO方式阻塞當前執行緒
8.2 基於遠端呼叫方式實現系統間的通訊
- 遠端呼叫方式
- 系統間通訊和系統內一樣
- 讓使用者感覺呼叫遠端同調用本地一樣
- 基於Java自身技術
- RMI:客戶端代理,stub,封裝物件,序列化為流,TCP/IP BIO,Skeleton,反序列化,獲取物件例項,呼叫
- WebService
- 服務端的服務生成WSDL檔案
- 將應用+WSDL檔案放入HTTP伺服器
- 借用Java輔助工具根據WSDL檔案生成客戶端stub程式碼
- stub將產生的物件請求資訊封裝為標準化的SOAP格式資料,併發請求到伺服器端
- 服端在接收到SOAP格式資料時進行轉化,反射呼叫相應的Java類
- SOAP優點支援跨語言,缺點對複雜物件結構難支援
8.3 基於開源框架
- Spring RMI
9. 多執行緒
9.1 執行緒資源同步機制
- JVM保證以下操作順序
- 同一執行緒操作
- 對於main Memory 上的同一個變數的操作
- 對於加了鎖的main Memory上的物件操作
- 為避免資源操作的髒資料問題,JVM提供了
- synchronized
- volatile
- lock/unlock
- 目的是控制資源競爭
9.2 執行緒互動機制
- 基於Object的wait/notify/notifyAll
- 為避免假喚醒,需要double check
- 呼叫物件的wait-->wait sets--->釋放鎖--->其他執行緒notify---->wait sets---->執行此物件執行緒--->刪除sets中此執行緒
- 基於JDK 5 併發包,支援執行緒互動
- Semphore的acquire,release
- Condition的await,signal
- CountDownLatch的await和countDown
9.3 執行緒狀態
- New
- Runnable
- Running
- Wait
- TimedWait
- Blocked
- Terminated
9.4 sleep()與wait()方法的區別
- sleep
- 暫停一段時間執行
- Thread的靜態方法
- 不釋放鎖
- 需要捕獲異常
- wait
- 使執行緒暫停執行
- Object方法,用於執行緒間通訊
- 釋放鎖
9.5 守護執行緒
- 後臺提供服務
- 使用者執行緒全部終止,只剩下守護執行緒時,JVM就會退出
- 呼叫start()之前,呼叫執行緒物件的setDaemon(true)
9.6 join
- 呼叫該方法的執行緒在執行完run()後,再執行join方法後面的程式碼
- 執行緒合併,實現同步功能
10. IO
10.1 流本質
- 資料傳輸
10.2 流分類
- 位元組流:不使用快取
- 字元流
- 碼錶對映
- 使用快取
10.3 裝飾者模式
- 執行時動態給物件增加額外的職責
- 是你還有你,一切拜託你
- FilterInputStream
10.4 Java Socket
- ServerSocket server = new ServerSocket(2000);
- Socker socket = server.accept();
- 客戶端:Socket socket = new Socket("localhost",2000);
10.5 NIO
- Channel--Selector--Buffer
- 反應器模式
10.6 序列化
- 物件持久化方式
- 解決在對物件流進行讀寫操作時引發的問題
- 物件寫進流裡進行網路傳輸,儲存到檔案,資料庫
10.7 如何實現序列化
- 實現Serializable介面
- 使用FileOutputStream來構造ObjectOutputStream物件
- 使用該物件的writeObject(obj)方法將物件寫出
- 要恢復時,使用對應的輸入流
10.8 序列化特點
- 一個類能被序列化,它的子類也能被序列化
- static代表類成員,transient代表臨時資料。均不能被序列化
- 序列化影響效能,需要才使用
- 需要通過網路來發送物件,或物件的狀態需要被持久化到資料庫或檔案中
- 序列化能實現深複製,即可以複製引用的物件
10.9 反序列化
- 將流轉化為物件
- UID最好自己定義。優點
- 提高程式執行效率。省去計算過程
- 提高程式不同平臺相容性。不同計算方式,反序列化失敗
- 增強程式各個版本的可相容性。加入新屬性,預設UID變化
10.10 外部序列化
- 實現Externalizable介面控制