Android Studio中的各種Java版本
創建一個項目,app模塊的build.gradle.kts默認配置如下:
plugins {alias(libs.plugins.android.application)alias(libs.plugins.kotlin.android)
}android {namespace = "cn.android666.javaversiontest"compileSdk = 35defaultConfig {applicationId = "cn.android666.javaversiontest"minSdk = 21targetSdk = 35versionCode = 1versionName = "1.0"testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"}buildTypes {release {isMinifyEnabled = falseproguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")}}compileOptions {sourceCompatibility = JavaVersion.VERSION_11targetCompatibility = 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_11targetCompatibility = 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_11targetCompatibility = 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 35defaultConfig {applicationId "cn.android666.javaonly"minSdk 21targetSdk 35versionCode 1versionName "1.0"testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"}buildTypes {release {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'}}compileOptions {sourceCompatibility JavaVersion.VERSION_1_11targetCompatibility JavaVersion.VERSION_1_11} }dependencies {implementation libs.appcompatimplementation libs.materialimplementation libs.activityimplementation libs.constraintlayouttestImplementation libs.junitandroidTestImplementation libs.ext.junitandroidTestImplementation libs.espresso.core }
因為是純
Java
項目,它只有sourceCompatibility
和targetCompatibility
,沒有kotlin
的jvmTarget
屬性了。
2. 1.8和1.8
修改sourceCompatibility
和targetCompatibility
為1.8版本,如下:
compileOptions {sourceCompatibility JavaVersion.VERSION_1_8targetCompatibility JavaVersion.VERSION_1_8
}
MainActivity.java
代碼如下:
public class MainActivity extends AppCompatActivity {@Overrideprotected 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 version2. Set a higher source/target version3. 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_7targetCompatibility 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() {@Overridepublic 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 version2. Set a higher source/target version3. 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_7targetCompatibility 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_11targetCompatibility 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 = 35defaultConfig {applicationId = "cn.android666.javaversiontest"minSdk = 21targetSdk = 35versionCode = 1versionName = "1.0"testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"}buildTypes {release {isMinifyEnabled = falseproguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")}}compileOptions {sourceCompatibility = JavaVersion.VERSION_11targetCompatibility = 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_11targetCompatibility = 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_7targetCompatibility = 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_11targetCompatibility = 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/jdkYou 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_17targetCompatibility = 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