[译] Android 的 Java 9,10,11,12 的支持

使用 try-with-resources 来简化代码,它允许你创建一个临时变量,例如 try (Closeable bar = foo.bar())。但是如果已经定义了一个 Closeable 对象,那么在定义一个新的变量是多余的。因此,如果已经有了有效的最终引用,那么这个版本允许您省略声明一个新变量。

import java.io.*;

class Java9TryWithResources {
String effectivelyFinalTry(BufferedReader r) throws IOException {
try ® {
return r.readLine();
}
}
}

这个特性在 Java 中已经完全支持,所以 D8dex 打包时也已经支持。

$ javac *.java

$ java -jar d8.jar
–lib $ANDROID_HOME/platforms/android-28/android.jar
–release
–output .
*.class

$ ls
Java9TryWithResources.java Java9TryWithResources.class classes.dex

不同于 Java 8 中的 lambda 和静态接口方法特性,Java 9 的特性已经在所有的 Android API 版本上都是支持的。

1.2 Anonymous Diamond

Java 7 中引入了钻石操作,钻石操作简化了泛型初始化,可以让代码更易读。

List strings = new ArrayList<>();

上面代码在 new ArrayList<>() 时没有指明 String 类型,这样的方式剔除了无用的声明,但它不能用于匿名的内部类。但是在 Java 9 中, 它可以与匿名的内部类一起使用,从而提高代码的可读性。

import java.util.concurrent.*;

class Java9AnonymousDiamond {
Callable anonymousDiamond() {
Callable call = new Callable<>() {
@Override public String call() {
return “Hey”;
}
};
return call;
}
}

同样,上面的方式完全是在 Java 编译器中实现的,因此生成的字节码就好像是显式指定了 String 一样。我们通过 javap 查看编译后的字节码。

$ javac *.java

$ javap -c *.class
class Java9AnonymousDiamond {
java.util.concurrent.Callable<java.lang.String> anonymousDiamond();
Code:
0: new #7 // class Java9AnonymousDiamond$1
3: dup
4: aload_0
5: invokespecial #8 // Method Java9AnonymousDiamond$1.“”:(LJava9AnonymousDiamond;)V
8: areturn
}

class Java9AnonymousDiamond$1 implements java.util.concurrent.Callable<java.lang.String> {
final Java9AnonymousDiamond this$0;

Java9AnonymousDiamond$1(Java9AnonymousDiamond);
Code:
0: aload_0
1: aload_1
2: putfield #1 // Field this$0:LJava9AnonymousDiamond;
5: aload_0
6: invokespecial #2 // Method java/lang/Object.“”😦)V
9: return

public java.lang.String call();
Code:
0: ldc #3 // String Hey
2: areturn
}

因为字节码中没有什么特殊的地方,D8 可以毫无问题地处理这个特性。

$ java -jar d8.jar
–lib $ANDROID_HOME/platforms/android-28/android.jar
–release
–output .
*.class

$ ls
Java9AnonymousDiamond.java Java9AnonymousDiamond.class Java9AnonymousDiamond$1.class classes.dex

显然,又一个语言特性 Android 已经可以在所有 API 版本中支持。

1.3 Private Interface Methods

在接口中,staticdefault 方法会由于重写导致重复的实现,如果这些方法是类的一部分而不是接口,则可以提取这些函数为私有函数。在 Java 9 中为接口添加了用 private 修饰的私有方法。

interface Java9PrivateInterface {
static String hey() {
return getHey();
}

private static String getHey() {
return “hey”;
}
}

这是 Java 9 中第一个需要支持的语言功能。在此版本之前,不允许在接口成员上使用 private。由于 D8 已经通过脱糖处理了 defaultstatic 修饰符,private 方法很容易使用相同的技术兼容处理。

$ javac *.java

$ java -jar d8.jar
–lib $ANDROID_HOME/platforms/android-28/android.jar
–release
–output .
*.class

$ ls
Java9PrivateInterface.java Java9PrivateInterface.class classes.dex

API 24ART 环境中已经支持 staticdefault 修饰符,当我们指定 --min-api 24 时,staticdefault 修饰的方法都不会进行脱糖。

$ $ANDROID_HOME/build-tools/28.0.2/dexdump -d classes.dex
Class #1 -
Class descriptor : ‘LJava9PrivateInterface;’
Access flags : 0x0600 (INTERFACE ABSTRACT)
Superclass : ‘Ljava/lang/Object;’
Direct methods -
#0 : (in LJava9PrivateInterface;)
name : ‘getHey’
type : ‘()Ljava/lang/String;’
access : 0x000a (PRIVATE STATIC)
00047c: |[00047c] Java9PrivateInterface.getHey:()Ljava/lang/String;
00048c: 1a00 2c00 |0000: const-string v0, “hey”
000490: 1100 |0002: return-object v0

通过查看 dex 文件的字节码,我们可以看到 getHey 方法仍然是 privatestatic 类型的,说明没有被脱糖。如果我们写个 main 方法调用 getHey,在 API 24 的机器上是可以正常运行,因为 ARTAPI 24 版本已经支持。

