关于java-10件你不知道的事

来源:互联网 发布:线切割hl绘图编程退出 编辑:程序博客网 时间:2024/05/17 00:11

1. 像Runtime exceptions一样使用Checked exceptions

下面的代码是正确的!

public class Test1 {    // No throws clause here    public static void main(String[] args) {        doThrow(new SQLException());    }    static void doThrow(Exception e) {        Test1.<RuntimeException>doThrow0(e);    }    @SuppressWarnings("unchecked")    static <E extends Exception>    void doThrow0(Exception e) throws E {        throw (E) e;    }}


2. 在一个类中可以做到重载只有返回类型不同的方法

直接按下面这个类code,是错误的:

class Test {    Object x() { return "abc"; }    String x() { return "123"; }}
它会有编译错误:x() is already defined。因为java语言不允许只有返回类型不同的重载,假设允许,请看这个调用:

void main() {    x();}
那么这个方法并不确定它调的哪个方法。

但是...等等,查看java文档,看到Class.getMethod(String, Class...)有如下描述:

“Note that there may be more than one matching method in a class because while the Java language forbids a class to declare multiple methods with the same signature but different return types, the Java virtual machine does not. This increased flexibility in the virtual machine can be used to implement various language features. For example, covariant returns can be implemented with bridge methods; the bridge method and the method being overridden would have the same signature but different return types.”

有桥这个概念,好,那按下面方法code:

abstract class Parent<T> {    abstract T x();} class Child extends Parent<String> {    @Override    String x() { return "abc"; }}
然后编译,查看Child类的字节码,可以看到如下:

Compiled from "Child.java"public class Child extends Parent<java.lang.String> {public Child();    java.lang.String x();    java.lang.Object x();}
看到了吧,其实,在编译后的字节码中,T就是Object类。这样就好理解了。


3. 所有这些其实都是二维数组

先上代码:

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

上面三个变量,其实都是一样的,都是二维数组。来个更疯狂的,用到JSR-308 / Java 8 中的 type annotations,可如下code:

@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 [] = {{}};}


4. 你并不了解二元表达式

看下面的例子:

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

o1和o2一样么?,答案是错误的!

写个输出语句:

System.out.println(o1.getClass() + "   " + o1);System.out.println(o2.getClass() + "  " + o2);
输出结果是:

class java.lang.Double   1.0
class java.lang.Integer  1

如果需要提升的话,二元表达式会对数字类型自动提升!

你能想象下面的代码会报空指针异常吗?

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


5. 你也不了解组合运算符

你觉得下面两个语句一样吗:

i += j;i = i + j;

答案是不一样!JLS(java语言规范)描述道:

“A compound assignment expression of the form E1 op= E2 is equivalent to E1 = (T)((E1) op (E2)), where T is the type of E1, except that E1 is evaluated only once.”

E1 op=E2结果会强转为E1类型!所以有:

byte b = 10;b *= 5.7;System.out.println(b); // prints 57
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);}

“有时候”,我们可能得到下面的结果:

12626371711324070119149193
你想说,这怎么可能呢!

是的,有可能!只需要重写JDK中的java.lang.Integer.IntegerCache即可,示例代码:

import java.lang.reflect.Field;import java.util.Random;public class Entropy {    public static void main(String[] args)            throws Exception {        // Extract the IntegerCache through reflection        Class<?> clazz = Class.forName(                "java.lang.Integer$IntegerCache");        Field field = clazz.getDeclaredField("cache");        field.setAccessible(true);        Integer[] cache = (Integer[]) field.get(clazz);        // Rewrite the Integer cache        for (int i = 0; i < cache.length; i++) {            cache[i] = new Integer(new Random().nextInt(cache.length));        }        // Prove randomness        for (int i = 0; i < 10; i++) {            System.out.println((Integer) i);        }    }}



7. GOTO

在java中,想使用goto关键字,不能直接用,只能用continue、break等来代替实现其语义。

但是,其实,java中也有goto关键字!follow me,先code以下代码:

<pre name="code" class="java">public static void main(String[] args) {    boolean check = false;    label: do {        // do stuff        if (check) continue label;       // do more stuff        break label;    } while(true);}

然后编译,查看字节码,可以看到有:

public static void main(java.lang.String[]);    Code:       0: iconst_0       1: istore_1       2: iload_1       3: ifeq          9       6: goto          2       9: return
可以看到真的有goto!向前跳转ifeq,向后跳转正是goto。


8. java也有类型别名

其他语言(比如:Ceylon),可以很容易的定义类型别名:

interface People => Set<Person>;
并且People和Set<Person>值可以互换:

People?      p1 = null;Set<Person>? p2 = p1;People?      p3 = p2;
对于java,在顶层是不能这样定义类型别名的。但是我们可以在类或方法范围定义。

比如我们可以这么写来把Integer、Long分别用简写I、L替代:

class Test<I extends Integer> {    public <L extends Long> void x(I i, L l) {        System.out.println(i.intValue() + ", " + l.longValue());    }}
在上面的代码中,我们把Integer在Test类中替换为I,把Long在x()方法中替换为L。

当然了,高手都知道,这是范型...


9. 有些类型的关系是不确定的

先定义两个类:

interface Type<T> {}class C implements Type<Type<? super C>> {}class D<P> implements Type<Type<? super D<D<P>>>> {}
那么问题来了,C和D的类型是什么呢?

这其实是一种递归,类似enum类型,比如:

public class Enum<E extends Enum<E>> { }
它其实是这个:

// Thisenum MyEnum {}// Is really just sugar for thisclass MyEnum extends Enum<MyEnum> {  }

回到之前C,D类的问题,下面这个会编译成功么?

class Test {    Type<? super C> c = new C();    Type<? super D<Byte>> d = new D<Byte>();}
答案肯定是编译不过,会报Error:java: java.lang.StackOverflowError,甚至eclipse下这么写会crash!

其实下面两个问题是不确定的:
(1) C  是 Type<? super C> 的子类吗?

Step 0) C <?: Type<? super C>Step 1) Type<Type<? super C>> <?: Type (inheritance)Step 2) C  (checking wildcard ? super C)Step . . . (cycle forever)
(2)D 是 Type<? super D<Byte>> 的子类吗?

Step 0) D<Byte> <?: Type<? super C<Byte>>Step 1) Type<Type<? super D<D<Byte>>>> <?: Type<? super D<Byte>>Step 2) D<Byte> <?: Type<? super D<D<Byte>>>Step 3) Type<type<? super C<C>>> <?: Type<? super C<C>>Step 4) D<D<Byte>> <?: Type<? super D<D<Byte>>>Step . . . (expand forever)


10 类型交集

java中有个很奇怪的特性叫类型交集,就是你可以声明一个是两个类型交集的(范型)类。举例:

class Test<T extends Serializable & Cloneable> {}
那么Test类实例中的范型参数T必须同时实现Serializable和Cloneable,必须String类就不行,Date类则可以。

// Doesn't compileTest<String> s = null; // CompilesTest<Date> d = null;

或者,还可以这么用:

<T extends Runnable & Serializable> void execute(T t) {}
配合java8下的lambda表达式使用:

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

完毕!

英文原文:https://blog.jooq.org/2014/11/03/10-things-you-didnt-know-about-java/














1 0
原创粉丝点击