主页»Java WEB»关于 Java 你不知道的 10 件事

关于 Java 你不知道的 10 件事

来历:oschina 发布时刻:2017-05-06 阅览次数:

  作为 Java 书呆子,比起有用技能,咱们会对介绍 Java 和 JVM 的概念细节更感兴趣。因而我想引荐 Lukas Eder 在 jooq.org 宣布的原创著作给咱们。

  你是从很早开端就一向运用 Java 吗?那你还记得它的曩昔吗?那时,Java 还叫 Oak,OO 仍是一个热门话题,C++ 的 folk 者以为 Java 是不或许火起来,Java 开发的小应用程序 Applets 还遭到注重。

  我敢打赌,下面我要介绍的这些事,有一半你都不知道。下面让咱们来深化探究 Java 的奥秘之处。

 1. 没有检查反常这种作业

  没错!JVM 不会知道这些作业,只要 Java 句子知道。

  现在咱们都以为检查反常是个过错。正如 Bruce Eckel 在布拉格 GeeCON 落幕时所说,Java 之后再没其他言语检查反常,乃至 Java 8 在新的 Stream API 中也不再干这个作业(假定你的 Lambda 运用 IO 和 JDBC,这其实仍是有点苦楚)。

  怎么证明 JVM 并不清楚检查反常一事?试试下面的代码:

public class Test {

    // No throws clause here
    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 就能办到。

  这篇文章能够看到更具体的相关内容,或许在 Stack Overflow 上看

 2. 你能够界说仅在回来值有差异的重载函数

  这样的代码无法编译,对不?

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

  对。 Java 言语不答应两个办法在同一个类中“等效重载”,而疏忽其比方throws自居或回来类型等的潜在的差异。

  检查 Class.getMethod(String, Class...) 的 Javadoc。 其间阐明如下:

请留意,类中或许有多个匹配办法,由于 Java 言语制止在一个类声明具有相同签名但回来类型不同的多个办法,但 Java 虚拟机并不是如此。虚拟机中添加的灵活功能够用于完成各种言语特征。例如,能够用桥接办法完成协变参回来; 桥接办法和被重写的办法将具有相同的签名但具有不同的回来类型。

  哇哦,有道理。实践上下面的代码暗藏着许多作业:

abstract class Parent<T> {
    abstract T x();
}

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

  来看看为 Child 生成的字节码:

// Method descriptor #15 ()Ljava/lang/String;
// Stack: 1, Locals: 1
java.lang.String x();
  0  ldc </String><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]

  其实在字节码中 T 真的仅仅 Object。这很好了解。

  组成的桥办法实践是由编译器生成的,由于 Parent.x() 签名中的回来类型在实践调用的时分正好是 Object。在没有这种桥办法的情况下引进泛型将无法在二进制下兼容。因而,改动 JVM 来答应这个特性所带来的苦楚会更小(副效果是答应协变凌驾于全部之上) 很聪明,不是吗?

  你看过言语内部的细节吗?无妨看看,在这儿会发现更多很有意思的东西

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

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

  你以为这很张狂?幻想在上面运用 JSR-308 / Java 8 类型注解 。语法的或许性指数激增!

@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周的假日之前。

  对你来说,上面的内容在你的实践运用中找到了吧。

 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);

  可是,现实并非如此。咱们来测验一下就知道了。

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

  输出成果:

1.0
1

  由此可见,三目条件运算符会在有需求的情况下,对操作数进行类型提高。留意,是只在有需求时才进行;不然,代码或许会抛出 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. 你还没搞懂复合赋值运算符

  很古怪吗?来看看下面这两行代码:

i += j;
i = i + j;

  直观看来它们等价,是吗?但可其实它们并不等价!JLS 解说如下:

E1 op= E2 办法的复合赋值表达式等价于 E1 = (T)((E1) op (E2)),这儿 T 是 E1 的类型,E1 只核算一次。

  十分好,我想引证 Peter Lawrey Stack Overflow 上的对这个问题的答复

  运用 *= 或 /= 来进行核算的比方

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'

  现在看到它的效果了吗?我会在应用程序中对字符串进行乘法核算。由于,你懂的...

 6. 随机整数

  现在有一个更难的谜题。不要去看答案,看看你能不能自己找到答案。假定运转下面的程序:

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

  … “有时分”,我会得到下面的输出:

