JDK核心JAVA源码解析(8)

想写这个系列很久了,对自己也是个总结与提高。原来在学JAVA时,那些JAVA入门书籍会告诉你一些规律还有法则,但是用的时候我们一般很难想起来,因为我们用的少并且不知道为什么。知其所以然方能印象深刻并学以致用。

本文基于 Java 14

在JDK1.5引入自动装箱/拆箱,让开发更高效。自动装箱时编译器调用valueOf()将原始类型值转换成对象,同时自动拆箱时,编译器通过调用类似intValue(),doubleValue()这类的方法将对象转换成原始类型值。 自动装箱是将 boolean 值转换成 Boolean 对象,byte 值转换成 Byte 对象,char 转换成 Character 对象,float 值转换成 Float 对象,int 转换成 Integer,long 转换成 Long,short 转换成 Short,自动拆箱则是相反的操作。

1public static void main(String[] args) { 2 Long v = 6L; 3 long a = v; 4 if (a > v) { 5 6 } 7 }

通过javap -c命令查看:

1 0 ldc2_w #68 <6> 2 3 invokestatic #66 <java/lang/Long.valueOf> 3 6 astore_1 4 7 aload_1 5 8 invokevirtual #70 <java/lang/Long.longValue> 611 lstore_2 712 lload_2 813 aload_1 914 invokevirtual #70 <java/lang/Long.longValue> 1017 lcmp 1118 ifle 21 (+3) 1221 return

可以看到,调用Long.valueOf自动封箱,调用Long.longValue自动拆箱。

自动装箱拆箱时机

1.赋值还有比较运算时,类型不一致,会自动装箱拆箱

1public static void main(String[] args) { 2 Long v = 6L; 3 long a = v; 4}

通过javap -c命令查看:

1 0 ldc2_w #68 <6> 2 3 invokestatic #66 <java/lang/Long.valueOf> 3 6 astore_1 4 7 aload_1 5 8 invokevirtual #70 <java/lang/Long.longValue> 611 lstore_2 712 return

可以看到,调用Long.valueOf自动封箱,调用Long.longValue自动拆箱。

2.方法调用时,类型不一致,会自动装箱拆箱

1public static void main(String[] args) { 2 Long v = 6L; 3 test1(v); 4 long a = v; 5 test2(a); 6} 7private static void test1(long v) { 8 9} 10private static void test2(Long v) { 11 12}

通过javap -c命令查看main方法:

1 0 ldc2_w #68 <6> 2 3 invokestatic #66 <java/lang/Long.valueOf> 3 6 astore_1 4 7 aload_1 5 8 invokevirtual #70 <java/lang/Long.longValue> 611 invokestatic #71 <com/hopegaming/order/revo/controller/backend/CustomerAnalysisController.test1> 714 aload_1 815 invokevirtual #70 <java/lang/Long.longValue> 918 lstore_2 1019 lload_2 1120 invokestatic #66 <java/lang/Long.valueOf> 1223 invokestatic #72 <com/hopegaming/order/revo/controller/backend/CustomerAnalysisController.test2> 1326 return 14

调用方法前,发生了自动拆箱与自动装箱。

3. 对于同时有封装类型和原始类型两种参数的重载,不会发生自动封箱拆箱

1public static void main(String[] args) { 2 Long v = 6L; 3 test1(v); 4 long a = v; 5 test1(a); 6} 7private static void test1(long v) { 8 9} 10private static void test1(Long v) { 11 12}

通过javap -c命令查看main方法:

1 0 ldc2_w #68 <6> 2 3 invokestatic #66 <java/lang/Long.valueOf> 3 6 astore_1 4 7 aload_1 5 8 invokestatic #70 <com/hopegaming/order/revo/controller/backend/CustomerAnalysisController.test1> 611 aload_1 712 invokevirtual #71 <java/lang/Long.longValue> 815 lstore_2 916 lload_2 1017 invokestatic #72 <com/hopegaming/order/revo/controller/backend/CustomerAnalysisController.test1> 1120 return 12

这次调用方法前,并没有发生自动拆箱与自动装箱。

自动封箱拆箱性能问题

由于自动封箱拆箱需要额外的操作,运算必须转化为原始类型,所以在**运算过程中,使用原始类型。存储数据的时候,用封装类型,**因为原始类型有默认值,我们有时候想用null代表这个数据不存在。

