成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

關(guān)于Java你可能不知道的10件事

開發(fā) 后端
呃,你是不是寫Java已經(jīng)有些年頭了?還依稀記得這些吧: 那些年,它還叫做Oak;那些年,OO還是個(gè)熱門話題;那些年,C++同學(xué)們覺(jué)得Java是沒(méi)有出路的;那些年,Applet還風(fēng)頭正勁……

呃,你是不是寫Java已經(jīng)有些年頭了?還依稀記得這些吧: 那些年,它還叫做Oak;那些年,OO還是個(gè)熱門話題;那些年,C++同學(xué)們覺(jué)得Java是沒(méi)有出路的;那些年,Applet還風(fēng)頭正勁……

但我打賭下面的這些事中至少有一半你還不知道。這周我們來(lái)聊聊這些會(huì)讓你有些驚訝的Java內(nèi)部的那些事兒吧。

1. 其實(shí)沒(méi)有受檢異常(checked exception)

是的!JVM才不知道這類事情,只有Java語(yǔ)言才會(huì)知道。

今天,大家都贊同受檢異常是個(gè)設(shè)計(jì)失誤,一個(gè)Java語(yǔ)言中的設(shè)計(jì)失誤。正如 Bruce Eckel 在布拉格的GeeCON會(huì)議上演示的總結(jié)中說(shuō)的, Java之后的其它語(yǔ)言都沒(méi)有再涉及受檢異常了,甚至Java 8的新式流API(Streams API)都不再擁抱受檢異常 (以lambda的方式使用IO和JDBC,這個(gè)API用起來(lái)還是有些痛苦的。)

