你不知道Java的10件事

本文揭示了Java编程中鲜为人知的秘密,包括检查异常、重载方法、条件表达式、复合赋值运算符、随机整数生成、GOTO语句、类型别名、不可判定的关系类型以及交集类型等。每部分都附有代码示例,帮助读者深入理解Java的底层机制。

我敢打赌你肯定不知道以下一半的事情。现在,让我们开始一些Java内部运作的大惊喜。

1. 并没有所谓的检查异常

没错,Java虚拟机(JVM)不知道异常,只有Java语言自己知道.

如今,每个人都同意检查异常是一个错误。正如Bruce Eckel 在Prague的 GeeCON 闭幕词上所说,在Java之后没有其他语言会约定使用检查异常,甚至 Java 8  新的流API都不再包含这些(在lambdas表达式中使用IO 或者JDBC时,是有点痛苦)。

(译者注:Java8 引入了lambads表达式,使用它能够使设计代码更简洁)

使用下面的代码可以证明JVM并不知道这些:


上面的代码不仅能通过编译,而且抛出了异常SQLException,你甚至不需要使用Lombok’s 的注解@SneakyThrows。

(译者注:Lombok是一个使用注解简化Java代码的库)

(译者注:如果你在方法中没有声明throws子句,当程序出现异常时Lomok 注解@SneakyThrows 会偷偷的抛出检查异常)点这里获取关于这篇文章的更多细节,或 Stack Overflow。

2. 重载仅返回类型不同的方法

这不能通过编译,不是吗?


是的。Java语言不允许在同一个类中存在"等价覆盖" 的两个方法.不管它们有不同的 throws 子句或是不同的返回类型。

在 Javadoc  Class.getMethod(String,Class...)。上面有如下说明:

注意:它在一个类中可能会匹配到多个方法,虽然Java语言禁止在一个类中声明多个签名相同而仅返回类型不同的方法,但是Java虚拟机不会如此。在Java虚拟机中,这种增强 的灵活性被用于实现多样的语言特性。例如,协变返回值类型能通过桥接方法实现;桥接方法和被覆盖的方法将有相同的签名,不同的返回类型。

这很有意义,事实上,下面的语句所发生的几乎就是这样。


查看生成的字节码:


因此,很好理解 T 在字节码中就是一个对象。

这个合成的桥接方法实际上是由编译器生成的,因为Parent.x()的返回类型签名在某些调用位置可能会被期望成 Object。

加入的泛型没有这样的桥接方法就不可能以一种二进制的方式兼容。

因此,改变JVM去支持这种特性只需很少的代价(同样也允许协变性压倒一切负效应)很聪明,不是吗?

你分析过语言的细节和内幕吗?点这里发现更多有趣的细节。

3. 这些都是二维数组


这是真的。你可能无法立刻理解上面方法的返回类型,但它们都是一样的。类似于下面的方法:


你肯定认为这疯了。想象一下,为上面的方法使用 JSR-308 / Java 8 的类型注解。句法的数量将会激增。


4. 你不明白条件表达式

你认为你在使用条件表达式时明白一切吗?让我告诉你吧,你不明白。大多数人都会认为下面两个代码片段是等价的:


真的一样吗?


不一样。我们来验证一下:


程序的输出结果为:

1.0
  1

没错!条件运算符在必要的时候将实现数据类型的提升。下面的语句将会抛出一个NullPointException。


点这里获取更多细节。

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

看下面的代码:


乍一看它们应该是等价的,但事实上不是。见 JSL(Java语言规范):

复合赋值表达式 E1 op= E2 与 E1 = (T)((E1) op (E2)) 是等价的,T的类型与E1相同,此外 E1仅计算一次。

这真是太美了,我想引用Peter Lawrey's 关于堆栈溢出问题的回答:


6. 随机整数

这是一个难题。不要参看解答,你能独立的解决问题吗?

运行下面的代码:


我有时候会得到如下输出:

92
221
45
48
236
183
39
193
33
84

这怎么可能?

答案在这。通过反射覆盖 JDK's 的 Integercache,然后使用自动装箱和自动拆箱机制。


运行上面的代码,你就可以得到类似的结果了。

7. GOTO

在Java中编写下列语句:


程序会编译失败,错误信息为:


