但我打賭下面的這些事中至少有一半你還不" />

国产成人精品无码青草_亚洲国产美女精品久久久久∴_欧美人与鲁交大毛片免费_国产果冻豆传媒麻婆精东

18143453325 在線咨詢 在線咨詢
18143453325 在線咨詢
所在位置: 首頁 > 營銷資訊 > 網(wǎng)絡(luò)營銷 > 你知道Java內(nèi)部的這些事兒嗎?

你知道Java內(nèi)部的這些事兒嗎?

時間:2022-05-25 20:18:01 | 來源:網(wǎng)絡(luò)營銷

時間:2022-05-25 20:18:01 來源:網(wǎng)絡(luò)營銷

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

但我打賭下面的這些事中至少有一半你還不知道,今天億企邦就跟大家一起來聊聊這些會讓你有些驚訝的Java內(nèi)部的那些事兒吧!

1、其實沒有受檢異常(checked exception)

是的!JVM才不知道這類事情,只有Java語言才會知道。

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

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

public class Test {
   // 方法沒有聲明throws
   public static void main(String[] args) {
  doThrow(new SQLException());
   }
   static void doThrow(Exception e) {
  Test.<RuntimeException> doThrow0(e);
   }
   @SuppressWarnings("unchecked")
   static <E extends Exception>
   void doThrow0(Exception e) throws E {
  throw (E) e;
   }
  }

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

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

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

class Test {
   Object x() { return "abc"; }
   String x() { return "123"; }
  }

是的!Java語言不允許一個類里有2個方法是“重載一致”的,而不會關(guān)心這2個方法的throws子句或返回類型實際是不同的。

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

注意,可能在一個類中會有多個匹配的方法,因為盡管Java語言禁止在一個類中多個方法簽名相同只是返回類型不同,但是JVM并不禁止,這讓JVM可以更靈活地去實現(xiàn)各種語言特性。

比如,可以用橋方法(bridge method)來實現(xiàn)方法的協(xié)變返回類型;橋方法和被重載的方法可以有相同的方法簽名,但返回類型不同。

嗯,這個說的通,實際上,當寫了下面的代碼時,就發(fā)生了這樣的情況:

abstract class Parent<T> {
   abstract T x();
  }
  class Child extends Parent<String> {
   @Override
   String x() { return "abc"; }
  }

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

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

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

合成的橋方法實際上是由編譯器生成的,因為在一些調(diào)用場景下,Parent.x()方法簽名的返回類型期望是Object, 添加泛型而不生成這個橋方法,不可能做到二進制兼容。

所以,讓JVM允許這個特性,可以愉快解決這個問題(實際上可以允許協(xié)變重載的方法包含有副作用的邏輯)。

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

class Test {
   int[][] a() { return new int[0][]; }
   int[] b() [] { return new int[0][]; }
   int c() [][] { return new int[0][]; }
  }

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

class Test {
   int[][] a = {{}};
   int[] b[] = {{}};
   int c[][] = {{}};
  }

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

@Target(ElementType.TYPE_USE)
  @interface Crazy {}
  class Test {
   @Crazy int[][] a1 = {{}};
   int @Crazy [][] a2 = {{}};
   int[] @Crazy [] a3 = {{}};
   @Crazy int[] b1[] = {{}};
   int @Crazy [] b2[] = {{}};
   int[] b3 @Crazy [] = {{}};
   @Crazy int c1[][] = {{}};
   int c2 @Crazy [][] = {{}};
   int c3[] @Crazy [] = {{}};
  }

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

4、你沒有掌握條件表達式

你認為自己知道什么時候該使用條件表達式?面對現(xiàn)實吧,你還不知道,大部分人會下面的2個代碼段是等價的:

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

等同于:

Object o2;
  if (true)
   o2 = new Integer(1);
  else
   o2 = new Double(2.0);

讓你失望了,來做個簡單的測試吧:

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

打印結(jié)果是:

1.0
  1

哦!如果“需要”,條件運算符會做數(shù)值類型的類型提升,這個“需要”有非常非常非常強的引號,因為,你覺得下面的程序會拋出NullPointerException嗎?

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

5、你沒有掌握復(fù)合賦值運算符

是不是覺得不服?來看看下面的2行代碼:

i += j;
  i = i + j;

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

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

這個做法太漂亮了,請允許我引用Peter Lawrey在Stack Overflow上的回答:

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

byte b = 10;
  b *= 5.7;
  System.out.println(b); // prints 57
  byte b = 100;
  b /= 2.5;
  System.out.println(b); // prints 40
  char ch = '0';
  ch *= 1.1;
  System.out.println(ch); // prints '4'
  char ch = 'A';
  ch *= 1.5;
  System.out.println(ch); // prints 'a'

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

