Android Studio中的各种Java版本区别

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 参数。
  • targetCompatibility:指定生成的 ​​字节码(.class 文件)​​ 的目标 JVM 版本

    • 确保字节码能在指定版本或更高版本的 JVM 上运行(例如设为 VERSION_11 时,字节码可运行在 JDK 11+ 环境)。
    • 对应 javac -target 参数。
    • 必须满足​​:targetCompatibility ≥ sourceCompatibility
  • jvmTarget:指定 ​​Kotlin 代码​​ 编译后的字节码目标版本。

    • 控制 Kotlin 编译器生成的字节码版本(类似 Java 的 targetCompatibility,但仅作用于 Kotlin 代码)。

Gradle JDK 需独立满足 AGP 要求:

APG版本最低 Gradle JDK最低Gradle
8.0+JDK 17Gradle 8.0
7.4JDK 11Gradle 7.5

总结

  • Gradle JDK​​:构建工具的“发动机”,需独立配置且版本受 AGP 限制 ✅。

  • ​​source/targetCompatibility​​:控制 Java 代码的语法和字节码兼容性 🧩。

  • jvmTarget​​:专为 Kotlin 代码设置字节码版本 🔧。

验证编译后的class的java版本

我的配置为:

compileOptions {
    sourceCompatibility = JavaVersion.VERSION_11
    targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
    jvmTarget = "11"
}

targetCompatibilityjvmTarget都设置为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.ktUtil.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.1JDK 17SDK 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项目,它只有sourceCompatibilitytargetCompatibility ,没有kotlinjvmTarget属性了。

2. 1.8和1.8

修改sourceCompatibilitytargetCompatibility 为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,说的就是sourceCompatibilitytargetCompatibility 的值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

设置好sourceCompatibilitytargetCompatibility 之后同步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默认使用运行GradleJDK来编译我们的代码。而运行GradleJDK版本是在设置中指定的,具体位置为: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来运行GradleGradle调用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为什么会有人用低版本写代码,然后又编译为高版本字节码?答案如下:

  1. ​​兼容旧代码库,但目标环境已升级至高版本 JVM​

    • 问题​​:遗留系统代码基于 Java 7 开发,无法立即升级语法,但生产环境已升级至 Java 8+ 的 JVM。
    • 解决方案​​:
      • 保持源码兼容 Java 7,避免引入高版本语法导致旧开发环境编译失败。
      • 编译为 Java 8 字节码,利用高版本 JVM 的性能优化(如元空间替代永久代、编译器优化等)。
  2. ​​依赖库强制要求高版本运行时环境​

    • 问题​​:某些第三方库(如新版 gRPC、Spring Boot 2.x+)需运行在 Java 8+ 环境,但项目代码未使用 Java 8 语法。

    • ​​解决方案​​:

      • 源码保持 Java 7 兼容性,确保开发工具链统一(如 CI/CD 环境仍用 JDK 7 编译)。
      • 输出 Java 8 字节码,满足依赖库的最低运行时要求。
  3. 阶段性迁移策略​

    • 问题​​:大型项目从 Java 7 升级到 Java 8 需分步进行:先确保字节码兼容新环境,再逐步重构代码使用新语法。
      ​​

    • 解决方案​​:

      • 先设置 targetCompatibility=1.8,验证系统在 Java 8 JVM 的稳定性。
      • 稳定后再逐步修改源码,启用 Lambda 等新特性(此时可同步升级 sourceCompatibility=1.8)。
  4. ​​规避低版本 JVM 的已知缺陷​​

    • ​​问题​​:Java 7 某些 JVM 实现存在性能或安全漏洞(如 HashCollision 攻击),但代码无法立即升级语法。
    • ​​解决方案​​:
      • 编译为 Java 8 字节码并部署到 Java 8+ 环境,直接规避低版本 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项目应该是差不多的,所以这里我就测试看有没有什么需要注意的地方。