因为goto是一个未使用的关键字。

虽然无法在源码中直接使用 goto 但是我们可以通过 break,continue 和 标记块实现。

字节码的 goto;

向前跳转:


它的字节码为:


向后跳转:


它的字节码为:


看,是不是出现了goto

8. Java 的类型别名

在其他语言中可以很简单的使用类型别名,例如Ceylon:

(译者注:Ceylon是一种新兴的计算机编程语言,号称"Java杀手",它不是Java,而是一种受Java影响的新语言。)


以这种方式构造的 People 可以被 Set<Person> 替换:


在Java中,我们无法在全局范围上定义类型别名。由于存在 class 域或方法域,

亦可以定义。考虑两个我们很不喜欢的命名 Integer 和 Long,为它们取个简短的名称 I 和 L:


上面的程序中,在 TestClass 域内 定义 Integer 别名为 I,在 x() 方法域中定义Long

别名为 L。我们可以这样使用上面的方法:


显然这种技术不值得重视。在这个例子中,Integer 和 Long 都是 final 类型,也就意味着类型 I 和 L 是有效的别名(那样的话,程序与类型兼容性也就无缘了)。如果我们使用的不是 final 类型,那么就应该使用泛型。

看够了这些无聊的把戏了吧!来点厉害的。

9. 一些不可判定的关系类型

让我们来点咖啡,集中你的注意力,这可是很时髦的东西。考虑下面两个类型:


现在,你知道 C 和 D 的类型吗?

它们包含了递归,Java、lang、Enum 也是递归的。这两种方式有些相似,但略有不同。


由上面的规范可知,Enum 实际上是由一种糖衣语法实现的。

(译者注:糖衣语法,指计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用)


考虑到这一点,让我们回到先前定义的两个类型。下列代码能编译成功吗?


很难回答,Ross Tate 有个答案,不可判定:


尝试在你的Eclipse中编译上面的代码,崩溃了吧!

看这句话:

有些类型关系在Java中是不可判定的。

如果你有兴趣了解这个奇怪的Java特性更多细节,就读一读 Ross Tate的论文"Taming Wildcards in Java's Type System"(与 Alan Leung 和 Sorin Lerner 合著),或者自己思考关联子类型多态性与泛型多态性。

10. 交集类型

Java有一个很独特的特性称作交集类型(type intersections)。你可以声明一个泛型,它由两个类型的交集构成。例如:


要使用绑定的泛型参数 T 去实例化Test 类,这个参数就必须同时实现 Serializable 和 Cloneable。例如 String 不是,而 Date 是:


为了让你有一个专门的交集类型,这种特性在Java8中得到了重用。如何用它呢?几乎没用。但是,当你在 lambda 表达式中强行应用这样的类型时,就只有此种方法可行。

假设你在方法中使用了这种疯狂的类型约束:


你需要一个实现了Runnable 和Serializable 的对象。为了让你可以在某些地方执行它,或是发送它,

Lambads 可以被序列化:

如果一个lambda 表达式的目标类型和所需参数是可序列化的,那么这个表达式就能序列化。

即使这是真的,那也不会自动的实现序列化标记接口,所以你必须自己动手。

现在你有一个可序列化的,但是它不能被执行。


所以你必须自己加上:

