类型转换
先看下面一段代码:
byte a=5;
byte b=6;
byte c=7;
a=b+c;
System.out.println(b);
编译时报错:“java: 不兼容的类型: 从int转换到byte可能会有损失”。
Java中,byte类型占1个字节,int类型占4个字节。
整数常量的默认数据类型是int,当其赋值给byte变量时,系统会先检查其大小是否在byte的取值范围(-128-127)内,如果在范围内,则会自动进行类型转换,把常量的前3个字节删除;如果不在范围,编译时就会报错。
再看上面的代码,a=b+c中b和c都是变量,因此编译时系统不能确定b和c的和是否在byte内,可能会丢失精度,所以报错。而如果将变量初始化为int类型则不会有此问题。
引用变量
先看下面一段代码:
public class Num{
int num =1;
public static void main(String[] args) {
Num a = new Num();
System.out.println("a="+a.num);
Num b = a;
b.num=2;
System.out.println("a="+a.num);
}
}
输出结果是:
a=1
a=2
要注意的是,新建一个Num对象的时候,a是一个类类型的引用变量,储存了该类对象在堆内存中的地址,而Num b = a表示新建一个类类型的引用变量b,并将a储存的地址复制给b,所以此时a和b都指向同一个对象,a和b对对象的操作是等价的。
不可变字符串与限定字符串
我们知道String并不是基本数据类型,而是一个类,JDK1.5以后的autoboxing特性,使得新建一个String对象变得更方便了:
//JDK1.5以前
String a = new String();
//JDK1.5以后
String a = "...";
注意,第一种是用new关键字来生成对象,后一种属于直接声明,得到字符串直接量。
对于字符串直接量,JVM会在内存中开辟一个“String Pool”,用来存放字符串直接量。
String类的对象是不可变的,称为不可变字符串。
看这一段代码:
String a = "abc";
a = "bcd";
System.out.println(a);
输出:
bcd
这说明String对象的内容可以被改动?但实际情况是,a储存的引用被改变了,从“abc”指向了“bcd”所在的地址。
通过获取hashcode可以明显的看出,a储存的地址发生了变化:
String a = "abc";
System.out.println(r.hashCode());
a = "bcd";
System.out.println(a);
System.out.println(r.hashCode());
输出:
96354
bcd
97347
a的指向被改变后,原来的对象“abc”并不会被清除,而是会一直存在于String Pool中,通过intern()方法,可以重新定位到“abc”对象,具体内容后面会讲到。
再看一段代码:
String a = new String("abc");
String b = new String("abc");
String c = "abc";
String d = "abc";
String e = a.intern();
System.out.println(a==b);
System.out.println(c==d);
System.out.println(c==a);
System.out.println(c==b);
System.out.println(c==e);
输出结果为:
false
true
false
false
true
这段代码体现出了String Pool的另一个特点,JVM为了节约资源,会对具有相同字符串的字符串直接量使用同一个实例,这样的实例称为限定的(interned)。
代码中c和d属于字符串直接量,而且其初始化的字符串相同,所以JVM会自动令c和d指向同一个实例对象,其Hashcode是相等的。
a和b不属于字符串直接量,它们在初始化时不会搜索String Pool,而是在堆内存分别创建对象,所以Hashcode不相同。
再看代码中的intern()方法,通过这个方法,可以将对象转化为限定的,系统会检查当前对象在对象池是否存在内容相同的对象,如果有则返回对象,如果没有就将当前对象变成String Pool中的一员。
回到之前的例子:
String a = "abc";
System.out.println(a.hashCode());
a = "bcd";
System.out.println(a);
System.out.println(a.hashCode());
//添加代码
String b = "abc";
String c = new String("abc");
String d = c.intern();
System.out.println(b==d);
System.out.println(b.hashCode());
输出:
96354
bcd
97347
true
96354
从结果可以看出,不论是新建字符串直接量,还是通过intern()方法,都可以重新定位到String Pool中的“ABC”对象。
bug…
重新测试了上一个主题的代码,发现了一个bug,希望高手能帮我来解答。。。
是这样的,看这一段代码:
String a = new String("abc");
String c = "abc";
System.out.println(a==c);
System.out.println(a.hashCode());
System.out.println(c.hashCode());
System.out.println(a.hashCode()==c.hashCode());
输出:
false
96354
96354
true
也就是说,其实a和c具有相同的哈希值,但是用“==”方法却返回false,不知道哪里出了问题…
补充:查了一些资料,原来Hashcode并不反映内存地址,两个对象的HashCode相同,并不一定表示两个对象就相同,java似乎也没有能够返内存地址的方法,但是通过“==”方法还是可以用来比较两个对象的内存地址是否相同的。
Integer autoboxing
和String类一样,Integer也可以实现autoboxing,看下面的例子:
Integer a = new Integer(127);
Integer b = new Integer(127);
System.out.println(a==b);
System.out.println(a.equals(b));
Integer c = 127;
Integer d = 127;
System.out.println(c==d);
System.out.println(c.equals(d));
Integer e = 128;
Integer f = 128;
System.out.println(e==f);
System.out.println(e.equals(f));
输出:
false
true
true
true
false
true
从输出可以看出,只有当装箱的对象是1个字节的时候,对象才是限定的,一旦对象超过1个字节,就会分别开辟新的空间。