1. 概述

除了 Java,还有多种语言可以直接运行在 JVM 上,比如 KotlinGroovyScala

本文将深入对比 Java 和 Kotlin 这两种主流的 JVM 语言。它们各有优势和适用场景,尤其在现代开发中,Kotlin 的崛起让很多团队开始重新评估技术选型。我们不会泛泛而谈“谁更好”,而是从实际开发角度出发,看看各自有哪些不可替代的特性,以及切换时可能遇到的“坑”。


2. Java 特有特性

2.1. 受检异常(Checked Exceptions)

✅ Java 支持受检异常机制 —— 所有受检异常必须显式声明或捕获。这迫使开发者在编码阶段就处理潜在错误,有助于提升代码健壮性。

⚠️ 但这也常被诟病为“过度设计”,容易导致大量 try-catch 堆砌,反而影响可读性。

public void readFile() throws IOException {
    // 必须声明异常
}

2.2. 原始类型(Primitive Types)

✅ Java 提供了原生的原始类型(如 int, boolean, double 等),共八种:byte, short, int, long, float, double, char, boolean

这些不是对象,直接存储值,性能更高。在集合类中使用时会自动装箱/拆箱(Boxing/Unboxing)。

❌ 相比之下,Kotlin 统一了类型系统,所有类型都是对象,底层仍优化为原始类型,但对开发者透明。

2.3. 通配符类型(Wildcard Types)

✅ Java 泛型支持通配符 ?,用于表示未知类型,增强泛型灵活性。

常见用法:

  • List<?>:任意类型的列表
  • List<? extends Number>:Number 及其子类
  • List<? super Integer>:Integer 及其父类
public void printList(List<?> list) {
    for (Object item : list) {
        System.out.println(item);
    }
}

2.4. 三元运算符(Ternary Operator)

✅ Java 提供 ?: 三元操作符,是 if-else 的简洁替代方案,适合简单条件赋值。

String result = score >= 60 ? "及格" : "不及格";

⚠️ 注意:虽然编译后等价于 if-else,但嵌套三元表达式可读性差,建议避免。

2.5. 静态成员(Static Members)

✅ Java 支持 static 关键字,允许定义类级别的变量和方法,无需实例即可访问。

public class MathUtils {
    public static final double PI = 3.14159;
    public static int add(int a, int b) { return a + b; }
}

Kotlin 中没有 static,但可通过 companion object 或顶层函数实现类似效果。

2.6. 隐式扩展转换(Implicit Widening Conversions)

✅ Java 允许小范围数据类型自动转为大范围类型(如 intlong),无需显式强转。

int a = 100;
long b = a; // 自动提升,安全

⚠️ 但反向转换需手动强转,存在精度丢失风险。

2.7. 非私有字段(Non-private Fields)

✅ Java 允许定义 public 或包级可见的字段,方便直接访问。

public class Point {
    public int x, y; // 直接暴露字段
}

⚠️ 虽然方便,但破坏封装性,通常推荐使用 getter/setter。


3. Kotlin 特有特性

3.1. 数据类(Data Classes)

✅ Kotlin 使用 data 关键字定义数据类,编译器自动生成 equals()hashCode()toString()copy() 和组件函数。

data class User(val name: String, val age: Int)

相当于 Java 中需要手写或靠 Lombok 生成的一大堆样板代码。

📌 注:Java 14+ 引入了 record 类型,也支持类似功能,但不可变性更强。

3.2. 字符串模板(String Templates)

✅ Kotlin 支持字符串内嵌表达式,语法直观。

val name = "Alice"
val greeting = "Hello, $name! You are ${age + 1} years old."

比 Java 的 String.format() 或拼接更简洁,减少出错概率。

3.3. 主构造函数(Primary Constructors)

✅ Kotlin 类可以直接在类头定义主构造函数,参数可用于属性初始化。

class Person(val name: String, var age: Int)

简洁明了,避免冗长的构造器写法。也可定义多个次构造函数(secondary constructors)。

3.4. 单例对象(Singletons)

✅ Kotlin 使用 object 关键字一键声明单例,线程安全且懒加载。

object DatabaseManager {
    fun connect() { /*...*/ }
}
// 调用:DatabaseManager.connect()

相比 Java 中双重检查锁或静态内部类方式,Kotlin 实现既简洁又可靠。