92
221
45
48
236
183
39
193
33
84

  这怎么或许??

  . spoiler… 持续答复…

  好了,答案在这儿 (https://blog.jooq.org/2013/10/17/add-some-entropy-to-your-jvm/),这有必要经过反射重写 JDK 的 Integer 缓存,然后运用主动装箱和拆箱。不要在家干这种作业!或许,咱们应该换种办法进行此类操作。

  我在4周前做最终一次提交的时分

 7. GOTO

  这是我的独爱之一。Java也有GOTO!输入下试试……

int goto = 1;

  将输出:

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

  这是由于goto是一个未运用的关键字, 仅仅是为了以防万一……

  但这不是最令人兴奋的部分。令人兴奋的部分是你能够运用 break、continue 和符号块来完成 goto 功用:

  向前跳:

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  ..

 8. Java 有类型别号

  其它言语 (比方 Ceylon) 中,咱们很简单为类型界说别号:

interface People => Set<Person>;

  这儿产生了 People 类型,运用它就跟运用 Set<Person> 相同:

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

  Java 中咱们不能在顶层效果域界说类型别号,可是咱们能够在类或办法效果域中干这个作业。假定咱们不喜欢 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 类效果域内 Integer 被赋予 I 这样的 “别号”,相似地,Long 在 x() 办法中被赋予 L 这样的 “别号”。之后咱们能够这样调用办法:

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

  这种技能当然不太会受注重。这种情况下,Integer 和 Long 都是 final 类型,也便是说,I 和 L 是现实上的别号(基本上赋值兼容性只需求考虑一种或许性)。假定咱们运用非 final 类型 (比方 Object),那便是一般的泛型。

  这些花招现已玩够了。现在来看看真实了不得的东西!

 9. 某些类型的联系并不确认!

  好了,这会很有目共睹,先来杯咖啡提提神。考虑一下下面两个类型:

// A helper type. You could also just use List
interface Type<T> {}

class C implements Type<Type <? super C>> {}
class D<P> implements Type<Type <? super D<D<P>>>> {}

  现在告诉我,类型 C 和 D 究竟是什么?

  它们存在递归,是一种相似 java.lang.Enum (但有稍微不同)的递归办法。看看:

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

  在上面的描绘中,enum 实践上仅仅单纯的语法糖:

// This
enum MyEnum {}

// Is really just sugar for this
class MyEnum extends Enum<MyEnum> { ... }

  认识到这一点之后咱们回过头来看看前面说到的两个类型,下面的代码会编译成什么样?

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

  十分难答复的问题,不过 Ross Tate 现已答复了。这个问题的答案是不行断定的:

  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)

  然后:

  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)

  在 Eclipse 中试着编译一下,它会溃散! (不必忧虑,我提交了 BUG 陈述)

  让这个作业沉下去…

Java 中某些类型的联系是不明确的!

  假定你对 Java 这个用法感到古怪之余也感兴趣,就去看看 Ross Tate 写的 “在 Java 的类型体系中运用通配符” (与 Alan Leung 和 Sorin Lerner 合著),咱们也在谈论泛型多态中的相关子类多态性

 10. 类型交集

  Java 有一个十分古怪的特性叫类型交集。你能够声明某个(泛型)类型,而它实践上是两个类型的交集,比方:

class Test<T extends Serializable & Cloneable> {
}

  绑定到 Test 类型实例的泛型类型参数 T 有必要完成 Serializable 和 Cloneable。比方,String 就不符合要求,但 Dete 满意:

// Doesn't compile
Test<String> s = null;

// Compiles
Test<Date> d = null;

  这个特性现已在 Java 8 中运用。这很有用吗?简直没用,可是假定你期望某个 Lambda 表达式是这种类型,还真没其他办法。假定你的办法有这种张狂的类型束缚:

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

  你想经过履行它得到一个能够序列化 (Serializable) 的 Runnable 方针。Lambda 和序列化也有点古怪。

  Lambda 能够序列经

假定 Lambda 的方针类型和参数类型都能够序列化,那么你能够序列化这个 Lambda

  可是即便是这样,他们都不能主动完成 Serializable 符号接口。你有必要强制转化类型。可是当你只扔给 Serializable 时...

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

  ... 那么 lambda 将不再是 Runnable 的。

  因而要把它转化为两种类型:

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

 定论

  一句话总结这篇文章便是:

Java 恰好是一种看起来奥秘的言语,其实不然。

  本文地址:https://www.oschina.net/translate/10-things-you-didnt-know-about-java

  原文地址:https://www.sitepoint.com/10-things-you-didnt-know-about-java/

QQ群:凯发娱乐官网官方群(515171538),验证音讯:10000
微信群:加小编微信 849023636 邀请您参加,验证音讯:10000
提示:更多精彩内容注重微信大众号:全栈开发者中心(fsder-com)
m88 188bet uedbet 威廉希尔 明升 bwin 明升88 bodog bwin 明升m88.com 18luck 188bet unibet unibet Ladbrokes Ladbrokes casino m88明升 明升 明升 m88.com 188bet m88 明陞 uedbet赫塔菲官网 365bet官网 m88 help
188bet www.188bet.com bwin 平博 unibet 明升 188bet uk Ladbrokes 德赢vwin 188bet m88.com w88 平博88 uedbet体育 188bet 188bet 威廉希尔 明升体育app 平博88 M88 Games vwin德赢 uedbet官网 bodog fun88 188bet
网友谈论(共0条谈论) 正在载入谈论......
沉着谈论文明上网,回绝歹意咒骂 宣布谈论 / 共0条谈论
登录会员中心