关于 Java 你不知道的十件事

作为Java 控,我们总是对不太可能直接使用,但能使我们更了解 Java 和 Java 虚拟机(Java Virtual Machine,JVM) 的晦涩细节感兴趣。这也是我将 Lukas Eder 在 jooq.org 上写的这篇文章发布出来的原因。

你在Java发布的时候就开始使用了吗?还记得那时它叫“Oak”,面向对象也 (Object Oriented, OO )还是个热门话题,C++ 程序员们觉得 Java 完全没机会成功,Applet的出现也是一件新鲜大事?

我打赌下文中至少一半的内容你都不知道。让我们来看看这些令人惊喜的 Java 细节吧。

1. 受检异常(checked exception)这件事是不存在的

是这样的,JVM 完全不知道这件事,都是Java语言做的[只有Java语言这么干]。

现在,异常检查被公认为是个错误,正如 Brue Eckel 在布拉格的 GeeCON 大会上的闭幕词中所说, Java 后的其他语言都不再使用异常检查了,就连 Java 8 都不愿在新的 Stream API 中使用它了(当你在 lambda 表达式中使用 IO 或者 JDBC 时,是很痛苦的)。

你想要证明 JVM 不知道异常检查这件事吗?尝试以下代码:

这个不仅会编译,还会抛出 SQLException ,你甚至不需要 Lombok 的 @SneakyThrows 标签。

更多详情请参考这篇文章,或者 Stack Overflow 上的这篇文章。

2. 可以使用不同的返回值类型来重载方法

以下代码是编译不过的,对吧?

是的,Java 不允许在一个类中通过不同的返回值类型和异常语句来重载方法。

不过稍等,Java 文档中关于 Class.getMethod(String, Class…) 这样写道:

请注意,在一个类中会有多个匹配的方法,因为虽然 Java 语法规则禁止一个类中存在多个方法函数签名相同仅仅返回类型不同,但 JVM 允许。这样提高了 JVM 的灵活性以实现各种语言特性。例如,可以用桥接方法(bridge method)来实现方法的协变返回类型,桥接方法和被重载的方法可以有相同的函数签名和不同的返回值类型。

喔,这是合理的。事实上,以下代码就是这样执行的,

Child 类编译后的字节码是这样的:

看,T 在字节码中就是 Object,这个很好理解。

合成桥接方法是编译器自动生成的,因为 Parent.x() 签名的返回值类型被认为是 Object。如果没有这样的桥接方法是无法在兼容二进制的前提下支持泛型的。因此,修改 JVM 是实现这个特性最简单的方法了(同时实现了协变式覆盖)。很聪明吧。

你明白语言的内部特性了吗? 这里有更多细节。

3. 这些都是二维数组

是的,这是真的。即使你人肉编译以上代码也无法立刻理解这些方法的返回值类型,但他们都是一样的,与以下代码类似:

你认为很疯狂是不是?如果使用 JSR-308 / Java 8 类型注解的话,语句的数量会爆炸性增长的!

类型注解,它的诡异性只是被他强大的功能掩盖了。

换句话说:

当我在4周假期之前的最后一次代码提交中这么做的话

为以上所有内容找到相应的实际用例的任务就交给你啦。

4. 你不懂条件表达式

你以为你已经很了解条件表达式了吗?我告诉你,不是的。大多数人会认为以下的两个代码片段是等效的:

与下边的等效吗?

答案是并非如此,我们做个小测试。

是的,在确有必要的情况下,条件表达式会升级数字类型。你希望这个程序抛出一个空指针异常吗?

5. 你也不懂复合赋值运算符

很诡异吗?让我们来看以下两段代码:

直觉上,他们是等价的吧?事实上不是,Java 语言规范(Java Language Standard,JLS)中这样写道:

符合赋值表达式 E1 op= E2 E1 = (T)((E1) op (E2)) 是等价的,这里 T E1 的类型,期望 E1 只被求值一次。

很美吧,我想引用 Peter Lawrey 在 Stack Overflow 上回复,

这种类型转换很好的一个例子是使用 *= or /=

这个很有用吧?我会将它们应用到我的程序里。原因你懂的。

6. 随机数

这更像是一道题,先别看结果。看你自己能否找到答案。当我运行以下程序时,

这是怎么回事?

答案已经在前面剧透了……

