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修饰之后,此时会在内存中保有一份局部变得的复制品,当内部类访问的时候其实访问的是这个复制品。这就好像是把局部变量的生命周期变长了。