時間: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還風頭正勁……。public class Test {不僅可以編譯通過,并且也拋出了SQLException,你甚至都不需要用上Lombok的SneakyThrows。
// 方法沒有聲明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;
}
}
class Test {是的!Java語言不允許一個類里有2個方法是“重載一致”的,而不會關(guān)心這2個方法的throws子句或返回類型實際是不同的。
Object x() { return "abc"; }
String x() { return "123"; }
}
abstract class Parent<T> {查看一下Child類所生成的字節(jié)碼:
abstract T x();
}
class Child extends Parent<String> {
@Override
String x() { return "abc"; }
}
// Method descriptor #15 ()Ljava/lang/String;在字節(jié)碼中,T實際上就是Object類型,這很好理解。
// 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]
class Test {是的,這是真的,盡管你的人肉解析器不能馬上理解上面這些方法的返回類型,但都是一樣的!下面的代碼也類似:
int[][] a() { return new int[0][]; }
int[] b() [] { return new int[0][]; }
int c() [][] { return new int[0][]; }
}
class Test {是不是覺得這個很2B?想象一下在上面的代碼中使用JSR-308/Java 8的類型注解,語法糖的數(shù)目要爆炸了吧!
int[][] a = {{}};
int[] b[] = {{}};
int c[][] = {{}};
}
@Target(ElementType.TYPE_USE)類型注解,這個設(shè)計引入的詭異在程度上僅僅被它解決問題的能力超過。
@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);打印結(jié)果是:
System.out.println(o2);
1.0哦!如果“需要”,條件運算符會做數(shù)值類型的類型提升,這個“需要”有非常非常非常強的引號,因為,你覺得下面的程序會拋出NullPointerException嗎?
1
Integer i = new Integer(1);5、你沒有掌握復(fù)合賦值運算符
if (i.equals(1))
i = null;
Double d = new Double(2.0);
Object o = true ? i : d; // NullPointerException!
System.out.println(o);
i += j;直覺上認為,2行代碼是等價的,對吧?但結(jié)果即不是!JLS(Java語言規(guī)范)指出:
i = i + j;
byte b = 10;為什么這個真是太有用了?如果我要在代碼中,就地對字符做轉(zhuǎn)型和乘法,然后,你懂的……。
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'
for (int i = 0; i < 10; i++) {然后要得到類似下面的輸出(每次輸出是隨機結(jié)果):
System.out.println((Integer) i);
}
92對于這個結(jié)果,你可能也是難以置信吧!難怪人們對Java的評價程度也是不一樣的(具體可查看億企邦《關(guān)于C語言、C++、Java和Python這4種程序開發(fā)語言的評價》的相關(guān)介紹)。
221
45
48
236
183
39
193
33
84
int goto = 1;結(jié)果是:
Test.java:44: error: <identifier> expected這是因為goto是個還未使用的關(guān)鍵字,保留了為以后可以用,但這不是我要說的讓你興奮的內(nèi)容。
int goto = 1;
^
label: {對應(yīng)的字節(jié)碼是:
// do stuff
if (check) break label;
// do more stuff
}
2 iload_1 [check]向后跳:
3 ifeq 6 // 向前跳
6 ..
label: do {對應(yīng)的字節(jié)碼是:
// do stuff
if (check) continue label;
// do more stuff
break label;
} while(true);
2 iload_1 [check]8、Java是有類型別名的
3 ifeq 9
6 goto 2 // 向后跳
9 ..
interface People => Set<Person>;這樣定義的People可以和Set<Person>互換地使用:
People? p1 = null;在Java中不能在頂級(top level)定義類型別名,但可以在類級別、或方法級別定義,如果對Integer、Long這樣名字不滿意,想更短的名字:I和L,很簡單:
Set<Person>? p2 = p1;
People? p3 = p2;
class Test<I extends Integer> {上面的代碼中,在Test類級別中I是Integer的“別名”,在x方法級別,L是Long的“別名”,可以這樣來調(diào)用這個方法:
<L extends Long> void x(I i, L l) {
System.out.println(
i.intValue() + ", " +
l.longValue()
);
}
}
new Test().x(1, 2L);當然這個用法不嚴謹,在例子中,Integer、Long都是final類型,結(jié)果I和L 效果上是個別名(大部分情況下是,賦值兼容性只是單向的),如果用非final類型(比如,Object),還是要使用原來的泛型參數(shù)類型。
// 一個輔助類。也可以直接使用List類型C和D是啥意思呢?
interface Type<T> {}
class C implements Type<Type<? super C>> {}
class D<P> implements Type<Type<? super D<D<P>>>> {}
public abstract class Enum<E extends Enum<E>> { ... }有了上面的類型聲明,一個實際的enum實現(xiàn)只是語法糖:
// 這樣的聲明記住上面的這點后,回到我們的2個類型聲明上,下面的代碼可以編譯通過嗎?
enum MyEnum {}
// 實際只是下面寫法的語法糖:
class MyEnum extends Enum<MyEnum> { ... }
class Test {很難的問題,Ross Tate回答過這個問題,答案實際上是不確定的:
Type<? super C> c = new C();
Type<? super D<Byte>> d = new D<Byte>();
}
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>>的子類嗎?試著在你的Eclipse中編譯上面的代碼,會Crash!(別擔心,我已經(jīng)提交了一個Bug)
步驟 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>>>
步驟 . . . (進入永遠的展開中)
class Test<T extends Serializable & Cloneable> {綁定到類Test的實例上的泛型類型參數(shù)T必須同時實現(xiàn)Serializable和Cloneable,比如,String不能做綁定,但Date可以:
}
// 編譯不通過!Java 8保留了這個特性,你可以轉(zhuǎn)型成臨時的類型交集,這有什么用?幾乎沒有一點用,但如果你想強轉(zhuǎn)一個lambda表達式成這樣的一個類型,就沒有其它的方法了,假定你在方法上有了這個蛋疼的類型限制:
Test<String> s = null;
// 編譯通過
Test<Date> d = null;
<T extends Runnable & Serializable> void execute(T t) {}你想一個Runnable同時也是個Serializable,這樣你可能在另外的地方執(zhí)行它并通過網(wǎng)絡(luò)發(fā)送它,lambda和序列化都有點古怪。
execute((Serializable) (() -> {}));則這個lambda表達式不再是一個Runnable。
execute((Runnable & Serializable) (() -> {}));我們不得不說,Java中包含的詭異在程度上僅僅被它解決問題的能力超過。
關(guān)鍵詞:內(nèi)部
微信公眾號
版權(quán)所有? 億企邦 1997-2022 保留一切法律許可權(quán)利。