字符串类

介绍

该文章记录一下经常使用的类,以及经常使用的方法。以便以后查阅使用

字符串(String)

字符串常量:

字符串数据是常量,存储在常量池中,常量池中不允许存储相同的数据,字符串可以直接将数据赋值给对象引用。

常量池的位置:jdk1.7之前 ,常量池的位置(jvm hotspot 永久代)在中,jdk1.7(包括1.7)之后,常量池放在堆中。(方法区是JavaSE规范中的一个概念)。

字符串类型是不可变的,指的是原来指向的数据没有变化,而是新开辟一个空间。

字符串传值方式和基本类型一致。

 String str = "123abc";
 str="hello world";
 str="世界你好";
 int n=10;//栈
 n=20;

内存分配:字符串实际数据存放在常量池中。

String类

常用构造方法

方法名 描述
String() 创建出一个字符串对象,此字符串中没有任何字符,空字符串
String(byte[] bytes, String charsetname) 通过使用指定的charset解码指定的 byte 数组,构造一个新的 String
String(String str) 初始化一个新创建的String对象,使其表示一个与参数相同的字符序列
//创建一个字符串对象
String s1="abc";
String s2 = new String("abc");

常用的成员方法:

​ 1 获取字符串的长度:

/*
int length()          返回此字符串的长度。 
*/
int len = s.length();//获取字符串s的长度

​ 2 获取某个字符或者字符串在原字符串中第一次出现的位置

/*
int indexOf(int ch)   
返回指定字符在此字符串中第一次出现处的索引。 
int indexOf(int ch, int fromIndex) 
返回在此字符串中第一次出现指定字符处的索引,从指定的索引开始搜索。 
int indexOf(String str)	  
返回指定子字符串在此字符串中第一次出现处的索引。 
int indexOf(String str, int fromIndex) 
返回指定子字符串在此字符串中第一次出现处的索引,从指定的索引开始。 
*/
int index1 = s.indexOf('g');//获取'g'字符在s字符串中第一次出现的索引

//注意:返回的是在源字符串中的索引
int index2 = s.indexOf('g',4);//获取'g'字符在s字符串中从索引为4开始第一次出现的索引

//注意:查找字符串时,返回的是第一个字母的下标
int index3 = s.indexOf("abc");//获取"abc"字符串在s字符串中第一次出现的索引

//注意:查询没有结果时则返回-1
int index4 = s.indexOf("abc",4);//获取"abc"字符串在s字符串中从索引为4开始第一次出现的索引

​ 3 获取某个字符或者字符串在原字符串中最后一次出现的位置

/*
int lastIndexOf(int ch) 
返回指定字符在此字符串中最后一次出现处的索引。 
int lastIndexOf(int ch, int fromIndex) 
返回指定字符在此字符串中最后一次出现处的索引,从指定的索引处开始进行反向搜索。 
int lastIndexOf(String str) 
返回指定子字符串在此字符串中最右边出现处的索引。 
int lastIndexOf(String str, int fromIndex) 
返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索。 
*/

//注意:从左往右进行查询,获取到的索引仍然是在原字符串中的索引
int index5 = s.lastIndexOf('g');//获取'g'字符在s字符串中最后一次出现的位置

​ 4 获取某个位置上的字符

/*
char charAt(int index)           返回指定索引处的 char 值。 
*/
char ch = s.charAt(6);//获取s字符串中索引为6的字符

​5 判断字符串中是否包含某个子字符串

/*
boolean contains(CharSequence s)    当且仅当此字符串包含指定的 char 值序列时,返回 true。 
*/
/注意判断包含的内容必须是连续的
boolean b1 = s.contains("hello");//判断s字符串是否包含"hello"字符串

​6 判断字符串中是否有内容

/*
boolean isEmpty()           当且仅当 length() 为 0 时返回 true。 
*/
boolean b2 = s.isEmpty();//判断s字符串是否是空字符串

​7 判断字符串是否是以某个前缀开始的

/*
​boolean startsWith(String prefix)           测试此字符串是否以指定的前缀开始。 
*/
//注意:区分startsWith和contains
boolean b3 = s.startsWith("hello");//判断s字符串是否以"hello"开头

​8 判断字符串是否是以某个后缀结束的

/*
​boolean endsWith(String suffix) 
​测试此字符串是否以指定的后缀结束。 
*/
boolean b4 = s.endsWith("hello");//判断s字符串是否以"hello"结尾

​9 判断字符串的内容是否相等

/*
boolean equals(Object anObject)           将此字符串与指定的对象比较。 
*/
String str1 = "hello world";
boolean b5 = s.equals(str1);//判断str1是否与s相同

10 忽略大小写相等比较

/*
boolean equalsIgnoreCase(String anotherString) 
将此 String 与另一个 String 比较,不考虑大小写。 
*/
boolean b6 = "Abc".equalsIgnoreCase("abc");//true
boolean b7 = "Abc".equals("abc");//false

11 替换