6、隨機Integer

這條其實是一個迷題,先不要看解答。看看你能不能自己找出解法,運行下面的代碼:

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

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

92
  221
  45
  48
  236
  183
  39
  193
  33
  84

對于這個結(jié)果,你可能也是難以置信吧!難怪人們對Java的評價程度也是不一樣的(具體可查看億企邦《關(guān)于C語言、C++、Java和Python這4種程序開發(fā)語言的評價》的相關(guān)介紹)。

7、GOTO

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

int goto = 1;

結(jié)果是:

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

這是因為goto是個還未使用的關(guān)鍵字,保留了為以后可以用,但這不是我要說的讓你興奮的內(nèi)容。

讓你興奮的是,你是可以用break、continue和有標簽的代碼塊來實現(xiàn)goto的:

向前跳:

label: {
   // do stuff
   if (check) break label;
   // do more stuff
  }

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

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

向后跳:

label: do {
   // do stuff
   if (check) continue label;
   // do more stuff
   break label;
  } while(true);

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

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

8、Java是有類型別名的

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

interface People => Set<Person>;

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

People? p1 = null;
  Set<Person>? p2 = p1;
  People? p3 = p2;

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

class Test<I extends Integer> {
   <L extends Long> void x(I i, L l) {
  System.out.println(
i.intValue() + ", " +
l.longValue()
  );
   }
  }

上面的代碼中,在Test類級別中I是Integer的“別名”,在x方法級別,L是Long的“別名”,可以這樣來調(diào)用這個方法:

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

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

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

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

好,這條會很稀奇古怪,你先來杯咖啡,再集中精神來看,看看下面的2個類型:

// 一個輔助類。也可以直接使用List
  interface Type<T> {}
  class C implements Type<Type<? super C>> {}
  class D<P> implements Type<Type<? super D<D<P>>>> {}

類型C和D是啥意思呢?

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

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

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

// 這樣的聲明
  enum MyEnum {}
  // 實際只是下面寫法的語法糖:
  class MyEnum extends Enum<MyEnum> { ... }

記住上面的這點后,回到我們的2個類型聲明上,下面的代碼可以編譯通過嗎?

class Test {
   Type<? super C> c = new C();
   Type<? super D<Byte>> d = new D<Byte>();
  }

很難的問題,Ross Tate回答過這個問題,答案實際上是不確定的:

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

然后:

D是Type<? super D<Byte>>的子類嗎?
  步驟 0) D<Byte> <?: Type<? super C<Byte>>
  步驟 1) Type<Type<? super D<D<Byte>>>> <?: Type<? super D<Byte>>
  步驟 2) D<Byte> <?: Type<? super D<D<Byte>>>
  步驟 3) List<List<? super C<C>>> <?: List<? super C<C>>
  步驟 4) D<D<Byte>> <?: Type<? super D<D<Byte>>>
  步驟 . . . (進入永遠的展開中)

試著在你的Eclipse中編譯上面的代碼,會Crash!(別擔心,我已經(jīng)提交了一個Bug)

10、類型交集(Type intersections)

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

class Test<T extends Serializable & Cloneable> {
  }

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

// 編譯不通過!
  Test<String> s = null;
  // 編譯通過
  Test<Date> d = null;

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

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

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

lambda是可以序列化的:

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

但即使?jié)M足這個條件,lambda表達式并沒有自動實現(xiàn)Serializable這個標記接口(marker interface),為了強制成為這個類型,就必須使用轉(zhuǎn)型,但如果只轉(zhuǎn)型成Serializable的話:

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

則這個lambda表達式不再是一個Runnable。

所以,就需要同時轉(zhuǎn)型成2個類型:

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

我們不得不說,Java中包含的詭異在程度上僅僅被它解決問題的能力超過。

億企邦點評:

Java技術(shù)具有卓越的通用性、高效性、平臺移植性和安全性,廣泛應(yīng)用于個人PC、數(shù)據(jù)中心、游戲控制臺、科學(xué)超級計算機、移動電話和互聯(lián)網(wǎng),同時擁有全球最大的開發(fā)者專業(yè)社群,在全球云計算和移動互聯(lián)網(wǎng)的產(chǎn)業(yè)環(huán)境下,Java更具備了顯著優(yōu)勢和廣闊前景。

關(guān)鍵詞:內(nèi)部

74
73
25
news

版權(quán)所有? 億企邦 1997-2022 保留一切法律許可權(quán)利。

為了最佳展示效果,本站不支持IE9及以下版本的瀏覽器,建議您使用谷歌Chrome瀏覽器。 點擊下載Chrome瀏覽器
關(guān)閉