一、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
改进: 通过字符串常量调用equals()方法,该常量永远不会是null,所以可以避免空指针异常
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来实现上述代码功能,而不产生大量垃圾。