Java JDK各個版本的新特性、屬性(JVM)

desaco發表於2016-02-25

三大Java虛擬機器:Oracle HotSpot, Oracle JRockit, IBM JVM.

Java-JDK各個版本的新特性- http://blog.csdn.net/shareus/article/details/50736159

JDK1.5,1.6,1.7,1.8,1.9的新特性- https://blog.csdn.net/qq_33204709/article/details/78948650

 

Java Specialists' Newsletter of  Dr Heinz Kabutz- http://www.javaspecialists.eu/about/heinz.jsp

Java 8無人談及的八大功能: http://www.cnblogs.com/lianghaoc/articles/5699871.html

  在Java 9之前,垃圾回收日誌並不是執行緒安全的,這意味著如果你使用了併發回收器的話,日誌有可能是損壞的。
  JDK、JRE、JVM的介紹:三者之間是包含關係,jdk包含jre,jre包含jvm。
  JVM是一種規範,可以使用軟體來實現,也可以使用硬體來實現,就是一個虛擬的用於執行bytecodes位元組碼的計算機。他也定義了指令集、暫存器集、結構棧、垃圾收集堆、記憶體區域。

 

  JVM負責將java位元組碼解釋執行,邊解釋邊執行,這樣,速度就會受到一定的影響。JAVA提供了另一種解釋執行的方法JIT(just in time),可以一次解釋完,再執行特定平臺上的機器碼,高階的JIT可以只能分析熱點程式碼,並將這些程式碼轉成本地機器碼,並將結果快取起來,下次直接從記憶體中呼叫,這樣就大大提高了執行JAVA程式碼的效率。這樣就實現了跨平臺、可移植的功能。

------------------------------

JDK doc - https://docs.oracle.com/javase/8/docs/technotes/tools/unix/toc.html

1.5
1.自動裝箱與拆箱:
2.列舉(常用來設計單例模式)
3.靜態匯入
4.可變引數
5.內省

1.6
1.Web服務後設資料
2.指令碼語言支援
3.JTable的排序和過濾
4.更簡單,更強大的JAX-WS
5.輕量級Http Server
6.嵌入式資料庫 Derby

1.7
1,switch中可以使用字串了
2.運用List<String> tempList = new ArrayList<>(); 即泛型例項化型別自動推斷
3.語法上支援集合,而不一定是陣列
4.新增一些取環境資訊的工具方法
5.Boolean型別反轉,空指標安全,參與位運算
6.兩個char間的equals 
7.安全的加減乘除 
8.map集合支援併發請求,且可以寫成 Map map = {name:"xxx",age:18};

1.8
1. 允許在介面中有預設方法實現
2. Lambda表示式
3. 函式式介面
4. 方法和建構函式引用
5. Lambda的範圍
6. 內建函式式介面
7. Streams
8. Parallel Streams
9. Map
10. 時間日期API
11. Annotations

1.9
1. Jigsaw 專案;模組化原始碼
2. 簡化程式API 
3. 輕量級 JSON API 
4. 錢和貨幣的API 
5. 改善鎖爭用機制
6. 程式碼分段快取
7. 智慧Java編譯, 第二階段
8. HTTP 2.0客戶端
9. Kulla計劃: Java的REPL實現

Java 9的14個新特性總結- http://geek.csdn.net/news/detail/196632

---------------------------------------------------------------------------

JDK1.5新特性:

1.自動裝箱與拆箱:

Integer iObj = 3;

System.out.println(iObj + 12);
   Integer i1 = 137(-128--127範圍時,為true);
   Integer i2 = 137(-128--127範圍時,為true);

   System.out.println(i1 == i2); //false,但是括號中時卻返回ture,原因是Integer採用的是享元模式

   Integer i3 = Integer.valueOf(213);
   Integer i4 = Integer.valueOf(213);
   System.out.println(i3==i4);//同上,另一種包裝形式

 

2.列舉(常用來設計單例模式)

public class EnumTest {


public static void main(String[] args) {
   WeekDay1 weekDay = WeekDay1.MON;
   System.out.println(weekDay.nextDay());
   WeekDay weekDay2 = WeekDay.FRI;
   System.out.println(weekDay2);
   System.out.println(weekDay2.name());
   System.out.println(weekDay2.ordinal()); 
   System.out.println(WeekDay.valueOf("SUN").toString());
   System.out.println(WeekDay.values().length);
   new Date(300){};
}

public enum WeekDay{

   SUN(1),MON(),TUE,WED,THI,FRI,SAT;
   private WeekDay(){System.out.println("first");}
   private WeekDay(int day){System.out.println("second");}
}

public enum TrafficLamp{
   RED(30){
    public TrafficLamp nextLamp(){
     return GREEN;
    }
   },
   GREEN(45){
    public TrafficLamp nextLamp(){
     return YELLOW;
    }   
   },
   YELLOW(5){
    public TrafficLamp nextLamp(){
     return RED;
    }   
   };
   public abstract TrafficLamp nextLamp();
   private int time;
   private TrafficLamp(int time){this.time = time;}
}
}

3.靜態匯入

import static java.lang.Math.*;

public class StaticImport {
public static void main(String[] args){
   int x = 1;
   try {
    x++;
   } finally {
    System.out.println("template");
   }
   System.out.println(x);
  
  
   System.out.println(max(3, 6));
   System.out.println(abs(3 - 6));
  
}
}
4.可變引數

public class VarableParameter {


public static void main(String[] args) {

   System.out.println(add(2,3));
   System.out.println(add(2,3,5));  
}


public static int add(int x,int... args){
   int sum = x;

  
   for(int arg : args){
    sum += arg;
   }
   return sum;
}

}
5.內省

ReflectPoint pt1 = new ReflectPoint(3,5);

BeanInfo beanInfo = Introspector.getBeanInfo(pt1.getClass());
   PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
   Object retVal = null;
   for(PropertyDescriptor pd : pds){
     Method methodGetX = pd.getReadMethod();
     retVal = methodGetX.invoke(pt1);

   }
   jdk1.6新特性:

1.Web服務後設資料

Java 裡的Web服務後設資料跟微軟的方案基本沒有語義上的區別,自從JDK5新增了後設資料功能(Annotation)之後,SUN幾乎重構了整個J2EE體 系, 由於變化很大,乾脆將名字也重構為Java EE, Java EE(當前版本為5.0)將後設資料納入很多規範當中,這其中就包括Web Services的相關規範, 加入後設資料之後的Web Services伺服器端程式設計模型就跟上面看到的C#片斷差不多了, 這顯然比以前的JAX-RPC程式設計模型簡單(當然, Axis的程式設計模型也很簡單).這裡要談的Web服務後設資料(JSR 181)只是Java Web 服務規範中的一個,它跟Common Annotations, JAXB2, StAX, SAAJ和JAX-WS等共同構成Java EE 5的Web Services技術堆疊.

package WebServices;

import java.io.File;
import java.io.IOException;
import javax.jws.Oneway;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.xml.ws.Endpoint;


@WebService(targetNamespace="http://blog.csdn.net/chinajash",serviceName="HelloService")
public class WSProvider {
     @WebResult(name="Greetings")//自定義該方法返回值在WSDL中相關的描述    
     @WebMethod
     public String sayHi(@WebParam(name="MyName") String name){
         return "Hi,"+name; //@WebParam是自定義引數name在WSDL中相關的描述
     }    
     @Oneway //表明該服務方法是單向的,既沒有返回值,也不應該宣告檢查異常
     @WebMethod(action="printSystemTime",operationName="printSystemTime")//自定義該方法在WSDL中相關的描述
     public void printTime(){
         System.out.println(System.currentTimeMillis());
     }
     public static void main(String[] args) {
         Thread wsPublisher = new Thread(new WSPublisher());
         wsPublisher.start();
     }    
     private static class WSPublisher implements Runnable{
         public void run() {
             //釋出WSProvider到http://localhost:8888/chinajash/WSProvider這個地址,之前必須呼叫wsgen命令
             //生成服務類WSProvider的支援類,命令如下:
             //wsgen -cp . WebServices.WSProvider
             Endpoint.publish("http://localhost:8888/chinajash/WSProvider",new WSProvider());
         }        
     }
}

