JAVA中的三大特殊类之String类
实例化方式
(1)直接赋值
String str1 = "hello world!";
System.out.println(str1);
str1是一个对象,其内容保存在堆内存上,这种赋值方式是最常用的;
(2)构造方法
因为String本身就是一个类,所以一定存在构造方法,看代码:
String str2 = new String("Hello World!!!");
System.out.println(str2);
既然会存在两种方法,那必然也会有其不同之处,那不同之处又在哪里呢?接下来做一个小小的测试。我们都知道,判断两个整型变量的是否相等时,可以使用==
,假如我们用到字符串上,是否也会如此呢?看代码:
public class Test12{
public static void main(String[] args){
//直接赋值法
String str1 = "hello";
//构造方法赋值
String str2 = new String("hello");
//结果返回 true false
System.out.println(str1 == str2);
}
}
我们猜测结果应该是true,但是,看结果:
D:\MySQL5.7\javawork>java Test12
false
为什么呢?明明两个字符串的内容是一样的,但是结果为什么是false?
上图便是内存图,可以发现,==
本身用于数值比较,但是现在用于两个字符串进行比较的话,就是两个对象所保存地址数值的比较,而并不是比较对象的内容,接着看:
public class Test12{
public static void main(String[] args){
//直接赋值法
String str1 = "hello";
String str2 = "hello";
String str3 = "hello";
//构造方法赋值
String str4 = new String("hello");
//结果返回 true false
System.out.println(str1 == str2);
System.out.println(str1 == str3);
System.out.println(str2 == str3);
System.out.println(str1 == str4);
}
}
看结果:
D:\MySQL5.7\javawork>java Test12
true
true
true
false
这个结果应该在意料之中,但是为什么呢?接着看他们的内存分配
问题:为什么定义多个字符串用直接赋值法时仍没有开辟新的堆内存空间呢?
答:因为String类的设计使用了共享设计模式。在JVM底层实际上会自动维护一个对象池(字符串对象池),如果采用了直接赋值的模式进行String类的对象实例化操作,那么该实例化对象(字符串内容)将自动保存到这个对象池中;如果下次继续使用直接赋值的模式声明String类对象,此时对象池之中如果有指定内容,将直接引用,如果没有,则开辟新的字符串对象然后将其保存在对象池中以供下次使用。所谓的对象池就是一个对象数组,其目的就是减少开销。采用构造方法赋值时,由第二张图可知:会开辟两块堆内存空间,当new一个新的内存空间时,上面的堆内存就会成为垃圾空间,当然使用构造方法除了会产生垃圾空间之外,还会对字符串共享产生问题。所以在String类中提供有方法入池操作:public String intern()
.
equals
说了这么多,那么字符串相等比较还有什么方法呢?==
进行对象所保存地址数值的比较,要想比较内容,则必须采用String类提供的equals
方法。看代码:
public class Test12{
public static void main(String[] args){
String str1 = "hello";
String str2 = new String("hello");
System.out.println(str1.equals(str2));
}
}
看结果:
D:\MySQL5.7\javawork>java Test12
true
String的匿名对象
在任何语言的底层,都不会提供直接的字符串类型。在java中也没有直接提供字符串常量的概念,所有使用“"定义的内容本质上来讲都是String的匿名对象。任何字符串常量都是String的匿名对象,所以该对象永远不会是null。
String类两种对象实例化的区别
(1)直接赋值:只会开辟一块内存空间,并且该字符串对象可以自动保存在对象池中以供下次使用;
(2)构造方法:对开辟两块内存空间,其中一块成为垃圾空间,不会自动保存在对象池中,但是可以使用intern()
方法手工入池。
字符串不可变更
字符串一旦定义不可改变。所有的语言对于字符串的底层实现,都是字符数组,数组的最大缺陷就是长度固定。看代码:对字符串对象的变更而并非是字符串常量
public class Test12{
public static void main(String[] args){
String str = " hello ";
str = str + " world ";
str += " !!! ";
System.out.println(str);
}
}
分析上述过程,开辟了五个堆内存,看内存图:
字符串上没有发生任何变化,但是字符串对象的引用一直在改变,而且形成了大量的垃圾空间,所以这样的代码不应该出现在开发过程中。
原则
(1)字符串使用采用直接赋值;
(2)字符串比较采用equals()
实现;
(3)字符串别改变太多。
String类的特点
任何字符串常量都是String对象,而且String的常量一旦声明不可改变,如果改变对象内容,改变的是其引用的指向而已。除此以外,String类还提供了很多操作方法,如:
字符数组转字符串及字符串转字符的操作方法public String(char value[]), public String(char value[],int offset,int count), public char charAt(int index), public char[] toCharArray()
;
字节与字符串public String(byte bytes[]), public String(byte bytes[],int offset,int length),,,
;
字符串比较public boolean equals(Object anObject), public int compareTo(String anotherString),,,
字符串查找;字符串替换;字符串拆分;字符串截取等操作。
StringBuffer类
方便字符串的修改。使用append()
方法进行字符串连接:
public synchronized StringBuffer append(各种数据类型 b)
----注意:String 和 StringBuffer类不能直接转换,如果想互相转换,采用以下原则:
(1)String->StringBuffer: 利用StringBuffer的构造方法或append()方法;
(2)StringBuffer->String: 调用toString()方法。
----StringBuffer 和 StringBuilder 的区别:
StringBuffer采用同步处理,属于线程安全操作;
StringBuilder采用异步处理,属于线程不安全操作。