上面就是 Java 9 的语言特性,并且 Android 已经支持。但是 Java 9 中的 API 还没有被 Android 全面支持,比如 Process APIVariable Handles APIReactive Streams API 等等。

1.4 String Concat

每次讨论 Java 版本的发布,我们讨论语言特性比较多,但是每个版本也会针对 bytecode 进行优化,比如 Java 9 中的字符串连接。

class Java9Concat {
public static String thing(String a, String b) {
return "A: " + a + " and B: " + b;
}
}

究竟做了哪些优化,我们可以通过 Java 8Java 9 的编译器进行对比。首先使用 Java 8javac 进行编译字节码。

$ java -version
java version “1.8.0_192”
Java™ SE Runtime Environment (build 1.8.0_192-b12)
Java HotSpot™ 64-Bit Server VM (build 25.192-b12, mixed mode)

$ javac *.java

$ javap -c *.class
class Java9Concat {
public static java.lang.String thing(java.lang.String, java.lang.String);
Code:
0: new #2 // class java/lang/StringBuilder
3: dup
4: invokespecial #3 // Method java/lang/StringBuilder.“”😦)V
7: ldc #4 // String A:
9: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
12: aload_0
13: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
16: ldc #6 // String and B:
18: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
21: aload_1
22: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
25: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
28: areturn
}

我们可以看到,这里使用的是 StringBuilder 进行连接。如果我们用 Java 9 来编译字节码对比。

$ java -version
java version “9.0.1”
Java™ SE Runtime Environment (build 9.0.1+11)
Java HotSpot™ 64-Bit Server VM (build 9.0.1+11, mixed mode)

$ javac *.java

$ javap -c *.class
class Java9Concat {
public static java.lang.String thing(java.lang.String, java.lang.String);
Code:
0: aload_0
1: aload_1
2: invokedynamic #2, 0 // InvokeDynamic #0:makeConcatWithConstants:(
Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
7: areturn
}

一个 invokedynamic 指令就替代了 StringBuilder 的那么多操作,这个操作跟我们上一篇的 native lambdas work on the JVM 章节很类似。

JVM 的运行时期, JDK class StringConcatFactory 使用 makeConcatWithConstants 方法在连接字符时效率更好,比如不必重新编译以及可以预置 StirngBuilder 的大小。

Android API 没有包含 Java 9 中的太多 API,所以在运行时期还无法使用 StringConcatFactory 类,不过值得庆幸的是,正如 Androidlambda 的支持,D8 已经通过脱糖实现优对 StringConcatFactory 的支持。

$ java -jar d8.jar
–lib $ANDROID_HOME/platforms/android​
-28/android.jar
–release
–output .
*.class