如果想看到Web Services Engine生成的WSDL檔案是否遵守上面的後設資料, 我們沒有必要將上面的WSProvider部署到支援JSR-181的應用伺服器或Servlet形式的Web Services Engine,現在JDK6已經提供了一個很簡單的機制可以用來測試和釋出Web Services,下面講講如何在JDK6環境下發布Web Services和檢視生成的WSDL
1.將<JDK_HOME>/bin加入path環境變數
2.在命令列下切換當前目錄到WSProvider的class檔案所在的目錄,執行下面命令
wsgen -cp . WebServices.WSProvider
在這個例子中會生成以下3個類的原始碼檔案及class檔案
SayHi
SayHiResponse
PrintTime
3.執行如下程式碼釋出WSProvider到http://localhost:8888/chinajash/WSProvider,在這裡可以執行WSProvider類的main方法就可以
Endpoint.publish("http://localhost:8888/chinajash/WSProvider",new WSProvider());
4.在瀏覽器輸入http://localhost:8888/chinajash/WSProvider?wsdl就可以看到生成的WSDL檔案,為了節省篇幅,這裡就不把生成的WSDL檔案貼上了,大家可以自己動手試試.
2.指令碼語言支援

JDK6增加了對指令碼語言的支援(JSR 223), 原理上是將指令碼語言編譯成bytecode,這樣指令碼語言也能享用Java平臺的諸多優勢,包括可移植性,安全等,另外,由於現在是編譯成 bytecode後再執行,所以比原來邊解釋邊執行效率要高很多。加入對指令碼語言的支援後,對Java語言也提供了以下好處。
1、許多指令碼語言都有動態特性,比如,你不需要用一個變數之前先宣告它,你可以用一個變數存放完全不同型別的物件,你不需要做強制型別轉換,因為轉換都是自動的。現在Java語言也可以通過對指令碼語言的支援間接獲得這種靈活性。
2、 可以用指令碼語言快速開發產品原型,因為現在可以Edit-Run,而無需Edit-Compile-Run,當然,因為Java有非常好的IDE支援,我 們完全可以在IDE裡面編輯原始檔,然後點選執行(隱含編譯),以此達到快速開發原型的目的,所以這點好處基本上可以忽略。
3、通過引入指令碼語言可以輕鬆實現Java應用程式的擴充套件和自定義,我們可以把原來分佈在在Java應用程式中的配置邏輯,數學表示式和業務規則提取出來,轉用JavaScript來處理。

Sun的JDK6實現包含了一個基於Mozilla Rhino的 指令碼語言引擎,支援JavaScript,這並不是說明JDK6只支援JavaScript,任何第三方都可以自己實現一個JSR-223相容的指令碼引擎 使得JDK6支援別的指令碼語言,比如,你想讓JDK6支援Ruby,那你可以自己按照JSR 223 的規範實現一個Ruby的指令碼引擎類,具體一點,你需要實現javax.script.ScriptEngine(簡單起見,可以繼承 javax.script.AbstractScriptEngine)和javax.script.ScriptEngineFactory兩個介面。 當然,在你實現自己的指令碼語言引擎之前,先到scripting.dev.java.net project 這裡看看是不是有人已經幫你做了工作,這樣你就可以直接拿來用就行。

Scripting API

--------------------------------------------------------------------------------
Scripting API是用於在Java裡面編寫指令碼語言程式的API, 在Javax.script中可以找到Scripting API,我們就是用這個API來編寫JavaScript程式,這個包裡面有一個ScriptEngineManager類,它是使用Scripting API的入口,ScriptEngineManager可以通過jar服務發現(service discovery)機制尋找合適的指令碼引擎類(ScriptEngine),使用Scripting API的最簡單方式只需下面三步
1、建立一個ScriptEngineManager物件
2、通過ScriptEngineManager獲得ScriptEngine物件
3、用ScriptEngine的eval方法執行指令碼

下面是一個Hello World程式