想證明JVM不理會(huì)受檢異常?試試下面的這段代碼:

  1. public class Test { 
  2.   
  3.     // 方法沒(méi)有聲明throws 
  4.     public static void main(String[] args) { 
  5.         doThrow(new SQLException()); 
  6.     } 
  7.   
  8.     static void doThrow(Exception e) { 
  9.         Test.<RuntimeException> doThrow0(e); 
  10.     } 
  11.   
  12.     @SuppressWarnings("unchecked"
  13.     static <E extends Exception> 
  14.     void doThrow0(Exception e) throws E { 
  15.         throw (E) e; 
  16.     } 

不僅可以編譯通過(guò),并且也拋出了SQLException,你甚至都不需要用上Lombok的@SneakyThrows

更多細(xì)節(jié),可以再看看這篇文章,或Stack Overflow上的這個(gè)問(wèn)題。

2. 可以有只是返回類型不同的重載方法

下面的代碼不能編譯,是吧?

  1. class Test { 
  2.     Object x() { return "abc"; } 
  3.     String x() { return "123"; } 

是的!Java語(yǔ)言不允許一個(gè)類里有2個(gè)方法是『重載一致』的,而不會(huì)關(guān)心這2個(gè)方法的throws子句或返回類型實(shí)際是不同的。

但是等一下!來(lái)看看Class.getMethod(String, Class...)方法的Javadoc:

注意,可能在一個(gè)類中會(huì)有多個(gè)匹配的方法,因?yàn)楸M管Java語(yǔ)言禁止在一個(gè)類中多個(gè)方法簽名相同只是返回類型不同,但是JVM并不禁止。 這讓JVM可以更靈活地去實(shí)現(xiàn)各種語(yǔ)言特性。比如,可以用橋方法(bridge method)來(lái)實(shí)現(xiàn)方法的協(xié)變返回類型;橋方法和被重載的方法可以有相同的方法簽名,但返回類型不同。

嗯,這個(gè)說(shuō)的通。實(shí)際上,當(dāng)寫了下面的代碼時(shí),就發(fā)生了這樣的情況:

  1. abstract class Parent<T> { 
  2.     abstract T x(); 
  3.   
  4. class Child extends Parent<String> { 
  5.     @Override 
  6.     String x() { return "abc"; } 

查看一下Child類所生成的字節(jié)碼:

  1. // Method descriptor #15 ()Ljava/lang/String; 
  2. // Stack: 1, Locals: 1 
  3. java.lang.String x(); 
  4.   0  ldc <String "abc"> [16
  5.   2  areturn 
  6.     Line numbers: 
  7.       [pc: 0, line: 7
  8.     Local variable table: 
  9.       [pc: 0, pc: 3] local: this index: 0 type: Child 
  10.   
  11. // Method descriptor #18 ()Ljava/lang/Object; 
  12. // Stack: 1, Locals: 1 
  13. bridge synthetic java.lang.Object x(); 
  14.   0  aload_0 [this
  15.   1  invokevirtual Child.x() : java.lang.String [19
  16.   4  areturn 
  17.     Line numbers: 
  18.       [pc: 0, line: 1

在字節(jié)碼中,T實(shí)際上就是Object類型。這很好理解。

合成的橋方法實(shí)際上是由編譯器生成的,因?yàn)樵谝恍┱{(diào)用場(chǎng)景下,Parent.x()方法簽名的返回類型期望是Object。 添加泛型而不生成這個(gè)橋方法,不可能做到二進(jìn)制兼容。 所以,讓JVM允許這個(gè)特性,可以愉快解決這個(gè)問(wèn)題(實(shí)際上可以允許協(xié)變重載的方法包含有副作用的邏輯)。 聰明不?呵呵~

你是不是想要扎入語(yǔ)言規(guī)范和內(nèi)核看看?可以在這里找到更多有意思的細(xì)節(jié)。

3. 所有這些寫法都是二維數(shù)組!

  1. class Test { 
  2.     int[][] a()  { return new int[0][]; } 
  3.     int[] b() [] { return new int[0][]; } 
  4.     int c() [][] { return new int[0][]; } 

是的,這是真的。盡管你的人肉解析器不能馬上理解上面這些方法的返回類型,但都是一樣的!下面的代碼也類似:

  1. class Test { 
  2.     int[][] a = {{}}; 
  3.     int[] b[] = {{}}; 
  4.     int c[][] = {{}}; 

是不是覺(jué)得這個(gè)很2B?想象一下在上面的代碼中使用JSR-308/Java 8的類型注解。 語(yǔ)法糖的數(shù)目要爆炸了吧!

  1. @Target(ElementType.TYPE_USE) 
  2. @interface Crazy {} 
  3.   
  4. class Test { 
  5.     @Crazy int[][]  a1 = {{}}; 
  6.     int @Crazy [][] a2 = {{}}; 
  7.     int[] @Crazy [] a3 = {{}}; 
  8.   
  9.     @Crazy int[] b1[]  = {{}}; 
  10.     int @Crazy [] b2[] = {{}}; 
  11.     int[] b3 @Crazy [] = {{}}; 
  12.   
  13.     @Crazy int c1[][]  = {{}}; 
  14.     int c2 @Crazy [][] = {{}}; 
  15.     int c3[] @Crazy [] = {{}}; 

類型注解。這個(gè)設(shè)計(jì)引入的詭異在程度上僅僅被它解決問(wèn)題的能力超過(guò)。

或換句話說(shuō):

在我4周休假前的最后一個(gè)提交里,我寫了這樣的代碼,然后。。。

[[123047]]

【譯注:然后,親愛的同事你,就有得火救啦,哼,哼哼,哦哈哈哈哈~】

請(qǐng)找出上面用法合適的使用場(chǎng)景,還是留給你作為一個(gè)練習(xí)吧。

4. 你沒(méi)有掌握條件表達(dá)式

呃,你認(rèn)為自己知道什么時(shí)候該使用條件表達(dá)式?面對(duì)現(xiàn)實(shí)吧,你還不知道。大部分人會(huì)下面的2個(gè)代碼段是等價(jià)的:

  1. Object o1 = true ? new Integer(1) : new Double(2.0); 

等同于:

  1. Object o2; 
  2.   
  3. if (true
  4.     o2 = new Integer(1); 
  5. else 
  6.     o2 = new Double(2.0); 

讓你失望了。來(lái)做個(gè)簡(jiǎn)單的測(cè)試吧:

  1. System.out.println(o1); 
  2. System.out.println(o2); 

打印結(jié)果是:

  1. 1.0 

哦!如果『需要』,條件運(yùn)算符會(huì)做數(shù)值類型的類型提升,這個(gè)『需要』有非常非常非常強(qiáng)的引號(hào)。因?yàn)?,你覺(jué)得下面的程序會(huì)拋出NullPointerException嗎?

  1. Integer i = new Integer(1); 
  2. if (i.equals(1)) 
  3.     i = null
  4. Double d = new Double(2.0); 
  5. Object o = true ? i : d; // NullPointerException! 
  6. System.out.println(o); 

關(guān)于這一條的更多的信息可以在這里找到。

5. 你沒(méi)有掌握復(fù)合賦值運(yùn)算符

是不是覺(jué)得不服?來(lái)看看下面的2行代碼:

  1. i += j; 
  2. ii = i + j; 

直覺(jué)上認(rèn)為,2行代碼是等價(jià)的,對(duì)吧?但結(jié)果即不是!JLS(Java語(yǔ)言規(guī)范)指出:

復(fù)合賦值運(yùn)算符表達(dá)式 E1 op= E2 等價(jià)于 E1 = (T)((E1) op (E2)) 其中T是E1的類型,但E1只會(huì)被求值一次。

這個(gè)做法太漂亮了,請(qǐng)?jiān)试S我引用Peter Lawrey在Stack Overflow上的回答

使用*=或/=作為例子可以方便說(shuō)明其中的轉(zhuǎn)型問(wèn)題:

  1. byte b = 10
  2. b *= 5.7
  3. System.out.println(b); // prints 57 
  4.   
  5. byte b = 100
  6. b /= 2.5
  7. System.out.println(b); // prints 40 
  8.   
  9. char ch = '0'
  10. ch *= 1.1
  11. System.out.println(ch); // prints '4' 
  12.   
  13. char ch = 'A'
  14. ch *= 1.5
  15. System.out.println(ch); // prints 'a' 

為什么這個(gè)真是太有用了?如果我要在代碼中,就地對(duì)字符做轉(zhuǎn)型和乘法。然后,你懂的……

#p#

6. 隨機(jī)Integer

這條其實(shí)是一個(gè)迷題,先不要看解答??纯茨隳懿荒茏约赫页鼋夥?。運(yùn)行下面的代碼:

  1. for (int i = 0; i < 10; i++) { 
  2.   System.out.println((Integer) i); 

…… 然后要得到類似下面的輸出(每次輸出是隨機(jī)結(jié)果):

  1. 92 
  2. 221 
  3. 45 
  4. 48 
  5. 236 
  6. 183 
  7. 39 
  8. 193 
  9. 33 
  10. 84 

這怎么可能?!

我要?jiǎng)⊥噶?hellip;… 解答走起……

好吧,解答在這里(http://blog.jooq.org/2013/10/17/add-some-entropy-to-your-jvm/), 和用反射覆蓋JDK的Integer緩存,然后使用自動(dòng)打包解包(auto-boxing/auto-unboxing)有關(guān)。 同學(xué)們請(qǐng)勿模仿!或換句話說(shuō),想想會(huì)有這樣的狀況,再說(shuō)一次:

在我4周休假前的最后一個(gè)提交里,我寫了這樣的代碼,然后。。。

[[123048]]

【譯注:然后,親愛的同事你,就有得火救啦,哼,哼哼,哦哈哈哈哈~】

7. GOTO

這條是我的最愛。Java是有GOTO的!打上這行代碼:

  1. int goto = 1

結(jié)果是:

  1. Test.java:44: error: <identifier> expected 
  2.     int goto = 1
  3.         ^ 

這是因?yàn)間oto是個(gè)還未使用的關(guān)鍵字,保留了為以后可以用……

但這不是我要說(shuō)的讓你興奮的內(nèi)容。讓你興奮的是,你是可以用break、continue和有標(biāo)簽的代碼塊來(lái)實(shí)現(xiàn)goto的:

向前跳:

  1. label: { 
  2.   // do stuff 
  3.   if (check) break label; 
  4.   // do more stuff 

對(duì)應(yīng)的字節(jié)碼是:

  1. 2  iload_1 [check] 
  2. 3  ifeq 6          // 向前跳 
  3. 6  .. 

向后跳:

  1. label: do { 
  2.   // do stuff 
  3.   if (check) continue label; 
  4.   // do more stuff 
  5.   break label; 
  6. while(true); 

對(duì)應(yīng)的字節(jié)碼是:

  1. 2  iload_1 [check] 
  2. 3  ifeq 9 
  3. 6  goto 2          // 向后跳 
  4. 9  .. 

8. Java是有類型別名的

在別的語(yǔ)言中(比如,Ceylon), 可以方便地定義類型別名:

  1. interface People => Set<Person>; 

這樣定義的People可以和Set<Person>互換地使用:

  1. People?      p1 = null
  2. Set<Person>? p2 = p1; 
  3. People?      p3 = p2; 

在Java中不能在頂級(jí)(top level)定義類型別名。但可以在類級(jí)別、或方法級(jí)別定義。 如果對(duì)Integer、Long這樣名字不滿意,想更短的名字:I和L。很簡(jiǎn)單:

  1. class Test<I extends Integer> { 
  2.     <L extends Long> void x(I i, L l) { 
  3.         System.out.println( 
  4.             i.intValue() + ", " +  
  5.             l.longValue() 
  6.         ); 
  7.     } 

上面的代碼中,在Test類級(jí)別中I是Integer的『別名』,在x方法級(jí)別,L是Long的『別名』。可以這樣來(lái)調(diào)用這個(gè)方法:

  1. new Test().x(1, 2L); 

當(dāng)然這個(gè)用法不嚴(yán)謹(jǐn)。在例子中,Integer、Long都是final類型,結(jié)果I和L 效果上是個(gè)別名 (大部分情況下是。賦值兼容性只是單向的)。如果用非final類型(比如,Object),還是要使用原來(lái)的泛型參數(shù)類型。

玩夠了這些惡心的小把戲。現(xiàn)在要上干貨了!

9. 有些類型的關(guān)系是不確定的

好,這條會(huì)很稀奇古怪,你先來(lái)杯咖啡,再集中精神來(lái)看??纯聪旅娴?個(gè)類型:

  1. // 一個(gè)輔助類。也可以直接使用List 
  2. interface Type<T> {} 
  3.   
  4. class C implements Type<Type<? super C>> {} 
  5. class D<P> implements Type<Type<? super D<D<P>>>> {} 

類型C和D是啥意思呢?

這2個(gè)類型聲明中包含了遞歸,和java.lang.Enum的聲明類似 (但有微妙的不同):

  1. public abstract class Enum<E extends Enum<E>> { ... } 

有了上面的類型聲明,一個(gè)實(shí)際的enum實(shí)現(xiàn)只是語(yǔ)法糖:

  1. // 這樣的聲明 
  2. enum MyEnum {} 
  3.   
  4. // 實(shí)際只是下面寫法的語(yǔ)法糖: 
  5. class MyEnum extends Enum<MyEnum> { ... } 

記住上面的這點(diǎn)后,回到我們的2個(gè)類型聲明上。下面的代碼可以編譯通過(guò)嗎?

  1. class Test { 
  2.     Type<? super C> c = new C(); 
  3.     Type<? super D<Byte>> d = new D<Byte>(); 

很難的問(wèn)題,Ross Tate回答過(guò)這個(gè)問(wèn)題。答案實(shí)際上是不確定的:

  1. C是Type<? super C>的子類嗎? 
  2.   
  3. 步驟 0) C <?: Type<? super C> 
  4. 步驟 1) Type<Type<? super C>> <?: Type (繼承) 
  5. 步驟 2) C (檢查通配符 ? super C) 
  6. 步驟 . . . (進(jìn)入死循環(huán)) 

然后:

  1. D是Type<? super D<Byte>>的子類嗎? 
  2.   
  3. 步驟 0) D<Byte> <?: Type<? super C<Byte>> 
  4. 步驟 1) Type<Type<? super D<D<Byte>>>> <?: Type<? super D<Byte>> 
  5. 步驟 2) D<Byte> <?: Type<? super D<D<Byte>>> 
  6. 步驟 3) List<List<? super C<C>>> <?: List<? super C<C>> 
  7. 步驟 4) D<D<Byte>> <?: Type<? super D<D<Byte>>> 
  8. 步驟 . . . (進(jìn)入永遠(yuǎn)的展開中) 

試著在你的Eclipse中編譯上面的代碼,會(huì)Crash?。▌e擔(dān)心,我已經(jīng)提交了一個(gè)Bug。)

我們繼續(xù)深挖下去……

在Java中有些類型的關(guān)系是不確定的!

如果你有興趣知道更多古怪Java行為的細(xì)節(jié),可以讀一下Ross Tate的論文『馴服Java類型系統(tǒng)的通配符』 (由Ross Tate、Alan Leung和Sorin Lerner合著),或者也可以看看我們?cè)?a rel="nofollow" target="_blank">子類型多態(tài)和泛型多態(tài)的關(guān)聯(lián)方面的思索。

10. 類型交集(Type intersections)

Java有個(gè)很古怪的特性叫類型交集。你可以聲明一個(gè)(泛型)類型,這個(gè)類型是2個(gè)類型的交集。比如:

  1. class Test<T extends Serializable & Cloneable> { 

綁定到類Test的實(shí)例上的泛型類型參數(shù)T必須同時(shí)實(shí)現(xiàn)Serializable和Cloneable。比如,String不能做綁定,但Date可以:

  1. // 編譯不通過(guò)! 
  2. Test<String> s = null
  3.   
  4. // 編譯通過(guò) 
  5. Test<Date> d = null

Java 8保留了這個(gè)特性,你可以轉(zhuǎn)型成臨時(shí)的類型交集。這有什么用? 幾乎沒(méi)有一點(diǎn)用,但如果你想強(qiáng)轉(zhuǎn)一個(gè)lambda表達(dá)式成這樣的一個(gè)類型,就沒(méi)有其它的方法了。 假定你在方法上有了這個(gè)蛋疼的類型限制:

  1. <T extends Runnable & Serializable> void execute(T t) {} 

你想一個(gè)Runnable同時(shí)也是個(gè)Serializable,這樣你可能在另外的地方執(zhí)行它并通過(guò)網(wǎng)絡(luò)發(fā)送它。lambda和序列化都有點(diǎn)古怪。

lambda是可以序列化的:

如果lambda表達(dá)式的目標(biāo)類型和它捕獲的參數(shù)(captured arguments)是可以序列化的,則這個(gè)lambda表達(dá)式是可序列化的。

但即使?jié)M足這個(gè)條件,lambda表達(dá)式并沒(méi)有自動(dòng)實(shí)現(xiàn)Serializable這個(gè)標(biāo)記接口(marker interface)。 為了強(qiáng)制成為這個(gè)類型,就必須使用轉(zhuǎn)型。但如果只轉(zhuǎn)型成Serializable …

  1. execute((Serializable) (() -> {})); 

… 則這個(gè)lambda表達(dá)式不再是一個(gè)Runnable。

呃……

So……

同時(shí)轉(zhuǎn)型成2個(gè)類型:

  1. execute((Runnable & Serializable) (() -> {})); 

結(jié)論

一般我只對(duì)SQL會(huì)說(shuō)這樣的話,但是時(shí)候用下面的話來(lái)結(jié)束這篇文章了:

Java中包含的詭異在程度上僅僅被它解決問(wèn)題的能力超過(guò)。

原文鏈接: Jooq 翻譯: ImportNew.com Jerry Lee
譯文鏈接: http://www.importnew.com/13859.html

責(zé)任編輯:張偉 來(lái)源: ImportNew
相關(guān)推薦

2010-05-19 09:01:00

2011-11-30 13:34:13

2015-02-02 14:12:03

云桌面

2021-07-12 23:21:52

MyISAM引擎InnoDB

2015-10-10 13:11:35

GitGithub工具

2013-06-18 11:05:40

Mac Pro開發(fā)工具蘋果

2017-05-02 11:36:00

Java

2019-11-20 10:25:06

sudoLinux

2024-03-25 00:10:00

JSON后端開發(fā)

2011-04-02 13:11:35

JARJava

2015-08-14 16:39:59

軟件開發(fā)老板

2024-10-14 12:42:06

2015-08-14 14:46:47

軟件開發(fā)

2010-08-24 14:08:33

喬布斯

2012-11-23 10:57:44

Shell

2020-03-05 11:10:18

Left join數(shù)據(jù)庫(kù)MySQL

2024-01-11 11:28:54

2018-05-10 11:50:13

Docker容器冷知識(shí)

2010-03-19 08:45:20

Windows Pho

2015-10-10 09:29:32

GitGithub
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 日韩视频一区在线观看 | 亚洲综合天堂 | 91在线精品一区二区 | 91久久国产综合久久 | 久久精品国产久精国产 | 91精品欧美久久久久久久 | 一级免费a| 国产成人综合一区二区三区 | 日韩午夜精品 | 亚洲综合在线播放 | 欧美日韩一区二区在线观看 | 麻豆久久久久久 | 狠狠干2020 | av福利网 | 成人午夜精品 | 一区二区成人 | 日韩精品久久一区二区三区 | 久久久久久国产精品免费 | 黄色网址在线免费播放 | 九九热在线免费视频 | 狠狠操狠狠干 | 国产一区二区三区在线 | 99久久日韩精品免费热麻豆美女 | 日韩av啪啪网站大全免费观看 | 性大毛片视频 | 成人亚洲网 | 青青草视频网 | 国产一区视频在线 | 黄色网页在线观看 | 亚洲三区视频 | 日韩高清一区 | 国产精品一区久久久久 | 一区二区三区视频 | 成人影院av | 国产精品一区二区三区在线 | 成人精品一区二区三区中文字幕 | 午夜色婷婷 | 国产无人区一区二区三区 | 干干干操操操 | 成人欧美一区二区三区在线观看 | 一区二区三区视频在线观看 |