栈内存
堆内存
基础类型,对象引用(堆内存地址)
由new创建的对象和数组,
存取速度快
相对于栈内存较慢
数据大小声明周期必须确定
分配的内存由java虚拟机自动垃圾回收器管理。动态分配内存大小
共享特性
栈中如果有字符串,则直接引用
如果没有,开辟新的空间存入值
每new一次在堆内存中生成一个新的对象。
创建之后值可以改变
String类声明后则不可改变
一、栈内存
基础类型int, short, long, byte, float, double, boolean,
char和对象引用
栈的共享特性
String str1 = "abc";
String str2 = "abc";
System.out.println(str1==str2); //true
1、编译器先处理String str1 =
"abc";它会在栈中创建一个变量为str1的引用,然后查找栈中是否有abc这个值,如果没找到,就将abc存放进来,然后将str1指向abc。
2、
接着处理String str2 =
"abc";在创建完b的引用变量后,因为在栈中已经有abc这个值,便将str2直接指向abc。这样,就出现了str1与str2同时均指向abc的情况。
二、堆内存
new、newarray、anewarray和multianewarray等指令建立
要注意: 我们在使用诸如String
str =
"abc";的格式定义类时,总是想当然地认为,创建了String类的对象str。担心陷阱!对象可能并没有被创建!而可能只是指向一个先前已经创建的
对象。只有通过new()方法才能保证每次都创建一个新的对象。
由于String类的immutable性质,当String变量需要经常变换其值时,应该考虑使用StringBuffer类,以提高程序效率。
三、== 内存地址比对
String str1 = "abc";
String str2 = "abc";
System.out.println(str1==str2);
//true str1和str2同时指向 栈内存
中同一个内存空间
String str3 = "abc";
String str4 = new String("abc") ;
System.out.println(str3 ==
str4); //flase str3值在栈内存中,str4值在堆内存中
String hello = "hello" ;
String hel = "hel" ;
String lo = "lo" ;
System.out.println(hello == "hel" +
"lo") ; //true
//两个常量相加,先检测栈内存中是否有hello如有有,指向已有的栈中的hello空间
System.out.println(hello == "hel" +
lo) ; //flase
System.out.println(hello == hel + lo)
; //flase
//lo是在常量池中,不检查栈内存,在堆中产生一个新的hello
四、equals 值进行比对
public boolean
equals(Object anObject)
将此字符串与指定的对象比较。当且仅当该参数不为
null,并且是与此对象表示相同字符序列的 String 对象时,结果才为
true。
String str5 =
"abc";
String str6 = new String("abc") ;
System.out.println(str5.equals(str6)); //true str5的值str6的值比对
五、intern
栈中值的内存地址
Public String intern()
当调用 intern
方法时
1、如果池已经包含一个等于此 String
对象的字符串(用equals(Object) 方法确定),则返回池中的字符串。
2、将此 String 对象添加到池中,并返回此
String 对象的引用。
String s7 = new String("abc")
;
String s8 = "abc" ;
System.out.println(s7 == s7.intern())
;//flase;
System.out.println(s8 == s7.intern()
);//true
1.检查栈内存中有没有abc对象如果有
2.将s7指向pool中abc
六、关于String
1.
在java中如果“==”被使用于两个对象类型的变量,他是用于比较两个变量是否引用自同一对象。
2.因此,不可用“==”比较两个字符串的字符内容是否相同,如果要比较两个字符串对象的字符值是否相同,要使用equals()方法。
3.在java中,使用“+”串联字符串会产生一个新的字符串对象。
4.intern()方法会先检查String池中是否存在字符部分相同的字符串对象,如果有就返回。如:
String str1 = "fly";
String str2 = "weight";
String str3 = "flyweight";
String str4 = null;
str4 = str1 + str2;
System.out.println(str3 == str4);//新对象,false
str4 = (str1 + str2).intern();
System.out.println(str3 = str4);//true
5.如果程序中想要附加字符串,并不建议使用“+”来进行字符串的串联,因为“+”会产生一个新的String实例。在面向对象程序设计中,最好是能重复运用已生成的对象,对象的生成需要内存空间和时间,不断地产生String实例是一个没有效率的行为。StringBuilder的出现就解决了这个问题,这个类产生的对象默认为16个字符的长度,若附加的字符过长,其会自动加长。两者的效率比较如下:
package ying.string;
public
class AppendStringTest {
public static void main(String[] args) {
String text = "";
long beginTime = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
text += i;
}
long endTime = System.currentTimeMillis();
System.out.println("+号执行时间: " + (endTime - beginTime));
StringBuilder builder = new StringBuilder();
beginTime = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
builder.append(String.valueOf(i));
}
endTime = System.currentTimeMillis();
System.out.println("StringBuilder执行时间: " + (endTime -
beginTime));
}
}
结果:+号执行时间:
709
StringBuilder执行时间: 14
因此,当你为一个类编写toString()方法时,如果字符串操作比较简单,那就可以信赖编译器,它会为你合理地构造最终的字符串结果。如果你要在toSring()方法中使用循环,最好自己创建一个StringBuilder对象,用它来构造最终的结果。如:
package ying.string;
import
java.util.Random;
public
class UsingStringBuilder {
//
public static Random rand=new
// Random(System.currentTimeMillis());//以当前时间为参数
public static Random rand = new Random(47);// new
Random也是有算法的,47相当于给了一个
//
初始值,因此以后每次rand.nextInt(100)都不变;如果不设初始值即new Random(),每次执行值都会变
public String toString() {
StringBuilder result = new StringBuilder("[");
for (int i = 0; i < 25; i++) {
result.append(rand.nextInt(100));
result.append(", ");
}
result.delete(result.length() - 2, result.length());
result.append("]");
return result.toString();
}
public
static void main(String[] args) {
UsingStringBuilder usb = new UsingStringBuilder();
System.out.println(usb);
}
}