在本文(PART I)中,我們將看到Java 7 Project Coin(JSR 334)中提出的一些小改動,然后(在第二部分中)我們將對它們進行反編譯,以查看編譯器在做什么(對于僅用于教育目的)。
你需要什么
- NetBeans 7+或任何其他支持Java 7的IDE
- JSDK 7+
- JAD反編譯 [R 或 Java的反編譯
鉆石算子
泛型幫助我們減少了ClassCastExceptions,但是有時它會使代碼難以閱讀。 鉆石算子是一個非常不錯的變化。 成像您需要按城市對客戶進行分組。 您將需要以下內容:
//suppose the classes City and Customer exist
...
Map<City,List<Customer>> map = new
HashMap<City,List<Customer>>();
...
現在,如果您還需要按國家/地區對數據進行分組怎么辦:
//suppose the classes Country, City and Customer exist
...
Map<Country,MapltCity,List<Customer>>> map = new HashMapl&t;Country,MapltCity,ListltCustomer>>>();
...
現在,它開始變得很難閱讀,對吧? 如果您還需要按地區分組怎么辦?
//suppose the classes Region, Country, City and Customer exist
...
Map<Region,Map<Country,Map<City,List<Customer>>>> map = new HashMap<Region, Map<Country,Map<City,List<Customer>>>>();
...
所以你怎么看? 讀取這樣的代碼根本不容易。 幸運的是,新的Diamond運算符對代碼的可讀性有很大幫助。 最后的代碼可以在Java 7中重新編碼,如下所示:
//suppose the classes Region, Country, City and Customer exist
...
Map<Region,Map<Country,Map<City,List<Customer>>>> map = new HashMap<>();
...
好了很多!
開關中的弦
我已經等了很多!!! 我記得我剛開始Java時代的日子,我確實需要在switch中使用Strings。 好吧,我的等待終于結束了。 在Java的早期版本中,您必須編寫如下代碼:
//in a class
...
public void stringToNumber(String str)
{if(str.equalsIgnoreCase("one")){System.out.println(1);}else if(str.equalsIgnoreCase("two")){System.out.println(2);}else if(str.equalsIgnoreCase("three")){System.out.println(3);}
}
...
在Java 7中,您可以這樣編寫:
//in a class
...
public void stringToNumber(String str)
{switch(str){case "one":System.out.println(1);break;case "two": System.out.println(2);break; case "three":System.out.println(3);break;}
}
...
甚至NetBeans也可以選擇自動轉換:
嘗試使用資源和多重捕獲
在此版本中,這是一個很好的增強,現在您不必擔心關閉那些ResultSet,States,FileInputStreams等。 您只需要使用新的try結構,編譯器就會為您服務。 您甚至可以通過新的try結構將自己的類創建為Closeable(這是一個新接口)。 以下是通過流進行的經典文件訪問:
//in a class
import java.io.*;
...
public static void copyFile(String path) throws IOException, NullPointerException
{File file = new File(path);FileOutputStream fout = null;FileInputStream fin = null;try {try {fout = new FileOutputStream("file.dat");fin = new FileInputStream(file);byte[] bytes = new byte[1024];int leido = 0;while ((leido = fin.read(bytes)) != -1) {fout.write(bytes, 0, leido);}} finally {if (fout != null) {fout.close();}if (fin != null) {fin.close();}}} catch (NullPointerException ex) {ex.printStackTrace();throw ex;}catch (IOException ex) {ex.printStackTrace();throw ex;}}
...
如果您注意到了,為了確保打開的流一旦完成就被關閉,則必須編寫一個try / finally塊并自己關閉它們。 在Java 7中,可以使用新的try結構和新的NIO.2類以更好的方式和更少的代碼行實現相同的行為:
//in a class
import java.nio.file.*;
import java.io.*;
...
public static void copyFile(String src) throws IOException, NullPointerException
{Path path = FileSystems.getDefault().getPath(src);try (FileOutputStream fout = new FileOutputStream("file.dat")) {Files.copy(path, fout);} catch (NullPointerException | IOException ex) {ex.printStackTrace();throw ex;}
}
...
二進制文字和下劃線
現在,您可以將整數表示為二進制文字,這在編程低級API時非常理想,也可以使用下劃線以使您的值更易讀:
//in a class
...
public static void coin()
{int binaryNum = 0b10; //This is number 2 in binary codedouble value1 = 1000000000; //hard to read?double value2 = 1_000_000_000; //Easy to read with Java 7double value3 = 0b101010110111; //hard to read?double value4 = 0b1010_1011_0111; //Easy to read with Java 7double pi = 3.14_15_92; //another example of readability
}
...
因此,更少的代碼,更高的生產率和更好的代碼可讀性是Project Coin的宗旨! (在這里沒有看到的其他東西)。
鉆石算子
這是我們在上一篇文章中剛剛看到的鉆石操作員示例:
//suppose the classes Region, Country, City and Customer exist
import java.util.*;
...
Map<region,map<country,map<city,list>>> map = new HashMap<>();
...</region,map<country,map<city,list
現在,讓我們看看編譯器生成的代碼是什么樣的:
import java.util.*;
...
java.util.Map map = new HashMap();
...
只是一個老派的地圖定義和實例化...為什么? 因為這就是泛型的工作方式:
When you take an element out of a Collection, you must cast it to the type of element that is stored in the collection. Besides being inconvenient, this is unsafe. The compiler does not check that your cast is the same as the collection's type, so the cast can fail at run time.Generics provides a way for you to communicate the type of a collection to the compiler, so that it can be checked. Once the compiler knows the element type of the collection, the compiler can check that you have used the collection consistently and can insert the correct casts on values being taken out of the collection.
這意味著編譯器將在編譯時檢查您是否使用了正確的類,并將向生成的類添加任何必需的強制轉換。 例如:
//suppose the classes Region, Country, City and Customer exist
import java.util.*;
...
Map<region,map<country,map<city,list>>> map = new HashMap<>();
Map<country,map<city,list>> m = map.get(new Region());
...
</country,map<city,list</region,map<country,map<city,list
您將獲得如下內容:
//suppose the class Region exists
import java.util.*;
...
Map map = new HashMap();
Map m = (Map)map.get(new Region()); //the compiler added the cast
...
開關中的弦
記住上一篇文章中介紹的Strings in switch示例:
//in a class
...
public void stringToNumber(String str)
{switch(str){case "one":System.out.println(1);break;case "two": System.out.println(2);break; case "three":System.out.println(3);break;}
}
...
反編譯之后,您會注意到開關狀態菜單現在如何支持字符串:
//in a class
...
public static void stringInSwitch(String str)
{String s = str;byte byte0 = -1;switch(s.hashCode()){case 110182: if(s.equals("one"))byte0 = 0;break;case 115276: if(s.equals("two"))byte0 = 1;break;case 110339486: if(s.equals("three"))byte0 = 2;break;}switch(byte0){case 0: // ''System.out.println(1);break;case 1: // '01'System.out.println(2);break;case 2: // '02'System.out.println(3);break;}
}
...
是的……這是一個小技巧。 并不是直接在switch語句中支持字符串,而是它們的hashCodes是(hashCodes是整數)。 通過查看代碼,我意識到最好不要在switch語句中使用Strings,因為最后,您將獲得兩個switch語句……
嘗試使用資源和多重捕獲
記住上一篇文章中的嘗試資源和多捕獲示例:
//in a class
import java.nio.file.*;
import java.io.*;
...
public static void copyFile(String src) throws IOException, NullPointerException
{Path path = FileSystems.getDefault().getPath(src);try (FileOutputStream fout = new FileOutputStream("file.dat")) {Files.copy(path, fout);} catch (NullPointerException | IOException ex) {ex.printStackTrace();throw ex;}
}
...
本示例在一個示例中使用try資源,并進行多捕獲。 當我嘗試使用JAD Java Decompiler對生成的類進行反編譯時,我對嵌套的try語句有很多誤解,因此我決定嘗試JD Java Decompiler,結果如下:
//in a class
import java.nio.file.*;
import java.io.*;
...
public static void copyFile(String src) throws IOException, NullPointerException
{Path path = FileSystems.getDefault().getPath(src, new String[0]);try {FileOutputStream fout = new FileOutputStream("file.dat"); Throwable localThrowable2 = null;try { Files.copy(path, fout);}catch (Throwable localThrowable1){localThrowable2 = localThrowable1; throw localThrowable1;} finally {if (fout != null) { //I added this { symbol for readabilityif (localThrowable2 != null) { //I added this { symbol for readabilitytry { fout.close(); } catch (Throwable x2) { localThrowable2.addSuppressed(x2); } } //I added this } symbol for readabilityelse { //I added this { symbol for readabilityfout.close(); } //I added this } symbol for readability} //I added this } symbol for readability}} catch (IOException ex) {ex.printStackTrace();throw ex;}
}
...
從最后的代碼中,我們可以看到編譯器如何使用新的(JDK 7) + addSuppressed(Throwable):void類Throwable來確保復制過程中拋出的任何異常不會丟失。 這很重要,因為在應用程序中查找錯誤時,您將需要所有可能的異常。 另外,請注意,所有關閉操作都是在finally語句中完成的,以確保在過程結束時始終關閉資源。
二進制文字和下劃線
我認為您可以弄清楚對最后一個功能進行反編譯后會得到什么……
//in a class
...
public static void coin()
{int binaryNum = 0b10; //This is number 2 in binary codedouble value = 1000000000; //hard to read?double value = 1_000_000_000; //Easy to read with Java 7double value = 0b101010110111; //hard to read?double value = 0b1010_1011_0111; //Easy to read with Java 7double pi = 3.14_15_92; //another example of readability
}
...
是的,沒有什么新東西了……編譯器只重寫沒有下劃線的值,并將二進制值轉換為整數值:
//in a class
...
public static void coin(
{int binaryNum = 2;double value1 = 1000000000D;double value2 = 1000000000D;double value3 = 2743D;double value4 = 2743D;double pi = 3.1415920000000002D;
}
...
好的,僅此而已。 希望大家都喜歡Java 7中的Project Coin(JSR334)的新功能。Java8中的Project Coin II還有更多改進,我們將在以后的文章中進行檢查。 再見!
參考:我們的JCG合作伙伴提供的 Java –項目硬幣反編譯和Java 7 –項目硬幣反編譯第二部分 Java和ME博客上的Alexis Lopez。
翻譯自: https://www.javacodegeeks.com/2012/04/java-7-project-coin-decompiled.html