public class HelloScript {public static void main(String[] args) throws Exception {         ScriptEngineManager factory = new ScriptEngineManager();//step 1         ScriptEngine engine = factory.getEngineByName("JavaScript");//Step 2             engine.eval_r("print('Hello, Scripting')");//Step 3     }     }執行上面程式,控制檯會輸出Hello, Scripting上面這個簡單的Scripting程式演示瞭如何在Java裡面執行指令碼語言,除此之外,我們還可以利用Scripting API實現以下功能1、暴露Java物件為指令碼語言的全域性變數2、在Java中呼叫指令碼語言的方法3、指令碼語言可以實現Java的介面4、指令碼語言可以像Java一樣使用JDK平臺下的類下面的類演示了以上4種功能package Scripting;import java.io.File;import javax.script.Invocable;import javax.script.ScriptEngine;import javax.script.ScriptEngineManager;import javax.script.ScriptException;public class ScriptingAPITester {     public static void main(String[] args) throws Exception {         ScriptEngineManager manager = new ScriptEngineManager();         ScriptEngine engine = manager.getEngineByName("JavaScript");         testScriptVariables(engine);//演示如何暴露Java物件為指令碼語言的全域性變數          testInvokeScriptMethod(engine);//演示如何在Java中呼叫指令碼語言的方法          testScriptInterface(engine);//演示指令碼語言如何實現Java的介面          testUsingJDKClasses(engine);//演示指令碼語言如何使用JDK平臺下的類     }         public static void testScriptVariables(ScriptEngine engine) throws ScriptException{         File file = new File("test.txt");         engine.put("f", file);         engine.eval_r("println('Total Space:'+f.getTotalSpace())");             }         public static void testInvokeScriptMethod(ScriptEngine engine) throws Exception{         String script = "function hello(name) { return 'Hello,' + name;}";         engine.eval_r(script);         Invocable inv = (Invocable) engine;         String res = (String)inv.invokeFunction("hello", "Scripting" );         System.out.println("res:"+res);     }         public static void testScriptInterface(ScriptEngine engine) throws ScriptException{         String script = "var obj = new Object(); obj.run = function() { println('run method called'); }";         engine.eval_r(script);         Object obj = engine.get("obj");         Invocable inv = (Invocable) engine;         Runnable r = inv.getInterface(obj,Runnable.class);         Thread th = new Thread(r);         th.start();     }         public static void testUsingJDKClasses(ScriptEngine engine) throws Exception{         //Packages是指令碼語言裡的一個全域性變數,專用於訪問JDK的package         String js = "function doSwing(t){var f=new Packages.javax.swing.JFrame(t);f.setSize(400,300);f.setVisible(true);}";         engine.eval_r(js);         Invocable inv = (Invocable) engine;         inv.invokeFunction("doSwing", "Scripting Swing" );     }}Scripting Tool

--------------------------------------------------------------------------------
SUN 提供的JDK6中有一個命令列工具??jrunscript,你可以在<JDK6_Home>/bin下面找到這個工 具,jrunscript是一個指令碼語言的解釋程式,它獨立於指令碼語言,但預設是用JavaScript,我們可以用jrunscript來測試自己寫的 指令碼語言是否正確,下面是一個在命令列執行jrunscript的簡單例子
jrunscript
js>println("Hello,JrunScript");
Hello,JrunScript
js>9*8
72.0
js>
3.JTable的排序和過濾

原來的JTable基本上是隻能顯示資料,在JDK6新增了對JTable的排序和過濾功能,下面程式碼演示了這兩個功能


public class JTableTester {
     static String data[][] = {
         {"China","Beijing","Chinese"},
         {"America","Washington","English"},
         {"Korea","Seoul","Korean"},
         {"Japan","Tokyo","Japanese"},
         {"France","Paris","French"},
         {"England","London","English"},
         {"Germany","Berlin","German"},
     };
     static String titles[] = {"Country","Capital","Language"};
     public static void main(String[] args) {        
         DefaultTableModel m = new DefaultTableModel(data,titles);
         JTable t = new JTable(m);
         final TableRowSorter sorter = new TableRowSorter(m); 
         t.setRowSorter(sorter); //為JTable設定排序器
        
         JScrollPane sPane = new JScrollPane();
         sPane.setViewportView(t);
        
         JPanel p = new JPanel();
         p.setLayout(new BoxLayout(p,BoxLayout.X_AXIS));
         JLabel l = new JLabel("Criteria:");
         final JTextField tf = new JTextField();
         JButton b = new JButton("Do Filter");
         p.add(l);
         p.add(tf);
         p.add(b);
         b.addActionListener(new ActionListener() {
             public void actionPerformed(ActionEvent e) {
                 if(tf.getText().length()==0){
                     sorter.setRowFilter(null);
                 }else{
                     sorter.setRowFilter(RowFilter.regexFilter(tf.getText()));//為JTable設定基於正規表示式的過濾條件
                 }
             }
         });
        
         JFrame f = new JFrame("JTable Sorting and Filtering");
         f.getContentPane().add(sPane,BorderLayout.CENTER);        
         f.getContentPane().add(p,BorderLayout.SOUTH);
         f.setSize(400,300);
         f.setVisible(true);
     }

執行上面程式,單擊JTable的某一個title,這個title對應的列就會按照升序/降序重新排列;在下面的Criteria文字框中輸入"ese",點選"Do Filter"按鈕,JTable將只顯示帶有"ese"字串的行,也就是China和Japan兩行,如果文字框裡面什麼都沒有,點選"Do Filter"按鈕,這時JTable會顯示所有的行。
4.更簡單,更強大的JAX-WS

JAX-WS2.0的來歷

--------------------------------------------------------------------------------
JAX-WS(JSR-224) 是Java Architecture for XML Web Services的縮寫,簡單說就是一種用Java和XML開發Web Services應用程式的框架, 目前版本是2.0, 它是JAX-RPC 1.1的後續版本, J2EE 1.4帶的就是JAX-RPC1.1, 而Java EE 5裡面包括了JAX-WS 2.0,但為了向後相容,仍然支援JAX-RPC. 現在,SUN又把JAX-WS直接放到了Java SE 6裡面,由於JAX-WS會用到Common Annotation(JSR 250),Java Web Services Metadata(JSR 181), JAXB2(JSR 222), StAX(JSR 173), 所以SUN也必須把後幾個原屬於Java EE範疇的Components下放到Java SE, 現在我們可以清楚地理解了為什麼Sun要把這些看似跟Java SE沒有關係的Components放進來,終極目的就是要在Java SE裡面支援Web Services.

JAX-WS2.0的架構

--------------------------------------------------------------------------------
JAX-WS不是一個孤立的框架,它依賴於眾多其他的規範,本質上它由以下幾部分組成
1.用來開發Web Services的Java API
2.用來處理Marshal/Unmarshal的XML Binding機制,JAX-WS2.0用JAXB2來處理Java Object與XML之間的對映,Marshalling就是把Java Object對映到XML,Unmarshalling則是把XML對映到Java Object.之所以要做Java Object與XML的對映,是因為最終作為方法引數和返回值的Java Object要通過網路傳輸協議(一般是SOAP)傳送,這就要求必須對Java Object做類似序列化和反序列化的工作,在SOAP中就是要用XML來表示Java object的內部狀態
3.眾多後設資料(Annotations)會被JAX-WS用來描述Web Services的相關類,包括Common Annotations, Web Services Metadata, JAXB2的後設資料和JAX-WS2.0規範自己的後設資料.
4.Annotation Processing Tool(APT) 是JAX-WS重要的組成部分,由於JAX-WS2.0規範用到很多後設資料,所以需要APT來處理眾多的Annotations. 在<JDK_HOME>/bin下有兩個命令wsgen和wsimport,就是用到APT和Compiler API來處理碰到的Annotations,wsgen可以為Web Services Provider產生並編譯必要的幫助類和相關支援檔案,wsimport以WSDL作為輸入為Web Service Consumer產生並編譯必要的幫助類和相關支援檔案.
5.JAX-WS還包括JAX-WS Runtime與應用伺服器和工具之間的契約關係

JAX-WS2.0的程式設計模型

--------------------------------------------------------------------------------
現在用JAX-WS2.0來編寫Web Services非常簡單,不像JAX-RPC,JAX-WS可以把任意POJO暴露為Web Services,服務類不需要實現介面,服務方法也沒有必要丟擲RMI異常.下面介紹在JDK6環境下用JAX-WS2.0開發和測試Web Services的步驟
1.編寫服務類,並用Web Services Metadata(JSR-181)標註這個服務類,我用我的另一篇BlogJDK6的新特性之十:Web服務後設資料中的WSProvider類作為服務類的例子,在此我重複貼一下WSProvider類的原始碼:

@WebService(targetNamespace="http://blog.csdn.net/chinajash",serviceName="HelloService")
public class WSProvider {
     @WebResult(name="Greetings")//自定義該方法返回值在WSDL中相關的描述    
     @WebMethod
     public String sayHi(@WebParam(name="MyName") String name){
         return "Hi,"+name; //@WebParam是自定義引數name在WSDL中相關的描述
     }    
     @Oneway //表明該服務方法是單向的,既沒有返回值,也不應該宣告檢查異常
     @WebMethod(action="printSystemTime",operationName="printSystemTime")//自定義該方法在WSDL中相關的描述
     public void printTime(){
         System.out.println(System.currentTimeMillis());
     }
     public static void main(String[] args) {
         Thread wsPublisher = new Thread(new WSPublisher());
         wsPublisher.start();
     }    
     private static class WSPublisher implements Runnable{
         public void run() {
             //釋出WSProvider到http://localhost:8888/chinajash/WSProvider這個地址,之前必須呼叫wsgen命令
             //生成服務類WSProvider的支援類,命令如下:
             //wsgen -cp . WebServices.WSProvider
             Endpoint.publish("http://localhost:8888/chinajash/WSProvider",new WSProvider());
         }        
     }
}
2.用wsgen生成上面服務類的必要的幫助類,然後呼叫用EndPoint類的靜態方法publish釋出服務類(步驟請參考我的另一篇Blog JDK6的新特性之十:Web服務後設資料),我在這裡是將服務類釋出到http://localhost:8888/chinajash/WSProvider
3.用wsimport為服務消費者(也就是服務的客戶端)生成必要的幫助類,命令如下:
wsimport http://localhost:8888/chinajash/WSProvider?wsdl
這會在<當前目錄>\net\csdn\blog\chinajash下生成客戶端的幫助類,在這個例子中會生成7個類
HelloService.class
ObjectFactory.class
package-info.class
PrintSystemTime.class
SayHi.class
SayHiResponse.class
WSProvider.class
4.在客戶端用下面程式碼即可呼叫步驟1定義的Web Service
HelloService hs = new HelloService();
WSProvider ws = hs.getWSProviderPort();
System.out.println(ws.sayHi("chinajash"));
ws.printSystemTime();
呼叫上述程式碼後客戶端控制檯輸出
hi,chinajash
服務端控制檯輸出伺服器當前系統時間
5.輕量級Http Server

JDK6的新特性之五:輕量級Http Server

JDK6提供了一個簡單的Http Server API,據此我們可以構建自己的嵌入式Http Server,它支援Http和Https協議,提供了HTTP1.1的部分實現,沒有被實現的那部分可以通過擴充套件已有的Http Server API 來實現,程式設計師必須自己實現HttpHandler介面,HttpServer會呼叫HttpHandler實現類的回撥方法來處理客戶端請求,在這裡, 我們把一個Http請求和它的響應稱為一個交換,包裝成HttpExchange類,HttpServer負責將HttpExchange傳給 HttpHandler實現類的回撥方法.下面程式碼演示了怎樣建立自己的Http Server


public class HTTPServerAPITester {
     public static void main(String[] args) {
         try {
             HttpServer hs = HttpServer.create(new InetSocketAddress(8888),0);//設定HttpServer的埠為8888
             hs.createContext("/chinajash", new MyHandler());//用MyHandler類內處理到/chinajash的請求
             hs.setExecutor(null); // creates a default executor
             hs.start();
         } catch (IOException e) {
             e.printStackTrace();
         }
     }
}

class MyHandler implements HttpHandler {
    public void handle(HttpExchange t) throws IOException {
        InputStream is = t.getRequestBody();
        String response = "<h3>Happy New Year 2007!--Chinajash</h3>";
        t.sendResponseHeaders(200, response.length());
        OutputStream os = t.getResponseBody();
        os.write(response.getBytes());
        os.close();
    }
}

執行程式後,在瀏覽器內輸入http://localhost:8888/xx,瀏覽器輸出
6.嵌入式資料庫 Derby

Derby是IBM送給開源社群的又一個禮物,是一個pure java的資料庫,現在已經被列入到java1.6中。
不知道對於大資料量的效能如何,但傳說中啟動derby只會給JVM新增2M的記憶體,對那些小資料庫應用,比如像用access那種應該是挺有誘惑力的。
另外,麻雀雖小,五臟俱全,功能要比access多得多咯,包括事務處理,併發,觸發器都有,管理又簡單,因此自己用來做點工具正好合適。
廢話少說,介紹一下我折騰了半天的經驗吧。
我的Derby配置過程:
1,下載db-derby-10.1.3.1-bin.tar.gz,derby_core_plugin_10.1.3.zip和derby_ui_plugin_1.1.0.zip,把兩個外掛安裝到eclipse上
2,開啟ecllipse,新建一個project
3,右鍵這個project,選擇Apache Derby,再選擇add apache derby native,發現只是給我的project新增了幾個derby的jar,還不是在我看著順眼的lib目錄裡,索性幹掉,換上db-derby- 10.1.3.1-bin.tar.gz解壓出來以後lib目錄下的jar檔案,在Build Path裡設定一下;
4,右鍵Project,在apache derby裡選擇start apache derby network server,控制檯可以看到derby啟動後打出的“伺服器準備在埠 1527 上接受連線。”
5,右鍵Project,在apache derby裡選擇ij(Interactive SQL),啟動SQL控制檯;
6,輸入connect jdbc:derby:testdb;create=true; 注意要有單引號,可以在工程跟目錄下建立testdb資料庫,可以看到一個新建的目錄testdb,那裡的檔案就是資料庫咯;
7,用標準的SQL語句來建一個資料庫試試:
create table test (a varchar(4) not null, b char(2) primary key);
居然可以用,太神奇了,呵呵
8,再插入一條語句試試呢,insert into test(a,b) values(a,11);,嗯,不錯,可以用select 查出來的哦。
9,再插一下:insert into test(a,b) values(a,11);,哦哦,報錯了,“錯誤 23505:語句異常終止,因為它導致“TEST”上所定義的“SQL060710092132480”標識的唯一或主鍵約束或唯一索引中出現重複鍵值。” 呵呵。
10,好了,現在可以像你控制的其他資料庫一樣來控制Derby了。


如果上述方法不行,或者你習慣了在eclipse之外使用和管理資料庫,那麼可以很方便的把Derby“裝”在系統裡。下面我說一下步驟:
1,把db-derby-10.1.3.1-bin.tar.gz解壓到c:\derby,使lib和framework兩個目錄在c:\derby下邊即可
2,設定環境變數

設定一個c:\derby\framework\embeded\bin或c:\derby\framework\NetworkServe\bin到Path中,這樣我們就可以直接執行上邊介紹的connect這樣的命令而不用每次鑽到那個目錄下去執行了 
設定c:\derby\lib\derby.jar;c:\derby\lib\derbytoos.jar到CLASSPATH中,以便讓這些java編成的命令能夠正確執行;
3,開啟cmd
4,敲入startNetworkServer,可以看到像在eclisp中提示的那樣啟動了server
5,再開啟一個cmd,敲入sysinfo,可以看到derby的環境資訊了,注意在java user dir這一項,也許是java使用者目錄上和上邊看到的會有所不同哦,這樣在connect jdbc:derby:testdb;create=true;的建的資料庫目錄就不一樣咯。
6,敲入ij,好了,進入到上邊的互動介面,可以建一個資料庫看看了。
7,最後在另外一個cmd中敲入stopNetworkServer就可以關閉資料庫了。

如果你兩種方法都試過了,那麼需要注意的,還是上邊步驟5的問題,這個問題是你可能隨時會啟動一個資料庫或新建一個資料庫,但如果你剛剛使用derby,你可能還沒有察覺。
derby實際上有兩種啟動方式,一種是嵌入式的,一種是網路伺服器的啟動。
1,我們在eclipse中右鍵start apache derby network server那個,就是網路伺服器的啟動方式,在這種方式下可以用另外一臺計算機在ij中以:
connect jdbc:derby://192.168.0.28:1527/testdb 
的方式進行連結。
2,第二種啟動方式是在ij裡邊就直接
connect jdbc:derby:testdb
這實際是在連當前配置環境下java user dir下那個目錄的資料庫。

看到這裡可能有點糊塗了,這麼就會出問題了那?
實際上derby的訪問更像是一種使用derby driver對本地檔案系統的訪問,不管啟動不啟動網路伺服器,都可以用driver訪問本地的資料庫。這樣,在ij裡邊像第二種方式那樣建立連線是完全可以的。啟動了網路伺服器,只不過是能夠讓其他主機訪問罷了。

另外一個問題是,在eclipse中和在系統中連線伺服器,在connect的時候這個當前配置環境是不一樣的,eclipse預設工程所在路徑是資料庫的所在路徑,而在系統中“裝”derby則會認為 c:\document and settings下邊那個使用者目錄是資料庫的所在路徑。
jdk1.7新特性:

1,switch中可以使用字串了
String s = "test";   
switch (s) {   
case "test" :   
     System.out.println("test"); 
case "test1" :   
    System.out.println("test1"); 
    break ;   
default :   
    System.out.println("break"); 
    break ;   
}

2.運用List<String> tempList = new ArrayList<>(); 即泛型例項化型別自動推斷
3.語法上支援集合,而不一定是陣列

final List<Integer> piDigits = [ 1,2,3,4,5,8 ];   
4.新增一些取環境資訊的工具方法

File System.getJavaIoTempDir() // IO臨時資料夾

File System.getJavaHomeDir() // JRE的安裝目錄

File System.getUserHomeDir() // 當前使用者目錄

File System.getUserDir() // 啟動java程式時所在的目錄5

5.Boolean型別反轉,空指標安全,參與位運算

Boolean Booleans.negate(Boolean booleanObj)

True => False , False => True, Null => Null

boolean Booleans.and(boolean[] array)

boolean Booleans.or(boolean[] array)

boolean Booleans.xor(boolean[] array)

boolean Booleans.and(Boolean[] array)

boolean Booleans.or(Boolean[] array)

boolean Booleans.xor(Boolean[] array)

6.兩個char間的equals 
boolean Character.equalsIgnoreCase(char ch1, char ch2)
7.安全的加減乘除 
int Math.safeToInt(long value)

int Math.safeNegate(int value)

long Math.safeSubtract(long value1, int value2)

long Math.safeSubtract(long value1, long value2)

int Math.safeMultiply(int value1, int value2)

long Math.safeMultiply(long value1, int value2)

long Math.safeMultiply(long value1, long value2)

long Math.safeNegate(long value)

int Math.safeAdd(int value1, int value2)

long Math.safeAdd(long value1, int value2)

long Math.safeAdd(long value1, long value2)

int Math.safeSubtract(int value1, int value2)

8.map集合支援併發請求,且可以寫成 Map map = {name:"xxx",age:18};

--------------------------------------------------------------------------------------------------------------

歡迎閱讀我編寫的Java 8介紹。本教程將帶領你一步一步地認識這門語言的新特性。通過簡單明瞭的程式碼示例,你將會學習到如何使用預設介面方法,Lambda表示式,方法引用和重複註解。看完這篇教程後,你還將對最新推出的API有一定的瞭解,例如:流控制,函式式介面,map擴充套件和新的時間日期API等等。

目 錄 [ - ]

