String基础: String两种创建对象方式的比较
- 字符串常量
在一般的语言中常量一旦声明则不可改变,在java中的字符串常量是以匿名对象来表示的
javaz中字符串两种定义方法:
- String strA= new String("hello");(符合习惯的声明方法)
- String strA="hello";(常用的声明方法)
一个基本事实:在java中字符串常量的本质是String类型的匿名对象
- 基本解释:习惯的定义方法和前面的一样,常用的定义方法中,将用于储存匿名对象"hello"的堆空间的使用权交给strA,strA指向该匿名空间
其实这里聪明的小伙伴已经发现了两者的差异了,在习惯声明的方式中,"hello"不仅是作为匿名对象,还会为new关键字开辟的对象所赋值
所有说习惯性的声明方法开辟了2次的内存空间
小插曲---分析字符串的比较
了解完上面的概念,对应字符串的比较就没有什么压力了
例子的引出
//分析字符串中==运算符
public class Compare{
public static void main(String[]args){
String strA="yootk";
String strB=new String("yootk");
String strC=strB;//进行引用传递指向strB所指向的堆地址
System.out.println(strA==strB);//false
System.out.println(strC==strB);//true
System.out.println(strA==strC);//false
}
}
内存指向分析
从前面的分析和空间的内存分析可以看出==运算符在字符串中是直接进行地址的比较的,其实也不难理解strA strB strC是该类对象的引用,储存的是对应的堆空间的地址,直接这样比较不就是比较地址吗
得到一个结论:==运算符在java中用于对象地址的比较
进行字符串内容的比较可以用String类的内置函数 equals()
该函数定义方式:Public boolean equals(String str)
根据两字符串的数值是否相等返回false或者true
//分析字符串中==运算符
public class Compare{
public static void main(String[]args){
String strA="yootk";
String strB=new String("yootk");
String strC=strB;//进行引用传递指向strB所指向的堆地址
System.out.println("yootk".equals(strB));//true
System.out.println("yootk".equals(strB));//true
System.out.println("yootk".equals(strC));//true
}
}
用equals完美的解决了问题,同时可以发现equals()函数是由"yootk"来调用的,也可以验证字符串常量就是一个匿名对象
建议当字符串常量和字符串变量在进行equals比较时,用字符串常量进行equals函数的调用,也就是将常量写在前面
原因:当输入的字符串没有进行实例化或者说没有被赋值的时候,此时字符串=null,此时如果进行equals方法的调用,就会出现空指针异常
public class Compare{
public static void main(String[]args){
String strA=null;
//后面没有进行实例化
System.out.println(strA.equals("hello"));
}
}
解决:用字符串常量进行equals方法调用,字符串常量本质上就是一个匿名对象永远不可能为空
public class Compare{
public static void main(String[]args){
String strA=null;
System.out.println("hello".equals(strA));//false 该处输入的strA为 空直接返回false
}
}
而且还有一个好处:equals()方法内部会自动进行Null的判断,所有这个时候如果发现输入的内容为空则直接返回false
两种声明方式的比较
问题的引出
public class Compare{
public static void main(String[]args){
String strA="yook.com";
String strB="yook.com";
System.out.println(strA==strB);//true
System.out.println(strB=="yook.com");//true
}
}
两个对象的地址相同,说明strA和strB都指向同一个对象
此时内存结构如图:
对于该情况的解释:
该处就相当于strB=strA 进行了引用传递 而没有开辟新的堆空间
用这种设计可以看出,当定义的多个常量值相同的时候都使用的是同一个对象,可以极大的节省内存空间的使用
分析:String strA=new String("hello")
用这种方式来定义字符串,前面已经解释了"hello"是一个匿名对象,当存在一个匿名对象会自动开辟一个堆空间储存hello,然后又通过new关键字开辟了一次堆空间,"hello"为堆空间赋值然后将new开辟的堆空间的地址给strA
可以从上述的分析中可以看到这种方式定义的字符串会多开辟一段空间,多开辟的空间将成为垃圾空间,会造成性能问题
通过new关键字声明的字符串不会进入常量共享池,java提供了一个手工入池的操作
总结:
分析:字符串常量池
*静态常量池举例*
public class Compare{
public static void main(String[]args){
String strA="helloworld";
String strB="hello"+"world";
System.out.println(strA==strB);//true
}
}
动态常量池举例
public class Compare{
public static void main(String[]args){
String strA="hello";
String strB=strA+"world";
String strC="helloworld";
System.out.println(strB==strC);//false
}
}
如果以后编写字符串过长的时候用+链接最方便
public class Compare{
public static void main(String[]args){
String strA="today "+" is"+ " a" +" good"+ " day!";
System.out.println(strA);
}
}
此时的程序是不会产生多个对象的,仅仅形成一个字符串常量,不影响程序的性能