【面试题】java装箱拆箱引发的思考

问题一:装箱拆箱举例

装箱拆箱的demo.java

public class Demo{
	public static void main(String[] args) {
		Integer i = 10;
		int n =i;
	}
}

看看装箱拆箱分别通过什么方法实现的?

反编译Demo.class即可知

public class Demo
{
  public static void main(String[] paramArrayOfString)
  {
    Integer localInteger = Integer.valueOf(10);
    int i = localInteger.intValue();
  }
}

装箱通过:

Integer localInteger = Integer.valueOf(10);
拆箱通过:

int i = localInteger.intValue();

如果到这就结束了,也太小瞧面试官了。新的面试题是这样的。

问题二、八种基本类型的包装类中的常量池与否

进阶的demo.java

public class Main {
    public static void main(String[] args) {
         
        Integer i1 = 100;
        Integer i2 = 100;
        Integer i3 = 200;
        Integer i4 = 200;
         
        System.out.println(i1==i2);
        System.out.println(i3==i4);
    }
}
答案是:true,false。

原因:

看看valueOf的源码:-128到128之外,则新建对象。(注释:java1.7源码)

public static Integer valueOf(int i) {
        return  i >= 128 || i < -128 ? new Integer(i) : SMALL_VALUES[i + 128];
    }
否则,用已经存在的对象cache的引用。常量数组SMALL_VALUES(也称常量池)引用源码如下

/**
     * A cache of instances used by {@link Integer#valueOf(int)} and auto-boxing
     */
    private static final Integer[] SMALL_VALUES = new Integer[256];

    static {
        for (int i = -128; i < 128; i++) {
            SMALL_VALUES[i + 128] = new Integer(i);
        }
    }

可能这么看还不直观,继续看反编译进阶demo的class的代码:看到全部都用的是valueOf方法,也就是上面的源代码。

import java.io.PrintStream;

public class Demo
{
  public static void main(String[] paramArrayOfString)
  {
    Integer localInteger1 = Integer.valueOf(100);
    Integer localInteger2 = Integer.valueOf(100);
    Integer localInteger3 = Integer.valueOf(200);
    Integer localInteger4 = Integer.valueOf(200);
    
    System.out.println(localInteger1 == localInteger2);
    System.out.println(localInteger3 == localInteger4);
  }
}

假如面试官只是为了考点是否深入其实到这儿就结束了。碰到一个较真的面试官,可能会一并问这个问题。

进阶的demo.java混淆视听代码:

public class Demo{
	public static void main(String[] args) {
         
        Double i1 = 100.0;
        Double i2 = 100.0;
        Double i3 = 200.0;
        Double i4 = 200.0;
         
        System.out.println(i1==i2);
        System.out.println(i3==i4);
    }
}

答案:false,false。

长得几乎一摸一样,只是类型由Integer变成了Double。

原因:value的valueOf方法源码如下:

public static Double valueOf(double d) {
        return new Double(d);
    }

是不是想问为什么设计者这么变态?因为常量数量有限,可以通过常量数组去处理加快速度,而类似double,float也用-127到128范围枚举的话就太多了。

总结记住以下:(记少不记多,记住第二句!)

Integer、Short、Byte、Character、Long、Boolean有常量池。

double,float没有常量池。

问题三、boolean和Boolean的区别

遇到这么变态的面试官,赶快喊爸爸。

public class Main {
    public static void main(String[] args) {
         
        Boolean i1 = false;
        Boolean i2 = false;
        Boolean i3 = true;
        Boolean i4 = true;
         
        System.out.println(i1==i2);
        System.out.println(i3==i4);
    }
}

答案,一眼看出答案,true,true,只要不是被上面3题弄晕了。

//然而这一题不是问答案,问的是Boolean和boolean的区别。什么时候用到Boolean

反编译后的class

import java.io.PrintStream;

public class Demo
{
  public static void main(String[] paramArrayOfString)
  {
    Boolean localBoolean1 = Boolean.valueOf(false);
    Boolean localBoolean2 = Boolean.valueOf(false);
    Boolean localBoolean3 = Boolean.valueOf(true);
    Boolean localBoolean4 = Boolean.valueOf(true);
    
    System.out.println(localBoolean1 == localBoolean2);
    System.out.println(localBoolean3 == localBoolean4);
  }
}
答案:

唯一只能使用Boolean上的就是从列表或者哈希表获取值时。
比如

boolean t = false;
Map map = new HashMap();
map.put("t", t);
那么获取值时只能用Boolean t1 = (Boolean) map.get(t); //前面只能用Boolean强制转换,不能使用boolean。

简而言之:

也就是需要强制类型转换的时候。


问题四、衍生的String的问题:

如果以上都问了,问到这题,面试官,请收下我的膝盖。你肯定是因为我长得太帅,而针对我!

public class Demo{
	public static void main(String[] args) {

        String s1 = new String("hello");
        String s2 = new String("hello");
        System.out.println(s1 == s2);//输出false
        System.out.println(s1.equals(s2));//输出true
        String s3 = "hello";
        String s4 = "hello";
        System.out.println(s3 == s4);//输出true
        System.out.println(s3.equals(s4));//输出true
    }
}

这题,作为一个资深面试官,我会问

答案是什么?

1. equals和==的区别?

2. String的equals方法和hashcode方法的源码是否阅读过?

如果还没晕的话,首先需要知道String是一个基本数据类型。那么这题不再是考包装类了。而是考基本数据类型和引用数据类型。了解这点,已经赢了一大半。

==比较:比较值是否相等。

equals的定义比较复杂:

单看String的equals方法源码如下:

@Override public boolean equals(Object other) {
        if (other == this) {
          return true;
        }
        if (other instanceof String) {
            String s = (String)other;
            int count = this.count;
            if (s.count != count) {
                return false;
            }
            if (hashCode() != s.hashCode()) {
                return false;
            }
            for (int i = 0; i < count; ++i) {
                if (charAt(i) != s.charAt(i)) {
                    return false;
                }
            }
            return true;
        } else {
            return false;
        }
    }
第2行,比较是否同一个对象。如果是则返回true。

第5行,先比较字符串的长度,然后比较字符串的hashcode(如果hashcode不相等,则字符串一定不相等。反过来则需要继续判断)。最后比较每个位置的字符的值。

看看String的hashCode方法:

初始是0,每次(原累计数hash)*31 加上当前位置字符的ASCII码,比如h字符的ASCII码是104。循环累加。得到hash值。

@Override public int hashCode() {
        int hash = hashCode;
        if (hash == 0) {
            if (count == 0) {
                return 0;
            }
            for (int i = 0; i < count; ++i) {
                hash = 31 * hash + charAt(i);
            }
            hashCode = hash;
        }
        return hash;
    }

结论来了,如果

两个一模一样的字符串,它的hashcode必然是一样的。而hashcode一样,却不一定是同一个字符串

为什么呢?

第一句的原因,原因是因为如果字符串相同,同一个算法,那么累积和必然是一个一摸一样的数值。比如“hello”,和“hello”。所以在比较字符串值是否相等的时候先比较hashcde是否一样,能加快比较速度。(因为比较一个数值肯定比挨个比较字符串要快)

第二句的原因,举个栗子“hello” 和 “hlleo”按照上面的算法,hashcode值是一样的,而事实不是同一个字符串。所以仍然需要继续比较下去。


先码到这,下波继续java。












  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值