主题
第 2 章 环境搭建 & 第一行 Kotlin
学习目标:能在自己的电脑上跑通 Kotlin 代码;理解 Kotlin 编译流程;会用 IDEA / 命令行 / Playground 三种方式写 Kotlin;理解一个 Kotlin 项目长啥样、为啥那样组织。
2.1 三种"试水"方式:先跑起来再说
不要一上来就装 IDEA + 配 Gradle,那是给做工程的人准备的。先用最快的方式让代码跑起来,建立信心。
2.1.1 方式 A:浏览器 Playground(5 秒入门,零安装)
打开 https://play.kotlinlang.org/ ,直接写代码点 ▶ 运行。
kotlin
fun main() {
println("Hello, Kotlin!")
}适合场景:
- 临时验证一个语法
- 看博客里的 demo 想试一下
- 完全没装环境时的应急方案
不适合:写超过 50 行的代码(没有自动补全、断点调试)。
2.1.2 方式 B:命令行 kotlinc(学习首选)
适合"想搞清楚编译产物长啥样"的人。
安装步骤(macOS / Linux)
bash
# 1. 必须先有 JDK 17+(kotlinc 依赖 JVM)
brew install openjdk@17 # macOS
# 或
apt install openjdk-17-jdk # Ubuntu
# 2. 装 Kotlin 命令行工具
brew install kotlin # macOS
# 或用 SDKMAN(推荐,跨平台)
curl -s "https://get.sdkman.io" | bash
sdk install kotlin 2.0.21Windows 用户
powershell
# 用 Scoop 或 Chocolatey
scoop install kotlin
# 或
choco install kotlinc验证安装
bash
kotlinc -version
# 输出:info: kotlinc-jvm 2.0.21 (JRE 17.0.10+7)第一段代码
bash
# 1. 写一个 Hello.kt
echo 'fun main() { println("Hello, Kotlin!") }' > Hello.kt
# 2. 方式 1:编译成 jar 然后用 java 运行
kotlinc Hello.kt -include-runtime -d hello.jar
java -jar hello.jar
# → Hello, Kotlin!
# 3. 方式 2:直接运行 .kts 脚本(无需编译)
cat > hello.kts <<EOF
println("Hello from script!")
EOF
kotlinc -script hello.kts
# → Hello from script!
# 4. 方式 3:进入 REPL 交互式环境
kotlinc
# >>> println("hi")
# hi
# >>> :quit📌 理解一下:
-include-runtime把 Kotlin 标准库(约 5MB)也打进 jar,这样脱离 Kotlin 环境也能跑。生产项目一般用 Maven/Gradle 的依赖管理,不会用这个 flag。
2.1.3 方式 C:IntelliJ IDEA(生产首选)
JetBrains 自家 IDE,对 Kotlin 支持是最强的(毕竟同一家公司)。
下载
- 社区版(免费够用):https://www.jetbrains.com/idea/download/
- 安装时会问要不要装 Kotlin 插件 —— 必装(默认勾上的)
创建第一个项目
File → New → Project
├─ Name: my-kotlin-demo
├─ Location: ~/projects/my-kotlin-demo
├─ Language: Kotlin ← 选这个
├─ Build sys: Gradle ← 推荐 Gradle,新手也行(IDEA 帮你配)
├─ JDK: 17 (Temurin) ← 选 JDK 17 或更高
├─ Gradle DSL: Kotlin ← build.gradle.kts,写起来更顺
└─ 点击 Create第一次建项目时 IDEA 会自动下载 Gradle、Kotlin 编译器、依赖,网速慢的话可能要 5-10 分钟,请耐心等待右下角进度条。
💡 国内加速:在
~/.gradle/init.gradle.kts加阿里云镜像,能快 10 倍。第 2.4 节会专门讲。
2.2 一个最简单的 Kotlin 项目长啥样
新建好的项目目录结构(删掉无关的):
my-kotlin-demo/
├── build.gradle.kts ← Gradle 构建脚本(Kotlin DSL)
├── settings.gradle.kts ← Gradle 项目设置
├── gradle.properties ← Gradle 全局参数
├── gradlew / gradlew.bat ← Gradle Wrapper 启动器
├── gradle/
│ └── wrapper/ ← Gradle 版本锁定
└── src/
├── main/
│ └── kotlin/ ← 主代码
│ └── Main.kt ← 入口
└── test/
└── kotlin/ ← 测试代码2.2.1 Main.kt
kotlin
fun main() {
println("Hello World!")
val name = "Kotlin"
println("Hello, $name!")
}📌 跟 Java 不同:Java 必须有一个
public class Main包住,类名必须跟文件名同名;Kotlin 顶层就能写函数,文件名也不强制跟函数名同名。
2.2.2 build.gradle.kts 解读
kotlin
plugins {
kotlin("jvm") version "2.0.21" // Kotlin JVM 编译插件
application // 让我们能用 ./gradlew run 启动 main
}
group = "com.example"
version = "1.0-SNAPSHOT"
repositories {
mavenCentral() // 从 Maven 中央仓库下载依赖
}
dependencies {
testImplementation(kotlin("test")) // 测试用 kotlin-test 库
}
application {
mainClass.set("MainKt") // 入口(注意 Kt 后缀,下面解释)
}
tasks.test {
useJUnitPlatform()
}
kotlin {
jvmToolchain(17) // 用 JDK 17 编译
}为啥 mainClass 是 "MainKt"?
Kotlin 把 Main.kt 文件里的所有顶层函数 / 属性,都编译进一个叫 MainKt 的类(自动加 Kt 后缀)。所以 JVM 找入口时找的是 MainKt.main(),不是 Main.main()。
💡 你可以用
@file:JvmName("MyApp")改这个名字,比如把MainKt改成MyApp。
2.2.3 运行项目
bash
# IDE 方式:点 main 函数旁边的 ▶ 绿色三角
# 命令行方式
./gradlew run
# → > Task :run
# → Hello World!
# → Hello, Kotlin!
# 打包成可分发 zip / tar
./gradlew distZip
# 跑测试
./gradlew test2.3 编译流程:从 .kt 到字节码到运行
这是面试官常问的"Kotlin 是怎么跑起来的"。
源代码 编译 运行
┌────────────┐ ┌──────────────┐ ┌──────────┐
│ Hello.kt │ ────────► │ kotlinc 编译 │ ──────► │ JVM 执行 │
│ │ │ (含 K2) │ │ │
└────────────┘ └──────┬───────┘ └────▲─────┘
│ │
┌───────▼────────┐ │
│ HelloKt.class │ 进入 JAR 后 │
│ (字节码 + 元数据)│ ─────────────┘
└────────────────┘2.3.1 看看实际编译产物
bash
# 写一个超简单的 Hello.kt
cat > Hello.kt <<'EOF'
fun main() {
println("Hi")
}
EOF
# 编译
kotlinc Hello.kt -d out
# 看产物(不要被吓到)
ls out/
# → HelloKt.class (主类,包顶层 main)
# → META-INF/Hello.kotlin_module (Kotlin 元信息)2.3.2 反编译看真相
如果你装了 javap(JDK 自带):
bash
javap -c -p out/HelloKt.class会看到 Kotlin 代码被编译成了等价的 Java 字节码:
public final class HelloKt {
public static final void main();
Code:
0: ldc #8 // String "Hi"
2: astore_0
3: getstatic #14 // System.out
6: aload_0
7: invokevirtual #20 // PrintStream.println
10: return
public static void main(java.lang.String[]);
Code:
0: invokestatic #25 // Method main:()V
3: return
}📌 重点观察:
println最终被编译成了System.out.println—— Kotlin 跟 Java 在 JVM 层面就是同一种东西。
2.3.3 K1 vs K2 编译器
Kotlin 2.0(2024 年)引入了重写的 K2 编译器:
| 维度 | K1(旧) | K2(新) |
|---|---|---|
| 历史 | 2010 年开始用 | 2024 年正式版 |
| 编译速度 | 基线 | 提升 ~2 倍 |
| 错误消息 | 一般 | 更友好 |
| 多平台支持 | 各端独立分析 | 统一前端 |
| IDE 性能 | 慢 | 显著改善 |
💡 建议:新项目直接用 Kotlin 2.0+,享受 K2 红利。
2.4 国内开发者的"加速三件套"
国内访问 Maven Central / Gradle 官方源经常很慢。下面是 3 个救命配置。
2.4.1 Gradle 国内镜像
新建 ~/.gradle/init.gradle.kts:
kotlin
allprojects {
repositories {
mavenLocal()
maven { url = uri("https://maven.aliyun.com/repository/public") }
maven { url = uri("https://maven.aliyun.com/repository/google") }
maven { url = uri("https://maven.aliyun.com/repository/gradle-plugin") }
mavenCentral()
google()
}
}2.4.2 Gradle Wrapper 镜像
gradle/wrapper/gradle-wrapper.properties 里把:
properties
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-all.zip改成:
properties
distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.10-all.zip2.4.3 Kotlin JVM 内存调大
gradle.properties:
properties
org.gradle.jvmargs=-Xmx2g -XX:+UseParallelGC
org.gradle.parallel=true
org.gradle.caching=true
kotlin.incremental=true📌 效果:以上三件套一开,新项目第一次构建从 10 分钟缩到 1-2 分钟。
2.5 写第一段"超出 Hello World"的代码
光打印 Hi 不算入门,咱来一段稍微有点意思的:
2.5.1 计算斐波那契数列
kotlin
// File: Fibonacci.kt
fun fib(n: Int): Long {
if (n <= 1) return n.toLong()
var a = 0L
var b = 1L
repeat(n - 1) {
val tmp = a + b
a = b
b = tmp
}
return b
}
fun main() {
println("斐波那契数列前 10 项:")
(0..9).forEach { i ->
println("fib($i) = ${fib(i)}")
}
}运行:
斐波那契数列前 10 项:
fib(0) = 0
fib(1) = 1
fib(2) = 1
fib(3) = 2
fib(4) = 3
fib(5) = 5
fib(6) = 8
fib(7) = 13
fib(8) = 21
fib(9) = 342.5.2 涉及到的"小知识点速览"
| 代码 | 含义 |
|---|---|
fun fib(n: Int): Long | 函数定义,参数 n: Int,返回值 Long |
n.toLong() | 显式类型转换(Kotlin 不像 Java 自动从 Int 升 Long) |
var a = 0L | var = 可变;0L 是 Long 字面量 |
repeat(n - 1) { ... } | Kotlin 标准库函数,重复执行 lambda |
(0..9).forEach { i -> ... } | 范围 + 集合操作;i -> 是 lambda 参数 |
"fib($i) = ${fib(i)}" | 字符串模板:$i 是简单插值,${...} 是表达式插值 |
2.6 IDEA 必备快捷键(强烈建议背下来)
这些快捷键能让你的 Kotlin 开发效率提升 3 倍:
| 操作 | macOS | Windows / Linux |
|---|---|---|
| 运行当前 main | Ctrl + R | Shift + F10 |
| Debug 当前 main | Ctrl + D | Shift + F9 |
| 智能补全 | Ctrl + Space | Ctrl + Space |
| 万能搜索 | Shift Shift | Shift Shift |
| 重构 → 重命名 | Shift + F6 | Shift + F6 |
| 跳到定义 | Cmd + 点击 | Ctrl + 点击 |
| 看反编译字节码 | Tools → Kotlin → Show Kotlin Bytecode | |
| 把 Kotlin 字节码反编译成 Java | 上一项窗口里点 "Decompile" | |
| 提取变量 | Cmd + Alt + V | Ctrl + Alt + V |
| 自动 import | Alt + Enter | Alt + Enter |
| 格式化代码 | Cmd + Alt + L | Ctrl + Alt + L |
💡 新手必学的两个:
Alt + Enter—— Kotlin IDE 的"魔法键",光标停在任何报错或警告上按一下,IDE 会列出所有可能的修复方案- Show Kotlin Bytecode —— 想搞懂"我这段 Kotlin 编译成 Java 长啥样"时神器,对学协程、
data class、object等高阶特性特别有用
2.7 常见踩坑 & 快速诊断
Q1:./gradlew 提示 Permission denied
bash
chmod +x gradlewQ2:JDK 找不到 / "JAVA_HOME is not set"
bash
# 检查
echo $JAVA_HOME
java -version
# macOS 设置
export JAVA_HOME=$(/usr/libexec/java_home -v 17)
# Linux 设置
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64加到 ~/.zshrc 或 ~/.bashrc 永久生效。
Q3:IDEA 报红 "Cannot resolve symbol kotlin.io.println"
通常是 Kotlin 标准库没下载。File → Invalidate Caches and Restart 一下。
Q4:编译报 "Unresolved reference: 某某类"
99% 是 import 没加。光标停在变红的类名上,按 Alt + Enter → "Import"。
Q5:用了协程报 "Suspend function 'xxx' should be called only from a coroutine or another suspend function"
suspend 函数只能在协程或另一个 suspend 函数里调,详见 第 9 章。临时验证可以包一层 runBlocking { ... }。
2.8 章末小结
★ 第 2 章核心知识图谱 ★
│
┌─────────────┼─────────────┐
│ │ │
┌──▼──┐ ┌──▼──┐ ┌──▼──┐
│入门 │ │项目 │ │工具 │
├─────┤ ├─────┤ ├─────┤
│Playgr│ │Gradle│ │IDEA │
│kotlinc│ │.kts │ │快捷键│
│REPL │ │MainKt│ │字节码│
└─────┘ └─────┘ └─────┘完成本章你应该:
- ✅ 在自己电脑装好了 JDK + Kotlin
- ✅ 用 3 种方式都跑通了 Hello World
- ✅ 理解了
MainKt是怎么来的 - ✅ 知道 Kotlin 编译成 JVM 字节码后跟 Java 等价
- ✅ 配置了国内 Gradle 镜像(如果你在国内)
🎤 2.9 章末面试题(8 道)
Q1. Kotlin 编译需要哪些工具?产物是什么?
答:
- 编译工具:
kotlinc(Kotlin 编译器,背后包含 K1 / K2 前端 + JVM 后端) - 运行依赖:JVM(JDK 8 起,推荐 17+)+ kotlin-stdlib(标准库)
- 产物:
.class字节码文件(跟 Java 同样格式)+ Kotlin metadata(用于反射)
Q2. 为什么写一个 Main.kt 文件,编译后的类名变成了 MainKt?
答:JVM 强制所有方法必须属于某个类。Kotlin 把文件里的所有顶层函数和属性收集起来,编译进一个自动生成的 facade 类,类名 = <文件名>Kt。所以 Main.kt → MainKt。
如果你想改名,可以加注解:
kotlin
@file:JvmName("MyApp")
fun main() { ... }
// 编译后类名变成 MyApp,不再是 MainKtQ3. kotlinc 跟 javac 的关系是什么?
答:两者是平级关系,都是把源代码编译成 JVM 字节码的编译器。kotlinc 不依赖 javac,但都依赖 JDK 提供的 JVM 工具链。一个混合 Java + Kotlin 的项目里:
.java文件 →javac编译.kt文件 →kotlinc编译- 两边产物在同一个 classpath 下相互调用
构建工具(Gradle/Maven)会协调它们的编译顺序。
Q4. .kt 跟 .kts 有什么区别?
答:
.kt:标准 Kotlin 源文件,需要先编译再运行(即"编译后部署"模式).kts:Kotlin Script,可以直接被kotlinc -script xxx.kts解释执行,不需要单独编译步骤
.kts 主要用于:
- Gradle 构建脚本(
build.gradle.kts) - 一次性脚本任务
- IDE 插件(IDEA 插件配置)
Q5. K1 编译器和 K2 编译器有什么区别?
答:K2 是 Kotlin 2.0(2024)正式发布的全新编译器前端:
- 速度:编译速度提升约 2 倍
- 架构:统一了多平台前端,K1 时代每个平台有独立的语义分析
- 错误信息:更准确、更友好
- IDE 性能:K2 模式下 IDEA 索引和补全显著加速
- 兼容性:高度向后兼容,绝大多数 K1 代码无需修改即可在 K2 编译
Q6. Gradle 项目第一次构建为什么这么慢?怎么加速?
答:慢的原因:
- 下载 Gradle 本身(百兆级)
- 下载 Kotlin 编译器(百兆级)
- 下载所有依赖到
~/.gradle/caches/ - 国内访问 Maven Central /
services.gradle.org经常被限速
加速方法:
- 配置阿里云 / 腾讯云 Maven 镜像(
~/.gradle/init.gradle.kts) - Gradle Wrapper 用国内镜像
- 开启并行 + 缓存:
org.gradle.parallel=trueorg.gradle.caching=true - 给 Gradle 多分配内存:
org.gradle.jvmargs=-Xmx2g
Q7. Kotlin 在 Android 项目里编译出来的产物是什么?怎么跑到手机上?
答:
.kt→kotlinc编译为 JVM 字节码.class.class→ D8/R8(Android 工具链) 转换为 Dalvik 字节码.dex.dex+ 资源文件 → 打包成.apk/.aab- 手机的 ART 运行时(Android Runtime)执行
.dex
📌 注意:Android 上跑的不是标准 JVM,而是 ART —— 字节码格式不同,但你写代码时不感知。
Q8. 如果一个项目同时有 Java 和 Kotlin 文件,编译顺序是什么?
答:Gradle 的 kotlin-jvm 插件会处理:
- 先用
kotlinc编译 Kotlin 代码(同时分析 Java 源代码以解析跨语言引用,但不编译 Java) - 再用
javac编译 Java 代码(此时可以引用上一步生成的 Kotlin.class) - 两边产物合并打包
所以 Kotlin 调 Java 没问题(Java 已经编译好了);Java 调 Kotlin 也没问题(Kotlin 先编译完了)。这就是"100% 互操作"的工程基础。
下一章 → 第 3 章 · 基本语法(变量、类型、控制流)→
🎬 可视化演示
演示加载缓慢或样式异常?点此在新标签页打开 ↗
💻 示例代码
txt
/**
* 第 2 章 · 环境搭建 — 标准 build.gradle.kts 模板
*
* 这是一个最小可用的 Kotlin JVM 项目构建脚本。
* 适合:CLI 工具 / 服务端 / 学习用项目。
*
* 使用:
* 1. 把这个文件放到项目根目录
* 2. 在 src/main/kotlin/ 下放你的 .kt 文件
* 3. ./gradlew run 运行
* 4. ./gradlew build 打包
*/
plugins {
kotlin("jvm") version "2.0.21"
application
}
group = "com.example.kotlin.demo"
version = "1.0-SNAPSHOT"
repositories {
// 国内开发者建议在 ~/.gradle/init.gradle.kts 配置阿里云镜像
mavenCentral()
}
dependencies {
// 标准库默认包含,无需声明
// 测试
testImplementation(kotlin("test"))
// 协程(第 9 章会用到)
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.1")
// JSON 序列化(第 10 章会用到)
// implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.1")
}
application {
// 入口类:Main.kt 的顶层 main 函数 → MainKt
mainClass.set("MainKt")
}
tasks.test {
useJUnitPlatform()
}
kotlin {
// 推荐用 JDK 17+(LTS 长期支持版本)
jvmToolchain(17)
// 编译期可选:开启严格模式
compilerOptions {
// 把所有警告升级为错误(CI 推荐)
// allWarningsAsErrors.set(true)
}
}kotlin
/**
* 第 2 章 · 环境搭建 — Fibonacci 示例
*
* 演示了:
* - 顶层函数定义
* - val / var
* - 显式类型转换 (toLong)
* - repeat 标准库函数
* - Range (0..9) 与 forEach
* - 字符串模板 ($i / ${expression})
*
* 运行方式:
* kotlinc Fibonacci.kt -include-runtime -d fib.jar
* java -jar fib.jar
*/
fun fib(n: Int): Long {
if (n <= 1) return n.toLong()
var a = 0L
var b = 1L
repeat(n - 1) {
val tmp = a + b
a = b
b = tmp
}
return b
}
fun main() {
println("斐波那契数列前 10 项:")
(0..9).forEach { i ->
println("fib($i) = ${fib(i)}")
}
println()
println("=== 使用尾递归优化版 ===")
println("fib(50) = ${fibTailrec(50)}")
}
/**
* 尾递归版 —— 用 tailrec 关键字让编译器把递归转成循环,避免栈溢出。
* 这是 Kotlin 的"小心机"特性,Java 里写不出来。
*/
tailrec fun fibTailrec(n: Int, a: Long = 0L, b: Long = 1L): Long {
if (n == 0) return a
return fibTailrec(n - 1, b, a + b)
}