紀念我曾經的 Java 知識
目前在搞 Node.js,曾經的 JAVA 知識忘了好多,為此整理了下,感嘆下工業語言還是有相當的優勢的。
目錄
- 流
- 異常
- 註解
- 安全性
- 類載入
- 關鍵字
- 初始化
- 多執行緒
- 執行緒池
- 記憶體模型
流
Java所有的流類位於java.io包中,都分別繼承字以下四種抽象流型別。
Type | 位元組流 | 字元流 |
---|---|---|
輸入流 | InputStream | Reader |
輸出流 | OutputStream | Writer |
繼承自InputStream/OutputStream的流都是用於向程式中輸入/輸出資料,且資料的單位都是位元組(byte=8bit)。
繼承自Reader/Writer的流都是用於向程式中輸入/輸出資料,且資料的單位都是字元(2byte=16bit)。
異常
Java的異常(包括Exception和Error)分為:
可查的異常(checked exceptions)
除了RuntimeException及其子類以外,其他的Exception類及其子類都屬於可查異常。這種異常的特點是Java編譯器會檢查它,也就是說,當程式中可能出現這類異常,要麼用try-catch語句捕獲它,要麼用throws子句宣告丟擲它,否則編譯不會通過。
不可查的異常(unchecked exceptions)
包括執行時異常(RuntimeException與其子類)和錯誤(Error)。
執行時異常和非執行時異常:
RuntimeException
NullPointerException(空指標異常)、IndexOutOfBoundsException(下標越界異常)等這些異常是不檢查異常,程式中可以選擇捕獲處理,也可以不處理。這些異常一般是由程式邏輯錯誤引起的,程式應該從邏輯角度儘可能避免這類異常的發生。執行時異常的特點是Java編譯器不會檢查它,也就是說,當程式中可能出現這類異常,即使沒有用try-catch語句捕獲它,也沒有用throws子句宣告丟擲它,也會編譯通過。
RuntimeException以外的Exception
從程式語法角度講是必須進行處理的異常,如果不處理,程式就不能編譯通過。如IOException、SQLException等以及使用者自定義的Exception異常,一般情況下不自定義檢查異常。
註解
Java SE5內建了三種標準註解:
@Override,表示當前的方法定義將覆蓋超類中的方法。 @Deprecated,使用了註解為它的元素編譯器將發出警告,因為註解@Deprecated是不贊成使用的程式碼,被棄用的程式碼。 @SuppressWarnings,關閉不當編譯器警告資訊。
Java還提供了4中註解,專門負責新註解的建立:
@Target:
表示該註解可以用於什麼地方,可能的ElementType引數有:
CONSTRUCTOR
:構造器的宣告
FIELD
:域宣告(包括enum
例項)
LOCAL_VARIABLE
:區域性變數宣告
METHOD
:方法宣告
PACKAGE
:包宣告
PARAMETER
:引數宣告
TYPE
:類、介面(包括註解型別)或enum
宣告
@Retention
表示需要在什麼級別儲存該註解資訊。可選的RetentionPolicy引數包括:
SOURCE
:註解將被編譯器丟棄
CLASS
:註解在class檔案中可用,但會被VM丟棄
RUNTIME
:VM將在執行期間保留註解,因此可以通過反射機制讀取註解的資訊
@Document
將註解包含在Javadoc中
@Inherited
允許子類繼承父類中的註解
Example
定義註解:
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface UseCase { public String id(); public String description() default "no description"; }
使用註解:
public class PasswordUtils { @UseCase(id = 47, description = "Passwords must contain at least one numeric") public boolean validatePassword(String password) { return (password.matches("\\w*\\d\\w*")); } @UseCase(id = 48) public String encryptPassword(String password) { return new StringBuilder(password).reverse().toString(); } }
解析註解:
public static void main(String[] args) { List<Integer> useCases = new ArrayList<Integer>(); Collections.addAll(useCases, 47, 48, 49, 50); trackUseCases(useCases, PasswordUtils.class); } public static void trackUseCases(List<Integer> useCases, Class<?> cl) { for (Method m : cl.getDeclaredMethods()) { UseCase uc = m.getAnnotation(UseCase.class); if (uc != null) { System.out.println("Found Use Case:" + uc.id() + " " + uc.description()); useCases.remove(new Integer(uc.id())); } } for (int i : useCases) { System.out.println("Warning: Missing use case-" + i); } } // Found Use Case:47 Passwords must contain at least one numeric // Found Use Case:48 no description // Warning: Missing use case-49 // Warning: Missing use case-50
安全性
- 嚴格遵循物件導向的規範。這樣封裝了資料細節,只提供介面給使用者。增加了資料級的安全性。
- 無指標運算。java中的操作,除了基本型別都是引用的操作。引用是不能進行增減運算,不能被直接賦予記憶體地址的,從而增加了記憶體級的安全性。
- 陣列邊界檢查。這樣就不會出現C/C++中的快取溢位等安全漏洞。
- 強制型別轉換。非同型別的物件之間不能進行轉換,否則會丟擲ClassCastException
- 語言對執行緒安全的支援。java從語言級支援執行緒。從而從語法和語言本身做了很多對執行緒的控制和支援。
- 垃圾回收。
- Exception。
類載入
原理
ClassLoader使用的是雙親委託模型來搜尋類的,每個ClassLoader例項都有一個父類載入器的引用(不是繼承的關係,是一個包含的關係),虛擬機器內建的類載入器(Bootstrap ClassLoader)本身沒有父類載入器,但可以用作其它ClassLoader例項的的父類載入器。
當一個ClassLoader例項需要載入某個類時,它會試圖親自搜尋某個類之前,先把這個任務委託給它的父類載入器,這個過程是由上至下依次檢查的,首先由最頂層的類載入器Bootstrap ClassLoader試圖載入,如果沒載入到,則把任務轉交給Extension ClassLoader試圖載入,如果也沒載入到,則轉交給App ClassLoader 進行載入,如果它也沒有載入得到的話,則返回給委託的發起者,由它到指定的檔案系統或網路等URL中載入該類。
如果它們都沒有載入到這個類時,則丟擲ClassNotFoundException異常。否則將這個找到的類生成一個類的定義,並將它載入到記憶體當中,最後返回這個類在記憶體中的Class例項物件。
JVM在判定兩個class是否相同時,不僅要判斷兩個類名是否相同,而且要判斷是否由同一個類載入器例項載入的。只有兩者同時滿足的情況下,JVM才認為這兩個class是相同的。
載入器
BootStrap ClassLoader
啟動類載入器,是Java類載入層次中最頂層的類載入器,負責載入JDK中的核心類庫,如:rt.jar、resources.jar、charsets.jar等。
URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs(); for (int i = 0; i < urls.length; i++) { System.out.println(urls[i].toExternalForm()); } // 也可以通過sun.boot.class.path獲取 System.out.println(System.getProperty("sun.boot.class.path"))
Extension ClassLoader
擴充套件類載入器,負責載入Java的擴充套件類庫,預設載入JAVA_HOME/jre/lib/ext/目下的所有jar。
App ClassLoader
系統類載入器,負責載入應用程式classpath目錄下的所有jar和class檔案
注意:
除了Java預設提供的三個ClassLoader之外,使用者還可以根據需要定義自已的ClassLoader,而這些自定義的ClassLoader都必須繼承自java.lang.ClassLoader類,也包括Java提供的另外二個ClassLoader(Extension ClassLoader和App ClassLoader)在內。Bootstrap ClassLoader不繼承自ClassLoader,因為它不是一個普通的Java類,底層由C++編寫,已嵌入到了JVM核心當中,當JVM啟動後,Bootstrap ClassLoader也隨著啟動,負責載入完核心類庫後,並構造Extension ClassLoader和App ClassLoader類載入器。
關鍵字
strictfp(strict float point)
strictfp 關鍵字可應用於類、介面或方法。使用strictfp關鍵字宣告一個方法時,該方法中所有的float和double表示式都嚴格遵守FP-strict的限制,符合IEEE-754規範。當對一個類或介面使用strictfp關鍵字時,該類中的所有程式碼,包括巢狀型別中的初始設定值和程式碼,都將嚴格地進行計算。嚴格約束意味著所有表示式的結果都必須是 IEEE 754演算法對運算元預期的結果,以單精度和雙精度格式表示。
如果你想讓你的浮點運算更加精確,而且不會因為不同的硬體平臺所執行的結果不一致的話,可以用關鍵字strictfp。
transiant
變數修飾符,如果用transient宣告一個例項變數,當物件儲存時,它的值不需要維持。
作為指令關鍵字,確保本條指令不會因編譯器的優化而省略,修飾變數,保證變數每次都是從記憶體中重新讀取。
final
- 修飾基礎資料成員(as const)
- 修飾類或物件的引用
- 修飾方法的final(cannot overwrite)
- 修飾類或者引數
初始化
父靜態->子靜態
父變數->父初始化區->父構造
子變數->子初始化區->子構造
多執行緒
java多執行緒實現方式主要有三種:繼承Thread類、實現Runnable介面、使用ExecutorService、Callable、Future實現有返回結果的多執行緒。其中前兩種方式執行緒執行完後都沒有返回值,只有最後一種是帶返回值的。
執行緒池
concurrent下的執行緒池:
名稱 | 功能 |
---|---|
ExecutorService | 真正的執行緒池介面 |
ScheduledExecutorService | 能和Timer/TimerTask類似,解決那些需要任務重複執行的問題 |
ThreadPoolExecutor | ExecutorService的預設實現 |
ScheduledThreadPoolExecutor | 繼承ThreadPoolExecutor的ScheduledExecutorService介面實現,週期性任務排程的類實現 |
Executors
newSingleThreadExecutor
建立一個單執行緒的執行緒池。這個執行緒池只有一個執行緒在工作,也就是相當於單執行緒序列執行所有任務。如果這個唯一的執行緒因為異常結束,那麼會有一個新的執行緒來替代它。此執行緒池保證所有任務的執行順序按照任務的提交順序執行。
newFixedThreadPool
建立固定大小的執行緒池。每次提交一個任務就建立一個執行緒,直到執行緒達到執行緒池的最大大小。執行緒池的大小一旦達到最大值就會保持不變,如果某個執行緒因為執行異常而結束,那麼執行緒池會補充一個新執行緒。
newCachedThreadPool
建立一個可快取的執行緒池。如果執行緒池的大小超過了處理任務所需要的執行緒,那麼就會回收部分空閒(60秒不執行任務)的執行緒,當任務數增加時,此執行緒池又可以智慧的新增新執行緒來處理任務。此執行緒池不會對執行緒池大小做限制,執行緒池大小完全依賴於作業系統(或者說JVM)能夠建立的最大執行緒大小。
newScheduledThreadPool
建立一個大小無限的執行緒池。此執行緒池支援定時以及週期性執行任務的需求。
記憶體模型
Java記憶體模型規定,對於多個執行緒共享的變數,儲存在主記憶體當中,每個執行緒都有自己獨立的工作記憶體,執行緒只能訪問自己的工作記憶體,不可以訪問其它執行緒的 工作記憶體。工作記憶體中儲存了主記憶體共享變數的副本,執行緒要操作這些共享變數,只能通過操作工作記憶體中的副本來實現,操作完畢之後再同步回到主記憶體當中。
如何保證多個執行緒操作主記憶體的資料完整性是一個難題,Java記憶體模型也規定了工作記憶體與主記憶體之間互動的協議,首先是定義了8種原子操作:
- lock:將主記憶體中的變數鎖定,為一個執行緒所獨佔
- unclock:將lock加的鎖定解除,此時其它的執行緒可以有機會訪問此變數
- read:將主記憶體中的變數值讀到工作記憶體當中
- load:將read讀取的值儲存到工作記憶體中的變數副本中。
- use:將值傳遞給執行緒的程式碼執行引擎
- assign:將執行引擎處理返回的值重新賦值給變數副本
- store:將變數副本的值儲存到主記憶體中。
- write:將store儲存的值寫入到主記憶體的共享變數當中。
記憶體組成
堆(Heap)
執行時資料區域,所有類例項和陣列的記憶體均從此處分配。Java虛擬機器啟動時建立。物件的堆記憶體由稱為垃圾回收器 的自動記憶體管理系統回收。
- News Generation(Young Generation即圖中的Eden + From Space + To Space)
- Eden 存放新生的物件
- Survivor Space 兩個 存放每次垃圾回收後存活的物件
- Old Generation(Tenured Generation 即圖中的Old Space) 主要存放應用程式中生命週期長的存活物件
非堆記憶體
JVM具有一個由所有執行緒共享的方法區。方法區屬於非堆記憶體。它儲存每個類結構,如執行時常數池、欄位和方法資料,以及方法和構造方法的程式碼。它是在Java虛擬機器啟動時建立的。除了方法區外,Java虛擬機器實現可能需要用於內部處理或優化的記憶體,這種記憶體也是非堆記憶體。例如,JIT編譯器需要記憶體來儲存從Java虛擬機器程式碼轉換而來的本機程式碼,從而獲得高效能。
- Permanent Generation (圖中的Permanent Space)存放JVM自己的反射物件,比如類物件和方法物件
- native heap
相關文章
- 我的創作紀念日
- 紀念即將逝去的nosqlSQL
- 計算機知識碎碎念計算機
- 分享我曾經的學習和找工作經歷
- 我曾經是怎麼做面試官的面試
- 免費線上戀愛紀念日、結婚紀念日計算器
- 最後的紀念---linuxGL(轉)Linux
- 紀念入坑隨筆
- 洛谷P1094 紀念品分組(Java)Java
- 紀念我HooK逝世的青春--XIgnCode3.TP.NP.HS.PP.GPKHook
- 我滴天!我曾經寫過 21 個巢狀的回撥巢狀
- 曾奇:談談我所認識的分散式鎖分散式
- 我的知識地圖地圖
- 《純數學教程(紀念版)》中的根式
- 為慶祝週年紀念日,Win10版《我的世界》將支援OculusRiftWin10
- 紀念程式設計師施凡程式設計師
- 我曾經得到的一個最好的程式設計建議程式設計
- 滴滴 曾奇:談談我所認識的分散式鎖分散式
- 曾經我認為C語言就是個弟弟C語言
- 【Java分享客棧】我曾經的兩個Java老師一個找不到工作了一個被迫轉行了Java
- 奉獻一波鵝廠的面經!紀念最後的校招!| 掘金技術徵文
- java需要掌握的知識Java
- 初識Java Java基礎知識Java
- 基於 Angular 開發的 紀念日計算工具Angular
- 改進《純數學教程(紀念版)》中的根式
- Java知識整理Java
- 我曾經遇到過的一個SAP gateway系統cache的問題Gateway
- 紀念DedeCMS創始人IT柏拉圖先生
- 知識經濟是人力資源的經濟
- M2 成立 30 週年紀念訪談:勤懇再現經典遊戲的 30 年遊戲
- 那些曾經逝去的記憶
- 我的個人知識管理工具一覽及相關經驗技巧
- 紀念皮拉內西:探索遊戲中的無限之境遊戲
- 曾經有個能幫我修電腦的程式設計師,我沒有好好珍惜……程式設計師
- 入園第一天,聊作紀念
- Java入門知識_Java初學者須知Java
- Java:變數的賦值操作(祭奠我那悲催的基礎知識)Java變數賦值
- java小知識點Java