記念我曾經的 JAVA 姿式

目前在搞 Node.js,曾經的 JAVA 知識忘了好多,爲此整理了下,感嘆下工業語言仍是有至關的優點的。java

Java全部的流類位於java.io包中,都分別繼承字如下四種抽象流類型。面試

Type 字節流 字符流
輸入流 InputStream Reader
輸出流 OutputStream Writer

Java流

繼承自InputStream/OutputStream的流都是用於向程序中輸入/輸出數據,且數據的單位都是字節(byte=8bit)。算法

繼承自Reader/Writer的流都是用於向程序中輸入/輸出數據,且數據的單位都是字符(2byte=16bit)。數組


異常

Java異常

Java的異常(包括ExceptionError)分爲:緩存

  • 可查的異常(checked exceptions)安全

除了RuntimeException及其子類之外,其餘的Exception類及其子類都屬於可查異常。這種異常的特色是Java編譯器會檢查它,也就是說,當程序中可能出現這類異常,要麼用try-catch語句捕獲它,要麼用throws子句聲明拋出它,不然編譯不會經過。網絡

  • 不可查的異常(unchecked exceptions)多線程

包括運行時異常(RuntimeException與其子類)和錯誤(Error)。併發

運行時異常和非運行時異常:ide

  • 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

返回目錄


安全性

  1. 嚴格遵循面向對象的規範。這樣封裝了數據細節,只提供接口給用戶。增長了數據級的安全性。

  2. 無指針運算。java中的操做,除了基本類型都是引用的操做。引用是不能進行增減運算,不能被直接賦予內存地址的,從而增長了內存級的安全性。

  3. 數組邊界檢查。這樣就不會出現C/C++中的緩存溢出等安全漏洞。

  4. 強制類型轉換。非同類型的對象之間不能進行轉換,不然會拋出ClassCastException

  5. 語言對線程安全的支持。java從語言級支持線程。從而從語法和語言自己作了不少對線程的控制和支持。

  6. 垃圾回收。

  7. Exception。

返回目錄


類加載

原理

ClassLoader使用的是雙親委託模型來搜索類的,每一個ClassLoader實例都有一個父類加載器的引用(不是繼承的關係,是一個包含的關係),虛擬機內置的類加載器(Bootstrap ClassLoader)自己沒有父類加載器,但能夠用做其它ClassLoader實例的的父類加載器。

當一個ClassLoader實例須要加載某個類時,它會試圖親自搜索某個類以前,先把這個任務委託給它的父類加載器,這個過程是由上至下依次檢查的,首先由最頂層的類加載器Bootstrap ClassLoader試圖加載,若是沒加載到,則把任務轉交給Extension ClassLoader試圖加載,若是也沒加載到,則轉交給App ClassLoader 進行加載,若是它也沒有加載獲得的話,則返回給委託的發起者,由它到指定的文件系統或網絡等URL中加載該類。

若是它們都沒有加載到這個類時,則拋出ClassNotFoundException異常。不然將這個找到的類生成一個類的定義,並將它加載到內存當中,最後返回這個類在內存中的Class實例對象。

JVM在斷定兩個class是否相同時,不只要判斷兩個類名是否相同,並且要判斷是否由同一個類加載器實例加載的。只有二者同時知足的狀況下,JVM才認爲這兩個class是相同的。

加載器

  1. 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"))
  1. Extension ClassLoader

擴展類加載器,負責加載Java的擴展類庫,默認加載JAVA_HOME/jre/lib/ext/目下的全部jar。

  1. 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聲明一個實例變量,當對象存儲時,它的值不須要維持。

volatile

做爲指令關鍵字,確保本條指令不會因編譯器的優化而省略,修飾變量,保證變量每次都是從內存中從新讀取。

final

  1. 修飾基礎數據成員(as const)

  2. 修飾類或對象的引用

  3. 修飾方法的final(cannot overwrite)

  4. 修飾類或者參數

返回目錄


初始化

父靜態->子靜態
父變量->父初始化區->父構造
子變量->子初始化區->子構造


多線程

JAVA多線程實現方式主要有三種:繼承Thread類、實現Runnable接口、使用ExecutorService、Callable、Future實現有返回結果的多線程。其中前兩種方式線程執行完後都沒有返回值,只有最後一種是帶返回值的。

返回目錄


線程池

concurrent下的線程池