/*
String replace(char oldChar, char newChar) 
返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。 
String replace(CharSequence target, CharSequence replacement) 
使用指定的字面值替换序列替换此字符串所有匹配字面值目标序列的子字符串。 
*/
String s = "hello java";
//注意:会替换原字符串中所有的指定字符
String s1 = s.replace('a','k');
System.out.println(s1);

//注意:将要替换的字符串可以和被替换的字符串长度不相等,当做一个整体被替换掉
String s2 = s.replace("java","php");
System.out.println(s2);

12 截取

/*
String substring(int beginIndex) 
返回一个新的字符串,它是此字符串的一个子字符串。 
String substring(int beginIndex, int endIndex) 
返回一个新字符串,它是此字符串的一个子字符串。 
*/
//从指定的下标开始截取后半部分
String str2 = str1.substring(3);
System.out.println(str2);

//包头不包尾:截取指定区间的子字符串
String str3 = str1.substring(3,9);
System.out.println(str3);

13 去除前面和尾部的空格

/*
String trim() 
返回字符串的副本,忽略前导空白和尾部空白。 
*/
String string1 = "   hello    hello  ";
String string2 = string1.trim();
System.out.println(string2);

14 格式化字符串:将字符串按照指定的格式输出

/*
static String format(String format, Object... args) 
使用指定的格式字符串和参数返回一个格式化字符串。 

%f   float
%d	  整型
%s		字符串
%c		char
%b		boolean
*/
//注意:可以保留小数点后几位
String string3 = String.format("%.2f--%b--%s",10.23766f,true,"hello");
System.out.println(string3);
System.out.println("hello" + 10.23766f + true);

15 比较

/*
int compareTo(String anotherString) 
按字典顺序比较两个字符串。 
int compareToIgnoreCase(String str) 
按字典顺序比较两个字符串,不考虑大小写。 
*/
/*
如果按字典顺序此 String 对象位于参数字符串之前,则比较结果为一个负整数。
如果按字典顺序此 String 对象位于参数字符串之后,则比较结果为一个正整数。
如果这两个字符串相等,则结果为 0
*/
	
int num1 = "abc".compareTo("def");
System.out.println(num1);//-3

int num2 = "def".compareTo("abc");
System.out.println(num2);//3

int num3 = "abc".compareTo("abc");
System.out.println(num3);

16 拼接

/*
String concat(String str) 
将指定字符串连接到此字符串的结尾。 
*/
//注意:在String类中,但凡返回值是String类型的方法,生成的都是一个新的字符串,跟原来的字符串没有关系
String newStr = str1.concat("hello");
System.out.println(str1);//welcome to china
System.out.println(newStr);//welcome to chinahello

17 拆分

拆分	
String s12="北京,上海,广州,深圳";
	
String[] cities=s12.split(",");
System.out.println(cities.length);

for (String c : cities) {
	System.out.println(c);
}

18 内存中的字符串

class StringUsageDemo01 {
	public static void main(String[] args) {
		//s1表示引用,存储在栈空间中,但是,“hello”存储在常量池中
		String s1 = "hello";
		String s2 = "hello";
		//注意1:使用一个字符串常量定义两个不同的变量,这时两个变量其实在内存中是同一块内存空间
		//原因:两个变量都拷贝了字符串常量的地址
		System.out.println(s1 == s2);//true
		System.out.println(s1.equals(s2));//true
		
		//注意2:但凡遇到new关键字,表示开辟了不同的空间
		//s3和s4分别指向了两个不同的有效空间
		String s3 = new String("hello");
		String s4 = new String("hello");
		System.out.println(s3 == s4);//false
		System.out.println(s3.equals(s4));//true
		
		System.out.println(s1 == s3);//false
		System.out.println(s1.equals(s3));//true
		
		/*
		​String s1 = "hello";		只有一个对象,是“hello”
		String s3 = new String("hello");	两个对象,一个是“hello”,另外一个就是new出现的对象
		​*/

		//字符串是一个特殊的对象,一旦被初始化之后将不能发生改变
		//注意3:不能发生改变指的是真正的对象【字符串常量对象和new出现的对象】
		s1 = "java";
		
		/*
		String str = "abc";
		等价于char[] arr = {'a','b','c'};
		
		*/
	}
}

StringBuffer类

StringBuffer类是String的增强类, 比如: 插入 、追加、替换功能更加强大, 比字符串更加节省内存。

字符串缓冲区:使用缓冲区操作字符串要比直接操作字符串效率高

StringBuffer中的常用构造方法:

StringBuffer(String str) 构造一个字符串缓冲区,并将其内容初始化为指定的字符串内容。
StringBuffer() 构造一个其中不带字符的字符串缓冲区,其初始容量为 16 个字符。
StringBuffer(CharSequence seq) 构造一个字符串缓冲区,它包含与指定的 CharSequence 相同的字符。
StringBuffer(int capacity) 构造一个不带字符,但具有指定初始容量的字符串缓冲区。

常用的成员方法

​1 追加:append()

