一、String类对象的实例化方式
1.直接使用"" 定义字符串赋值给String类的对象
2.使用String类中的构造方法:public String(String str);
public class StringDemo{
public static void main(String args[]){
String str="Hello world!"; //直接赋值实例化String类的对象
String str=new String("Hello world!"); //通过String类的构造方法实例化String类的对象
System.out.println(str);
}
}
二、String类对象的比较问题
1、==操作符——比较两个对象的内存地址,并未比较两个字符串的数值
public class StringDemo{
public static void main(String args[]){
String str1="Hello";
String str2=new String("Hello");
String str3=str2;
System.out.println(str1==str2); //false
System.out.println(str1==str3); //false
System.out.println(str2==str3); //true
}
}
2、equals方法——比较两个字符串的内容(public boolean equals(String other))
public class StringDemo{
public static void main(String args[]){
String str1="Hello";
String str2=new String("Hello");
String str3=str2;
System.out.println(str1.equals(str2)); //true
System.out.println(str1.equals(str3)); //true
System.out.println(str2.equals(str3)); //true
}
}
严格来说,equals比较的是堆内存中的内容
面试题:请解释String的两种比较方式?
- "=="比较的是两个字符串的内存地址的数值,属于数值比较
- equals()比较的是两个字符串的内容
注意:一个字符串常量是String的匿名对象
java中String并不是一个基本数据类型,所以Java会自动把一个字符串常量当成一个String类的匿名对象来处理
public class StringDemo{
public static void main(String args[]){
String str="Hello";
System.out.println("Hello".equals(str); //true
}
}
小技巧:开发中使用以下方式可以避免NullPointException
例如:
public class StringDemo{
public static void main(String args[]){
String str="null";
System.out.println(str.equals("Hello");
}
}
会产生空指针异常NullPointException
改进:
public class StringDemo{
public static void main(String args[]){
String str="null";
System.out.println("Hello".equals(str));
}
}
3、两种实例化方法的比较
1)java的共享设计模式
共享设计是在JVM中为用户提供一个对象池,当用户创建了一个 新的且池中没有的对象时,处理将这个对象分配内存之外,还会在对象池中进行保留,以后如果有其他对象社么了与之一样的内容时候,不会重复申明,而是从对象池中取出内容继续使用,String类正是使用该机制。
当用户采用直接赋值实例化String对象的时候,如果是第一次定义,则会自动将对象内容保留在字符串对象池中,以后如果其他的字符串对象依然采用直接赋值的话,可以直接通过对象池取出已经保存的内容继续使用,而不会重新开辟新的空间,如以下程序:
public class StringDemo{
public static void main(String args[]){
String str1="Hello";
String str2="Hello";
String str3="Hello";
System.out.println(str1==str1); //true
System.out.println(str1==str3); //true
System.out.println(str2==str3); //true
}
}
其内存关系为
2)通过String类的构造方法
但通过该法创建的String对象的内容不能自动入池,
但用户可以使用String类中的intern()方法手工入池。例如:
public class StringDemo{
public static void main(String args[]){
String str1=new String("Hello");
String str2="Hello";
String str3="Hello";
System.out.println(str1==str1); //false
System.out.println(str1==str3); //false
}
}
但是:
public class StringDemo{
public static void main(String args[]){
String str1=new String("Hello").intern();
String str2="Hello";
String str3="Hello";
System.out.println(str1==str1); //true
System.out.println(str1==str3); //true
}
}
面试题:String对象的两种实例化方式的区别?
- 直接赋值:只开辟一个堆内存空间,而且采用共享设计模式,可以自动入池,以备下次对象继续使用
- 构造方法:开辟两个内存空间,其中一块将成为垃圾,且不会自动入池,但可以采用intern()方法继续手工入池
4、字符串的内容一旦声明则不可改变
public class StringDemo{
public static void main(String args[]){
String str="Hello";
str += "World";
str = str +"!!!"
System.out.println(str); // 输出为Hello World!!!
}
}
其内存空间变化如下
可以看出实际上对于String中的字符串内容并没有发生任何的变化,而最后内容的改变实际上改变的是String对象的内存地址的指向,所以字符串内容依然没有任何变化,但这样会产生大量的垃圾,所以在开发中对以下代码必须要进行回避:
public class StringDemo{
public static void main(String args[]){
String str="";
for(int x=0; x<1000;x++){
str += x;
}
System.out.println(str);
}
}
相当于“断开-连接”1000次,且产生大量的垃圾空间;在实际开发中,可以用StringBuffer来实现上述代码功能,而不产生大量垃圾。