  1. 允許在介面中有預設方法實現
  2. Lambda表示式
  3. 函式式介面
  4. 方法和建構函式引用
  5. Lambda的範圍
  6. 內建函式式介面
  7. Streams
  8. Parallel Streams
  9. Map
  10. 時間日期API
  11. Annotations
  12. 總結

允許在介面中有預設方法實現 Top

Java 8 允許我們使用default關鍵字,為介面宣告新增非抽象的方法實現。這個特性又被稱為擴充套件方法。下面是我們的第一個例子: 

Java程式碼

  1. interface Formula {  
  2.     double calculate(int a);  
  3.    
  4.     default double sqrt(int a) {  
  5.         return Math.sqrt(a);  
  6.     }  
  7. }  


在介面Formula中,除了抽象方法caculate以外,還定義了一個預設方法sqrt。Formula的實現類只需要實現抽象方法caculate就可以了。預設方法sqrt可以直接使用。 

Java程式碼

  1. Formula formula = new Formula() {  
  2.     @Override  
  3.     public double calculate(int a) {  
  4.         return sqrt(a * 100);  
  5.     }  
  6. };  
  7.    
  8. formula.calculate(100);     // 100.0  
  9. formula.sqrt(16);           // 4.0  


formula物件以匿名物件的形式實現了Formula介面。程式碼很囉嗦:用了6行程式碼才實現了一個簡單的計算功能:a*100開平方根。我們在下一節會看到,Java 8 還有一種更加優美的方法,能夠實現包含單個函式的物件。 

Lambda表示式 Top

讓我們從最簡單的例子開始,來學習如何對一個string列表進行排序。我們首先使用Java 8之前的方法來實現: 

Java程式碼

  1. List<String> names = Arrays.asList("peter""anna""mike""xenia");  
  2.    
  3. Collections.sort(names, new Comparator<String>() {  
  4.     @Override  
  5.     public int compare(String a, String b) {  
  6.         return b.compareTo(a);  
  7.     }  
  8. });  


靜態工具方法Collections.sort接受一個list,和一個Comparator介面作為輸入引數,Comparator的實現類可 以對輸入的list中的元素進行比較。通常情況下,你可以直接用建立匿名Comparator物件,並把它作為引數傳遞給sort方法。 

除了建立匿名物件以外,Java 8 還提供了一種更簡潔的方式,Lambda表示式。 

Java程式碼