targetCompatibilityjvmTarget必须要一致,否则编译就会报错,比如:

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兼容性不一致。

没找到targetCompatibilityjvmTarget 必须要求一致的官方说法,如果必须一致的话那为什么弄两个属性,一个不就行了吗?

工具链设置:

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?

创建项目时sourceCompatibilitytargetCompatibilityjvmTarget 都是默认为11的,但是并没有指定java工具链,所以会默认使用Gradle JDK的版本,对于我的环境就是JDK 21。这里我就有个疑问了,即然目标是11,那为什么不指定工具链为11,于是问了DeepSeek,得到的一种说法是JDK 21编译器更高效,编译速度更快(尤其增量编译),只有当默认编译出错的时候,再考虑手动添加工具链为JDK11,看是否是JDK21编译的问题,比如某些库可能在JDK21下行为异常,又比如KAPTKotlin注解处理器)在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 7try-with-resourcesswitch 支持字符串、二进制字面量try (FileInputStream fis = ...) {}
    Java 8Lambda 表达式、方法引用、默认接口方法、Stream API(a, b) -> a + b
    Java 9模块系统(module-info.java)、私有接口方法module my.module {}
    Java 10局部变量类型推断(varvar list = new ArrayList<String>();
    Java 14switch 表达式、record 类型(预览)switch (x) { case 1 -> "one"; }
    Java 16+record 正式引入record Point(int x, int y) {}

sourceCompatibility 只是限制语言特性,它并不限制API,API是指标准类库里面的类,比如Java 8引入的新API:

LocalDate.now();

语言特性是由JDK编译器控制的,而APIandroid.jar提供,使用哪个版本的android.jar进行编译,取决于compileSdk

假设我们把sourceCompatibility 设置为Java 7targetCompatibility设置为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出的,报错能理解,但是为什么报的错不一样呢?这是因为两种不同的兼容性检查机制​:

  1. LocalDate.now() 的报错:​​Android API级别检查​
    • 触发机制​​:Android构建工具(如Lint)在编译时会对代码进行​​API级别校验​​,发现LocalDateAPI 26引入的类,而你的minSdk=21,我们在Android官方文档中查看LocalDate也能看到它是API 26引入的。
  2. List.of() 的报错:​​Java语言级别检查​
    • 触发机制​​:Java编译器(javackotlinc)在编译时根据sourceCompatibility=1.7检查语法,发现List.of()Java 9的语法特性,虽然说List.of() 是一个API,但是它是使用Java 9的语言特性的:接口静态方法

我设置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。
  • 右边是运行GradleJDK和运行Android StudioJDK。所以,加起来一共就有7个JDK版本了。
  • 源代码依赖于JDK API,而它由compileSdk指定。
  • 单元测试(Unit Tests)的运行使用Toolchain JDK
  • Kotlin jvmTargettargetCompatibility JDKsourceCompatibility 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

  1. STUDIO_JDK 环境变量
  2. studio.jdk 目录(在 Android Studio 发行版中)
  3. Android Studio 发行版中的 jbr 目录(JetBrains 运行时)。推荐。
  4. JDK_HOME 环境变量
  5. JAVA_HOME 环境变量
  6. 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_HOMEGRADLE_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。

AndroidJava支持的 API 和语言功能
14 (API 34)17核心库
13 (API 33)11核心库
12 (API 32)11Java 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 源代码。
  • sourceCompatibilitytargetCompatibility 的默认值。

我们建议您始终指定 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 二进制功能?

targetCompatibilityjvmTarget 属性分别决定为编译的 JavaKotlin 源代码生成字节码时使用的 Java 类格式版本。

在添加等效的 Java 功能之前,就已经存在一些 Kotlin 功能。早期的 Kotlin 编译器必须自行创建表示这些 Kotlin 功能的方法。其中一些功能后来被添加到了 Java 中。在较高版本的 jvmTarget 中,Kotlin 编译器可能会直接使用 Java 功能,这可能会带来更好的性能。

不同版本的 Android 支持不同的 Java 版本。您可以通过提高 targetCompatibilityjvmTarget 来使用其他 Java 功能,但这可能会迫使您也提高最低 Android SDK 版本,以确保该功能可用。

请注意,targetCompatibility 必须大于或等于 sourceCompatibility。在实践中,sourceCompatibilitytargetCompatibilityjvmTarget 通常应使用相同的值。您可以按如下方式进行设置:

android {
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_17
        targetCompatibility = JavaVersion.VERSION_17
    }
    kotlinOptions {
        jvmTarget = "17"
    }
}