3.5. 区间表达式(Range Expressions)

✅ 使用 .. 操作符创建区间,结合 in 判断非常自然。

for (i in 1..10) println(i)         // 正序
for (i in 10 downTo 1) println(i)   // 逆序
if (x in 1..100) println("InRange")

代码更具可读性,告别传统 for (int i = 0; i < n; i++) 的繁琐。

3.6. 操作符重载(Operator Overloading)

✅ Kotlin 允许对预定义操作符进行重载,使自定义类型也能使用 +, -, [] 等。

data class Vector(val x: Int, val y: Int) {
    operator fun plus(other: Vector) = Vector(x + other.x, y + other.y)
}

val v1 = Vector(1, 2)
val v2 = Vector(3, 4)
val v3 = v1 + v2 // 结果:Vector(4, 6)

⚠️ 要合理使用,避免滥用导致语义混乱。

3.7. 隐式委托(Implicit Delegation)

✅ 借助 by 关键字实现组合优于继承的设计模式。

interface Printer { fun print() }
class RealPrinter : Printer { override fun print() { println("Printing...") } }

class PrinterWrapper(private val printer: Printer) : Printer by printer

val wrapper = PrinterWrapper(RealPrinter())
wrapper.print() // 直接调用被委托对象的方法

有效规避多重继承问题,同时简化代理逻辑。

3.8. 尾随 Lambda(Trailing Lambdas)

✅ 当函数最后一个参数是 lambda 时,可将其移出括号外。

list.filter { it > 5 }.map { it * 2 }

等价于:

list.filter({ it > 5 }).map({ it * 2 })

这种语法让 DSL 构建(如 Android Jetpack Compose)变得极其优雅。

3.9. 单参数 Lambda 中的 it

✅ 若 lambda 只有一个参数,可用默认名称 it 引用。

strings.map { it.uppercase() }

省去命名烦恼,提升简洁度。但如果上下文不清晰,建议显式命名以提高可读性。

3.10. 中缀函数(Infix Notation)

✅ 标记为 infix 的单参数函数可省略点号和括号调用。

infix fun Int.times(str: String) = str.repeat(this)
val result = 3 times "Hi " // 输出:"Hi Hi Hi "

让某些操作看起来像自然语言,适合构建领域特定语言(DSL)。

3.11. 命名参数与默认参数

✅ Kotlin 支持函数参数带默认值,并允许调用时通过名称指定参数。

fun createUser(name: String, age: Int = 18, isActive: Boolean = true) { ... }

createUser("Bob")                          // 使用默认值
createUser("Alice", isActive = false)      // 跳过中间参数

彻底摆脱 Java 中常见的方法重载“爆炸”问题。

3.12. 延迟初始化(Lazy Initialization)

✅ 提供 lazy 委托实现延迟加载,首次访问才计算,且线程安全。

val expensiveData by lazy {
    loadFromNetwork() // 只会在第一次 get 时执行
}

适用于配置、资源初始化等耗时操作,避免启动开销。


4. Java vs. Kotlin 对比

4.1. 代码简洁性(Brevity)

维度 Java Kotlin
数据类 需手写 getter/setter data class 一行搞定
构造函数 多个重载构造器 主构造函数 + 默认参数
空安全 易引发 NPE 编译期检查,类型区分可空/非空

✅ Kotlin 显著减少样板代码,相同功能通常只需 Java 30%-50% 的行数。

4.2. 空安全(Null Safety)

❌ Java 中任何引用都可为 null,极易触发 NullPointerException

✅ Kotlin 在类型系统层面区分可空类型(String?)和非空类型(String),强制在编译期处理空值。

var name: String? = null
println(name?.length)      // 安全调用
println(name!!.length)     // 断言非空(可能崩溃)
name = "Kotlin"
println(name.length)       // 直接访问

这是 Kotlin 最被推崇的安全特性之一,能大幅降低线上崩溃率。

4.3. 扩展函数(Extension Functions)

❌ Java 要扩展已有类行为,只能继承或写工具类。

✅ Kotlin 支持为任意类添加扩展函数,无需修改源码。

fun String.isValidEmail(): Boolean {
    return this.contains("@")
}

"test@example.com".isValidEmail() // true

非常适合为标准库或第三方库补充实用功能,且调用方式如同原生方法。