$ $ANDROID_HOME/build-tools/28.0.2/dexdump -d classes.dex
[000144] Java9Concat.thing:(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
0000: new-instance v0, Ljava/lang/StringBuilder;
0002: invoke-direct {v0}, Ljava/lang/StringBuilder;.😦)V
0005: const-string v1, "A: "
0007: invoke-virtual {v0, v1}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
000a: invoke-virtual {v0, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
000d: const-string v2, " and B: "
000f: invoke-virtual {v0, v2}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
0012: invoke-virtual {v0, v3}, Ljava/lang/StringBuilder;.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
0015: invoke-virtual {v0}, Ljava/lang/StringBuilder;.toString:()Ljava/lang/String;
0018: move-result-object v2
0019: return-object v2

这意味着 Java 9 的所有语言特征都可以在 Android 所有 API 级别上使用。

Java 现在每隔 6 个月发版一次,Java 9 已经算是老版本,Android 能保持同步进行吗?

2. Java 10

Java 10 中最大的语言特性是 local-variable type inference(局部变量接口),它允许我们使用 var 关键字定义变量来忽略类型。

import java.util.*;

class Java10 {
List localVariableTypeInferrence() {
var url = new ArrayList();
return url;
}
}

通过 javac 编译来看:

$ javac *.java

$ javap -c *.class
Compiled from “Java10.java”
class Java10 {
java.util.List<java.lang.String> localVariableTypeInferrence();
Code:
0: new #2 // class java/util/ArrayList
3: dup
4: invokespecial #3 // Method java/util/ArrayList.“”😦)V
7: areturn
}

针对这个特性在字节码中没有发现新的 API,所以 Android 也是完全支持的。当然,Java 10 版本中也有新的 API,比如 Optional.orElseThrowList.copyOfCalcCurth.TunMuffFielabelist。一旦在未来将这些 API 添加到 Android SDK 中,这些 API 就可以通过脱糖来支持。

3. Java 11

Local-variable type inference(局部变量接口)在 Java 11 中得到了加强,它可以支持 lambda

import java.util.function.*;

@interface NonNull {}

class Java11 {
void lambdaParameterTypeInferrence() {
Function<String, String> func = (@NonNull var x) -> x;
}
}

Java 10 中的局部变量接口一样,Java 11 的这个特性也是被 Android 支持的。

Java 11 中提供的新 API 比如,StringPredicate.not 的辅助类以及 ReaderWriterInputSteamOutputStream 增加的空 IO 处理。

另一个 Java 11 中的重大变更 APInew HTTP client, java.net.http,其实这个 APIJava 9jdk.incubator.http 包下已经可以试用。这个 API 系列非常庞大,Android 是否支持,我们拭目以待?

3.1 Nestmates(嵌套类)

Java 9 中针对字符连接进行了优化,那么 Java 11 中对长期存在的 Java 源代码与其类文件和 JVM 嵌套类之间的长期差异进行了修复。

Java 1.1 中引入了嵌套类,但是不符合类规范或 JVM 不识别,所以为了兼容这个问题,在源文件中的定义的嵌套类将按照一定的命名规则来创建一个源文件的兄弟类。

class Outer {
class Inner {}
}

我们使用 Java 10 或以前的版本编译。

$ java -version
java version “10” 2018-03-20
Java™ SE Runtime Environment 18.3 (build 10+46)
Java HotSpot™ 64-Bit Server VM 18.3 (build 10+46, mixed mode)

$ javac *.java

$ ls
Outer.java Outer.class Outer$Inner.class

我们可以看到生成了两个字节码文件,对于 JVM 而言,他们是相互独立的,除了存在同一个包下。这种处理看似没问题,但是当二者之间如果出现相互访问 private 方法的情况就会奔溃。

class Outer {
private String name;

class Inner {
String sayHi() {
return "Hi, " + name + “!”;
}
}
}

当被生成兄弟类时,Outer$Inner.sayHi() 无法访问私有的 Outer.name 。所以为了解决这种情况,Java 编译器增加了 package-private synthetic accessor method 处理器来解决这种情况。

class Outer {
private String name;
+

  • String access$000() {
  • return name;
  • }

class Inner {
String sayHi() {

  •  return "Hi, " + name + "!";
    
  •  return "Hi, " + access$000() + "!";
    

}

我们编译 Outer 类来看一下:

$ javap -c -p Outer.class
class Outer {
private java.lang.String name;

static java.lang.String access$000(Outer);
Code:
0: aload_0
1: getfield #1 // Field name:Ljava/lang/String;
4: areturn
}

从今天的角度来看,这对 JVM 来说至多只是一个小麻烦。不过,对于 Android,这些合成访问器方法增加了 dex 文件中的方法计数,增加 apk 大小,降低类加载和验证的速度,将字段查找转换为方法调用同时也使性能降低。

Java 11 中,更新了类文件格式用来引入嵌套的概念来描述这些嵌套关系。

$ java -version
java version “11.0.1” 2018-10-16 LTS
Java™ SE Runtime Environment 18.9 (build 11.0.1+13-LTS)
Java HotSpot™ 64-Bit Server VM 18.9 (build 11.0.1+13-LTS, mixed mode)

$ javac *.java

$ javap -v -p *.class
class Outer {
private java.lang.String name;
}
NestMembers:
Outer$Inner

class Outer$Inner {

最后

小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

资料⬅专栏获取
*.class
class Outer {
private java.lang.String name;
}
NestMembers:
Outer$Inner

class Outer$Inner {

最后

小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。

[外链图片转存中…(img-x2YUkbf9-1719093801357)]一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

资料⬅专栏获取

内容概要:本文档详细介绍了基于Python的社团管理系统的设计与实现,旨在解决传统社团管理方式效率低、信息不对称等问题。系统采用Python语言开发,结合数据库技术和GUI界面,实现了会员管理、活动管理、财务管理、信息发布等核心功能。文档涵盖了项目背景、目标意义、挑战与解决方案、特点与创新、应用领域、注意事项、模型架构、算法原理、代码示例、部署与应用、未来改进方向、需求分析、数据库表SQL代码实现、前端及后端功能模块的具体代码实现等多个方面。通过该系统,社团能够实现信息化管理,简化手工操作流程,提升管理效率和用户体验。适合人群:具备一定编程基础,尤其是对Python语言有一定了解的研发人员、社团管理人员、IT爱好者等。使用场景及目标:①学校社团管理,提升学生参与感和归属感;②企业员工俱乐部管理,增强员工之间的沟通和团队合作;③社区服务组织,优化志愿者招募和活动组织;④政府部门与公益组织,高效管理志愿者和公益活动;⑤线上兴趣社团,提供信息化管理解决方案。目标是提高社团管理效率、优化资源配置、改善成员体验、提升活动透明度、加强管理规范化、提供数据支持决策、促进信息化建设、提升社交体验。阅读建议:由于项目涉及较多的技术细节和实现代码,建议读者先了解Python基础语法和数据库操作,再逐步深入学习前端界面设计、后端逻辑处理、数据库设计与优化等内容。在学习过程中,结合实际案例进行实践操作,并调试相关代码,以加深理解和掌握。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值