如果未指定,这些属性默认为 Java 工具链版本。如果您未使用 Java 工具链,则默认值可能会有所不同,并导致构建问题。因此,我们建议您始终明确指定这些值或使用 Java 工具链

脱糖

sourceCompatibilitytargetCompatibility实现了低版本写代码高版本输出,但是这并不常用,而高版本写代码低版本输出呢,这才是常用的,而且我们Adnroid开发可以说天天都在用,那这是怎么实现的?比如Android 5Adnroid 6系统原生只支持到Java 7。我们写Android项目时,对于兼容性,一般来说指定minSdk就可以了,比如minSdk设置为21,对应为Android 5,也就是说这个项目需要兼容的最低版本是Android 5,假设我们把targetCompatibilityjvmTarget设置为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
在这里插入图片描述
表格说明:

  1. ​​版本 (Release)​​

    • ​​LTS​​:长期支持版本(Long-Term Support),Oracle提供5年以上支持
    • ​​非LTS​​:过渡版本,仅支持6个月(企业环境不推荐)
  2. ​​GA日期 (GA Date)​​
    正式发布可用日期(General Availability)

  3. ​​首要支持截止 (Premier Support Until)​​

    • ​​核心支持期​​:免费提供安全更新、错误修复和技术支持
    • ​​免费用户注意​​:社区用户免费更新仅到此日期
  4. ​​扩展支持截止 (Extended Support Until)​​

    • ​​付费支持期​​:需购买Oracle商业许可(费用高昂)
    • ​​特殊豁免​​:
      • Java 8扩展支持免费豁免至2030年12月
      • Java 11扩展支持免费豁免至2032年1月
      • 豁免的意思就是说Oracle对指定版本的个人/开发者枉开一面,免费延长支持,但企业商用则需要从截止日期起付费。
  5. ​​持续支持 (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

### 如何在 Android Studio 中配置 Java 版本 为了确保项目能够正确识别并使用特定版本Java,在 Android Studio 中需要进行一系列配置。这些配置不仅涉及项目的构建文件,还可能涉及到操作系统的环境变量。 #### 修改 `build.gradle` 文件中的 Java 版本 对于 Gradle 构建工具而言,可以在模块级别的 `build.gradle` 文件中指定所需的 Java 编译器版本: ```groovy android { ... compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } } ``` 此部分设定指定了源码兼容性和目标字节码版本均为 Java 8[^1]。 #### 设置全局 JDK 路径 如果希望整个 IDE 使用统一的 JDK,则可以通过如下方式调整: - 打开 **File** -> **Project Structure...** - 在弹出窗口左侧选择 **SDK Location** - 对于 macOS 用户来说,默认安装路径可能是 `/Library/Java/JavaVirtualMachines/<your-jdk-version>/Contents/Home`[^2] 通过上述方法可以更改当前工作空间所使用的 JDK 版本。 #### 更新操作系统上的 JAVA_HOME 变量 为了让命令行工具或其他依赖项也能访问到正确的 JDK,还需要更新操作系统的环境变量 `JAVA_HOME`: macOS 下可通过终端执行以下命令来设置: ```bash export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_191.jdk/Contents/Home ``` 请注意替换实际安装位置以匹配本地环境。 完成以上步骤之后重启 Android Studio 即可生效新的 Java 版本设置。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

android_cai_niao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值