JDK5新特性目錄導航:
- 自動拆裝箱
- Foreach
- 靜態導入
- 可變參數 Var args
- 枚舉
- 格式化輸出
- 泛型
- ProcessBuilder
- 內省
- 線程並發庫(JUC)
- 監控和管理虛擬機
- 元數據
自動拆裝箱
Java數據類型分兩種:基本數據類型 和 引用數據類型(對象)
有時候我們需要將基本數據類型包裝為對象進行處理
在JKD5以前我們的處理方式:
//int 轉換為 Integer int i = 10; Integer integer = new Integer(i); //Integer 轉換為 int Integer integer1 = new Integer(100); int i1 = integer1.intValue();
自動拆裝箱處理方式:
//int 轉換為 Integer Integer integer = 10; //Integer 轉換為 int int i = integer;
將class反編譯可以看出自動拆裝箱的代碼如下:
Integer integer = Integer.valueOf(10); int i = integer.intValue();
以上就是自動拆裝箱的效果,同理其余基本類型也可以自動裁裝箱對應的對象。詳細對應關系如下表:
基本數據類型 | 封裝類 |
int | Integer |
byte | Byte |
short | Short |
long | Long |
char | Character |
double | Double |
float | Float |
boolean | Boolean |
Foreach
增強for循環,新增一種循環語法,格式:for( : )
普通for與增強for循環對比如下:
List<String> list = new ArrayList<String>(); list.add("111"); list.add("222"); list.add("333"); //JDK5 以前循環需要定義下標“index”並初始化,判斷是否小於集合長度並自增,循環體還需要賦值 for (int index = 0; index < list.size(); index++ ) { String str = list.get(index); System.out.println("str: " + str); } //foreach 增強for循環只需要下面代碼即可完成上面的操作。 for (String str : list){ System.out.println("str:" + str); }
反編譯class文件可以看到增強for循環會被編譯器自動處理如下代碼:
Iterator var4 = list.iterator(); while(var4.hasNext()) { str = (String)var4.next(); System.out.println("str:" + str); }
具體編譯成什么類型還的根據循環數據實際的數據類型,例如:
//int數組 foreach int[] ints = new int[]{1, 2, 3, 4, 5}; for(int i : ints){ System.out.println("i: " + i); } //class反編譯結果 int[] ints = new int[]{1, 2, 3, 4, 5}; int[] var9 = ints; int var4 = ints.length; for(int var5 = 0; var5 < var4; ++var5) { int i = var9[var5]; System.out.println("i: " + i); }
從上面代碼可以大致了解foreach,它基本可以替換掉你所有用普通for循環的代碼。
靜態導入
靜態導入可以將靜態方法和靜態變量通過 import static 和導包一樣的方式直接導入,使用的時候無需使用類名即可直接訪問。
1 import static java.lang.System.out; 2 import static java.lang.Math.*; 3 4 public class ImportStaticTest { 5 6 public static void main(String[] args) { 7 /* 8 * 使用靜態導入 import static java.lang.System.out; 9 * out.println 可以直接使用調用 而不再需要類System對象去調用 10 * 同時也支持*通配符 11 */ 12 out.println("max(3,5): " + max(3,5)); 13 out.println("random(): " + random()); 14 } 15 }
輸出結果:
max(3,5): 5
random(): 0.7808341266194762
可變參數 Var args
當傳入到方法的參數不固定時,就可以使用可變參數 格式:數據類型... 參數名
1 public class VarArgsTest { 2 3 // Tips: 和以往main方式不一樣,一般這樣寫 public static void main(String[] args) 4 public static void main(String... args) { 5 varArgs(1); 6 varArgs(2,3); 7 8 // ... 9 10 varArgs(7,8,9,10,11); 11 } 12 13 // 可變參數的格式: 數據類型... 參數名 14 public static void varArgs(int... ints) { 15 for (int i : ints){ 16 System.out.println(i); 17 } 18 } 19 }
輸出結果:
1 2 3 7 8 9 10 11
枚舉
關鍵字enum表示枚舉類型,它的作用相當於類聲明中的class關鍵字,
注意事項:
1.不能含有public修飾的構造器,即構造器只能是private修飾,如果沒有構造器編譯器同樣也會自動生成一個帶private修飾無參默認構造器。;
2.所有的枚舉值默認都是public static final 修飾的;
3.枚舉值與值之間用逗號分割,最后一個用分號,如果枚舉值后面沒有任何東西該分號可以省略;
4.每一個枚舉值就是一個枚舉類型的實例;
5.枚舉類型中可以定義帶任意修飾符的非枚舉值變量;
6.枚舉值必須位於枚舉類型的第一位置,即非枚舉值必須位於枚舉值之后;
7.枚舉類型自帶兩個方法,values() 和 value(String name) 兩個方法。
枚舉代碼示例:
//定義枚舉類型 public enum SexEnum { MAN,WOMAN }
class反編譯結果:
public enum SexEnum { MAN, WOMAN; private SexEnum() { } }
枚舉類型方法默認方法示例:
1 public class Test { 2 public static void main(String[] args) { 3 4 SexEnum man = SexEnum.valueOf("MAN"); 5 System.out.println("man: " + man); 6 SexEnum woman = SexEnum.valueOf("WOMAN"); 7 System.out.println("woman: " + woman); 8 9 SexEnum[] sexEnums = SexEnum.values(); 10 for (SexEnum sex : sexEnums) { 11 System.out.println("SexEnum: " + sex); 12 } 13 } 14 }
輸出結果:
man: MAN
woman: WOMAN
SexEnum: MAN
SexEnum: WOMAN
格式化輸出
JDK5推出了printf-style格式字符串的解釋器 java.util.Formatter 工具類,和C語言的printf()有些類似。
簡單示例:
//創建對象時指定將結果輸出到控制台 Formatter formatter = new Formatter(System.out); formatter.format("x = %d , y = %s\n",1 , "test"); formatter.close();
輸出結果:
x = 1 , y = test
Formatter類可以將一些特定風格的字符轉換為另一種格式進行輸出,給出一下常用格式轉換。
d | 整數型 |
s | String |
f | 浮點數 |
c | Unicode字符 |
b | 布爾值 |
e | 科學計數 |
x | 整數(16進制) |
h | 散列碼(16進制) |
System.out.printf 和 System.out.foramt 方法的格式化輸出就是調用了Formatter工具類。其中System.out.printf 的源碼實際就是調用用了System.out.foramt方法。
System.out.printf 源碼如下:
public PrintStream printf(String format, Object ... args) { return format(format, args); }
泛型
泛型格式:<>
JDK5引入泛型是一個很大的功能增強,使用也比較廣泛。使用多態進行數據傳輸時,JDK5之前使用Object傳輸,然后進行向下轉型,這里可能在運行期強轉失敗拋ClassCastException異常,導致程序異常終止。引入泛型可以將此運行期異常轉移到編譯異常,在編寫代碼時就可以檢測出問題。
import java.util.ArrayList; import java.util.List; public class Test { public static void main(String[] args) { //此處只能在運行期報ClassCastException異常。 Object obj = new String(); Integer i = (Integer) obj; //泛型 List<String> list = new ArrayList<String>(); list.add("abc"); list.add("efg"); //此處編譯不通過,類型檢測不能通過,在編譯期就能解決錯誤。 // list.add(1); // list.add(false); // list.add(0.5); } }
泛型關鍵技術:
1.通配符問號(?)表示任意類型.如"List<?>"表示可以存放任意對象類型的List,和List<Object>一樣。
2.通配符可以連接限制符:extends 和 super
如:上邊界List<? extends Parent> 申明的List只能存放Parent及其子類,而下邊界 List<? super Child> 申明的List只能存放Child及其父類。
3.通用類型,通常使用一個大寫字母表示如:T(這里可以使用任意符合命名規則的字符都可以,不過我通常喜歡使用一個大寫字母表示),它能代表任何類型。
如果使用通用類型申明一個變量,那么必須在類申明后面加上<T>,尖括號里面的符號必須是前面申明的通用類型變量,如果有多個可以使用逗號分割如:<T,D>;
如果使用通用類型申明一個方法返回值或者方法參數,要么如上在類申明后加使用<>申明通用類型,要么在方法前申明通用類型。
1 //在類申明后申明通用類型T,則可以在變量、方法返回值和方法參數使用 2 public class Test<T> { 3 4 //在變量處使用通用類型,且並需在類申明后申明通用類型 5 T t; 6 //此處報錯因為,變量通用類型必須在類申明后申明 7 // E e; 8 9 //在方法返回值處使用通用類型 10 public T getT() { 11 return t; 12 } 13 14 //在方法參數使用通用類型 15 public String getType(T t) { 16 return t.getClass().getSimpleName(); 17 } 18 19 //方法返回值通用類型 和 方法參數通用類型 可以在方法前申明 20 public <E> E getE(E e) { 21 return e; 22 } 23 }
ProcessBuilder
ProcessBuilder可以用來創建操作系統進程,它的每一個實例管理着Process集合,start()方法可以創建一個新的Process實例
主要方法:
1.ProcessBuilder的start()方法:執行命令並返回一個Process對象;
2.ProcessBuilder的environment()方法:返回運行進程的環境變量Map<String,String>集合;
3.ProcessBuilder的directory()方法:返回工作目錄;
4.Process的getInputStream()方法:獲得進程的標准輸出流;
5.Process的getErrorStream()方法:獲得進程的錯誤輸出流。
演示代碼
import java.io.IOException; import java.io.InputStream; import java.util.Map; public class Test { public static void main(String[] args) { //創建進程 ProcessBuilder processBuilder = new ProcessBuilder("ipconfig","/all"); //獲取當前進程的環境變量 Map<String, String> map = processBuilder.environment(); Process process = null; try { //執行 ipconfig/all 命令並返回Process對象 process = processBuilder.start(); } catch (IOException e) { e.printStackTrace(); } //獲取進程標准輸出流 InputStream in = process.getInputStream(); StringBuffer sb = new StringBuffer(); int readbytes = -1; byte[] b = new byte[1024]; try{ while((readbytes = in.read(b)) != -1){ sb.append(new String(b,0,readbytes)); } }catch(IOException e1){ }finally { try{ in.close(); }catch (IOException e2){ } } System.out.println(sb.toString()); } }
內省
內省(Introspector) 是Java 語言對 JavaBean 類屬性、事件的一種缺省處理方法。JavaBean是一種特殊的類,主要用於傳遞數據信息,這種類中的方法主要用於訪問私有的字段,且方法名符合某種命名規則。如果在兩個模塊之間傳遞信息,可以將信息封裝進JavaBean中,這種對象稱為“值對象”(Value Object),或“VO”,這些信息儲存在類的私有變量中,通過set()、get()獲得,如下所示:
Person類示例:
public class Person { private String name; private int age; private String address; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } }
在類Person中有屬性name、age和address。通過 getName/setName來訪問name屬性,getAge/setAge來訪問age屬性,getAddress/setAddress來訪問address屬性,這是我們默認遵循的規則。Java JDK中提供了一套 API用來訪問某個屬性的 getter/setter 方法,這就是內省。
內省類庫:PropertyDescriptor類庫:
PropertyDescriptor類表示JavaBean類通過存儲器導出一個屬性。主要方法:
1.getPropertyType():獲得屬性的class對象;
2.getReadMeth():獲得讀取屬性值的方法,返回Method對象;
3.getWriteMethod():獲得寫入屬性值的方法,返回Method對象;
4.hashCode():獲得對象的哈希值;
5.setReadMethod(Method readMethod):設置讀取屬性值;
6.setWriteMethod(Method writeMethod):設置寫入屬性值。
//創建Person對象,並賦初始值 Person person = new Person(); person.setName("niannianjiuwang"); PropertyDescriptor propertyDescriptor = new PropertyDescriptor("name",Person.class); //獲得屬性的Class對象 System.out.println("Class: " + propertyDescriptor.getPropertyType().getSimpleName()); Method method = propertyDescriptor.getReadMethod(); System.out.println("Value: " + method.invoke(person)); System.out.println("HashCode: " + propertyDescriptor.hashCode());
線程並發庫(JUC)
JDK5提供了線程處理的高級功能,在(java.util.concurrent)包下。包括:
1.線程護斥:Lock 類、ReadWriteLock接口
Lock的方法:
ReadWriteLock的方法:
2.線程通信:Condition接口
Condition的方法:
3.線程池:ExecutorService接口
ExecutorService的方法:
4.同步隊列:ArrayBlockingQueue類
ArrayBlockingQueue的方法:
5.同步集合:ConcurrentHashMap類、CopyOnWriteArrayList類
ConcurrentHashMap相當於一個HashMap集合,但前者是線程安全的,所以性能上比后者略低。
CopyOnWriteArrayList相當於一個ArrayList集合,前者其所有可變操作(add和set等)都是通過對底層的數組進行一次復制來實現,所以代價非常昂貴。
6.線程同步工具:Semaphore類
Semaphore的方法:
關於JUC的並發庫類容非常的多,這里將不一一列舉。
監控和管理虛擬機
在JDK5中使用Bean監控和管理Java虛擬機,java.lang.management.ManagementFactory是管理Bean的工廠類,通過它的get系列方法能夠獲得不同的管理Bean的實例。
ManagementFactory的方法:
詳細講解以下幾個對象:
1.MemoryMXBean:該Bean用於管理Java虛擬機的內存系統,一個Java虛擬機具有一個實例。
2.ClassLoadingMXBean:該Bean用於管理Java虛擬機的類加載系統,一個Java虛擬機具有一個實例。
3.TreadMXBean:該Bean用於管理Java虛擬機的線程系統,一個Java虛擬機具有一個實例。
4.RuntimeMXBean:該Bean用於管理Java虛擬機的線程系統,一個Java虛擬機具有一個實例。
5.OperatingSystemMXBean:該Bean用於管理操作系統,一個Java虛擬機具有一個實例。
6.CompilationMXBean:該Bean用於管理Java虛擬機的編譯系統,一個Java虛擬機具有一個實例。
7.GarbageCollectorMXBean:該Bean用於管理Java虛擬機的垃圾回收系統,一個Java虛擬機具有一個或者多個實例。
演示代碼:
1 import java.lang.management.*; 2 import java.util.List; 3 4 public class Test { 5 public static void main(String[] args){ 6 //Java虛擬機的內存系統 7 MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean(); 8 System.out.println("虛擬機的堆內存使用量: " + memoryMXBean.getHeapMemoryUsage()); 9 System.out.println("虛擬機的非堆內存使用量: " + memoryMXBean.getNonHeapMemoryUsage()); 10 //Java虛擬機的類加載系統 11 ClassLoadingMXBean classLoadingMXBean = ManagementFactory.getClassLoadingMXBean(); 12 System.out.println("當前加載到Java虛擬機中的類的數量: " + classLoadingMXBean.getLoadedClassCount()); 13 System.out.println("自Java虛擬機開始執行到目前已經加載的類的總數: " + classLoadingMXBean.getTotalLoadedClassCount()); 14 //Java虛擬機的線程系統 15 ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); 16 System.out.println("當前線程的總CPU時間: " + threadMXBean.getCurrentThreadCpuTime()); 17 System.out.println("當前活動線程的數目,包括守護線程和非守護線程: " + threadMXBean.getThreadCount()); 18 //Java虛擬機的線程系統 19 RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean(); 20 System.out.println("當前Java庫路徑: " + runtimeMXBean.getLibraryPath()); 21 System.out.println("當前Java虛擬機實現提供商: " + runtimeMXBean.getVmVendor()); 22 //操作系統 23 OperatingSystemMXBean operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean(); 24 System.out.println("當前Java虛擬機可以使用的處理器數目: " + operatingSystemMXBean.getAvailableProcessors()); 25 System.out.println("當前操作系統名稱: " + operatingSystemMXBean.getName()); 26 //Java虛擬機的編譯系統 27 CompilationMXBean compilationMXBean = ManagementFactory.getCompilationMXBean(); 28 System.out.println("當前(JIT)編譯器的名稱: " + compilationMXBean.getName()); 29 System.out.println("當前即時(JIT)編譯器的名稱: " + compilationMXBean.getTotalCompilationTime()); 30 //Java虛擬機的垃圾回收系統 31 List<GarbageCollectorMXBean> garbageCollectorMXBeanList = ManagementFactory.getGarbageCollectorMXBeans(); 32 for (GarbageCollectorMXBean garbageCollectorMXBean : garbageCollectorMXBeanList) { 33 System.out.println("當前垃圾收集器的名字: " + garbageCollectorMXBean.getName()); 34 System.out.println("當前垃圾收集器累計回收總次數: " + garbageCollectorMXBean.getCollectionCount()); 35 System.out.println("當前垃圾收集器累計回收總時間: " + garbageCollectorMXBean.getCollectionTime()); 36 } 37 } 38 }
輸出結果:
虛擬機的堆內存使用量: init = 134217728(131072K) used = 4094288(3998K) committed = 128974848(125952K) max = 1900019712(1855488K) 虛擬機的非堆內存使用量: init = 2555904(2496K) used = 5223536(5101K) committed = 8060928(7872K) max = -1(-1K) 當前加載到Java虛擬機中的類的數量: 586 自Java虛擬機開始執行到目前已經加載的類的總數: 586 當前線程的總CPU時間: 546875000 當前活動線程的數目,包括守護線程和非守護線程: 6 當前Java庫路徑: D:\Program Files\Java\jdk1.8.0_121\bin;C:\Windows\Sun\Java\bin;C:\Windows\system32;C:\Windows;C:\ProgramData\Oracle\Java\javapath;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;D:\Program Files (x86)\IDM Computer Solutions\UltraEdit;D:\development\maven\apache-maven-3.5.2\bin;D:\Program Files (x86)\ClockworkMod\Universal Adb Driver;C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;D:\Program Files\Git\bin;. 當前Java虛擬機實現提供商: Oracle Corporation 當前Java虛擬機可以使用的處理器數目: 8 當前操作系統名稱: Windows 10 當前(JIT)編譯器的名稱: HotSpot 64-Bit Tiered Compilers 當前即時(JIT)編譯器的名稱: 99 當前垃圾收集器的名字: PS Scavenge 當前垃圾收集器累計回收總次數: 0 當前垃圾收集器累計回收總時間: 0 當前垃圾收集器的名字: PS MarkSweep 當前垃圾收集器累計回收總次數: 0 當前垃圾收集器累計回收總時間: 0
元數據
元數據也可以叫注解,這個名字估計容易理解,格式:@注解名
注解的作用范圍,可以通過java.lang.annotation.ElementType查看:
1.TYPE:類、接口(包括注釋類型)或enum聲明
2.FIELD:字段聲明(包括enum常量)
3.METHOD:方法申明
4.PARAMETER:參數申明
5.CONSTRUCTOR:構造器申明
6.LOCAL_VARIABLE:局部變量申明
7.ANNOTATION_TYPE:注解類型申明
8.PACKAGE:包申明
JDK內置三種標准注解:
@Override: 注解只能使用在方法上,表示當前的方法定義將覆蓋超類中的方法。如果你不小心拼寫錯誤,或者方法簽名對不上被覆蓋的方法,編譯器就會發出錯誤的提示
@Deprecated: 注解可使用在構造器、字段、局部變量、方法、包、類接口以及枚舉上,表示被棄用,不鼓勵使用,編譯器會發出警告信息。通常是因為它是危險的,或則因為有更好的選擇。
@SuppressWarnings:注解可以使用在構造器、字段、局部變量、方法、類接口以及枚舉上,必須指定value值,關閉不當的編譯器警告信息。告訴編譯器不提示某某警告信息。
注意的幾個問題:
1. 當注解的元素沒有默認值的時候,在使用的時候必須為其指定初始值
2. 如果注解元素有了初始值,那么在使用的時候可以為其賦新的值,否則將使用默認值
3. 一個較為特殊的情況:注解元素當且僅當其只有一個元素且名稱為value時,在使用該注解的時候為其賦值時可以不用寫屬性(元素)名稱
元注解:
java內置了4種元注解,元注解負責注解其它的注解,可以理解成java中用來注解Annotation的Annotation
@Retention: 保留策略,表示注解有多長保留,先了解JAVA文件的三個時期:SOURCE 源文件期(*.java文件) -> CLASS 編譯器編譯期(*.class文件) -> RUNTIME jvm運行時期。
@Target: 表示注解使用的上下文,TYPE、FIELD、METHOD、PARAMETER、CONSTRUCTOR、LOCAL_VARIABLE、ANNOTATION_TYPE和PACKAGE。詳細說明返回看注解的作用范圍。
@Documented: 表示將被javadoc記錄。
@Inherited: 表明注釋類型是自動繼承的。如果一個繼承的元注釋出現在注釋類型上聲明,用戶在一個類上查詢注釋類型聲明,類聲明沒有這種類型的注釋,然后該類的超類將自動被查詢注釋類型。這個過程將會重復直到這個注釋類型被找到,或者類層次結構的頂部(對象)是達到了。如果沒有超類有這種類型的注釋,那么查詢將表明的類沒有這樣的注釋。