String类
1.实例化方式
- 直接赋值
String str = "Hello"; //str是一个对象,那么Hello就应该保存在堆内存中
System.out.println(str);
这种直接赋值的方法最常用,但是string类毕竟是一个类,即然是类就有构造方法,string类的其中一种构造方法如下:
- 传统方法(构造方法):
String str = new String("Hello");
System.out.println(str);
构造方法需要使用关键字new进行对象实例化
2. 字符串相等比较
1. 对于基本数据类型来说,比较相等可以用 == 号.
2.对于引用类型String类来说,有两种可以比较的方式,那它们有什么区别呢?
- 观察字符串==比较
String str1 = "Hello";
String str2 = new String("Hello");
System.out.println(str1 == str2); //false
str1是直接赋值的方法,而str2是进行了对象实例化
为什么结果会是false呢?保持疑问,继续往下看,来看如下内存图分析
==进行的是数值比较,如果用于对象比较,那么它所比较的应该是对象所在地址数值的大小,并没有比价内容,如图,很明显两个"Hello"对象的地址不一样,所以它们不相等
那么如果想进行 两个字符串内容的比较,则必须采用String类提供的equals()方法
- 使用equals()方法进行内容比较
String str1 = "Hello" ;
String str = new String("Hello") ;
System.out.println(str1.equals(str));
扩展:
- 因为String类覆写了equals()方法,所有String.equals可以比较两个字符串内容相等
- Object类的equals()方法,比较的是两个字符串的内容
- ==始终比较的是两个变量的地址
3. String的匿名对象
观察字符串常量
String str1 = "Hello" ;
String str = new String("Hello") ;
System.out.println(str1.equals(str));
System.out.println("Hello".equals(str));//这个Hello是一个字符串匿名对象
//匿名对象保存在堆内存中
那么在之前出现的String str = "Hello";,本质上就是讲一个匿名的String类对象设置名字,而且匿名对象一定保存在堆内存中
4. 两种实例化方式的区别
1.采用直接赋值:
String str1 = "hello" ;
String str2 = "hello" ;
String str3 = "hello" ;
System.out.println(str1 == str2); // true
System.out.println(str1 == str3); // true
System.out.println(str2 == str3); // true
从图上可以看出str1,str2,str3都同时指向了"hello",为什么没有开辟新的堆内存空间呢?
String类的设计使用了共享设计模式
在JVM底层实际上会自动维护一个对象池(字符串对象池),如果现在采用了直接赋值的模式进行String类的对象
实例化操作,那么该实例化对象(字符串内容)将自动保存到这个对象池之中。如果下次继续使用直接赋值的模式
声明String类对象,此时对象池之中如若有指定内容,将直接进行引用;如若没有,则开辟新的字符串对象而后将
其保存在对象池之中以供下次使用
所谓的对象池就是一个对象数组(目的就是减少开销)
2. 采用构造方法
类对象使用构造方法实例化是标准做法。分析如下程序:
String str = new String("hello") ;
通过分析可知,如果使用String构造方法就会开辟两块堆内存空间,并且其中一块堆内存将成为垃圾空间。除了这
一缺点之外,也会对字符串共享产生问题。
观察字符串共享问题
// 该字符串常量并没有保存在对象池之中
String str1 = new String("hello") ;
String str2 = "hello" ;
System.out.println(str1 == str2); // false
在String类中提供有方法入池操作 public String intern() ;
观察入池操作
String str1 = new String("hello").intern() ;
String str2 = "hello" ;
System.out.println(str1 == str2); // true
总结String类两种对象实例化的区别:
- 直接赋值:只会开辟一块堆内存空间,并且该字符串对象可以自动保存在对象池中以供下次使用
- 构造方法:会开辟两块堆内存空间,其中一块称为垃圾空间,不会自动保存在对象池中,可以使用Intern()方法手工入池
一般采用直接赋值的方法
5. 字符串不可变更
字符串一旦定义不可改变
观察如下代码:
String str = "hello" ;
str = str + " world" ;
str += "!!!" ;
System.out.println(str); // hello world!!!
以上字符串的变更时对象的变更而非字符串常量
可以发现字符串上没有发生任何变化,但是字符串对象的引用一直在改变,而且会形成大量的垃圾空间。
6. 字符与字符串
字符可以转换为字符串,字符串也可以转换为字符
String str = "helloworld" ;
// 将字符串变为字符数组
char[] data = str.toCharArray() ;
for (int i = 0; i < data.length; i++) {
data[i] -= 32 ;
System.out.print(data[i]+"、");
}
// 字符数组转为字符串
System.out.println(new String(data)); // 全部转换
System.out.println(new String(data,5,5)); // 部分转换
7. 字节与字符串
字节数组可以转换为字符串,字符串也可以转换为字节数组
实现字节数组与字符串的转换
String str = "helloworld" ;
byte[] data = str.getBytes() ;
for (int i = 0; i < data.length; i++) {
data[i] -= 32 ;
System.out.print(data[i]+"、");
}
System.out.println(new String(data));
通过程序可以发现,字节并不适合处理中文,只有字符适合处理中文。按照程序的概念来讲,一个字符等于两个字
节。字节只适合处理二进制数据。
8. 字符串比较
除了equals()方法还有一种能够进行区分大小写相等的方法
不区分大小写比较
String str1 = "hello" ;
String str2 = "Hello" ;
System.out.println(str1.equals(str2)); // false
System.out.println(str1.equalsIgnoreCase(str2)); // true
在String类中compareTo()方法是一个非常重要的方法,该方法返回一个整型,该数据会根据大小关系返回三类内
容:
1. 相等:返回0.
2. 小于:返回内容小于0.
3. 大于:返回内容大于0。
观察compareTo()比较
System.out.println("A".compareTo("a")); // -32
System.out.println("a".compareTo("A")); // 32
System.out.println("A".compareTo("A")); // 0
System.out.println("AB".compareTo("AC")); // -1
System.out.println("刘".compareTo("杨"));
compareTo()是一个可以区分大小关系的方法,是String方法里是一个非常重要的方法
9. 字符串查找
字符串查找,最好用最方便的就是contains()
String str = "helloworld" ;
System.out.println(str.contains("world")); // true
使用indexOf()方法进行位置查找
String str = "helloworld" ;
System.out.println(str.indexOf("world")); // 5,w开始的索引
System.out.println(str.indexOf("bit")); // -1,没有查到
if (str.indexOf("hello") != -1) {
System.out.println("可以查到指定字符串!");
}
使用indexOf()需要注意的是,如果内容重复,它只能返回查找的第一个位置
判断开头或结尾
String str = "**@@helloworld!!" ;
System.out.println(str.startsWith("**")); // true
System.out.println(str.startsWith("@@",2)); // ture
System.out.println(str.endsWith("!!")); // true
10. 字符串替换
字符串的替换处理
String str = "helloworld" ;
System.out.println(str.replaceAll("l", "_"));
System.out.println(str.replaceFirst("l", "_"));
11. 字符串的拆分
可以将一个完整的字符串按照指定的分隔符划分为若干个子字符串。
实现字符串的拆分处理
String str = "hello world hello bit" ;
String[] result = str.split(" ") ; // 按照空格拆分
for(String s: result) {
System.out.println(s);
}
字符串的部分拆分
String str = "hello world hello bit" ;
String[] result = str.split(" ",2) ;
for(String s: result) {
System.out.println(s);
}
12. 字符串截取
从一个完整的字符串之中截取出部分内容。
观察字符串截取
String str = "helloworld" ;
System.out.println(str.substring(5));
System.out.println(str.substring(0, 5));
索引从0开始
13. StringBuffer类
由于String的不可更改特性,为了方便字符串的修改,提供StringBuffer类。
在String中使用"+"来进行字符串连接,但是这个操作在StringBuffer类中需要更改为append()方法:
public synchronized StringBuffer append(各种数据类型 b)
观察StringBuffer使用
public class Test{
public static void main(String[] args) {
StringBuffer sb = new StringBuffer();
sb.append("Hello").append("World");
fun(sb);
System.out.println(sb);
}
public static void fun(StringBuffer temp) {
temp.append("\n").append("www.bit.com.cn");
}
}
String和StringBuffer最大的区别在于:String的内容无法修改,而StringBuffer的内容可以修改。频繁修改字符串
的情况考虑使用StingBuffer。
注意:
String和StringBuffer类不能直接转换。如果要想互相转换,可以采用如下原则:
- String变为StringBuffer:利用StringBuffer的构造方法或append()方法
- StringBuffer变为String:调用toString()方法。
观察字符串反转:
StringBuffer sb = new StringBuffer("helloworld");
System.out.println(sb.reverse());
String、StringBuffer、StringBuilder的区别:
- String的内容不可修改,StringBuffer与StringBuilder的内容可以修改.
- StringBuffer采用同步处理,属于线程安全操作;而StringBuilder采用异步处理,属于线程不安全操作。