String类也就是我们常说的字符串类,在java开发可以说是处处可见。在String类中封装了很多关于字符串的处理方法,下面我们来研究研究这些方法如何使用。
2、实例化我们都知道java的对象实例化一般都是通过关键字new来实例化,对于基本类型我们可以通过=直接赋值,对于String对象来说这两种方法都是可以的,但是有一定的区别。
2.1、new创建与直接赋值首先我们来看一下这两种赋值的比较结果:
public static void main(String[] args) {
String str1 = "hello";
String str2 = "hello";
String str3 = new String("hello");
String str4 = new String("hello");
System.out.println(str1 == str2);
System.out.println(str1 == str3);
System.out.println(str4 == str3);
}
这四个字符串的值都是hello,但是通过直接赋值的,他们的存储地址是相同的,而通过关键字new创建的,互相之间的存储地址不相同,new创建的与直接赋值的地址也不相同。
为什么会出现有的地址相同,有的地址不相同的情况?这就涉及到常量池了,通过直接赋值的方式,我们的对象是直接指向了常量池中hello的存储地址,而通过new实例化的String对象,是现在虚拟机的堆内存中存放,堆内存中存放的是hello字符串常量的地址,这就是为什么会出现上述情况的原因。
2.2、String类构造方法这是String类中定义的构造方法,大至可以分为
-
通过字符串常量构造字符串
- 不传入string常量则为空值
-
通过char[]构造字符串
- 默认全部构造
- 传入offer(从索引index起始),count(count个字符),来构造字符串
-
通过int[]构造字符串
-
数组中为字符对应的ascall码值
-
默认全部构造
-
传入offer(从索引index起始),count(count个字符),来构造字符串
-
-
通过byte[]构造字符串
-
数组中为字符对应的ascall码值
-
默认全部构造
-
默认编码方式ISO-8859-1
-
传入offer(从索引index起始),count(count个字符),来构造字符串
-
传入String或者Charset为编码格式(例如utf-8)
-
-
通过StringBuffer或者StringBuilder构造字符串
- 关于这两个类的解释见后面
public static void main(String[] args) {
char[] chs = {'h','e', 'l','l','o'};
int[] ins = {45, 46, 47, 48, 49};
byte[] bs = {91, 92, 93, 94, 95};
StringBuilder sb = new StringBuilder("hello");
System.out.println("hello");
System.out.println(new String());
System.out.println(new String("hello"));
System.out.println(new String(chs));
System.out.println(new String(chs, 0, 3));
System.out.println(new String(ins, 0, 5));
System.out.println(new String(bs));
System.out.println(new String(bs, StandardCharsets.UTF_8));
System.out.println(new String(sb));
}
3、常用方法
- length()返回字符串的长度
- charAt(int index) 返回索引index处的字符串
- equals(String str) 判断两个字符串是否相同(大小写敏感)
- equalsIgnoreCase(String str) 判断连两个字符串是否相同(大小写不敏感)
- indexOf(String str) 返回字串str在字符串第一次出现的索引
- lastIndexOf(String str) 返回字串str在字符串最后一次出现的索引
- substring(int beginIndex, int endIndex)从索引beginIndex到endIndex切割字符串
- replace(char oldChar, char newChar)替换字符串中所有的字符oldChar,替换为newChar
- replaceFirst(String regex, String replacement)替换字符串中第一个出现的子串regex,替换为replacement
- replaceAll(String regex, String replacement)替换字符串中所有的子串regex,替换为replacement
- split(String regex)以字符串regex为分隔符分割字符串,返回字符串集合
- toLowerCase()将字符串中全部字字母更改为小写字母
- toUpperCase()将字符串中全部字字母更改为大写字母
- toCharArray()将字符串转换成字符数组
- valueOf()将对象转换为字符串
- matcher(String regex)正则表达式regex是否匹配字符串
例:
public static void main(String[] args) {
String str = "Hello world!";
System.out.println(str.length());
System.out.println(str.equals("hello world!"));
System.out.println(str.equals("Hello world!"));
System.out.println(str.equalsIgnoreCase("hello world!"));
System.out.println(str.indexOf("o"));
System.out.println(str.lastIndexOf("o"));
System.out.println(str.substring(0, 5));
System.out.println(str.replace('l', 'a'));
System.out.println(str.replaceFirst("l", "b"));
System.out.println(str.replaceAll("l", "c"));
System.out.println(str.split(" ")[1]);
System.out.println(str.toLowerCase());
System.out.println(str.toUpperCase());
System.out.println(String.valueOf(566.5));
System.out.println(String.valueOf(new Dog("小黄", 18)));
System.out.println(str);
}
在上面的例子中我最后输出了一遍str,发项相比于最开始的值没有发生变化,但是我们对这个字符串调用了很多方法,这是为什么嘞?
在String源代码中:
我们可以看出String类以及其中的value都是使用final修饰的,这就表明的String一旦实例化就不能被修改。
4、字符串相加机制Java语言为+连接符以及对象转换为字符串提供了特殊的支持,字符串对象可以使用“+”连接其他对象。其中字符串的连接是通过StringBuilder或者StringBuffer类与它们的append方法实现的,对象转换字符串是通过toString()方法实现的。我们可以认为:
//下两者是等价的
str = i + "";
str = String.valueOf(i);
//下两者是等价的
str = "hello" + i;
str = new StringBuilder("hello").append(i).toString();
在通常情况下,+连接符的使用不会造成效率上的损失,但在进行大量循环拼接字符串时,例如:
String str = "hello";
for (int i = 0;i < 1000; i++) {
str += "hello";
}
在这种情况下每进行依次+,JVM就会创建一次StringBuilder对象,大量的StringBuilder对象留在堆内存中,肯定导致了效率的损失,所以在大量字符串相加的时候建议使用StringBuilder或者StringBuffer,通过append方法来拼接。
5、字符串常量池在Java的内存分配中,总共3种常量池,分别是Class常量池、运行时常量池、字符串常量池。
字符串的分配和其他对象分配一样,是需要消耗高昂的时间和空间的,而且字符串使用的非常多。JVM为了提高性能和减少内存的开销,在实例化字符串的时候进行了一些优化:使用字符串常量池。每次在创建一个字符串的时候,JVM会先去查看字符串常量池,如果创建的字符串已存在那么就直接返回常量池中该字符的存储地址。否则就会实例化该字符串并放到字符串常量池中。由于String字符串的不可变性,常量池中一定不存在两个相同的字符串。
这也就解释了我们在2.1中两种方法地址不同的原因。
6、String、StringBuilder和StringBuffer三个类的继承关系如上,蓝色代表:extend,绿色虚线代表:implements
最主要的区别就是:
- String时不可变的字符序列, StringBuilder与StringBuffer都是可变字符序列。
- 执行速度上StringBuilder> StringBuffer> String。
7020)]
三个类的继承关系如上,蓝色代表:extend,绿色虚线代表:implements
最主要的区别就是:
- String时不可变的字符序列, StringBuilder与StringBuffer都是可变字符序列。
- 执行速度上StringBuilder> StringBuffer> String。
- StringBuffer时线程安全的,StringBuilder是非线程安全的。