java ​/* ​StringBuffer append(String str) ​StringBuffer insert(int offset, String str) ​*/ ​StringBuffer sb2 = sb1.append("hello"); ​ ​//区别于String类:面盆理论 ​System.out.println(sb1); ​System.out.println(sb2); ​System.out.println(sb1 == sb2); ​ ​sb1.append("java"); ​System.out.println(sb1); ​System.out.println(sb2); ​ ​//方法链 ​sb1.append("java").append("java").append("java").append("java"); ​System.out.println(sb1); ​System.out.println(sb2); ​ ​//插入 ​sb1.insert(2,"hhhhhhhh"); ​System.out.println(sb1); ​

​2 删除

java ​/* ​StringBuffer delete(int start, int end) ​StringBuffer deleteCharAt(int index) ​*/ ​ ​//删除指定区间的字符串 ​sb1.delete(2,3); ​System.out.println(sb1); ​ ​//删除指定位置上的字符 ​sb1.deleteCharAt(0); ​System.out.println(sb1); ​

​3 替换

java ​/* ​StringBuffer replace(int start, int end, String str) ​void setCharAt(int index, char ch) ​*/ ​//替换指定区间的字符串 ​sb1.replace(2,5,"nnnnnn"); ​System.out.println(sb1); ​ ​//替换指定位置上的字符 ​sb1.setCharAt(0,'x'); ​System.out.println(sb1); ​

​4 获取

java ​//和String类中的用法相同 ​/* ​indexOf ​lastIndexOf ​charAt ​length ​substring ​*/ ​

​5 反转

java ​// StringBuffer reverse() ​StringBuffer sb3 = new StringBuffer("my name is zhansan"); ​sb3.reverse(); ​System.out.println(sb3); ​

StringBuilder类

StringBuilder类也是字符串缓冲区,类中的方法与StringBuffer类中的方法使用方法一样,区别在于StringBuilder类中的方法都是线程不安全的,而StringBuffer类中的方法都是线程安全。 StringBuilder效率比StringBuffer的高。

补充:String.intern()方法

intern方法用来返回常量池中的某字符串,如果常量池中已经存在该字符串,则直接返回常量池中该对象的引用。否则,在常量池中加入该对象,然后返回引用。

intern()判断这个常量是否存在于常量池。

  • (1)如果存在
  • 判断存在内容是引用还是常量
  • 如果是引用,返回引用地址指向堆空间对象
  • 如果是常量,直接返回常量池常量
  • (2)如果不存在
  • 将当前对象引用复制到常量池,并且返回的是当前对象的引用
String a1 = "AA";
System.out.println(a1 == a1.intern()); //true
String a2 = new String("B") + new String("B");  //a2 堆中
a2.intern();// 把堆中a2的地址放入常量池
String a3 = new String("B") + new String("B"); //a3 堆中
System.out.println(a2 == a3.intern());//true
System.out.println(a3 == a3.intern());//false
String a4 = new String("C") + new String("C");// a4堆中
System.out.println(a4 == a4.intern()); //true

intern()方法总结

1.只在常量池上创建常量
String a1 = "AA";
2.只在堆上创建对象
String a2 = new String("A") + new String("A");
3.在堆上创建对象在常量池上创建常量
String a3 = new String("AA");
4.在堆上创建对象在常量池上创建引用
String a4 = new String("A") + new String("A");//只在堆上创建对象AA
a4.intern();//将该对象AA的引用保存到常量池上
5.在堆上创建对象在常量池上创建引用,不会在常量池中再创建常量
String a5 = new String("A") + new String("A");//只在堆上创建对象
a5.intern();//在常量池上创建引用
String a6 = "AA";//此时不会再在常量池上创建常量AA,而是将a5的引用返回给a6
System.out.println(a5 == a6); //true

String StringBuffer 和 StringBuilder 的区别是什么? String 为什么是不可变的?

可变性

简单的来说:String 类中使用 final 关键字修饰字符数组来保存字符串,private final char value[],所以 String 对象是不可变的。而StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder 类,在 AbstractStringBuilder 中也是使用字符数组保存字符串char[]value 但是没有用 final 关键字修饰,所以这两种对象都是可变的。

StringBuilder 与 StringBuffer 的构造方法都是调用父类构造方法也就是 AbstractStringBuilder 实现的,大家可以自行查阅源码。

AbstractStringBuilder.java

abstract class AbstractStringBuilder implements Appendable, CharSequence {
   char[] value;
   int count;
   AbstractStringBuilder() {
   }
   AbstractStringBuilder(int capacity) {
       value = new char[capacity];
   }

线程安全性

String 中的对象是不可变的,也就可以理解为常量,线程安全。AbstractStringBuilder 是 StringBuilder 与 StringBuffer 的公共父类,定义了一些字符串的基本操作,如 expandCapacity、append、insert、indexOf 等公共方法。StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的。

性能

每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的 String 对象。StringBuffer 每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用 StringBuilder 相比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。

对于三者使用的总结:

  1. 操作少量的数据: 适用String
  2. 单线程操作字符串缓冲区下操作大量数据: 适用StringBuilder
  3. 多线程操作字符串缓冲区下操作大量数据: 适用StringBuffer

三者区别摘抄与下面地址


转载请注明:Memory的博客 » 点击阅读原文

打赏一个呗

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