  1. Collections.sort(names, (String a, String b) -> {  
  2.     return b.compareTo(a);  
  3. });  


你可以看到,這段程式碼就比之前的更加簡短和易讀。但是,它還可以更加簡短: 

Java程式碼

  1. Collections.sort(names, (String a, String b) -> b.compareTo(a));  


只要一行程式碼,包含了方法體。你甚至可以連大括號對{}和return關鍵字都省略不要。不過這還不是最短的寫法: 

Java程式碼

  1. Collections.sort(names, (a, b) -> b.compareTo(a));  


Java編譯器能夠自動識別引數的型別,所以你就可以省略掉型別不寫。讓我們再深入地研究一下lambda表示式的威力吧。 

函式式介面 Top

Lambda表示式如何匹配Java的型別系統?每一個lambda都能夠通過一個特定的介面,與一個給定的型別進行匹配。一個所謂的函式式介面必須要有 且僅有一個抽象方法宣告。每個與之對應的lambda表示式必須要與抽象方法的宣告相匹配。由於預設方法不是抽象的,因此你可以在你的函式式介面裡任意添 加預設方法。 

任意只包含一個抽象方法的介面,我們都可以用來做成lambda表示式。為了讓你定義的介面滿足要求,你應當在介面前加上@FunctionalInterface 標註。編譯器會注意到這個標註,如果你的介面中定義了第二個抽象方法的話,編譯器會丟擲異常。 

舉例: 

Java程式碼

  1. @FunctionalInterface  
  2. interface Converter<F, T> {  
  3.     T convert(F from);  
  4. }  
  5.    
  6. Converter<String, Integer> converter = (from) -> Integer.valueOf(from);  
  7. Integer converted = converter.convert("123");  
  8. System.out.println(converted);    // 123  


注意,如果你不寫@FunctionalInterface 標註,程式也是正確的。 
 

方法和建構函式引用 Top

上面的程式碼例項可以通過靜態方法引用,使之更加簡潔: 

Java程式碼

  1. Converter<String, Integer> converter = Integer::valueOf;  
  2. Integer converted = converter.convert("123");  
  3. System.out.println(converted);   // 123  


Java 8 允許你通過::關鍵字獲取方法或者建構函式的的引用。上面的例子就演示瞭如何引用一個靜態方法。而且,我們還可以對一個物件的方法進行引用: 

Java程式碼

  1. class Something {  
  2.     String startsWith(String s) {  
  3.         return String.valueOf(s.charAt(0));  
  4.     }  
  5. }  
  6.    
  7. Something something = new Something();  
  8. Converter<String, String> converter = something::startsWith;  
  9. String converted = converter.convert("Java");  
  10. System.out.println(converted);    // "J"  


讓我們看看如何使用::關鍵字引用建構函式。首先我們定義一個示例bean,包含不同的構造方法: 

Java程式碼

  1. class Person {  
  2.     String firstName;  
  3.     String lastName;  
  4.    
  5.     Person() {}  
  6.    
  7.     Person(String firstName, String lastName) {  
  8.         this.firstName = firstName;  
  9.         this.lastName = lastName;  
  10.     }  
  11. }  


接下來,我們定義一個person工廠介面,用來建立新的person物件: 

Java程式碼

  1. interface PersonFactory<P extends Person> {  
  2.     P create(String firstName, String lastName);  
  3. }  


然後我們通過建構函式引用來把所有東西拼到一起,而不是像以前一樣,通過手動實現一個工廠來這麼做。 

Java程式碼

  1. PersonFactory<Person> personFactory = Person::new;  
  2. Person person = personFactory.create("Peter""Parker");  


我們通過Person::new來建立一個Person類建構函式的引用。Java編譯器會自動地選擇合適的建構函式來匹配PersonFactory.create函式的簽名,並選擇正確的建構函式形式。 
 

Lambda的範圍 Top

對於lambdab表示式外部的變數,其訪問許可權的粒度與匿名物件的方式非常類似。你能夠訪問區域性對應的外部區域的區域性final變數,以及成員變數和靜態變數。 

訪問區域性變數 

我們可以訪問lambda表示式外部的final區域性變數: 

Java程式碼

  1. final int num = 1;  
  2. Converter<Integer, String> stringConverter =  
  3.         (from) -> String.valueOf(from + num);  
  4.    
  5. stringConverter.convert(2);     // 3  


但是與匿名物件不同的是,變數num並不需要一定是final。下面的程式碼依然是合法的: 

Java程式碼

  1. int num = 1;  
  2. Converter<Integer, String> stringConverter =  
  3.         (from) -> String.valueOf(from + num);  
  4.    
  5. stringConverter.convert(2);     // 3  


然而,num在編譯的時候被隱式地當做final變數來處理。下面的程式碼就不合法: 

Java程式碼

  1. int num = 1;  
  2. Converter<Integer, String> stringConverter =  
  3.         (from) -> String.valueOf(from + num);  
  4. num = 3;  


在lambda表示式內部企圖改變num的值也是不允許的。 

訪問成員變數和靜態變數 

與區域性變數不同,我們在lambda表示式的內部能獲取到對成員變數或靜態變數的讀寫權。這種訪問行為在匿名物件裡是非常典型的。 

Java程式碼

  1. class Lambda4 {  
  2.     static int outerStaticNum;  
  3.     int outerNum;  
  4.    
  5.     void testScopes() {  
  6.         Converter<Integer, String> stringConverter1 = (from) -> {  
  7.             outerNum = 23;  
  8.             return String.valueOf(from);  
  9.         };  
  10.    
  11.         Converter<Integer, String> stringConverter2 = (from) -> {  
  12.             outerStaticNum = 72;  
  13.             return String.valueOf(from);  
  14.         };  
  15.     }  
  16. }  


訪問預設介面方法 

還記得第一節裡面formula的那個例子麼? 介面Formula定義了一個預設的方法sqrt,該方法能夠訪問formula所有的物件例項,包括匿名物件。這個對lambda表示式來講則無效。 

預設方法無法在lambda表示式內部被訪問。因此下面的程式碼是無法通過編譯的: 

Java程式碼

  1. Formula formula = (a) -> sqrt( a * 100);  


 

內建函式式介面 Top

JDK 1.8 API中包含了很多內建的函式式介面。有些是在以前版本的Java中大家耳熟能詳的,例如Comparator介面,或者Runnable介面。對這些現 成的介面進行實現,可以通過@FunctionalInterface 標註來啟用Lambda功能支援。 

此外,Java 8 API 還提供了很多新的函式式介面,來降低程式設計師的工作負擔。有些新的介面已經在Google Guava庫中很有名了。如果你對這些庫很熟的話,你甚至閉上眼睛都能夠想到,這些介面在類庫的實現過程中起了多麼大的作用。 

Predicates 

Predicate是一個布林型別的函式,該函式只有一個輸入引數。Predicate介面包含了多種預設方法,用於處理複雜的邏輯動詞(and, or,negate): 

Java程式碼

  1. Predicate<String> predicate = (s) -> s.length() > 0;  
  2.    
  3. predicate.test("foo");              // true  
  4. predicate.negate().test("foo");     // false  
  5.    
  6. Predicate<Boolean> nonNull = Objects::nonNull;  
  7. Predicate<Boolean> isNull = Objects::isNull;  
  8.    
  9. Predicate<String> isEmpty = String::isEmpty;  
  10. Predicate<String> isNotEmpty = isEmpty.negate();  


Functions 

Function介面接收一個引數,並返回單一的結果。預設方法可以將多個函式串在一起(compse, andThen): 

Java程式碼

  1. Function<String, Integer> toInteger = Integer::valueOf;  
  2. Function<String, String> backToString = toInteger.andThen(String::valueOf);  
  3.    
  4. backToString.apply("123");     // "123"  


Suppliers 

Supplier介面產生一個給定型別的結果。與Function不同的是,Supplier沒有輸入引數。 

Java程式碼

  1. Supplier<Person> personSupplier = Person::new;  
  2. personSupplier.get();   // new Person  


Consumers 

Consumer代表了在一個輸入引數上需要進行的操作。 

Java程式碼

  1. Consumer<Person> greeter = (p) -> System.out.println("Hello, " + p.firstName);  
  2. greeter.accept(new Person("Luke""Skywalker"));  


Comparators 

Comparator介面在早期的Java版本中非常著名。Java 8 為這個介面新增了不同的預設方法。 

Java程式碼