4.4. 类型推断(Type Inference)

❌ Java 要求显式声明变量类型(虽有 var 局部变量,但仍有限制)。

✅ Kotlin 编译器能根据赋值自动推断类型。

val name = "Kotlin"    // 推断为 String
val list = listOf(1,2,3) // 推断为 List<Int>

代码更清爽,同时保持静态类型安全。

4.5. 智能类型转换(Smart Casting)

❌ Java 中类型判断后仍需手动强转。

if (obj instanceof String) {
    String s = (String) obj; // 必须显式转换
    System.out.println(s.toUpperCase());
}

✅ Kotlin 编译器自动完成类型转换。

if (obj is String) {
    println(obj.toUpperCase()) // obj 已自动转为 String
}

减少冗余代码,提升安全性。

4.6. 函数式编程支持

❌ Java 虽支持 Lambda 和 Stream API,但受限较多(如 checked exception 不兼容)。

✅ Kotlin 原生融合 OOP 与 FP 特性,支持高阶函数、内联函数(inline)、尾递归优化等。

inline fun retry(times: Int, block: () -> Unit) {
    repeat(times) { block() }
}

配合协程,在异步编程中表现尤为出色。

4.7. 协程支持(Coroutines)

❌ Java 多线程依赖 ThreadExecutorService,回调嵌套深,难管理。

✅ Kotlin 原生支持协程,以同步风格写异步代码,轻量高效。

suspend fun fetchData() {
    withContext(Dispatchers.IO) {
        // 执行网络请求
    }
}

协程比线程更轻量,适合高并发场景,已成为 Android 开发首选异步方案。

4.8. 密封类(Sealed Classes)

两者均支持密封类,用于限制类继承层级,常用于状态建模。

特性 Java Kotlin
子类位置限制 同模块 同文件
显式许可 使用 permits 列出允许的子类 文件内所有子类均可继承
switch 表达式匹配 Java 17+ 支持预览模式匹配 支持 when 表达式穷举匹配,编译期检查完整性

✅ Kotlin 的 when 配合 sealed class 更强大,常用于状态机、UI 渲染等场景。

4.9. 编译速度

❌ **Java 编译速度平均快 15%-20%**。

✅ 但在增量构建(Incremental Build)场景下,Kotlin 编译性能接近 Java。

⚠️ 大型项目初期编译较慢,建议开启 Gradle 的 Kotlin 增量编译和缓存优化。

4.10. 平台兼容性

目标平台 Java Kotlin
JVM
JavaScript ✅(Nashorn/Rhino) ✅(Kotlin/JS)
Native ✅(Kotlin/Native)
多平台 ✅(Kotlin Multiplatform)

✅ Kotlin 支持跨平台统一代码库,适合需要共享业务逻辑的多端项目(如 Android/iOS/Backend)。

4.11. 应用场景

场景 推荐语言 原因说明
企业级后端服务 Java / Kotlin Spring 生态成熟,Kotlin 更适合新项目
Android 开发 ✅ Kotlin Google 官方推荐,Jetpack Compose 原生支持
脚本/小型工具 Kotlin 脚本模式 .kts 启动快,语法简洁
高性能计算/低延迟系统 Java JIT 优化更成熟,GC 控制精细
跨平台移动应用 Kotlin Multiplatform 可共享核心逻辑

📌 总结:Android 新项目无脑选 Kotlin;后端可根据团队熟悉度选择,但 Kotlin 是趋势。


5. 总结

Java 和 Kotlin 各有千秋:

  • Java 成熟稳定,生态庞大,适合保守型企业级系统。
  • Kotlin 现代化、安全、简洁,特别适合 Android 和追求开发效率的新项目。

📌 技术选型不应非此即彼。很多团队采用 Java/Kotlin 混编策略,逐步迁移。关键是要理解两者的差异,在合适的地方用合适的工具。

📌 推荐学习路径:

  1. 掌握 Java 基础与 JVM 原理
  2. 学习 Kotlin 核心语法与特性
  3. 实践 Kotlin 在 Spring Boot 与 Android 中的应用
  4. 深入协程、DSL、Multiplatform 等高级主题

最终目标不是“站队”,而是成为能灵活运用多种语言解决问题的高级开发者。


原始标题:Java vs. Kotlin