纪念我曾经的 JAVA 姿势--转
原文地址:https://segmentfault.com/a/1190000007122432?hmsr=toutiao.io&utm_medium=toutiao.io&utm_source=toutiao.io
目前在搞 Node.js,曾經(jīng)的 JAVA 知識忘了好多,為此整理了下,感嘆下工業(yè)語言還是有相當?shù)膬?yōu)勢的。
目錄
-
流
-
異常
-
注解
-
安全性
-
類加載
-
關(guān)鍵字
-
初始化
-
多線程
-
線程池
-
內(nèi)存模型
流
Java所有的流類位于java.io包中,都分別繼承字以下四種抽象流類型。
| 輸入流 | InputStream | Reader |
| 輸出流 | OutputStream | Writer |
繼承自InputStream/OutputStream的流都是用于向程序中輸入/輸出數(shù)據(jù),且數(shù)據(jù)的單位都是字節(jié)(byte=8bit)。
繼承自Reader/Writer的流都是用于向程序中輸入/輸出數(shù)據(jù),且數(shù)據(jù)的單位都是字符(2byte=16bit)。
異常
Java的異常(包括Exception和Error)分為:
-
可查的異常(checked exceptions)
除了RuntimeException及其子類以外,其他的Exception類及其子類都屬于可查異常。這種異常的特點是Java編譯器會檢查它,也就是說,當程序中可能出現(xiàn)這類異常,要么用try-catch語句捕獲它,要么用throws子句聲明拋出它,否則編譯不會通過。
-
不可查的異常(unchecked exceptions)
包括運行時異常(RuntimeException與其子類)和錯誤(Error)。
運行時異常和非運行時異常:
-
RuntimeException
NullPointerException(空指針異常)、IndexOutOfBoundsException(下標越界異常)等這些異常是不檢查異常,程序中可以選擇捕獲處理,也可以不處理。這些異常一般是由程序邏輯錯誤引起的,程序應(yīng)該從邏輯角度盡可能避免這類異常的發(fā)生。運行時異常的特點是Java編譯器不會檢查它,也就是說,當程序中可能出現(xiàn)這類異常,即使沒有用try-catch語句捕獲它,也沒有用throws子句聲明拋出它,也會編譯通過。
-
RuntimeException以外的Exception
從程序語法角度講是必須進行處理的異常,如果不處理,程序就不能編譯通過。如IOException、SQLException等以及用戶自定義的Exception異常,一般情況下不自定義檢查異常。
返回目錄
<span id="target"></span>
注解
Java SE5內(nèi)置了三種標準注解:
@Override,表示當前的方法定義將覆蓋超類中的方法。@Deprecated,使用了注解為它的元素編譯器將發(fā)出警告,因為注解@Deprecated是不贊成使用的代碼,被棄用的代碼。@SuppressWarnings,關(guān)閉不當編譯器警告信息。Java還提供了4中注解,專門負責新注解的創(chuàng)建:
-
@Target:
表示該注解可以用于什么地方,可能的ElementType參數(shù)有:
CONSTRUCTOR:構(gòu)造器的聲明
FIELD:域聲明(包括enum實例)
LOCAL_VARIABLE:局部變量聲明
METHOD:方法聲明
PACKAGE:包聲明
PARAMETER:參數(shù)聲明
TYPE:類、接口(包括注解類型)或enum聲明
-
@Retention
表示需要在什么級別保存該注解信息。可選的RetentionPolicy參數(shù)包括:
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返回目錄
安全性
嚴格遵循面向?qū)ο蟮囊?guī)范。這樣封裝了數(shù)據(jù)細節(jié),只提供接口給用戶。增加了數(shù)據(jù)級的安全性。
無指針運算。java中的操作,除了基本類型都是引用的操作。引用是不能進行增減運算,不能被直接賦予內(nèi)存地址的,從而增加了內(nèi)存級的安全性。
數(shù)組邊界檢查。這樣就不會出現(xiàn)C/C++中的緩存溢出等安全漏洞。
強制類型轉(zhuǎn)換。非同類型的對象之間不能進行轉(zhuǎn)換,否則會拋出ClassCastException
語言對線程安全的支持。java從語言級支持線程。從而從語法和語言本身做了很多對線程的控制和支持。
垃圾回收。
Exception。
返回目錄
類加載
原理
ClassLoader使用的是雙親委托模型來搜索類的,每個ClassLoader實例都有一個父類加載器的引用(不是繼承的關(guān)系,是一個包含的關(guān)系),虛擬機內(nèi)置的類加載器(Bootstrap ClassLoader)本身沒有父類加載器,但可以用作其它ClassLoader實例的的父類加載器。
當一個ClassLoader實例需要加載某個類時,它會試圖親自搜索某個類之前,先把這個任務(wù)委托給它的父類加載器,這個過程是由上至下依次檢查的,首先由最頂層的類加載器Bootstrap ClassLoader試圖加載,如果沒加載到,則把任務(wù)轉(zhuǎn)交給Extension ClassLoader試圖加載,如果也沒加載到,則轉(zhuǎn)交給App ClassLoader?進行加載,如果它也沒有加載得到的話,則返回給委托的發(fā)起者,由它到指定的文件系統(tǒng)或網(wǎng)絡(luò)等URL中加載該類。
如果它們都沒有加載到這個類時,則拋出ClassNotFoundException異常。否則將這個找到的類生成一個類的定義,并將它加載到內(nèi)存當中,最后返回這個類在內(nèi)存中的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
系統(tǒng)類加載器,負責加載應(yīng)用程序classpath目錄下的所有jar和class文件
注意:
除了Java默認提供的三個ClassLoader之外,用戶還可以根據(jù)需要定義自已的ClassLoader,而這些自定義的ClassLoader都必須繼承自java.lang.ClassLoader類,也包括Java提供的另外二個ClassLoader(Extension ClassLoader和App ClassLoader)在內(nèi)。Bootstrap ClassLoader不繼承自ClassLoader,因為它不是一個普通的Java類,底層由C++編寫,已嵌入到了JVM內(nèi)核當中,當JVM啟動后,Bootstrap ClassLoader也隨著啟動,負責加載完核心類庫后,并構(gòu)造Extension ClassLoader和App ClassLoader類加載器。返回目錄
關(guān)鍵字
strictfp(strict float point)
strictfp 關(guān)鍵字可應(yīng)用于類、接口或方法。使用strictfp關(guān)鍵字聲明一個方法時,該方法中所有的float和double表達式都嚴格遵守FP-strict的限制,符合IEEE-754規(guī)范。當對一個類或接口使用strictfp關(guān)鍵字時,該類中的所有代碼,包括嵌套類型中的初始設(shè)定值和代碼,都將嚴格地進行計算。嚴格約束意味著所有表達式的結(jié)果都必須是 IEEE 754算法對操作數(shù)預(yù)期的結(jié)果,以單精度和雙精度格式表示。
如果你想讓你的浮點運算更加精確,而且不會因為不同的硬件平臺所執(zhí)行的結(jié)果不一致的話,可以用關(guān)鍵字strictfp。
transiant
變量修飾符,如果用transient聲明一個實例變量,當對象存儲時,它的值不需要維持。
volatile
作為指令關(guān)鍵字,確保本條指令不會因編譯器的優(yōu)化而省略,修飾變量,保證變量每次都是從內(nèi)存中重新讀取。
final
修飾基礎(chǔ)數(shù)據(jù)成員(as const)
修飾類或?qū)ο蟮囊?/p>
修飾方法的final(cannot overwrite)
修飾類或者參數(shù)
返回目錄
初始化
父靜態(tài)->子靜態(tài)
父變量->父初始化區(qū)->父構(gòu)造
子變量->子初始化區(qū)->子構(gòu)造
多線程
JAVA多線程實現(xiàn)方式主要有三種:繼承Thread類、實現(xiàn)Runnable接口、使用ExecutorService、Callable、Future實現(xiàn)有返回結(jié)果的多線程。其中前兩種方式線程執(zhí)行完后都沒有返回值,只有最后一種是帶返回值的。
返回目錄
線程池
concurrent下的線程池:
| ExecutorService | 真正的線程池接口 |
| ScheduledExecutorService | 能和Timer/TimerTask類似,解決那些需要任務(wù)重復(fù)執(zhí)行的問題 |
| ThreadPoolExecutor | ExecutorService的默認實現(xiàn) |
| ScheduledThreadPoolExecutor | 繼承ThreadPoolExecutor的ScheduledExecutorService接口實現(xiàn),周期性任務(wù)調(diào)度的類實現(xiàn) |
Executors
newSingleThreadExecutor
創(chuàng)建一個單線程的線程池。這個線程池只有一個線程在工作,也就是相當于單線程串行執(zhí)行所有任務(wù)。如果這個唯一的線程因為異常結(jié)束,那么會有一個新的線程來替代它。此線程池保證所有任務(wù)的執(zhí)行順序按照任務(wù)的提交順序執(zhí)行。
newFixedThreadPool
創(chuàng)建固定大小的線程池。每次提交一個任務(wù)就創(chuàng)建一個線程,直到線程達到線程池的最大大小。線程池的大小一旦達到最大值就會保持不變,如果某個線程因為執(zhí)行異常而結(jié)束,那么線程池會補充一個新線程。
newCachedThreadPool
創(chuàng)建一個可緩存的線程池。如果線程池的大小超過了處理任務(wù)所需要的線程,那么就會回收部分空閑(60秒不執(zhí)行任務(wù))的線程,當任務(wù)數(shù)增加時,此線程池又可以智能的添加新線程來處理任務(wù)。此線程池不會對線程池大小做限制,線程池大小完全依賴于操作系統(tǒng)(或者說JVM)能夠創(chuàng)建的最大線程大小。
newScheduledThreadPool
創(chuàng)建一個大小無限的線程池。此線程池支持定時以及周期性執(zhí)行任務(wù)的需求。
返回目錄
內(nèi)存模型
Java內(nèi)存模型規(guī)定,對于多個線程共享的變量,存儲在主內(nèi)存當中,每個線程都有自己獨立的工作內(nèi)存,線程只能訪問自己的工作內(nèi)存,不可以訪問其它線程的 工作內(nèi)存。工作內(nèi)存中保存了主內(nèi)存共享變量的副本,線程要操作這些共享變量,只能通過操作工作內(nèi)存中的副本來實現(xiàn),操作完畢之后再同步回到主內(nèi)存當中。
如何保證多個線程操作主內(nèi)存的數(shù)據(jù)完整性是一個難題,Java內(nèi)存模型也規(guī)定了工作內(nèi)存與主內(nèi)存之間交互的協(xié)議,首先是定義了8種原子操作:
-
lock:將主內(nèi)存中的變量鎖定,為一個線程所獨占
-
unclock:將lock加的鎖定解除,此時其它的線程可以有機會訪問此變量
-
read:將主內(nèi)存中的變量值讀到工作內(nèi)存當中
-
load:將read讀取的值保存到工作內(nèi)存中的變量副本中。
-
use:將值傳遞給線程的代碼執(zhí)行引擎
-
assign:將執(zhí)行引擎處理返回的值重新賦值給變量副本
-
store:將變量副本的值存儲到主內(nèi)存中。
-
write:將store存儲的值寫入到主內(nèi)存的共享變量當中。
內(nèi)存組成
堆(Heap)
運行時數(shù)據(jù)區(qū)域,所有類實例和數(shù)組的內(nèi)存均從此處分配。Java虛擬機啟動時創(chuàng)建。對象的堆內(nèi)存由稱為垃圾回收器 的自動內(nèi)存管理系統(tǒng)回收。
-
News Generation(Young Generation即圖中的Eden + From Space + To Space)
-
Eden 存放新生的對象
-
Survivor Space 兩個 存放每次垃圾回收后存活的對象
-
-
Old Generation(Tenured Generation 即圖中的Old Space) 主要存放應(yīng)用程序中生命周期長的存活對象
非堆內(nèi)存
JVM具有一個由所有線程共享的方法區(qū)。方法區(qū)屬于非堆內(nèi)存。它存儲每個類結(jié)構(gòu),如運行時常數(shù)池、字段和方法數(shù)據(jù),以及方法和構(gòu)造方法的代碼。它是在Java虛擬機啟動時創(chuàng)建的。除了方法區(qū)外,Java虛擬機實現(xiàn)可能需要用于內(nèi)部處理或優(yōu)化的內(nèi)存,這種內(nèi)存也是非堆內(nèi)存。例如,JIT編譯器需要內(nèi)存來存儲從Java虛擬機代碼轉(zhuǎn)換而來的本機代碼,從而獲得高性能。
-
Permanent Generation? (圖中的Permanent Space)存放JVM自己的反射對象,比如類對象和方法對象
-
native heap
-
轉(zhuǎn)載于:https://www.cnblogs.com/davidwang456/p/5952738.html
總結(jié)
以上是生活随笔為你收集整理的纪念我曾经的 JAVA 姿势--转的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Introduction to the
- 下一篇: ultraedit中换行键的替换