Android Studio中的各种Java版本
创建一个项目,app模块的build.gradle.kts默认配置如下:
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
}
android {
namespace = "cn.android666.javaversiontest"
compileSdk = 35
defaultConfig {
applicationId = "cn.android666.javaversiontest"
minSdk = 21
targetSdk = 35
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = "11"
}
}
dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.appcompat)
implementation(libs.material)
implementation(libs.androidx.activity)
implementation(libs.androidx.constraintlayout)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
}
这里有关Java版本设置的地方有两个:
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = "11"
}
在设置中还有一个Gradle JDK可以设置和Java版本相关的,如下:
各种Java版本功能如下:
-
Gradle JDK
:用于运行 Gradle 的JDK(Gradle 是 Java 编写的工具,所以需要用JDK来运行)- 影响 Gradle 守护进程(Daemon)的启动和执行效率。
- 从 Gradle 7.0 开始要求最低 JDK 11(AGP 8.0+ 强制要求 JDK 17)。
- 与项目代码的编译版本无关,仅影响构建过程本身。
-
sourceCompatibility
:指定 Java 源代码的语言版本(语法兼容性)- 限制代码中使用的语法特性(例如:Java 11 支持
var
,但若设为VERSION_1_8
则不允许使用。注:只限制语法特性,不限制API,比如sourceCompatibility
设置为1.7,然后使用Java 8的时间API这是可以的,只要minSdk为26+就行,因为从26+的系统就开始支持Java8的时间API)。 - 对应 javac -source 参数。
- 限制代码中使用的语法特性(例如:Java 11 支持
-
targetCompatibility
:指定生成的 字节码(.class 文件) 的目标 JVM 版本- 确保字节码能在指定版本或更高版本的 JVM 上运行(例如设为 VERSION_11 时,字节码可运行在 JDK 11+ 环境)。
- 对应 javac -target 参数。
- 必须满足:
targetCompatibility ≥ sourceCompatibility
-
jvmTarget
:指定 Kotlin 代码 编译后的字节码目标版本。- 控制 Kotlin 编译器生成的字节码版本(类似 Java 的
targetCompatibility
,但仅作用于 Kotlin 代码)。
- 控制 Kotlin 编译器生成的字节码版本(类似 Java 的
Gradle JDK 需独立满足 AGP 要求:
APG版本 | 最低 Gradle JDK | 最低Gradle |
---|---|---|
8.0+ | JDK 17 | Gradle 8.0 |
7.4 | JDK 11 | Gradle 7.5 |
总结
-
Gradle JDK
:构建工具的“发动机”,需独立配置且版本受 AGP 限制 ✅。 -
source/targetCompatibility
:控制 Java 代码的语法和字节码兼容性 🧩。 -
jvmTarget
:专为 Kotlin 代码设置字节码版本 🔧。
验证编译后的class的java版本
我的配置为:
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = "11"
}
targetCompatibility
和jvmTarget
都设置为java 11
,也就是说编译的java
文件和kotlin
文件都会被编译为java 11
的字节码。且高版本的JDK编译器可以指定生成低版本字节码,比如使用JDK21编译生成JDK11的字节码:javac -release 11 YourClass.java
,Kotlin也有类似的编译,比如:kotlinc Hello.kt -jvm-target 11 -d output.jar
我的Android项目中同时有Kotlin文件和Java文件:MainActivity.kt
、Util.java
,运行项目到手机,然后代码会被编译。
-
java编译结果保存路径:
app\build\intermediates\javac\debug\compileDebugJavaWithJavac\classes
-
kotlin编译结果保存路径:
app\build\tmp\kotlin-classes\debug
Android Studio 支持直接反编译class文件的,双击编译后的文件,截图如下:
从反编译文件可以看到信息:Decompiled .class file, bytecode version: 55.0(Java 11)
,中文翻译为:反编译的.class文件,字节码版本:55.0(Java 11)
,字节码版本为55.0
,对应的就是Java 11
版本。
验证各种Java版本
一、纯Java项目
1. 当前配置
-
Android Studio
版本:Android Studio Meerkat Feature Drop | 2024.3.2 Patch 1
-
Gradle JDK
版本:Java 21
-
JAVA_HOME
版本:Java 11
-
Gradle
版本:8.11.1
-
构建脚本使用的是Groovy语言
-
AGP
版本:8.10.1
(此版本AGP对其它工具的最低版本要求:Gradle 8.11.1
、JDK 17
、SDK Build Tools 35.0.0
) -
创建一个纯Java的Android项目,它的minSdk设置为21,完整build.gradle文件如下:
plugins { alias(libs.plugins.android.application) } android { namespace 'cn.android666.javaonly' compileSdk 35 defaultConfig { applicationId "cn.android666.javaonly" minSdk 21 targetSdk 35 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } compileOptions { sourceCompatibility JavaVersion.VERSION_1_11 targetCompatibility JavaVersion.VERSION_1_11 } } dependencies { implementation libs.appcompat implementation libs.material implementation libs.activity implementation libs.constraintlayout testImplementation libs.junit androidTestImplementation libs.ext.junit androidTestImplementation libs.espresso.core }
因为是纯
Java
项目,它只有sourceCompatibility
和targetCompatibility
,没有kotlin
的jvmTarget
属性了。
2. 1.8和1.8
修改sourceCompatibility
和targetCompatibility
为1.8版本,如下:
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
MainActivity.java
代码如下:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = findViewById(R.id.button);
button.setOnClickListener(v -> {
Toast.makeText(this, "Hello World!", Toast.LENGTH_SHORT).show();
});
}
}
运行app,一切正常。查看编译出来的MainActivity
字节码,如下:
显示字节码版本为52.0,对应Java 8。
编译运行时,Build
面板中有一些警告信息,如下:
> Task :app:compileDebugJavaWithJavac
Java compiler version 21 has deprecated support for compiling with source/target version 8.
Try one of the following options:
1. [Recommended] Use Java toolchain with a lower language version
2. Set a higher source/target version
3. Use a lower version of the JDK running the build (if you're not using Java toolchain)
For more details on how to configure these settings, see https://developer.android.com/build/jdks.
To suppress this warning, set android.javaCompile.suppressSourceTargetDeprecationWarning=true in gradle.properties.
警告: [options] 源值 8 已过时,将在未来发行版中删除
警告: [options] 目标值 8 已过时,将在未来发行版中删除
警告: [options] 要隐藏有关已过时选项的警告, 请使用 -Xlint:-options。
3 个警告
这里的源值8和目标值8,说的就是sourceCompatibility
和targetCompatibility
的值JavaVersion.VERSION_1_8
是过时,将在未来发行版中删除。所以啊,Java 8
都已经过时了,我们要赶紧抛弃Java 8
了。未来这个值将会被删掉,那时你想用Java 8都用不了。
JavaVersion.VERSION_1_8
即 jdk1.8版本对应Java 8
版本。
早期的Java版本都是1.x命令的,1为主版本号,x为次版本号,比如1.5、1.6、1.7、1.8,Java 在 2017 年 9 月发布 Java 9 时,正式启用了新的版本号方案,只用主版本号,次版本号不要了。可能是由于历史原因,Gradle API的JavaVersion
类中对Java 9和10还是延用了旧的方式,即:VERSION_1_9、VERSION_1_10,到了11就只有主版本号了:VERSION_11。
2. 1.7和1.8
设置sourceCompatibility
为1.7,targetCompatibility
为1.8,如下:
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_8
}
设置sourceCompatibility JavaVersion.VERSION_1_7
,则控制了的代码语法版本为Java 7,但是我的电脑并没有安装有JDK 1.7
,它是如何实现语法控制的?这是因为现代JDK内置了多版本语法校验能力,比如使用JDK21的编译器进行编译:
javac -source 1.7 -target 1.7 -bootclasspath /path/to/android-jar Main.java
设置好sourceCompatibility
和targetCompatibility
之后同步Gradle,此时代码直接报错了,如下:
错误提示说:Lambda表达式在语言级别7是不支持的。因为我们把sourceCompatibility
设置为JavaVersion.VERSION_1_7
,所以在代码中就不能使用Java 8
的特性了,修正代码:
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "Hello World!", Toast.LENGTH_SHORT).show();
}
});
把项目Clean
一下,然后运行,报错了,如下:
Execution failed for task ':app:compileDebugJavaWithJavac'.
> Java compiler version 21 has removed support for compiling with source/target version 7.
Try one of the following options:
1. [Recommended] Use Java toolchain with a lower language version
2. Set a higher source/target version
3. Use a lower version of the JDK running the build (if you're not using Java toolchain)
For more details on how to configure these settings, see https://developer.android.com/build/jdks.
Set Java Toolchain to 11
Change Java language level and jvmTarget to 11 in all modules if using a lower level.
Pick a different compatibility level...
Pick a different JDK to run Gradle...
More information...
错误翻译如下:
任务`:app:compileDebugJavaWithJavac`执行失败。
> Java编译器版本21已经删除了对`source/target`版本为7的编译支持。
尝试以下选项之一:
1. [推荐]使用较低语言版本的Java工具链。
2. 设置更高的`source/target`版本。
3. 使用较低版本的JDK来运行构建(如果您没有使用Java工具链)
有关如何配置这些设置的更多详细信息,请参阅[https://developer.android.com/build/jdks](https://developer.android.com/build/jdks)。
设置Java工具链为11。
如果使用较低级别,则将所有模块中的Java语言级别和jvmTarget更改为11。
选择一个不同的`compatibility`级别。
选择一个不同的`JDK`来运行Gradle。
更多信息。。。
这个报错信息里面有很多链接是可以点击的,点击之后会跳到对应的页面,展示了对应的解决方案。
错误原因已经说的很明白了,IDE使用JDK21
来编译我们的源文件,而我们通过sourceCompatibility JavaVersion.VERSION_1_7
来表明我们的源文件是JDK1.7
的,JDK21
的编译器已经不支持编译JDK1.7
的源代码或者输出JDK1.7
目标的字节码了,所以报错了。
为什么IDE会选择使用JDK21
来编译我们的代码呢?这是因为IDE
默认使用运行Gradle
的JDK
来编译我们的代码。而运行Gradle
的JDK
版本是在设置中指定的,具体位置为:File > Settings > Build, Execution, Deployment > Build Tools > Gradle > Gradle JDK
,如下图:
Gradle JDK
默认使用 GRADLE_LOCAL_JAVA_HOME
变量,而这个变量默认指向Android Studio
自带的JetBrains Runtime
,在我的版本中,它是21.0.6
版本,对应JAVA 21
。
既然JAVA 21
的编译器不支持编译JAVA 7
的代码,那我们就可以设置一个低一点的版本来编译代码,前面报错的信息中提供了多种方法,我选择使用改变工具链的方式,代码如下:
android {
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_8
}
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(11)
}
}
如上代码,我们设置了Java
工具链为Java 11
,也就是说Gradle
会使用JDK 11
的编译器来编译我们的代码,Gradle会自动从电脑中查找JDK11,如果找不到则会自动下载一个。运行项目,一切正常,查看编译出来的MainActivity
字节码,如下:
到这里,我们发现了4个关于Java版本的地方,总结本次运行用到的Java版本:使用JDK 21
来运行Gradle
,Gradle
调用JDK 11
的编译器来把JDK 1.7
的源代码编译为JDK 1.8
的字节码。
对于工具链设置还有一个:
kotlin {
jvmToolchain(11)
}
因为创建的这个项目是纯Java项目,我使用这个代码的时候提示说找不到这个kotlin
函数,说明这个DSL是Kotlin插件提供的,而我在这个项目中并没有应用Kotlin插件。
3. 疑问
为什么会有人用低版本写代码,然后又编译为高版本字节码?而且sourceCompatibility
必须是小于或等于targetCompatibility
,如果说我想用JDK 1.8
写代码,然后你给我编译为JDK 1.7
,这样才是比较合理的嘛,比如我在代码中就可以使用Lambda
表达式,然后编译的时候,你把Lambda
表达式翻译为接口以便能在JDK 1.7
中运行,这种需求才比较合理嘛!但是这种需求它不允许,因为: sourceCompatibility <= targetCompatibility
,如果sourceCompatibility
设置大于targetCompatibility
,则运行时就会直接报错了。
带着疑问,我问了DeepSeek为什么会有人用低版本写代码,然后又编译为高版本字节码?答案如下:
-
兼容旧代码库,但目标环境已升级至高版本 JVM
- 问题:遗留系统代码基于 Java 7 开发,无法立即升级语法,但生产环境已升级至 Java 8+ 的 JVM。
- 解决方案:
- 保持源码兼容 Java 7,避免引入高版本语法导致旧开发环境编译失败。
- 编译为 Java 8 字节码,利用高版本 JVM 的性能优化(如元空间替代永久代、编译器优化等)。
-
依赖库强制要求高版本运行时环境
-
问题:某些第三方库(如新版 gRPC、Spring Boot 2.x+)需运行在 Java 8+ 环境,但项目代码未使用 Java 8 语法。
-
解决方案:
- 源码保持 Java 7 兼容性,确保开发工具链统一(如 CI/CD 环境仍用 JDK 7 编译)。
- 输出 Java 8 字节码,满足依赖库的最低运行时要求。
-
-
阶段性迁移策略
-
问题:大型项目从 Java 7 升级到 Java 8 需分步进行:先确保字节码兼容新环境,再逐步重构代码使用新语法。
-
解决方案:
- 先设置
targetCompatibility=1.8
,验证系统在Java 8 JVM
的稳定性。 - 稳定后再逐步修改源码,启用
Lambda
等新特性(此时可同步升级sourceCompatibility=1.8
)。
- 先设置
-
-
规避低版本 JVM 的已知缺陷
- 问题:Java 7 某些 JVM 实现存在性能或安全漏洞(如
HashCollision
攻击),但代码无法立即升级语法。 - 解决方案:
- 编译为 Java 8 字节码并部署到 Java 8+ 环境,直接规避低版本 JVM 的问题。
- 问题:Java 7 某些 JVM 实现存在性能或安全漏洞(如
总结就是:过渡期用 source=1.7
, target=1.8
验证新环境;稳定后升级 source=1.8
启用新语法。
4. 11和17
设置sourceCompatibility
为11,targetCompatibility
为17,工具链设置为21,如下:
android {
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_17
}
}
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
Clean
项目,然后运行,一切正常,而且这次编译中没有报任何警告了。查看字节码版本:
成功把java11
的代码编译为java17
的字节码。
二、Java与Kotlin混合项目
1. 当前配置
Android Studio
版本:Android Studio Meerkat Feature Drop | 2024.3.2 Patch 1
Gradle JDK
版本:Java 21
JAVA_HOME
版本:Java 11
Gradle
版本:8.11.1
- 构建脚本使用的是Kotlin语言
AGP
版本:8.10.1
(此版本AGP对其它工具的最低版本要求:Gradle 8.11.1、JDK 17、SDK Build Tools 35.0.0)Kotlin
插件版本:2.0.21
- 创建一个Kotlin语言的Android项目,它的minSdk设置为21,完整build.gradle.kts文件如下:
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
}
android {
namespace = "cn.android666.javaversiontest"
compileSdk = 35
defaultConfig {
applicationId = "cn.android666.javaversiontest"
minSdk = 21
targetSdk = 35
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = "11"
}
}
dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.appcompat)
implementation(libs.material)
implementation(libs.androidx.activity)
implementation(libs.androidx.constraintlayout)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
}
2. 测试
和纯Java项目应该是差不多的,所以这里我就测试看有没有什么需要注意的地方。
targetCompatibility
和jvmTarget
必须要一致,否则编译就会报错,比如:
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = "17"
}
编译报错信息为:Inconsistent JVM-target compatibility detected for tasks 'compileDebugJavaWithJavac' (11) and 'compileDebugKotlin' (17).
,翻译:检测到任务‘compileDebugJavaWithJavac’(11)
和‘compileDebugKotlin’(17)
的JVM-target
兼容性不一致。
没找到targetCompatibility
和jvmTarget
必须要求一致的官方说法,如果必须一致的话那为什么弄两个属性,一个不就行了吗?
工具链设置:
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(11))
}
}
kotlin { jvmToolchain(11) }
这里有两个方式设置Java的工具链,第一种针对Java,它来自Android插件。第二种针对Kotlin,它来自Kotlin插件,这两个插件即build.gradle.kts
文件开头的那两个,如下:
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
}
这两个插件声明其中一个就可以了,它们都是会同时作用于Java和Kotlin的,第二种方式比较简洁,所以推荐第二种 。(但是有DeepSeek有提到当指定的工具链不存在时,java { toolchain }
会自动下载JDK,而kotlin { jvmToolchain }
不会,仅用于控制编译输出。)
我发现两个都写时,可以写不一样的,比如:
android {
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
}
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(8))
}
}
kotlin { jvmToolchain(11) }
由于看不到内部运行,不知道Gradle是会统一使用JDK 11来编译Java和Kotlin的源文件,还是说用JDK 8来编译Java,用JDK11来编译Kotlin?
创建项目时sourceCompatibility
、targetCompatibility
、jvmTarget
都是默认为11的,但是并没有指定java工具链,所以会默认使用Gradle JDK
的版本,对于我的环境就是JDK 21
。这里我就有个疑问了,即然目标是11,那为什么不指定工具链为11,于是问了DeepSeek
,得到的一种说法是JDK 21
编译器更高效,编译速度更快(尤其增量编译),只有当默认编译出错的时候,再考虑手动添加工具链为JDK11
,看是否是JDK21
编译的问题,比如某些库可能在JDK21
下行为异常,又比如KAPT
(Kotlin
注解处理器)在JDK21
下存在Bug
。
总结就是没问题就用默认,有问题再尝试指定工具链。
验证工具链的使用:
-
如何证明默认用的是JDK21,当不设置工具链时,设置
sourceCompatibility = JavaVersion.VERSION_1_7
时,运行时报错提示:“Java compiler version 21 has removed support for compiling with source/target version 7.
”,这说明是在使用Java 21的编译器,这个版本正好和Gradle JDK
的版本一致。 -
如何证明下面两种方式都能修改工具链:
java { toolchain { languageVersion.set(JavaLanguageVersion.of(17)) } } kotlin { jvmToolchain(11) }
和上一个例子一样,设置
sourceCompatibility = JavaVersion.VERSION_1_7
,运行时就会报错,于是分别单独使用上述两种方式设置低版本的工具链,然后运行都正常了,说明两种方式都是OK的。 -
如何证明两种方式同时使用时,到底用哪一个?当我只设置
languageVersion.set(JavaLanguageVersion.of(17))
时,在Gradle控制面板执行:clean
,确保编译的代码被清空,然后再执行编译代码:app:compileDebugJavaWithJavac --info
,查看控制台,如下:
可看到,使用的工具链是Java 17。
在不删除任何配置的同时添加kotlin { jvmToolchain(11) }
,然后执行相同操作,即先clean
,然后再编译代码,结果如下:
这是否可以证明当同时使用两种方式设置工具链时,不论两种方式设置的版本是否相同,java和kotlin代码都只会使用同一个版本的工具链,且使用kotlin { jvmToolchain() }
中设置的版本。
类似的命令:app:compileDebugKotlin --info
,但是这个命令并不会输出工具链版本。
总之,通过前面的例子,最少能证明只设置kotlin { jvmToolchain() }
时,它也是能作用于Java的。所以,以后写代码尽量用kotlin的方式来设置工具链,java的那个设置方式比较长,也不好记。
各种Java版本总结
android {
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = "11"
}
}
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(11))
}
}
kotlin { jvmToolchain(11) }
画图分析如下:
Android中的各种Java版本如上图所示,解释如下:
-
需要使用JDK来运行Gradle
-
在命令行运行Gradle,使用的JDK会优选使用
JAVA_HOME
环境变量,如果没有这个变量,则使用系统默认的,即从PATH
环境变量中找JDK来运行,找到哪个就用哪个,找不到就运行不了。Gradle还分启动器进程和守护进程,启动器进程由JAVA_HOME
或系统默认JDK运行,然后还会再启动一个守护进程,如果有在gradle.properties
中指定org.gradle.java.home
,则用这个属性指定的JDK来运行守护进程,如果没指定这个属性,则使用和启动器进程一样的JDK来运行守护进程。 -
如果是Android Studio调用Gradle,比如点击运行按钮,点击Build菜单中的各种构建命令,在Gradle面板中执行Gradle任务等,这些都是属于Android Studio去调用Gradle来执行任务,它会使用的设置里面的
Gradle JDK
来运行Gradle。 -
Gradle调用javac来编译java源代码,用kotlinc来编译kotlin源代码,javac是jdk里面的,kotlinc是kotlin里面的,但是它也是要依赖jdk来进行编译的,而这个jdk默认会使用Gradle JDK,如果你想用别的版本的JDK来编译代码,不建议修改Gradle JDK,因为Gradle JDK用于运行Gradle,而且有时候你还改不了它,比如
AGP 8.0
要求运行它的JDK最少是JDK 17
版本,如果此时你想使用JDK 11
来编译代码,但是你的AGP
又是8.0
或更高的版本,那就不能把Gradle JDK修改为JDK 11了。所以,推荐的做法是设置工具链,如果是纯java项目,可以使用java { toolchain }
来设置工具链,它是在Gradle的java插件中定义的,虽然我们的项目只应用了Android插件,但是Android插件内部依赖并隐式应用了 Java 插件。如果是kotlin项目,则可以使用java { toolchain }
或kotlin { jvmToolchai }
来设置工具链,这两个设置其中一个即可,它们都可以同时作用于java和kotlin。kotlin { jvmToolchain }
是在kotlin插件中定义的,所以在纯java项目中没应用这个插件就用不到这个了。另外DeepSeek有提到java { toolchain }
会自动下载工具链(如果系统找不到指定的工具链),而kotlin { jvmToolchai }
不会自动下载工具链,仅用于控制编译输出(有待验证)。 -
运行Gradle的JDK理论上来说能高一点就高一点,因为高一点的JDK运行效率一般会高一些。
-
Java工具链的版本理论上来说也是能高一点就高一点,因为效率高,比如编译速度快。一般来说保持默认使用
Gradle JDK
即可。 -
在使用javac编译java代码时,通过
compileOptions { targetCompatibility }
可以指定编译为哪个版本的字节码。 -
在使用kotlinc编译kotlin代码时,通过
kotlinOptions { jvmTarget}
可以指定编译为哪个版本的字节码。 -
compileOptions { sourceCompatibility }
的作用是指定 Java 源代码的语法版本(即编译时支持的 Java 语言特性)。语言特性指的是什么?它是指在 Java 代码中可以使用的语法规则、语义结构和关键字,这些特性是由 Java 不同版本引入的新功能。常见的语言特性及其 Java 版本如下:Java 版本 引入的语言特性 示例 Java 5 泛型、增强 for 循环、自动装箱 List<String> list = new ArrayList<>();
Java 7 try-with-resources
、switch
支持字符串、二进制字面量try (FileInputStream fis = ...) {}
Java 8 Lambda
表达式、方法引用、默认接口方法、Stream API
(a, b) -> a + b
Java 9 模块系统( module-info.java
)、私有接口方法module my.module {}
Java 10 局部变量类型推断( var
)var list = new ArrayList<String>();
Java 14 switch 表达式、 record
类型(预览)switch (x) { case 1 -> "one"; }
Java 16+ record
正式引入record Point(int x, int y) {}
sourceCompatibility
只是限制语言特性,它并不限制API,API是指标准类库里面的类,比如Java 8引入的新API:
LocalDate.now();
语言特性是由JDK编译器控制的,而API
由android.jar
提供,使用哪个版本的android.jar
进行编译,取决于compileSdk
。
假设我们把sourceCompatibility
设置为Java 7
,targetCompatibility
设置为JAVA 11
,则在写代码的时候,如果使用超出Java 7
的语言特性,则相关代码直接显示红色,比如我使用了var,和Lambda,var是Java10才有的语言特性,Lambda是Java8才有的语言特性,所以使用这些会直接报红色,如下:
而对于使用高版本API,它是能调用出来的,导包也没问题,只是在你代码下方显示红色波浪线,比如我使用Java 8中的LocaDate
类:
把鼠标移到报错上面会提示错误原因,如下:
有的API报错还不一样,如下:
提示的错误信息为:
总结如下:
- 调用
LocalDate.now()
提示:Call requires API level 26 (current min is 21) - 调用
List.of(1, 2, 3)
提示:Static interface method calls are not supported at language level ‘7’
由于我的sourceCompatibility
设置为Java 7
,而LocalDate.now()
是Java8
出的,List.of()
是Java9
出的,报错能理解,但是为什么报的错不一样呢?这是因为两种不同的兼容性检查机制:
LocalDate.now()
的报错:Android API级别检查- 触发机制:Android构建工具(如
Lint
)在编译时会对代码进行API
级别校验,发现LocalDate
是API 26
引入的类,而你的minSdk=21
,我们在Android官方文档中查看LocalDate
也能看到它是API 26
引入的。
- 触发机制:Android构建工具(如
List.of()
的报错:Java语言级别检查- 触发机制:Java编译器(
javac
或kotlinc
)在编译时根据sourceCompatibility=1.7
检查语法,发现List.of()
是Java 9
的语法特性,虽然说List.of()
是一个API,但是它是使用Java 9
的语言特性的:接口静态方法。
- 触发机制:Java编译器(
我设置minSdk 26
,然后 LocalDate.now()
就不报错了,而List.of()
依然报错,这充分说明sourceCompatibility
只是限制语法,并不限制API。报错时提示Static interface method calls are not supported at language level ‘7’,说的就是Java 7不支持静态接口方法,这指的就是语法问题,而非API问题。
来自官方的Java版本
Android build 中的 Java 版本
此章节内容完全来自于官网:https://developer.android.google.cn/build/jdks?hl=zh-cn
无论您的源代码是用 Java 还是 Kotlin 编写的,您都必须在多个位置为 build 选择 JDK 或 Java 语言版本。
还得是官方啊,一图就把所有Java版本总结出来了,如上图,主要分成3块内容,左、中、右。
- 左边是Source,即各种源代码
- 中间为 “
Versions in Build Files
”,即 “在构建文件里面的Java版本
”,也就是声明在build.gradle.kts
文件中的Java
版本,有5个,分别为:Kotlin jvmTarget
用于设置Kotlin
源代码编译后的字节码版本。targetCompatibility JDK
用于设置Java
源代码编译后的字节码版本。sourceCompatibility JDK
用于在编译时指定Java
源代码的版本,同时也会用作 IDE 代码辅助和 lint 的默认选项,所以写代码的时候如果有错误就会立马报错提示,而无需等到编译时期。Toolchain JDK
用于设置工具链的版本,比如用什么版本的JDK执行编译操作。用什么版本的JDK运行单元测试等。JDK API
,它由构建文件中的compileSdk
指定,指定的是Android的编译版本,不同的Android版本提供不同的版本的JDK API。
- 右边是运行
Gradle
的JDK
和运行Android Studio
的JDK
。所以,加起来一共就有7个JDK
版本了。 - 源代码依赖于
JDK API
,而它由compileSdk
指定。 - 单元测试(
Unit Tests
)的运行使用Toolchain JDK
。 Kotlin jvmTarget
、targetCompatibility JDK
、sourceCompatibility JDK
在不设置的情况下,默认为Toolchian JDK
,而Toolchian JDK
本身也没置的情况下,Toolchian JDK
默认为Gradle JDK
。对于Gradle JDK
的设置,又分两个叉,一个是用于Android Studio构建,它定义在Android Studio的设置中,Android Studio本身又运行在JetBrains Runtime JDK
之上,另一个用于命令行构建,它定义在gradle.properties
文件中的org.gradle.java.home
属性中,如果该属性没设置的话,默认使用JAVA_HOME
术语库
-
Java 开发套件 (JDK),包含:
- 工具,例如编译器、性能分析器和归档创建工具。 这些文件会在构建过程中在后台使用,以创建您的应用。
- 包含您可以从 Kotlin 或 Java 源代码调用的 API 的库。请注意,并非所有功能都适用于 Android 设备。
- Java 虚拟机 (JVM),一种用于执行 Java 应用的解释器。您可以使用 JVM 运行 Android Studio IDE 和 Gradle 构建工具。JVM 不会在 Android 设备或模拟器上使用。
-
JetBrains 运行时 (JBR),JetBrains Runtime (JBR) 是一种增强型 JDK,随 Android Studio 分发。 它包含多项优化,可在 Studio 和相关 JetBrains 产品中使用,但也可用于运行其他 Java 应用。
如何选择用于运行 Android Studio 的 JDK?
我们建议您使用 JBR
运行 Android Studio。该插件随 Android Studio 一起部署并用于测试 Android Studio,其中包含一些增强功能,可确保最佳 Android Studio 使用体验。为此,请勿设置 STUDIO_JDK
环境变量。
Android Studio 的启动脚本会按以下顺序查找 JVM
:
STUDIO_JDK
环境变量studio.jdk
目录(在 Android Studio 发行版中)- Android Studio 发行版中的
jbr
目录(JetBrains 运行时)。推荐。 JDK_HOME
环境变量JAVA_HOME
环境变量PATH
环境变量中的 java 可执行文件
如何选择运行 Gradle build 的 JDK?
如果您使用 Android Studio 中的按钮运行 Gradle,则 Android Studio 设置中设置的 JDK 将用于运行 Gradle。如果您在终端(Android Studio 内或外)中运行 Gradle,则 JAVA_HOME 环境变量(如果已设置)决定了哪个 JDK 运行 Gradle 脚本。如果未设置 JAVA_HOME,则会对 PATH 环境变量使用 java 命令。
为了获得最一致的结果,请务必将 JAVA_HOME 环境变量和 Android Studio 中的 Gradle JDK 配置设置为同一 JDK。
注意 :如果您使用 IDE 右键点击并选择运行突出显示的命令,在 Android Studio 终端中运行 Gradle 命令,则它会使用 Android Studio 设置中的 JDK,而不是 JAVA_HOME。
运行 build 时,Gradle 会创建一个名为 “守护程序” 的进程来执行实际 build。只要 build 使用的是相同的 JDK 和 Gradle 版本,就可以重复使用此过程。重复使用守护程序可缩短启动新 JVM 和初始化构建系统的时间。
如果您使用不同的 JDK 或 Gradle 版本启动 build,系统会创建其他守护程序,从而消耗更多 CPU 和内存。
提示: 同时处理多个项目时,请尽可能在其 gradle-wrapper.properties 文件中指定相同的 Gradle 版本,以减少创建的 Gradle 守护程序数量。
Android Studio 中的 Gradle JDK 配置
如需修改现有项目的 Gradle JDK 配置,请依次前往 File(或在 macOS 上依次前往 Android Studio) > Settings > Build, Execution, Deployment > Build Tools > Gradle,打开 Gradle 设置。Gradle JDK 下拉菜单包含以下可供选择的选项:
- 宏(例如
JAVA_HOME
和GRADLE_LOCAL_JAVA_HOME
) vendor-version
格式的 JDK 表条目(例如jbr-17
),存储在 Android 配置文件中- 下载 JDK
- 添加特定 JDK
- 从操作系统的默认 JDK 安装目录本地检测到的 JDK\
所选选项存储在项目的 .idea/gradle.xml
文件中的 gradleJvm
选项中,其 JDK 路径解析用于在通过 Android Studio 启动时运行 Gradle。
这些宏支持动态选择项目 JDK
路径:
JAVA_HOME
:使用同名的环境变量GRADLE_LOCAL_JAVA_HOME
:使用.gradle/config.properties
文件中的java.home
属性,默认为 JetBrains 运行时。
所选的 JDK
用于运行 Gradle build,并在修改 build 脚本和源代码时解析 JDK API 引用。请注意,在修改和构建源代码时,指定的 compileSdk
会进一步限制可用的 Java 符号。
注意 :在大多数情况下,我们建议使用 GRADLE_LOCAL_JAVA_HOME,这是新创建的项目的默认值。这样,您无需先打开项目,即可定义特定于项目的 JDK。
请务必选择高于或等于您在 Gradle build 中使用的插件所使用的 JDK 版本的 JDK 版本。如需确定 Android Gradle 插件 (AGP) 的最低要求 JDK 版本,请参阅版本说明中的兼容性表格。
例如,Android Gradle 插件版本 8.x 需要 JDK 17。如果您尝试运行使用较低版本 JDK 的 Gradle build,系统会报告如下消息:
An exception occurred applying plugin request [id: 'com.android.application']
> Failed to apply plugin 'com.android.internal.application'.
> Android Gradle plugin requires Java 17 to run. You are currently using Java 11.
Your current JDK is located in /usr/local/buildtools/java/jdk
You can try some of the following options:
- changing the IDE settings.
- changing the JAVA_HOME environment variable.
- changing `org.gradle.java.home` in `gradle.properties`.
我可以在 Java 或 Kotlin 源代码中使用哪些 Java API?
Android 应用可以使用 JDK 中定义的部分 API,但不能使用所有 API。Android SDK 在其可用 API 中定义了许多 Java 库函数的实现。compileSdk
属性用于指定在编译 Kotlin 或 Java 源代码时要使用的 Android SDK 版本。
android {
...
compileSdk = 33
}
每个版本的 Android 都支持特定版本的 JDK 以及其可用 Java API 的一部分。如果您使用的 Java API 在 compileSdk
中可用,但在指定的 minSdk 中不可用,则您或许可以通过称为脱糖的流程在较低版本的 Android 中使用该 API。如需了解受支持的 API,请参阅通过脱糖提供的 Java 11 及更高版本 API。
您可以使用此表格确定每个 Android API 支持哪个 Java 版本,以及在哪里可以详细了解可用的 Java API。
Android | Java | 支持的 API 和语言功能 |
---|---|---|
14 (API 34) | 17 | 核心库 |
13 (API 33) | 11 | 核心库 |
12 (API 32) | 11 | Java API |
11 及更低版本 | Android 版本 |
哪个 JDK 会编译我的 Java 源代码?
Java 工具链 JDK 包含用于编译任何 Java 源代码的 Java 编译器,此 JDK 还会在构建期间运行 javadoc
和单元测试。
工具链默认为用于运行 Gradle 的 JDK。如果您使用默认设置并在不同的计算机(例如本地计算机和单独的持续集成服务器)上运行 build,那么如果使用不同的 JDK 版本,build 的结果可能会有所不同。
如需创建更一致的 build,您可以明确指定 Java 工具链版本。指定此属性:
- 在运行 build 的系统上查找兼容的 JDK。
- 如果不存在兼容的 JDK(并且已定义工具链解析器),则下载一个。 - 公开工具链 Java API,以便从源代码进行调用。
- 使用 Java 语言版本编译 Java 源代码。
sourceCompatibility
和targetCompatibility
的默认值。
我们建议您始终指定 Java 工具链,并确保已安装指定的 JDK,或者将工具链解析器添加到 build 中。
无论您的源代码是用 Java 还是 Kotlin 编写的,您都可以指定工具链。在模块的 build.gradle(.kts) 文件的顶层指定工具链。
java {
toolchain {
languageVersion = JavaLanguageVersion.of(17)
}
}
如果您的源代码是 Kotlin、Java 或两者的混合,此方法适用。
工具链 JDK 版本可以与用于运行 Gradle 的 JDK 相同,但请注意,它们的用途不同。
我可以在 Java 源代码中使用哪些 Java 语言源代码功能?
sourceCompatibility
属性决定了在编译 Java 源代码期间可用的 Java 语言功能。它不会影响 Kotlin 源代码。
在模块的 build.gradle(.kts)
文件中指定 sourceCompatibility
,如下所示:
android {
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
}
}
如果未指定,此属性默认为 Java 工具链版本。如果您不使用 Java 工具链,则默认使用 Android Gradle 插件选择的版本(例如 Java 8 或更高版本)。
注意 :从 Android Studio Giraffe 开始,在导入项目时,sourceCompatibility 选项还会在编写 Java 源代码时用作 IDE 代码辅助和 lint 的默认选项。某些 Java 语言功能需要库支持,并且在 Android 上不可用。compileSdk 选项决定了哪些库可用。 其他功能(例如 switch 表达式)只需要 Java 编译器,并且适用于 Android。
在编译 Kotlin 或 Java 源代码时,可以使用哪些 Java 二进制功能?
targetCompatibility
和 jvmTarget
属性分别决定为编译的 Java
和 Kotlin
源代码生成字节码时使用的 Java 类格式版本。
在添加等效的 Java 功能之前,就已经存在一些 Kotlin 功能。早期的 Kotlin 编译器必须自行创建表示这些 Kotlin 功能的方法。其中一些功能后来被添加到了 Java 中。在较高版本的 jvmTarget
中,Kotlin 编译器可能会直接使用 Java 功能,这可能会带来更好的性能。
不同版本的 Android 支持不同的 Java 版本。您可以通过提高 targetCompatibility
和 jvmTarget
来使用其他 Java 功能,但这可能会迫使您也提高最低 Android SDK 版本,以确保该功能可用。
请注意,targetCompatibility
必须大于或等于 sourceCompatibility
。在实践中,sourceCompatibility
、targetCompatibility
和 jvmTarget
通常应使用相同的值。您可以按如下方式进行设置:
android {
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
}
}
如果未指定,这些属性默认为 Java 工具链版本。如果您未使用 Java 工具链,则默认值可能会有所不同,并导致构建问题。因此,我们建议您始终明确指定这些值或使用 Java 工具链。
脱糖
sourceCompatibility
和targetCompatibility
实现了低版本写代码高版本输出,但是这并不常用,而高版本写代码低版本输出呢,这才是常用的,而且我们Adnroid开发可以说天天都在用,那这是怎么实现的?比如Android 5
、Adnroid 6
系统原生只支持到Java 7
。我们写Android项目时,对于兼容性,一般来说指定minSdk
就可以了,比如minSdk
设置为21,对应为Android 5
,也就是说这个项目需要兼容的最低版本是Android 5
,假设我们把targetCompatibility
和jvmTarget
设置为11,那生成的字节码是Java 11
版本的字节码,为什么可以在Android 5
上运行?Android 5
系统原生只支持到Java 7
啊,为什么运行没问题?这是因为Android并不是直接运行.class
字节码文件的,Android运行的是dex
,所以在Java 11
字节码文件转换为dex
的时候,转换工具就动了手脚,它会根据你声明的minSdk
来转换为对应Java
版本可以支持的dex
,这种操作叫“脱糖”,比如你在代码中使用了Lambda表达式,这是Java 8的特性,脱糖会把Lambda转换为接口的对应实现,简单理解就是:本来你用的是高版本的特性实现的一些功能,脱糖就是给你转换为低版本实现的对应功能。
这篇文章已经很长了,关于脱糖的更多知识可以查看我的另一篇文章:https://blog.csdn.net/android_cai_niao/article/details/148627756
Java SE支持路线图
来自Oracle官方网站:https://www.oracle.com/java/technologies/java-se-support-roadmap.html
表格说明:
-
版本 (Release)
- LTS:长期支持版本(Long-Term Support),Oracle提供5年以上支持
- 非LTS:过渡版本,仅支持6个月(企业环境不推荐)
-
GA日期 (GA Date)
正式发布可用日期(General Availability) -
首要支持截止 (Premier Support Until)
- 核心支持期:免费提供安全更新、错误修复和技术支持
- 免费用户注意:社区用户免费更新仅到此日期
-
扩展支持截止 (Extended Support Until)
- 付费支持期:需购买Oracle商业许可(费用高昂)
- 特殊豁免:
- Java 8扩展支持免费豁免至2030年12月
- Java 11扩展支持免费豁免至2032年1月
- 豁免的意思就是说Oracle对指定版本的个人/开发者枉开一面,免费延长支持,但企业商用则需要从截止日期起付费。
-
持续支持 (Sustaining Support)
无时间限制的付费支持(不包含新功能或安全更新)
这个解析来自DeepSeek,不知道对不对,如果对的话,那Java8和11的免费支持都已经到期了,而Java 17也即将在2026年到期,Java 21在2028年到期,好像都没多久啊!!
不过做Android开发好像也不用关心这个,因为Android用的并不是Oracle JDK,用的是OpenJDK,这个是可以随便用且没有限制,不需要给Oracle付钱。但是就是说停止支持意味着这个版本更容易过时,所以,如果可能的话,还是尽快切换到新版本。
命令行运行gradle时Java版本不对的问题
我当前使用的Android Studio为截止目前的最新版本:Android Studio Meerkat Feature Drop | 2024.3.2 Patch 1
创建一个Android项目,然后运行到Android手机上,这是没问题的,编译/运行都完全OK,然后在Terminal中执行:.\gradlew tasks
命令,结果如下:
* What went wrong:
An exception occurred applying plugin request [id: 'com.android.application', version: '8.10.1']
> Failed to apply plugin 'com.android.internal.application'.
> Android Gradle plugin requires Java 17 to run. You are currently using Java 11.
这里只贴了部分输出,显示应用AGP 8.10.1插件需要使用Java 17,而当前是Java 11。
这里提及这个问题,是想说,还有一个地方可以设置java版本,在gradle.properties
文件中最后添加一行:
org.gradle.java.home=C\:\\Program Files\\Java\\jdk-17
然后再执行.\gradlew tasks
命令就正常了。
即使我不设置org.gradle.java.home,我在Android Studio的Gradle面板中执行tasks任务又是OK,这个问题可以在我的另一篇文章中找到答案,查看该文章的验证Gradle使用的JDK版本
段落:https://blog.csdn.net/android_cai_niao/article/details/148588362