–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
在接口中,static
或 default
方法会由于重写导致重复的实现,如果这些方法是类的一部分而不是接口,则可以提取这些函数为私有函数。在 Java 9
中为接口添加了用 private
修饰的私有方法。
interface Java9PrivateInterface {
static String hey() {
return getHey();
}
private static String getHey() {
return “hey”;
}
}
这是 Java 9
中第一个需要支持的语言功能。在此版本之前,不允许在接口成员上使用 private
。由于 D8
已经通过脱糖处理了 default
和 static
修饰符,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 24
的 ART
环境中已经支持 static
和 default
修饰符,当我们指定 --min-api 24
时,static
和 default
修饰的方法都不会进行脱糖。
$ $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
方法仍然是 private
和 static
类型的,说明没有被脱糖。如果我们写个 main
方法调用 getHey
,在 API 24
的机器上是可以正常运行,因为 ART
在 API 24
版本已经支持。
上面就是 Java 9
的语言特性,并且 Android
已经支持。但是 Java 9
中的 API
还没有被 Android
全面支持,比如 Process API
、Variable Handles API
、Reactive 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 8
和 Java 9
的编译器进行对比。首先使用 Java 8
的 javac
进行编译字节码。
$ 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
类,不过值得庆幸的是,正如 Android
对 lambda
的支持,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.orElseThrow
、List.copyOf
和 CalcCurth.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
比如,String
、Predicate.not
的辅助类以及 Reader
、Writer
、InputSteam
和 OutputStream
增加的空 IO
处理。
另一个 Java 11
中的重大变更 API
是 new HTTP client, java.net.http,其实这个 API
在 Java 9
的 jdk.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;
总结:心得体会
既然选择这个行业,选择了做一个程序员,也就明白只有不断学习,积累实战经验才有资格往上走,拿高薪,为自己,为父母,为以后的家能有一定的经济保障。
学习时间都是自己挤出来的,短时间或许很难看到效果,一旦坚持下来了,必然会有所改变。不如好好想想自己为什么想进入这个行业,给自己内心一个答案。
面试大厂,最重要的就是夯实的基础,不然面试官随便一问你就凉了;其次会问一些技术原理,还会看你对知识掌握的广度,最重要的还是你的思路,这是面试官比较看重的。
最后,上面这些大厂面试真题都是非常好的学习资料,通过这些面试真题能够看看自己对技术知识掌握的大概情况,从而能够给自己定一个学习方向。包括上面分享到的学习指南,你都可以从学习指南里理顺学习路线,避免低效学习。
大厂Java架构核心笔记(适合中高级程序员阅读):
学习时间都是自己挤出来的,短时间或许很难看到效果,一旦坚持下来了,必然会有所改变。不如好好想想自己为什么想进入这个行业,给自己内心一个答案。
面试大厂,最重要的就是夯实的基础,不然面试官随便一问你就凉了;其次会问一些技术原理,还会看你对知识掌握的广度,最重要的还是你的思路,这是面试官比较看重的。
最后,上面这些大厂面试真题都是非常好的学习资料,通过这些面试真题能够看看自己对技术知识掌握的大概情况,从而能够给自己定一个学习方向。包括上面分享到的学习指南,你都可以从学习指南里理顺学习路线,避免低效学习。
大厂Java架构核心笔记(适合中高级程序员阅读):
[外链图片转存中…(img-iEnUdhIe-1714483792045)]