  1. Comparator<Person> comparator = (p1, p2) -> p1.firstName.compareTo(p2.firstName);  
  2.    
  3. Person p1 = new Person("John""Doe");  
  4. Person p2 = new Person("Alice""Wonderland");  
  5.    
  6. comparator.compare(p1, p2);             // > 0  
  7. comparator.reversed().compare(p1, p2);  // < 0  


Optionals 

Optional不是一個函式式介面,而是一個精巧的工具介面,用來防止NullPointerEception產生。這個概念在下一節會顯得很重要,所以我們在這裡快速地瀏覽一下Optional的工作原理。 

Optional是一個簡單的值容器,這個值可以是null,也可以是non-null。考慮到一個方法可能會返回一個non-null的值,也可能返回一個空值。為了不直接返回null,我們在Java 8中就返回一個Optional。 

Java程式碼

  1. Optional<String> optional = Optional.of("bam");  
  2.    
  3. optional.isPresent();           // true  
  4. optional.get();                 // "bam"  
  5. optional.orElse("fallback");    // "bam"  
  6.    
  7. optional.ifPresent((s) -> System.out.println(s.charAt(0)));     // "b"  


 

Streams Top

java.util.Stream表示了某一種元素的序列,在這些元素上可以進行各種操作。Stream操作可以是中間操作,也可以是完結操作。完結操作 會返回一個某種型別的值,而中間操作會返回流物件本身,並且你可以通過多次呼叫同一個流操作方法來將操作結果串起來(就像StringBuffer的 append方法一樣————譯者注)。Stream是在一個源的基礎上建立出來的,例如java.util.Collection中的list或者 set(map不能作為Stream的源)。Stream操作往往可以通過順序或者並行兩種方式來執行。 

我們先了解一下序列流。首先,我們通過string型別的list的形式建立示例資料: 

Java程式碼

  1. List<String> stringCollection = new ArrayList<>();  
  2. stringCollection.add("ddd2");  
  3. stringCollection.add("aaa2");  
  4. stringCollection.add("bbb1");  
  5. stringCollection.add("aaa1");  
  6. stringCollection.add("bbb3");  
  7. stringCollection.add("ccc");  
  8. stringCollection.add("bbb2");  
  9. stringCollection.add("ddd1");  


Java 8中的Collections類的功能已經有所增強,你可以之直接通過呼叫Collections.stream()或者Collection.parallelStream()方法來建立一個流物件。下面的章節會解釋這個最常用的操作。 

Filter 

Filter接受一個predicate介面型別的變數,並將所有流物件中的元素進行過濾。該操作是一箇中間操作,因此它允許我們在返回結果的基 礎上再進行其他的流操作(forEach)。ForEach接受一個function介面型別的變數,用來執行對每一個元素的操作。ForEach是一個 中止操作。它不返回流,所以我們不能再呼叫其他的流操作。 

Java程式碼

  1. stringCollection  
  2.     .stream()  
  3.     .filter((s) -> s.startsWith("a"))  
  4.     .forEach(System.out::println);  
  5.    
  6. // "aaa2", "aaa1"  


Sorted 

Sorted是一箇中間操作,能夠返回一個排過序的流物件的檢視。流物件中的元素會預設按照自然順序進行排序,除非你自己指定一個Comparator介面來改變排序規則。 

Java程式碼

  1. stringCollection  
  2.     .stream()  
  3.     .sorted()  
  4.     .filter((s) -> s.startsWith("a"))  
  5.     .forEach(System.out::println);  
  6.    
  7. // "aaa1", "aaa2"  


一定要記住,sorted只是建立一個流物件排序的檢視,而不會改變原來集合中元素的順序。原來string集合中的元素順序是沒有改變的。 

Java程式碼

  1. System.out.println(stringCollection);  
  2. // ddd2, aaa2, bbb1, aaa1, bbb3, ccc, bbb2, ddd1  


Map 

map是一個對於流物件的中間操作,通過給定的方法,它能夠把流物件中的每一個元素對應到另外一個物件上。下面的例子就演示瞭如何把每個 string都轉換成大寫的string. 不但如此,你還可以把每一種物件對映成為其他型別。對於帶泛型結果的流物件,具體的型別還要由傳遞給map的泛型方法來決定。 

Java程式碼

  1. stringCollection  
  2.     .stream()  
  3.     .map(String::toUpperCase)  
  4.     .sorted((a, b) -> b.compareTo(a))  
  5.     .forEach(System.out::println);  
  6.    
  7. // "DDD2", "DDD1", "CCC", "BBB3", "BBB2", "AAA2", "AAA1"  


Match 

匹配操作有多種不同的型別,都是用來判斷某一種規則是否與流物件相互吻合的。所有的匹配操作都是終結操作,只返回一個boolean型別的結果。 

Java程式碼

  1. boolean anyStartsWithA =   
  2.     stringCollection  
  3.         .stream()  
  4.         .anyMatch((s) -> s.startsWith("a"));  
  5.    
  6. System.out.println(anyStartsWithA);      // true  
  7.    
  8. boolean allStartsWithA =   
  9.     stringCollection  
  10.         .stream()  
  11.         .allMatch((s) -> s.startsWith("a"));  
  12.    
  13. System.out.println(allStartsWithA);      // false  
  14.    
  15. boolean noneStartsWithZ =   
  16.     stringCollection  
  17.         .stream()  
  18.         .noneMatch((s) -> s.startsWith("z"));  
  19.    
  20. System.out.println(noneStartsWithZ);      // true  


Count 

Count是一個終結操作,它的作用是返回一個數值,用來標識當前流物件中包含的元素數量。 

Java程式碼

  1. long startsWithB =   
  2.     stringCollection  
  3.         .stream()  
  4.         .filter((s) -> s.startsWith("b"))  
  5.         .count();  
  6.    
  7. System.out.println(startsWithB);    // 3  


Reduce 

該操作是一個終結操作,它能夠通過某一個方法,對元素進行削減操作。該操作的結果會放在一個Optional變數裡返回。 

Java程式碼

  1. Optional<String> reduced =  
  2.     stringCollection  
  3.         .stream()  
  4.         .sorted()  
  5.         .reduce((s1, s2) -> s1 + "#" + s2);  
  6.    
  7. reduced.ifPresent(System.out::println);  
  8. // "aaa1#aaa2#bbb1#bbb2#bbb3#ccc#ddd1#ddd2"  


 

Parallel Streams Top

像上面所說的,流操作可以是順序的,也可以是並行的。順序操作通過單執行緒執行,而並行操作則通過多執行緒執行。 

下面的例子就演示瞭如何使用並行流進行操作來提高執行效率,程式碼非常簡單。 

首先我們建立一個大的list,裡面的元素都是唯一的: 

Java程式碼

  1. int max = 1000000;  
  2. List<String> values = new ArrayList<>(max);  
  3. for (int i = 0; i < max; i++) {  
  4.     UUID uuid = UUID.randomUUID();  
  5.     values.add(uuid.toString());  
  6. }  


現在,我們測量一下對這個集合進行排序所使用的時間。 

順序排序 

Java程式碼

  1. long t0 = System.nanoTime();  
  2.    
  3. long count = values.stream().sorted().count();  
  4. System.out.println(count);  
  5.    
  6. long t1 = System.nanoTime();  
  7.    
  8. long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);  
  9. System.out.println(String.format("sequential sort took: %d ms", millis));  
  10.    
  11. // sequential sort took: 899 ms  


並行排序 

Java程式碼

  1. long t0 = System.nanoTime();  
  2.    
  3. long count = values.parallelStream().sorted().count();  
  4. System.out.println(count);  
  5.    
  6. long t1 = System.nanoTime();  
  7.    
  8. long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);  
  9. System.out.println(String.format("parallel sort took: %d ms", millis));  
  10.    
  11. // parallel sort took: 472 ms  


如你所見,所有的程式碼段幾乎都相同,唯一的不同就是把stream()改成了parallelStream(), 結果並行排序快了50%。 
 

Map Top

正如前面已經提到的那樣,map是不支援流操作的。而更新後的map現在則支援多種實用的新方法,來完成常規的任務。 

Java程式碼

  1. Map<Integer, String> map = new HashMap<>();  
  2.    
  3. for (int i = 0; i < 10; i++) {  
  4.     map.putIfAbsent(i, "val" + i);  
  5. }  
  6.    
  7. map.forEach((id, val) -> System.out.println(val));  


上面的程式碼風格是完全自解釋的:putIfAbsent避免我們將null寫入;forEach接受一個消費者物件,從而將操作實施到每一個map中的值上。 

下面的這個例子展示瞭如何使用函式來計算map的編碼: 

Java程式碼

  1. map.computeIfPresent(3, (num, val) -> val + num);  
  2. map.get(3);             // val33  
  3.    
  4. map.computeIfPresent(9, (num, val) -> null);  
  5. map.containsKey(9);     // false  
  6.    
  7. map.computeIfAbsent(23, num -> "val" + num);  
  8. map.containsKey(23);    // true  
  9.    
  10. map.computeIfAbsent(3, num -> "bam");  
  11. map.get(3);             // val33  


接下來,我們將學習,當給定一個key值時,如何把一個例項從對應的key中移除: 

Java程式碼