例如下面的代码,这个封箱就是没有必要的,会浪费性能:

1Long l = 0L; 2for(int i = 0; i < 50000; i++) { 3 l += 1L; 4}

三目运算符与性能问题

对于三目运算符,比如冒号表达式,如果有原始类型,则会发生自动拆箱。

1public static void main(String[] args) { 2 Long v = null; 3 Long a = new Random().nextBoolean() ? v : 0L; 4}

通过javap -c命令查看main方法:

1 0 aconst_null 2 1 astore_1 3 2 new #68 <java/util/Random> 4 5 dup 5 6 invokespecial #69 <java/util/Random.<init>> 6 9 invokevirtual #70 <java/util/Random.nextBoolean> 712 ifeq 22 (+10) 815 aload_1 916 invokevirtual #71 <java/lang/Long.longValue> 1019 goto 23 (+4) 1122 lconst_0 1223 invokestatic #66 <java/lang/Long.valueOf> 1326 astore_2 1427 return

可以看出,冒号表达式其实变成了:

Long a = Long.valueOf(new Random().nextBoolean() ? v.longValue() : 0L);

这样的话,如果 Random 随机的是 true,则会抛出NullPointerException

三目运算符判断null返回默认值效率问题

有时候,我们需要null转换成默认值,一般像common-langObjectUtils里面的defaultIfNull这么写:

1Long a = 6L; 2a = a != null ? a : 0L;

这样,虽然没错,但是会多出一步自动拆箱,再封箱,查看字节码:

1 0 ldc2_w #68 <6> 2 3 invokestatic #66 <java/lang/Long.valueOf> 3 6 astore_1 4 7 aload_1 5 8 ifnull 18 (+10) 611 aload_1 712 invokevirtual #70 <java/lang/Long.longValue> 815 goto 19 (+4) 918 lconst_0 1019 invokestatic #66 <java/lang/Long.valueOf> 1122 astore_1 1223 return 13

相当于:

a = Long.valueOf(a != null ? a.longValue() : 0L);

这样的话,无论 a 是不是 null,都会多出来这些拆箱封箱,效率不好。

对于这种场景,考虑到效率,还是老老实实,写 if-else 不要用三目运算符了。

点赞
收藏

评论区

加载中...

相关推荐

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

java架构师之路:推荐的15本书

一对于没有Java编程经验的程序员要入门,随便读什么入门书籍都一样,这个阶段需要你快速的掌握Java基础语法和基本用法,宗旨就是“囫囵吞枣不求甚解”,先对Java熟悉起来再说。用很短的时间快速过一遍Java语法,连懵带猜多写写代码,要“知其然”。在有了一定的Java编程经验之后,你需要“知其所以然”了。这个时候《Java编程

一文了解 Java 中的构造器

一文了解Java中的构造器C引入了构造器(constructor,也叫构造函数)的概念,它是在创建对象时被自动调用的特殊方法。Java也采用了构造器,并且还提供了一个垃圾收集器(garbagecollector),当不再使用内存资源的时候,垃圾收集器会自动将其释放。构造器定义在Java中,可以通过编写构造器来确保每个对象的初始化。但是这

Integer比较相等

Integertype1;??? 我们知道Integer是int的包装类,在jdk1.5以上,可以实现自动装箱拆箱,就是jdk里面会自动帮我们转换,不需要我们手动去强转,所以我们经常在这两种类型中随意写,平时也没什么注意但Integer他是对象,我们知道比较的是堆中的地址,但有个奇怪的事是,如果Integera123,

JDK核心JAVA源码解析(1)

想写这个系列很久了,对自己也是个总结与提高。原来在学JAVA时,那些JAVA入门书籍会告诉你一些规律还有法则,但是用的时候我们一般很难想起来,因为我们用的少并且不知道为什么。知其所以然方能印象深刻并学以致用。首先我们从所有类的父类Object开始:1\.Object类(1)hashCode方法和equals方法

Java之基本数据类型

本文章分为四个部分:1、基本数据类型的介绍2、类型转换3、装箱和拆箱4、有道练习5、增:编码的那点事儿_\基本数据类型的介绍_Java有8种基本数据类型,其中有