Back

Java面试之基础题(Integer、String、内部类与final)

Integer

Integer a = 1000,b=1000;
Integer c = 100,d=100;    
@Test
public void testIntegerDD(){
    System.out.println(a==b);//false  
    System.out.println(c==d);//true
}

运行代码,我们会得到 false true。这道题笔试用经常出现,原理也很简单 ,可看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);
}

Integer 的缓存范围虽然默认是 -128 到 127,但是在特别的应用场景,比如我们明确知道应用会频繁使用更大的数值,这时候应该怎么办呢?
缓存上限值实际是可以根据需要调整的,JVM 提供了参数设置:
-XX:AutoBoxCacheMax=N

接下来,简单的扩展几个Integer的关系运算符==的比较。

@Test
public void testIntegerDD(){
	
    Integer a = 1000,b=1000;
    Integer c = 100,d=100;   
    Integer e = new Integer(100);
    Integer f = new Integer(100);
    int g = 1000;
    Integer h = new Integer(1000);	


    //1、进行自动装箱操作;2、Integer中把-128-127 缓存了下来
    System.out.println(a==b);//false  
    System.out.println(c==d);//true
	
    //这里并不是用的缓存,而是new创建的对象存放在堆内存中,俩个变量指向不同引用,所以结果是false
    System.out.println(e==f);//false
    //当int和Integer进行==比较的时候,Java会把Integer进行自动拆箱为int类型的值
    System.out.println(g==h);//true
	
    //进行自动装箱操作
    System.out.println(c==e);//false
    System.out.println(a==h);//false
}

String

==如果作用于基本数据类型的变量,则直接比较其存储的 “值”是否相等;如果作用于引用类型的变量,则比较的是所指向的对象的地址。

@Test
public void testStringDD() {
    String s1 = "hello";
    String s2 = "hello";
    String s3 = new String("hello");
	
    System.out.println(s1==s2); // true
    System.out.println(s1==s3); // false
}

文章解释:
一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配,而堆内存中则存放new出来的对象和数组。然而除此之外还有一块区域叫做常量池。
像我们通常想String s1 = “hello”; 这样申明的字符串对象,其值就是存储在常量池中。
当我们创建String s1 =“hello"这样一个对象之后,“hello"就存储到了常量池(也可叫做字符串池)中,当我们创建引用String s2 = “hello” 的时候,Java底层会优先在常量池中查找是否存在"hello”,如果存在则让s2指向这个值,不会重新创建,如果常量池中没有则创建并添加的池中。这就是为什么答案是true 和false的原因。

接下来,还是简单的扩展几个String的关系运算符==的比较。

@Test
public void testStringDD() {
    String s1 = "hello";
    String s2 = "hello";
    String s3 = new String("hello");
	
    //都存储在常量池中,指向同一常量
    System.out.println(s1==s2); // true
    //存储在常量池中,存储在堆内存中
    System.out.println(s1==s3); // false
	
    String s4 = "helloo";
    /**
     * 会重新在常量池创建常量(String不可变属性)
     */
    String s5 = s1+"o";
    //字符串拼接有变量参与,底层调用StringBuffer处理,相当于在堆内存中开辟了新空间
    System.out.println(s4 == s5); // false
    //常量相加,先在常量池找,找到即用
    System.out.println(s4 == "hello"+"o");//true

    //hello
    String s6 = s4.substring(0, s4.length() - 1);
    //substring返回的也是new出来的String对象
    System.out.println(s1 == s6); // false
    //地址值不一样
    System.out.println(s3 == s6); // false
}

扩展:equals方法。equals方法不能作用于基本数据类型的变量,如果没有对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地址;诸如String、Date等类对equals方法进行了重写的话,比较的是所指向的对象的内容。

内部类与final

public void mRun(final String name){
    new Runnable() {
        public void run() {
            try {
              Thread.sleep(1000);
           } catch (InterruptedException e) {
             // TODO Auto-generated catch block
            e.printStackTrace();
           }  
           System.out.println(name);
        }
    }.start();
}

文章解释:
这种代码相信大家写过很多,当内部类访问局部变量的时候,需要在局部变量前加final修饰符,不然编译器就会报错。通常我们也是这么干的。

为什么要加final修饰符?
首先内部类的生命周期是成员级别的,而局部变量的生命周期实在方法体之类。也就是说会出现这样一种情况,当mRun方法执行,new 的线程运行,新线程里面会睡一秒。
主线程会继续执行,mRun执行完毕,name属性生命周期结束。1秒之后,Syetem.out.printh(name)执行。然而此时name已经寿终正寝,不在内存中了。

Java就是为了杜绝这种错误,严格要求内部类中方位局部变量,必须使用final关键字修饰。
局部变量被final修饰之后,此时会在内存中保有一份局部变得的复制品,当内部类访问的时候其实访问的是这个复制品。这就好像是把局部变量的生命周期变长了。

Licensed under CC BY-NC-SA 4.0
comments powered by Disqus