  1. map.remove(3"val3");  
  2. map.get(3);             // val33  
  3.    
  4. map.remove(3"val33");  
  5. map.get(3);             // null  


另一個有用的方法: 

Java程式碼

  1. map.getOrDefault(42"not found");  // not found  


將map中的例項合併也是非常容易的: 

Java程式碼

  1. map.merge(9"val9", (value, newValue) -> value.concat(newValue));  
  2. map.get(9);             // val9  
  3.    
  4. map.merge(9"concat", (value, newValue) -> value.concat(newValue));  
  5. map.get(9);             // val9concat  


合併操作先看map中是否沒有特定的key/value存在,如果是,則把key/value存入map,否則merging函式就會被呼叫,對現有的數值進行修改。 
 

時間日期API Top

Java 8 包含了全新的時間日期API,這些功能都放在了java.time包下。新的時間日期API是基於Joda-Time庫開發的,但是也不盡相同。下面的例子就涵蓋了大多數新的API的重要部分。 

Clock 

Clock提供了對當前時間和日期的訪問功能。Clock是對當前時區敏感的,並可用於替代 System.currentTimeMillis()方法來獲取當前的毫秒時間。當前時間線上的時刻可以用Instance類來表示。Instance 也能夠用於建立原先的java.util.Date物件。 

Java程式碼

  1. Clock clock = Clock.systemDefaultZone();  
  2. long millis = clock.millis();  
  3.    
  4. Instant instant = clock.instant();  
  5. Date legacyDate = Date.from(instant);   // legacy java.util.Date  


Timezones 

時區類可以用一個ZoneId來表示。時區類的物件可以通過靜態工廠方法方便地獲取。時區類還定義了一個偏移量,用來在當前時刻或某時間與目標時區時間之間進行轉換。 

Java程式碼

  1. System.out.println(ZoneId.getAvailableZoneIds());  
  2. // prints all available timezone ids  
  3.    
  4. ZoneId zone1 = ZoneId.of("Europe/Berlin");  
  5. ZoneId zone2 = ZoneId.of("Brazil/East");  
  6. System.out.println(zone1.getRules());  
  7. System.out.println(zone2.getRules());  
  8.    
  9. // ZoneRules[currentStandardOffset=+01:00]  
  10. // ZoneRules[currentStandardOffset=-03:00]  


LocalTime 

本地時間類表示一個沒有指定時區的時間,例如,10 p.m.或者17:30:15,下面的例子會用上面的例子定義的時區建立兩個本地時間物件。然後我們會比較兩個時間,並計算它們之間的小時和分鐘的不同。 

Java程式碼

  1. LocalTime now1 = LocalTime.now(zone1);  
  2. LocalTime now2 = LocalTime.now(zone2);  
  3.    
  4. System.out.println(now1.isBefore(now2));  // false  
  5.    
  6. long hoursBetween = ChronoUnit.HOURS.between(now1, now2);  
  7. long minutesBetween = ChronoUnit.MINUTES.between(now1, now2);  
  8.    
  9. System.out.println(hoursBetween);       // -3  
  10. System.out.println(minutesBetween);     // -239  


LocalTime是由多個工廠方法組成,其目的是為了簡化對時間物件例項的建立和操作,包括對時間字串進行解析的操作。 

Java程式碼

  1. LocalTime late = LocalTime.of(235959);  
  2. System.out.println(late);       // 23:59:59  
  3.    
  4. DateTimeFormatter germanFormatter =  
  5.     DateTimeFormatter  
  6.         .ofLocalizedTime(FormatStyle.SHORT)  
  7.         .withLocale(Locale.GERMAN);  
  8.    
  9. LocalTime leetTime = LocalTime.parse("13:37", germanFormatter);  
  10. System.out.println(leetTime);   // 13:37  


LocalDate 

本地時間表示了一個獨一無二的時間,例如:2014-03-11。這個時間是不可變的,與LocalTime是同源的。下面的例子演示瞭如何通過加減日,月,年等指標來計算新的日期。記住,每一次操作都會返回一個新的時間物件。 

Java程式碼

  1. LocalDate today = LocalDate.now();  
  2. LocalDate tomorrow = today.plus(1, ChronoUnit.DAYS);  
  3. LocalDate yesterday = tomorrow.minusDays(2);  
  4.    
  5. LocalDate independenceDay = LocalDate.of(2014, Month.JULY, 4);  
  6. DayOfWeek dayOfWeek = independenceDay.getDayOfWeek();  
  7. System.out.println(dayOfWeek);    // FRIDAY<span style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; font-size: 13px; line-height: 19px;">Parsing a LocalDate from a string is just as simple as parsing a LocalTime:</span>  


解析字串並形成LocalDate物件,這個操作和解析LocalTime一樣簡單。 

Java程式碼

  1. DateTimeFormatter germanFormatter =  
  2.     DateTimeFormatter  
  3.         .ofLocalizedDate(FormatStyle.MEDIUM)  
  4.         .withLocale(Locale.GERMAN);  
  5.    
  6. LocalDate xmas = LocalDate.parse("24.12.2014", germanFormatter);  
  7. System.out.println(xmas);   // 2014-12-24  


LocalDateTime 

LocalDateTime表示的是日期-時間。它將剛才介紹的日期物件和時間物件結合起來,形成了一個物件例項。LocalDateTime是不可變的,與LocalTime和LocalDate的工作原理相同。我們可以通過呼叫方法來獲取日期時間物件中特定的資料域。 

Java程式碼

  1. LocalDateTime sylvester = LocalDateTime.of(2014, Month.DECEMBER, 31235959);  
  2.    
  3. DayOfWeek dayOfWeek = sylvester.getDayOfWeek();  
  4. System.out.println(dayOfWeek);      // WEDNESDAY  
  5.    
  6. Month month = sylvester.getMonth();  
  7. System.out.println(month);          // DECEMBER  
  8.    
  9. long minuteOfDay = sylvester.getLong(ChronoField.MINUTE_OF_DAY);  
  10. System.out.println(minuteOfDay);    // 1439  


如果再加上的時區資訊,LocalDateTime能夠被轉換成Instance例項。Instance能夠被轉換成以前的java.util.Date物件。 

Java程式碼

  1. Instant instant = sylvester  
  2.         .atZone(ZoneId.systemDefault())  
  3.         .toInstant();  
  4.    
  5. Date legacyDate = Date.from(instant);  
  6. System.out.println(legacyDate);     // Wed Dec 31 23:59:59 CET 2014  


格式化日期-時間物件就和格式化日期物件或者時間物件一樣。除了使用預定義的格式以外,我們還可以建立自定義的格式化物件,然後匹配我們自定義的格式。 

Java程式碼

  1. DateTimeFormatter formatter =  
  2.     DateTimeFormatter  
  3.         .ofPattern("MMM dd, yyyy - HH:mm");  
  4.    
  5. LocalDateTime parsed = LocalDateTime.parse("Nov 03, 2014 - 07:13", formatter);  
  6. String string = formatter.format(parsed);  
  7. System.out.println(string);     // Nov 03, 2014 - 07:13  


不同於java.text.NumberFormat,新的DateTimeFormatter類是不可變的,也是執行緒安全的。 

更多的細節,請看這裡 
 

Annotations Top

Java 8中的註解是可重複的。讓我們直接深入看看例子,弄明白它是什麼意思。 

首先,我們定義一個包裝註解,它包括了一個實際註解的陣列 

Java程式碼

  1. @interface Hints {  
  2.     Hint[] value();  
  3. }  
  4.    
  5. @Repeatable(Hints.class)  
  6. @interface Hint {  
  7.     String value();  
  8. }  


只要在前面加上註解名:@Repeatable,Java 8 允許我們對同一型別使用多重註解: 

變體1:使用註解容器(老方法): 

Java程式碼

  1. @Hints({@Hint("hint1"), @Hint("hint2")})  
  2. class Person {}  


變體2:使用可重複註解(新方法): 

Java程式碼

  1. @Hint("hint1")  
  2. @Hint("hint2")  
  3. class Person {}  


使用變體2,Java編譯器能夠在內部自動對@Hint進行設定。這對於通過反射來讀取註解資訊來說,是非常重要的。 

Java程式碼