名稱 功能
ExecutorService 真正的線程池接口
ScheduledExecutorService 能和Timer/TimerTask相似,解決那些須要任務重複執行的問題
ThreadPoolExecutor ExecutorService的默認實現
ScheduledThreadPoolExecutor 繼承ThreadPoolExecutor的ScheduledExecutorService接口實現,週期性任務調度的類實現

Executors

  1. newSingleThreadExecutor

建立一個單線程的線程池。這個線程池只有一個線程在工做,也就是至關於單線程串行執行全部任務。若是這個惟一的線程由於異常結束,那麼會有一個新的線程來替代它。此線程池保證全部任務的執行順序按照任務的提交順序執行。

  1. newFixedThreadPool

建立固定大小的線程池。每次提交一個任務就建立一個線程,直到線程達到線程池的最大大小。線程池的大小一旦達到最大值就會保持不變,若是某個線程由於執行異常而結束,那麼線程池會補充一個新線程。

  1. newCachedThreadPool

建立一個可緩存的線程池。若是線程池的大小超過了處理任務所須要的線程,
那麼就會回收部分空閒(60秒不執行任務)的線程,當任務數增長時,此線程池又能夠智能的添加新線程來處理任務。此線程池不會對線程池大小作限制,線程池大小徹底依賴於操做系統(或者說JVM)可以建立的最大線程大小。

  1. newScheduledThreadPool

建立一個大小無限的線程池。此線程池支持定時以及週期性執行任務的需求。


線程安全

線程安全是一個很大的問題,Java 最多見的 HttpServlet 就是單實例多線程,解決這樣的問題,有多種方式:

  1. ThreadLocal

    ThreadLocal 看下一節的內存圖就很好理解,每一個線程都有本身的工做內存,ThreadLocal 就是將變量存到線程本身的工做內存中,因此不會有併發問題。

  2. Synchronized
    synchronized鎖住的是括號裏的對象,而不是代碼。對於非 staticsynchronized 方法,鎖的就是對象自己也就是 this 。該關鍵字能夠加到:

    • 實例方法

    • 靜態方法

    • 實例方法中的同步塊

    • 靜態方法中的同步塊

  3. ReentrantLock / Condition
    synchronized 不夠靈活,例如讀寫文件,讀和讀之間不該該互斥,這個時候就可使用 ReentrantLock 增長併發能力。Condition 是綁定到 Lock 上的,能夠用於線程間通訊,例如這個面試題,就可使用 Condition 喚起線程寫本身的 name

    有四個線程一、二、三、4。線程1的功能就是輸出1,線程2的功能就是輸出2,以此類推.........如今有四個文件ABCD。初始都爲空。現要讓四個文件呈以下格式:A:1 2 3 4 1 2.... B:2 3 4 1 2 3.... C:3 4 1 2 3 4.... D:4 1 2 3 4 1....

  4. 併發容器
    常見的 ConcurrentHashMap CopyOnWriteArrayList 用於多線程下存放數據,Queue BlockingQueue 用於排隊消費。

  5. Atomic 包
    在 Atomic 包裏一共有 12 個類,四種原子更新方式,分別是原子更新基本類型,原子更新數組,原子更新引用和原子更新字段。某些併發問題,須要無鎖解決時,就能夠考慮使用原子方法。

返回目錄


內存模型

Java內存模型

Java內存模型規定,對於多個線程共享的變量,存儲在主內存當中,每一個線程都有本身獨立的工做內存,線程只能訪問本身的工做內存,不能夠訪問其它線程的 工做內存。工做內存中保存了主內存共享變量的副本,線程要操做這些共享變量,只能經過操做工做內存中的副原本實現,操做完畢以後再同步回到主內存當中。

如何保證多個線程操做主內存的數據完整性是一個難題,Java內存模型也規定了工做內存與主內存之間交互的協議,首先是定義了8種原子操做:

  • lock:將主內存中的變量鎖定,爲一個線程所獨佔

  • unclock:將lock加的鎖定解除,此時其它的線程能夠有機會訪問此變量

  • read:將主內存中的變量值讀到工做內存當中

  • load:將read讀取的值保存到工做內存中的變量副本中。

  • use:將值傳遞給線程的代碼執行引擎

  • assign:將執行引擎處理返回的值從新賦值給變量副本

  • store:將變量副本的值存儲到主內存中。

  • write:將store存儲的值寫入到主內存的共享變量當中。

內存組成

Java內存組成

堆(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

返回目錄

相關文章
相關標籤/搜索