public class Test { // No throws clause here public static void main(String[] args) { doThrow(new SQLException()); } static void doThrow(Exception e) { Test.
doThrow0(e); } @SuppressWarnings("unchecked") static
void doThrow0(Exception e) throws E { throw (E) e; } }
class Test { Object x() { return "abc"; } String x() { return "123"; } }
正確的。Java 語言不允許在同一個類別中同時等效地重寫兩個方法,無論它們的拋出或傳回類型有何不同。但等一下。再次檢查 Class.getMethod(String, Class...) 的文件。它說:
請注意,一個類別中可能有多個相應的方法,因為雖然Java語言禁止多個具有相同簽章但傳回類型不同的方法,但Java虛擬機卻不這樣做。虛擬機器的這種靈活性可用於實現各種語言功能。例如,協變返回可以透過橋接方法來完成;橋接方法和重寫方法將具有相同的簽名,但返回類型不同。 哇,這很有道理。當您編寫以下內容時,實際上發生了很多事情: 查看生成的字節碼: 所以 t 實際上是字節碼中的一個物件。這是很好理解的。合成橋方法實際上是由編譯器產生的,因為在呼叫的某些部分可以預期 Parent.x() 的回傳類型。在二進位表示中將不再可能添加沒有此類橋接方法的泛型。因此,更改 JVM 以允許這樣的功能產生更少的痛苦(這也允許協變方法重寫作為副作用...)聰明吧?
3. 以下均為二維數組。 這實際上是真的。即使您的心理分析器無法立即理解上述方法的返回類型,它們都是相同的!就像下一段程式碼一樣。 你認為這很瘋狂嗎?寫作的機會之多也簡直爆炸想像力!
鍵入註解。
這種裝置的神秘性僅次於它的力量。 或者換句話說:當我在 4 週假期之前做出最後一次承諾時。 我允許您以任何您喜歡的方式使用它。
4. 你不會得到條件語句 那麼,當你開始使用條件語句時,你認為你已經了解條件語句的所有內容了嗎?讓我讓你失望吧——你錯了。大多數人都會認為下面兩個例子是等價的: 等價於這個? 不。讓我們快速測試一下 程式將輸出以下內容: 是的!如果需要,條件運算子將執行類型轉換。因為否則你會期望程式拋出 NullPointerException?
5. 您也不會得到複合賦值運算子。 足智多謀就夠了嗎?我們來看下面兩個程式碼片段:
abstract class Parent
{ abstract T x(); } class Child extends Parent
{ @Override String x() { return "abc"; } }
// Method descriptor #15 ()Ljava/lang/String; // Stack: 1, Locals: 1 java.lang.String x(); 0 ldc
[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]
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[][] = {{}}; }
@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 [] = {{}}; }
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);
1.0 1
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);
i += j; i = i + j;
直觀上來說,它們應該是相等的,對吧?但你知道嗎——它們是不同的。JLS 規範規定:
E1 op = E2 類型的複合表達式等效於 E1 = (T) ((E1) op (E2)),其中 T 是 E1 的類型,但 E1 僅計算一次。 一個很好的例子是使用 *= 或 /= :
byte b = 10; b *= 5.7; System.out.println(b); // prints 57
or:
byte b = 100; b /= 2.5; System.out.println(b); // prints 40
or:
char ch = '0'; ch *= 1.1; System.out.println(ch); // prints '4'
or:
char ch = 'A'; ch *= 1.5; System.out.println(ch); // prints 'a'
那麼,這仍然是一個有用的工具嗎?
6. 隨機整數 現在來做一個更困難的任務。不要閱讀解決方案。看看你自己是否能找到答案。當我運行以下程式時:
for (int i = 0; i < 10; i++) { System.out.println((Integer) i); }
有時我會得到以下輸出:
92 221 45 48 236 183 39 193 33 84
但這怎麼可能呢?好吧,答案在於透過反射來覆蓋 JDK 的 Integer 緩存,然後使用自動裝箱和自動拆箱。未經成人許可,請勿這樣做!或者換句話說:
7. GOTO 我的最愛之一。Java 有 GOTO!寫這篇:
int goto = 1;
你會得到這個: 那是因為 goto 是一個未使用的保留字,以防萬一......但這不是令人興奮的部分。最酷的是,你可以包含 goto 與break、continue和標記區塊配對: 在字節碼中向前跳: 在字節碼中 向後跳轉 :
8. Java有類型別名 在其他語言(如Ceylon )中,我們可以定義型別別名非常簡單: 這裡的 People 類別的建構方式可以與 Set 互換
Test.java:44: error:
expected int goto = 1; ^
label: { // do stuff if (check) break label; // do more stuff }
2 iload_1 [check] 3 ifeq 6 // Jumping forward 6 ..
label: do { // do stuff if (check) continue label; // do more stuff break label; } while(true);
2 iload_1 [check] 3 ifeq 9 6 goto 2 // Jumping backward 9 ..
interface People => Set
;
People? p1 = null; Set
? p2 = p1; People? p3 = p2;
class Test {
void x(I i, L l) { System.out.println( i.intValue() + ", " + l.longValue() ); } }
new Test().x(1, 2L);
// A helper type. You could also just use List interface Type
{} class C implements Type
> {} class D
implements Type
那麼C和D到底是什麼意思呢?從某種意義上說,它們是遞歸的,類似於 java.lang.Enum 中的遞歸。考慮到: 考慮到上面的規範,枚舉的實際實現只是語法糖: 考慮到這一點,讓我們回到我們的兩種類型。下面的程式碼能編譯通過嗎? 一個難題……而且實際上還沒有解決? C 是 Type 的子類型嗎? 嘗試在您的 Eclipse 或 Idea 中編譯它,他們會告訴您他們的想法。把它衝進下水道… Java中的一些型別關係是不可判定的!10. 類型交集 Java 有一個非常有趣的特性,稱為型別交集。您可以聲明一個實際上是兩種類型的交集的(通用)類型。例如: 與 Test 類別的實例關聯的自訂類型參數 T 必須同時包含 Serialized 和 Cloneable 介面。例如,String 不能受到限制,但 Date 可以: 此功能在 Java8 中有多種用途,您可以在其中強制轉換類型。這有什麼幫助?幾乎什麼都沒有,但如果你想將 lambda 表達式轉換為你需要的類型,那麼沒有其他方法。假設您的方法有一個瘋狂的限制: 只有當您想在另一個地方執行它並透過網路發送結果時,您才需要 Runnable ,同時它也是可序列化的。Lambda 和序列化增添了一點諷刺意味。 如果 lambda 表達式的目標類型和參數是可序列化的,則可以序列化它。 但即使這是真的,它們也不會自動啟用 Serialized 介面。你必須自己把它們變成這種類型。但是,當您僅強制轉換為 Serialized: 時,lambda 將不再是 Runnable,因此將它們強制轉換為兩種類型: 總而言之: public abstract class Enum
// This enum MyEnum {} // Is really just sugar for this class MyEnum extends Enum
class Test { Type c = new C(); Type> d = new D
Step 0) C Step 1) Type
>> > Step 4) D
class Test
// Doesn't compile Test
execute((Serializable) (() -> {}));
execute((Runnable & Serializable) (() -> {}));
GO TO FULL VERSION