程序员最近都爱上了这个网站  程序员们快来瞅瞅吧!  it98k网:it98k.com

本站消息

站长简介/公众号

  出租广告位,需要合作请联系站长


+关注
已关注

分类  

暂无分类

标签  

暂无标签

日期归档  

2023-06(4)

可能一眼看不懂的几行Kotlin代码

发布于2021-05-29 19:25     阅读(1144)     评论(0)     点赞(16)     收藏(0)


看两段代码

第一段代码:

val a: Int = 100
val boxedA: Int? = a
val anotherBoxedA: Int? = a

val b: Int = 10000
val boxedB: Int? = b
val anotherBoxedB: Int? = b

println(boxedA === anotherBoxedA) // true
println(boxedB === anotherBoxedB) // false

第二段代码:

val a: Int = 10000
println(a == a) // Prints 'true'
val boxedA: Int? = a
val anotherBoxedA: Int? = a
println(boxedA == anotherBoxedA) // Prints 'true'

代码来自【Kotlin官方文档】:https://kotlinlang.org/docs/basic-types.html#numbers-representation-on-the-jvm

疑问

第二段代码好理解,boxedAanotherBoxedA 进行值比较调用的是 equal() 方法,比较结果必然为 true 。但是第一段代码,两个表达式都是比较对象引用,为什么一个是 true 另一个是 false ?我们尝试将第一段代码换写为Java语言,得到的比较结果和Kotlin一样。

int a = 100;
Integer aBox = a;
Integer aAnotherBox = a;

int b = 10000;
Integer bBox = b;
Integer bAnotherBox = b;

System.out.println(aBox == aAnotherBox); // true
System.out.println(bBox == bAnotherBox); // false

同时我们在末尾再加上一段测试代码,采用直接new的方式构建对象,得到的比较结果为false

int c= 100;
Integer cBox = new Integer(c);
Integer cAnotherBox = new Integer(c);
System.out.println(cBox == cAnotherBox); // false

原理

基础知识

  • Kotlin中三等号(===) 比较的是两个引用在内存中指向的是不是同一对象(即同一内存空间),双等号(==) 比较的是值;
  • Kotlin中的非空Number类型对应到JVM平台是基本类型:int,double等等;
  • Kotlin中的可空Number类型对应到JVM平台是封装类型:Integer,Double等等;
  • Java中双等号(==)比较的是两个引用在内存中指向的是不是同一对象(即同一内存空间);
  • Kotlin中三等号等价于Java中的双等号;

字节码分析

val a: Int = 100
L0
 LINENUMBER 12 L0
 BIPUSH 100
 ISTORE 1
val boxedA: Int? = a
val anotherBoxedA: Int? = a
L1
 LINENUMBER 13 L1
 ILOAD 1
 INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
 ASTORE 2
L2
 LINENUMBER 14 L2
 ILOAD 1
 INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
 ASTORE 3

从字节码不难看出,非空 Int 型数据,直接使用BIPUSH压栈(取值 -128~127 时,JVM 采用 BIPUSH 指令将常量压栈)。而针对非空Int型变量赋值给可空 Int 型声明,是通过 Integer 类的 public static Integer valueOf(int i) 方法实现。

查看 Integer 类中 public static Integer valueOf(int i) 方法源码:

public static Integer valueOf(int i) {    if (i >= IntegerCache.low && i <= IntegerCache.high)        return IntegerCache.cache[i + (-IntegerCache.low)];    return new Integer(i);}

看到 IntegerCache 的一瞬间感觉一切都清晰了。JDK 从 1.5 版本开始,把 -128~127(high的默认值) 的数字缓存起来了,用于提升性能和节省内存,通过 -XX:AutoBoxCacheMax=<size> 来控制high的取值。所以,当数字在缓存范围内时,通过valueOf()方式拿到的对象引用全部来自于缓存列表,所以对于相同的值,对象引用相同;若是超过缓存范围,则是重新生成的对象,自然也就不相等了。此时回头看开头的两段 Kotlin 代码和我们改写的 Java 代码,就很清晰了。

/** * Cache to support the object identity semantics of autoboxing for values between * -128 and 127 (inclusive) as required by JLS. * * The cache is initialized on first usage.  The size of the cache * may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option. * During VM initialization, java.lang.Integer.IntegerCache.high property * may be set and saved in the private system properties in the * sun.misc.VM class. */private static class IntegerCache {    static final int low = -128;    static final int high;    static final Integer cache[];    static {        // high value may be configured by property        int h = 127;        String integerCacheHighPropValue =            sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");        if (integerCacheHighPropValue != null) {            try {                int i = parseInt(integerCacheHighPropValue);                i = Math.max(i, 127);                // Maximum array size is Integer.MAX_VALUE                h = Math.min(i, Integer.MAX_VALUE - (-low) -1);            } catch( NumberFormatException nfe) {                // If the property cannot be parsed into an int, ignore it.            }        }        high = h;        cache = new Integer[(high - low) + 1];        int j = low;        for(int k = 0; k < cache.length; k++)            cache[k] = new Integer(j++);        // range [-128, 127] must be interned (JLS7 5.1.7)        assert IntegerCache.high >= 127;    }    private IntegerCache() {}}

IDEA 配置调整high值

通过配置-Djava.lang.Integer.IntegerCache.high=<size>或者-XX:AutoBoxCacheMax=<size>调整阈值。

配置方式一

配置方式二

延伸

除了 Integer类,CharacterByteShortLong 等类型也有类似的缓存,只是除了 Integer 外,其他类的缓存范围是不可变的。

private static class ByteCache {    private ByteCache(){}    static final Byte cache[] = new Byte[-(-128) + 127 + 1];    static {        for(int i = 0; i < cache.length; i++)            cache[i] = new Byte((byte)(i - 128));    }}
private static class CharacterCache {    private CharacterCache(){}    static final Character cache[] = new Character[127 + 1];    static {        for (int i = 0; i < cache.length; i++)            cache[i] = new Character((char)i);    }}
private static class ShortCache {    private ShortCache(){}    static final Short cache[] = new Short[-(-128) + 127 + 1];    static {        for(int i = 0; i < cache.length; i++)            cache[i] = new Short((short)(i - 128));    }}
private static class LongCache {    private LongCache(){}    static final Long cache[] = new Long[-(-128) + 127 + 1];    static {        for(int i = 0; i < cache.length; i++)            cache[i] = new Long(i - 128);    }}

原文链接:https://blog.csdn.net/poorkick/article/details/117265706



所属网站分类: 技术文章 > 博客

作者:怎么没有鱼儿上钩呢

链接:http://www.javaheidong.com/blog/article/207056/bf7360b1f4e08b6bc707/

来源:java黑洞网

任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任

16 0
收藏该文
已收藏

评论内容:(最多支持255个字符)