  1. Hint hint = Person.class.getAnnotation(Hint.class);  
  2. System.out.println(hint);                   // null  
  3.    
  4. Hints hints1 = Person.class.getAnnotation(Hints.class);  
  5. System.out.println(hints1.value().length);  // 2  
  6.    
  7. Hint[] hints2 = Person.class.getAnnotationsByType(Hint.class);  
  8. System.out.println(hints2.length);          // 2  


儘管我們絕對不會在Person類上宣告@Hints註解,但是它的資訊仍然可以通過getAnnotation(Hints.class)來讀 取。並且,getAnnotationsByType方法會更方便,因為它賦予了所有@Hints註解標註的方法直接的訪問許可權。 

Java程式碼

  1. @Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})  
  2. @interface MyAnnotation {}  


 

總結 Top

Java 8程式設計指南就到此告一段落。當然,還有很多內容需要進一步研究和說明。這就需要靠讀者您來對JDK 8進行探究了,例如:Arrays.parallelSort, StampedLock和CompletableFuture等等 ———— 我這裡只是舉幾個例子而已。 

我希望這個博文能夠對您有所幫助,也希望您閱讀愉快。完整的教程原始碼放在了GitHub上。您可以盡情地fork,並請通過Twitter告訴我您的反饋。 

原文連結: winterbe 翻譯: ImportNew.com 黃小非 
譯文連結: http://www.importnew.com/10360.html

------------------------------------------------------------------------------------------------------------------------------------

加快OpenJDK的開發速度:繼2014年3月份釋出了Java 8之後,我們進入下一個兩年的釋出週期。 Java 9預計在2016年釋出,並且已經公佈了JEP(JDK改進提議)中的前期列表。同時,我們已經把一些新特性整理到了JSR(Java規範請求),還有提 出了一些希望包括在新版本中的其他特性。

 

這些重要的特性都包括在Jigsaw專案中。顯著的效能改善和期待已久的API包括:程式API更新,JSON將成為java.util的一部分,貨幣處理API對於想處在技術最前沿的你,可從這裡獲得Java 9的初期版本。

 

被接受的特性

1. Jigsaw 專案;模組化原始碼

Jigsaw專案是為了模組化Java程式碼、將JRE分成可相互協作的元件,這也是Java 9 眾多特色種的一個。JEP是邁向Jigsaw四步中的第一步,它不會改變JRE和JDK的真實結構。JEP是為了模組化JDK原始碼,讓編譯系統能夠模組 編譯並在構建時檢查模組邊界。這個專案原本是隨Java 8釋出的,但由於推遲,所以將把它加到Java 9.

 

一旦它完成,它可能允許根據一個專案需求自定義元件從而減少rt.jar的大小。在JDK 7 和JDK 8的rt.jar包中有大約20,000個類,但有很多類在一些特定的環境裡面並沒有被用到(即使在Java 8的緊湊分佈特性中已經包含了一部分解決方法也存在著類冗餘)。這麼做是為了能讓Java能夠容易應用到小型計算裝置(比如網路裝置)中,提高它的安全和 效能,同時也能讓開發者更容易構建和維護這些類庫。

 

2. 簡化程式API 

截止到目前,Java控制與管理系統程式的能力是有限的。舉個例子,現在為了簡便獲取你程式的程式PID,你要麼呼叫本地程式要麼要自己使用一些變通方案。更多的是,每個(系統)平臺需要有一個不同實現來確保你能獲得正確的結果。 

期望程式碼能獲取Linux PIDS,現在是如下方式: 



 

在Java 9中,可以變換成如下方式(支援所有的作業系統): 



 

這次更新將會擴充套件Java與作業系統的互動能力:新增一些新的直接明瞭的方法去處理PIDs,程式名字和狀態以及列舉多個JVM和程式以及更多事情。 

 

3. 輕量級 JSON API 

目前有多種處理JSON的Java工具,但JSON API 獨到之處在於JSON API將作為Java語言的一部分,輕量並且運用Java 8的新特性。它將放在java.util包裡一起釋出(但在JSR 353裡面的JSON是用第三方包或者其他的方法處理的). 

 

4. 錢和貨幣的API 

在Java 8引進了日期和時間的API之後, Java 9引入了新的貨幣API, 用以表示貨幣, 支援幣種之間的轉換和各種複雜運算. 關於這個專案的具體情況, 請訪問https://github.com/JavaMoney,裡面已經給出了使用說明和示例, 以下是幾個重要的例子: 



 

更多關於 JSR 354的內容 

 

5. 改善鎖爭用機制

鎖爭用是限制許多Java多執行緒應用效能的瓶頸. 新的機制在改善Java物件監視器的效能方面已經得到了多種基準(benchmark)的驗證, 其中包括Volano. 測試中通訊伺服器開放了海量的程式來連線客戶端, 其中有很多連線都申請同一個資源, 以此模擬重負荷日常應用.

 

通過諸如此類的壓力測試我們可以估算JVM的極限吞吐量(每秒的訊息數量). JEP在22種不同的測試中都得到了出色的成績, 新的機制如果能在Java 9中得到應用的話, 應用程式的效能將會大大提升.

關於JEP 143的更多內容 

 

6. 程式碼分段快取

Java 9的另一個效能提升來自於JIT(Just-in-time)編譯器. 當某段程式碼被大量重複執行的時候, 虛擬機器會把這段程式碼編譯成機器碼(native code)並儲存在程式碼快取裡面, 進而通過訪問快取中不同分段的程式碼來提升編譯器的效率.

 

和原來的單一快取區域不同的是, 新的程式碼快取根據程式碼自身的生命週期而分為三種:

  • 永駐程式碼(JVM 內建 / 非方法程式碼)
  • 短期程式碼(僅在某些條件下適用的配置性(profiled)程式碼)
  • 長期程式碼(非配置性程式碼)

快取分段會在各個方面提升程式的效能, 比如做垃圾回收掃描的時候可以直接跳過非方法程式碼(永駐程式碼), 從而提升效率.

更多關於JEP 197的內容 

 

7. 智慧Java編譯, 第二階段

智慧Java編譯工具sjavac的第一階段開始於JEP 139這個專案, 用於在多核處理器上提升JDK的編譯速度. 現在這個專案已經進入第二階段(JEP 199), 目的是改進sjavac並讓其成為取代目前JDK編譯工具javac的Java預設的通用編譯工具.

 

其他值得期待的內容:

8. HTTP 2.0客戶端

HTTP 2.0標準雖然還沒正式釋出, 但是已經進入了最終審查階段, 預計可以在Java 9釋出之前審查完畢. JEP 110將會重新定義並實現一個全新的Java HTTP客戶端, 用來取代現在的HttpURLConnection, 同時也會實現HTTP 2.0和網路介面(原文websockets). 它現在還沒被JEP正式認可但我們希望在Java 9中包含這一專案的內容.

官方的HTTP 2.0 RFC(Request for Comments, 官方技術討論/會議記錄等等的一系列文件記錄)預訂於2015年2月釋出, 它是基於Google釋出的SPDY(Speedy, 快速的)協議. 基於SPDY協議的網路相對於基於HTTP 1.1協議的網路有11.81%到47.7%之間的顯著提速, 現在已經有瀏覽器實現了這個協議.

 

9. Kulla計劃: Java的REPL實現

這個取名為Kulla的專案最近宣佈將於2015年4月整合測試, 雖然已經不太有希望能趕上Java 9的釋出, 但如果進度快的話或許剛好能趕上. 現在Java並沒有來自官方的REPL(Read-Eval-Print-Loop)方式, 也就是說現在如果你想要跑幾行Java程式碼做一個快速的測試, 你仍然需要把這幾行程式碼封裝在專案或者方法裡面. 雖然在一些流行的IDE裡面有Java REPL工具, 但它們並沒有官方支援, 而Kulla專案或許就能成為Java官方釋出的REPL解決方案. 

更多關於Kulla計劃的內容 

 

這些新功能出自何處?

 

JEP和JSR並不是無中生有,下面就介紹一下Java發展的生態環境:

小組 - 對特定技術內容, 比如安全、網路、Swing、HotSpot、有共同興趣的組織和個人。

 

專案 - 編寫程式碼, 文件以及其他工作,至少由一個小組贊助和支援,比如最近的Lambda計劃,Jigsaw計劃和Sumatra計劃。

 

JDK改進提案(JEP) - 每當需要有新的嘗試的時候, JEP可以在JCP(Java Community Process)之前或者同時提出非正式的規範(specification),被正是認可的JEP正式寫進JDK的發展路線圖並分配版本號。

 

Java規範提案(JSR) - 新特性的規範出現在這一個階段,可以來自於小組 / 專案、JEP、 JCP成員或者Java社群(community)成員的提案,每個Java版本都由相應的JSR支援, Java 9暫時還沒有。

相關文章