<think>好的,我现在要回答用户关于Java知识的介绍。首先,用户希望我作为Java专家,列出我所了解的Java相关知识点,然后根据这些内容提出相关问题。我需要确保回答全面,覆盖Java的核心概念和高级主题,同时按照用户的要求,每个部分之后给出四个相关问题。 首先,我应该从基础开始,比如Java语言基础、OOP概念、异常处理、集合框架、多线程、IO/NIO、网络编程、JDBC、JVM、设计模式、新特性、框架、工具和高级主题。每个部分需要简要说明,然后按照示例的格式,分点列出关键内容,并给出解释。 接下来,我需要确保每个知识点下的子点足够详细,但过于冗长。例如,在OOP部分,要提到封装、继承、多态、抽象类、接口等。在多线程中,要包括线程创建方式、同步机制、线程池等。同时,要注意使用正确的术语,比如JVM的内存结构、GC算法等。 然后,根据这些知识点,生成相关问题。问题需要与提到的内容直接相关,帮助用户深入理解每个主题。例如,如果提到集合框架,问题可以是ArrayList和LinkedList的区别,或者HashMap的工作原理。 最后,检查格式是否正确,代码部分是否仅限Java,问题是否按照要求用§§符号分隔,并且每个问题编号正确。确保没有使用Markdown格式,语言口语化,但结构清晰。</think><< Java是一门广泛使用的高级编程语言,以下是Java相关的主要知识点概览: ### 一、Java语言基础 1. **数据类型与运算符** - 基本类型(`int`, `double`, `boolean`等)和引用类型(对象、数组) - 自动装箱/拆箱(如`Integer`与`int`的转换) 2. **流程控制** - `if-else`、`switch`、循环(`for`、`while`、`do-while`) 3. **字符串处理** - `String`可变性,`StringBuilder`和`StringBuffer`的区别 4. **数组与集合** - 数组初始化、集合框架(`List`, `Set`, `Map`) ### 二、面向对象编程(OOP) 1. **四大特性** - 封装(访问修饰符`private`/`protected`/`public`) - 继承(`extends`)、多态(方法重写与重载) - 抽象类(`abstract class`)与接口(`interface`) 2. **类与对象** - 构造方法、静态成员(`static`)、实例成员 3. **设计原则** - SOLID原则(如单一职责、开闭原则) ### 三、异常处理 1. **异常分类** - 检查异常(`IOException`)与未检查异常(`RuntimeException`) 2. **处理机制** - `try-catch-finally`、`throw`和`throws` 3. **自定义异常** - 继承`Exception`或`RuntimeException` ### 四、集合框架 1. **核心接口** - `List`(`ArrayList`、`LinkedList`)、`Set`(`HashSet`、`TreeSet`) - `Map`(`HashMap`、`ConcurrentHashMap`) 2. **迭代器与比较器** - `Iterator`遍历、`Comparator`排序 ### 五、多线程与并发 1. **线程创建** - 继承`Thread`类、实现`Runnable`或`Callable`接口 2. **同步机制** - `synchronized`关键字、`ReentrantLock`、`volatile`变量 3. **线程池** - `ExecutorService`、`ThreadPoolExecutor` ### 六、IO/NIO 1. **传统IO** - `InputStream`/`OutputStream`、`Reader`/`Writer` 2. **NIO** - `Buffer`、`Channel`、`Selector`非阻塞IO ### 七、网络编程 1. **Socket编程** - `Socket`和`ServerSocket`实现TCP通信 2. **HTTP客户端** - `HttpURLConnection`、第三方库(如Apache HttpClient) ### 八、JDBC与数据库 1. **数据库连接** - `DriverManager`、`DataSource`、连接池(如HikariCP) 2. **务管理** - ACID特性、`commit()`和`rollback()` ### 九、JVM与性能优化 1. **内存模型** - 堆、栈、方法区、GC算法(标记清除、G1) 2. **调优工具** - JConsole、VisualVM、MAT分析内存泄漏 ### 十、设计模式 1. **创建型模式** - 单例模式(双重检查锁)、工厂模式 2. **结构型模式** - 适配器模式、装饰器模式 3. **行为型模式** - 观察者模式、策略模式 ### 十一、新特性(Java 8+) 1. **Lambda表达式** - 简化匿名内部类,如`(a, b) -> a + b` 2. **Stream API** - 集合操作:`filter`, `map`, `reduce` 3. **模块化系统** - Java 9模块化(`module-info.java`) ### 十二、框架与工具 1. **Spring生态** - Spring Boot、Spring MVC、Spring Data JPA 2. **构建工具** - Maven、Gradle依赖管理 ### 十三、高级主题 1. **反射** - `Class`类、动态调用方法(`Method.invoke()`) 2. **注解** - 自定义注解(`@Retention`、`@Target`) 3. **函数式编程** - `Function`接口、方法引用(`::`操作符) ### 示例代码:多线程的两种实现方式 ```java // 继承Thread类 class MyThread extends Thread { @Override public void run() { System.out.println("Thread running"); } } // 实现Runnable接口 class MyRunnable implements Runnable { @Override public void run() { System.out.println("Runnable running"); } } public class Main { public static void main(String[] args) { new MyThread().start(); new Thread(new MyRunnable()).start(); } } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值