答案在这里( https://blog.jooq.org/2013/10/17/add-some-entropy-to-your-jvm/ ),需要通过反射来重载 JDK 中的 Integer 缓存,然后使用自动装箱(auto-boxing)和自动拆箱(auto-unboxing)。千万不要这么做,我们假设如果再做一次

我在4周假期之前的最后一次代码提交中这么做了

7. GOTO

这是我喜欢的一个。Java 有 GOTO 语句!输入以下:

但这不是最激动人心的部分。最给力的是你可以通过 break、continue 以及标签代码块来实现 goto。

向前跳转

8. Java 支持类型别名(type aliases)

在其它语言中(例如:Ceylon),定义类型别名是很容易的。

这个技术别太当真。在上边的例子里,Integer 和 Long 都是 final 类型, 也就是说 I 和 L 效果上是类型别名(大多数情况下,赋值兼容是单向的)。如果我们用非 final 的类型(例如 Object),就需要使用原来的泛型了。

以上是一些雕虫小技,下面才是真正有用的!

9. 一些类型之间的关系是不确定的!

这个会很有趣的,所以来一杯咖啡然后集中注意力。假设以下两种类型:

这是个很难的问题,Ross Tate 已经回答了。答案是不确定的:

C 是 <? super C> 的子类型吗?

尝试在 Eclipse 中编译以上代码,Eclipse 会挂掉的!(不要担心,我已经提过 bug 了)

理解下这个…

Java 中的一些类型的关系是不确定的!

如果你想了解更多关于 Java 的这个特性,请阅读 Ross Tate 与 Alan Leung 和 Sorin Lerner 共同编著的论文 “Taming Wildcards in Java’s Type System”或者我们自己总结的 correlating subtype polymorphism with generic polymorphism。

10. 类型交集(Type intersections)

Java 有个特性叫做类型交集。你可以声明一个泛型,这个泛型是两个类型的交集,例如:

Java 8 中保留了这个功能,你可以将类型转换为临时的类型交集。这有用吗?几乎没用,但如果你想要将lambda表达式强制转换为这个类型,除此就别无他法了。我们假设你的方法有这个疯狂的类型限制:

你想要同时支持 Runnable 和 Serializable,是为了以防万一要在网络的另一处执行它。Lambda 和序列化都有些古怪:

Lambda 表达式可以被序列化:

如果一个 lambda 表达式的返回值和输入参数可以被序列化,则这个表达式是可以被序列化的。

但即使这是真的,它也不会自动继承 Serializable 接口。你需要转换才能成为那个类型。但如果你只是转换为 Serializable…

lambda 就不支持 Runnable 了。

所以,

把它转换为两个类型:

结论

我经常只这么说 SQL,但现在要用下边的话来总结这篇文章了:

Java 语言的诡异性只是被它解决问题的能力掩盖了。

这是一个来自每日分享架构和项目管理的程序员的公众号,走心的文字,用心精选的每个技术以及业内文章。

每日励志名言,和你们一起成长,每天精选优质文章,每天推出一篇结构或技术好文,采用代码+文章+git源码。

让你在巨人的肩膀成长,不定期邀请高级程序员或架构师发推,我们一起成长。

原文发布于微信公众号 - java一日一条(mjx_java)

原文发表时间:2017-08-04

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏web编程技术分享

JavaScript: 零基础轻松学闭包(2)

2769
来自专栏ACM算法日常

最高的牛Tallest Cow(前缀和)- POJ 3263

FJ's N (1 ≤ N ≤ 10,000) cows conveniently indexed 1..N are standing in a line. E...

1071
来自专栏青玉伏案

设计模式(十):从电影院中认识"迭代器模式"(Iterator Pattern)

上篇博客我们从醋溜土豆丝与清炒苦瓜中认识了“模板方法模式”,那么在今天这篇博客中我们要从电影院中来认识"迭代器模式"(Iterator Pattern)。“迭代...

19710
来自专栏编程

Python利器之迭代器

各位小伙伴们 大家周四愉快 今天要和大家探讨一个 Python的特色功能 也是Python有别于其他变成语言的 强大利器 迭代器 迭代这一个词可能有的小伙伴不理...

1907
来自专栏鹅厂优文

Python 工匠:善用变量来改善代码质量

我一直觉得编程某种意义上是一门『手艺』,因为优雅而高效的代码,就如同完美的手工艺品一样让人赏心悦目。

98210
来自专栏C语言及其他语言

[每日一题]大、小写问题

题目描述 输入一串字符,将其中的大写变成小写,若不为大写则原样输出 输入 任意字符串(长度在100以内)以回车表示输入结束 输出 将其中的大写 输出相应的小写,...

2886
来自专栏Java学习网

困扰开发人员的八大代码异常问题

代码异常对于每个开发人员时时刻刻要面对的问题,兴奋的写完一段代码,信心满满的运行一下,结果一个异常给你当头 一棒,你还得意吗?异常处理的好不好,考验着每个开发人...

2764
来自专栏程序你好

Java和c++构造函数的区别是什么?

如果你是一个c++程序员,现在正在学习Java,你会发现这两种流行的面向对象编程语言有很多相似之处。这两种语言都支持抽象、封装、类、对象和其他OOP概念。但是,...

974
来自专栏好好学java的技术栈

轻松看懂java设计模式简单工厂模式

简单工厂模式属于类的创建型模式,又叫做静态工厂方法模式。通过专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。

1552
来自专栏Java呓语

工厂方法模式(延迟到子类来选择实现)

1、工厂方法模式理念介绍 2、它与简单方法模式的区别 3、推荐使用工厂方法的场景 4、在Android 源码中的应用

744

扫码关注云+社区