今天开始慢慢拾起以前记住的或者记住但模糊的java的基础的东西。以便时刻浏览谨记。
一、java基本数据类型
byte 1个字节
short 2个字节
int 4个字节
long 8个字节
float 4个字节
double 8个字节
char 2个字节
boolean 1个bit
二、java内存分析
栈(stack):存放基本数据类型的变量数据和对象的引用。
堆(heap):存放new出来的对象。
常量池(constant pool):用来存放显式的String常量和基本数据类型常量,另外存储不经常改变的东西(public static final)
三、‘==’和‘equals’的异同
同:都是用来进行比较的
异:泛泛地说‘==’是用来比较两个变量值是否相等的,也就是用于比较变量所对应的内存中所存储的数值是否相同。要比较两个基本类型的数据或者两个引用变量是否相等,使用‘==’。引用变量在栈中,存储的是堆中对象的首地址。
‘equals’是用来比较两个独立对象的内容是否相同的,是两个独立对象,即存储首地址不一样。
四、一些测试语句
public static void main(String[] args) {
// 以下两条语句创建了1个对象。"凤山"存储在字符串常量池中
String str1 = "凤山";
String str2 = "凤山";
System.out.println(str1==str2);//true
//以下两条语句创建了3个对象。"天峨",存储在字符串常量池中,两个new String()对象存储在堆内存中
String str3 = new String("天峨");
String str4 = new String("天峨");
System.out.println(str3==str4);//false
//以下两条语句创建了1个对象。9是存储在栈内存中
int i = 9;
int j = 9;
System.out.println(i==j);//true
//由于没有了装箱,以下两条语句创建了2个对象。两个1对象存储在堆内存中
Integer l1 = new Integer(1);
Integer k1 = new Integer(1);
System.out.println(l1==k1);//false
//以下两条语句创建了1个对象。1对象存储在栈内存中。自动装箱时对于值从127之间的值,使用一个实例。
Integer l = 20;//装箱
Integer k = 20;//装箱
System.out.println(l==k);//true
//以下两条语句创建了2个对象。i1,i2变量存储在栈内存中,两个256对象存储在堆内存中
Integer i1 = 256;
Integer i2 = 256;
System.out.println(i1==i2);//false
}
下面是几个常见例子的比较分析和理解:摘自点击打开链接
final StringBuffer a = new StringBuffer("111");
final StringBuffer b = new StringBuffer("222");
a=b;//此句编译不通过
final StringBuffer a = new StringBuffer("111");
a.append("222");//编译通过
分析:final只对引用的"值"(即内存地址)有效,它迫使引用只能指向初始指向的那个对象,改变它的指向会导致编译期错误。至于它所指 向的对象 的变化,final是不负责的。
String a = "ab";
String bb = "b"; String b = "a" + bb;
System.out.println((a == b));
//result = false
分析:JVM对于字符串引用,由于在字符串的"+"连接中,有字符串引用存在,而引用的值在程序编译期是无法确定的,即"a" + bb无法被编译器优化,只有在程序运行期来动态分配并将连接后的新地址赋给b。所以上面程序的结果也就为false。
String a = "ab";
final String bb = "b";
String b = "a" + bb;
System.out.println((a == b));
//result = true
分析:和[3]中唯一不同的是bb字符串加了final修饰,对于final修饰的变量,它在编译时被解析为常量值的一个本地拷贝存储到自己的常量池中或嵌入到它的字节码流中。所以此时的"a" + bb和"a" + "b"效果是一样的。故上面程序的结果为true。
String a = "ab"; final String bb = getBB();
String b = "a" + bb;
System.out.println((a == b));
//result = false private static String getBB() { return "b"; }
分析:JVM对于字符串引用bb,它的值在编译期无法确定,只有在程序运行期调用方法后,将方法的返回值和"a"来动态连接并分配地址为b,故上面程序的结果为false。通过上面4个例子可以得出得知:
String s = "a" + "b" + "c";
就等价于String s = "abc";
String a = "a"; String b = "b";
String c = "c";
String s = a + b + c;
这个就不一样了,最终结果等于:
StringBuffer temp = new StringBuffer();
temp.append(a).append(b).append(c);
String s = temp.toString();
由上面的分析结果,可就不难推断出String 采用连接运算符(+)效率低下原因分析,形如这样的代码:
public class Test { public static void main(String args[]) {
String s = null;
for(int i = 0; i < 100; i++) { s += "a"; } } }
每做一次 + 就产生个StringBuilder对象,然后append后就扔掉。下次循环再到达时重新产生个StringBuilder对象,然后 append 字符串,如此循环直至结束。 如果我们直接采用 StringBuilder 对象进行 append 的话,我们可以节省 N - 1 次创建和销毁对象的时间。所以对于在循环中要进行字符串连接的应用,一般都是用StringBuffer或StringBulider对象来进行append操作。String对象的intern方法理解和分析:
public class Test4 {
private static String a = "ab";
public static void main(String[] args){
String s1 = "a";
String s2 = "b";
String s = s1 + s2;
System.out.println(s == a);//false
System.out.println(s.intern() == a);//true
}
}
这里用到Java里面是一个常量池的问题。对于s1+s2操作,其实是在堆里面重新创建了一个新的对象,s保存的是这个新对象在堆空间的的内容,所以s与a的值是不相等的。而当调用s.intern()方法,却可以返回s在JVM常量池中的地址值,因为a的值存储在常量池中,故s.intern和a的值相等。