前言
我们在学习C语言,对于字符串是没有专门的类型进行定义的,而Java语言是有专门的类型定义字符串的——String,这篇博客将会详细的介绍Java中对于字符串类型的讲解。
创建字符串
常见的构造String的方式
public class TestDemo2 {
public static void main(String[] args) {
String str = "hello";
String str2 = new String("hello");
char[] chars = {'a', 'b', 'c'};
String str3 = new String(chars);
System.out.println(str3);
}
}
注意事项:
- “hello”这样的字符串字面值常量,类型也是String。
- String也是引用类型。
引用类型:
引用类似于 C 语言中的指针, 只是在栈上开辟了一小块内存空间保存一个地址。但是引用和指针又不太相同, 指针能进行各种数字运算(指针+1)之类的, 但是引用不能, 这是一种 “没那么灵活” 的指针。 另外, 也可以把引用想象成一个标签, “贴” 到一个对象上。一个对象可以贴一个标签, 也可以贴多个。如果一个对象上面一个标签都没有, 那么这个对象就会被 JVM 当做垃圾对象回收掉。 Java 中数组, String, 以及自定义的类都是引用类型。
因此,String str = “hello”;这样的代码内存布局如下: 因此,String str1 = “Hello”;String str2 = str1;这样的代码内存布局如下: 那么问题来了,如果修改str1,那么str2会不会随之改变呢?
str1 = "world";
System.out.println(str2);
Hello
实际上,str1 = “world”,并不算真正的“修改”字符串,而是让str1这个引用指向一个新的String对象。 因此,我们知道了,传引用不一定就能改变对应的值,而是要从底层的出发,看这个引用的作用是什么,又干了些什么。
字符串比较相等
引用比较
如果现在有两个int型变量,判断其相等可以使用==完成。
int x = 10 ;
int y = 10 ;
System.out.println(x == y);
true
那么如果是String类对象使用==呢? 代码1
String str1 = "Hello";
String str2 = "Hello";
System.out.println(str1 == str2);
true
代码2
String str1 = new String("Hello");
String str2 = new String("Hello");
System.out.println(str1 == str2);
false
为什么会出现这种情况呢? 对于代码1内存布局是这样的: 我们发现,str1 和 str2 是指向同一个对象的。此时如 “Hello” 这样的字符串常量是在字符串常量池中。
字符串常量池: 如 “Hello” 这样的字符串字面值常量,也是需要一定的内存空间来存储的。这样的常量具有一个特点,就是不需要修改(常量嘛)。所以如果代码中有多个地方引用都需要使用 “Hello” 的话,就直接引用到常量池的这个位置就行了,而没必要把 “Hello” 在内存中存储两次。也就是说字符串常量值是为了提高效率而存在的。
而对于代码2内存布局是这样的: 通过String str1 = new String(“Hello”); 这样的方式创建的String对象相当于再堆上另外开辟了空间来存储"Hello"的内容, 也就是内存中存在两份"Hello"。
内容比较
上面的String使用==比较并不是在比较字符串内容,而是比较两个引用是否是指向同一个对象。那么如何比较字符串中的内容呢?很简单,我们可以通过String类提供的equals方法进行比较。举个例子:
String str1 = new String("Hello");
String str2 = new String("Hello");
System.out.println(str1.equals(str2));
true
如此我们就可以比较字符串的内容了,当然equals()方法有注意事项的。举一个例子:
String str = new String("Hello");
System.out.println(str.equals("Hello"));
System.out.println("Hello".equals(str));
从本质上来看,这两种方法都可以,但是使用方式一会有一个问题,当str为null时,方式一会抛出空指针异常,因此我们在使用时需要注意,使用equals()方法的对象会不会为null。
理解字符串不可变
字符串是一种不可变对象。它的内容不可改变。 String 类的内部实现也是基于 char[] 来实现的,但是 String 类并没有提供 set 方法之类的来修改内部的字符数组。 我们来看这样一组代码:
String str = "hello" ;
str = str + " world" ;
str += "!!!" ;
System.out.println(str);
hello world!!!
形如 += 这样的操作,表面上好像是修改了字符串,其实不是。内存变化如下: 首先为“hello”开辟一块空间,由str指向“hello”对象。 其次为“world”开辟一块空间,再将“hello”和“world”连起来,再为“hello world”开辟一块空间,最后由str指向“hello world”。 下面的也是如此,因此我们知道:+= 之后 str 打印的结果却是变了,但是不是 String 对象本身发生改变,而是 str 引用到了其他的对象。所以在编程时,我们不能出现下面类似的代码,否则效率会很低。
String str = "hello" ;
for(int x = 0; x < 1000; x++) {
str += x ;
}
System.out.println(str);
字符,字节与字符串
字符与字符串
字符串内部包含一个字符数组,String 可以和 char[] 相互转换。 代码1:
public static void main(String[] args) {
char[] val = {'a', 'b', 'c'};
String str = new String(val);
System.out.println(str);
}
作用:将字符数组中的所有内容变成字符串。 解释:String(char val[ ])构造方法,将val[ ]数组的内容变成字符串。 结果:
代码2:
public static void main(String[] args) {
char[] val = {'a', 'b', 'c','d','e'};
String str = new String(val,1,3);
System.out.println(str);
}
作用:将部分字符数组中的内容变成字符串。 解释:String(char value[ ],int offset, int count)构造方法,将val[ ]数组从下标为offset的位置到后面count个元素变成字符串。 结果: 代码3:
public static void main(String[] args) {
String str = "hello";
char ch = str.charAt(2);
System.out.println(ch);
}
作用:获得指定索引位置的字符,索引从0开始。 解释:charAt(int index)方法,获得字符串中第index下标的字符。 结果: 代码4:
public static void main(String[] args) {
String str = "hello";
char[] chars = str.toCharArray();
System.out.println(Arrays.toString(chars));
}
作用:将字符串变成字符数组返回。 解释:toCharArray()方法,将调用的字符串变成字符数组,并返回该数组。 结果:
字节与字符串
代码1:
public static void main(String[] args) {
byte[] bytes = {97, 98, 99, 100};
String str = new String(bytes);
System.out.println(str);
}
作用:将字节数组变成字符串 解释:String(byte bytes[ ])构造方法,将bytes[ ]字节数组变成对应的字符串。 结果: 代码2:
public static void main(String[] args) {
byte[] bytes = {97, 98, 99, 100};
String str = new String(bytes,1,3);
System.out.println(str);
}
作用:将部分字节数组中的内容变成字符串。 解释:String(byte bytes[ ],int offset,int length)构造方法,将val[ ]数组从下标为offset的位置到后面count个元素变成字符串。 结果: 代码3:
public static void main(String[] args) {
String str2 = "abcd";
byte[] bytes = str2.getBytes();
System.out.println(Arrays.toString(bytes));
}
作用:将字符串以字节数组的形式返回。 解释:getBytes()方法,将调用的字符串变成字节数组,并返回该数组。 结果:
小结
- byte[ ]是把String按照一个字节一个字节的方式处理,这种适合在网络传输,数据存储这样的场景下使用。更适合针对二进制数据来操作。
- char[ ]是把String按照一个字符一个字符的方式处理,更适合针对文本数据来操作,尤其是包含中文的时候。
字符串常见的操作
字符串的比较
上面使用过String类提供的equals()方法,该方法本身是可以进行区分大小写的相等判断。除了这个方法之外,String类还提供有如下的比较操作:
NO. | 方法名称 | 类型 | 描述 |
---|
1. | public boolean equals(Object anObject) | 普通 | 区分大小写的比较 | 2. | public boolean equalsIgnoreCase(String anotherString) | 普通 | 不区分大小写 | 3. | public int compareTo(String anotherString) | 普通 | 比较两个字符串大小关系 |
代码示例:
String str1 = "hello" ;
String str2 = "Hello" ;
System.out.println(str1.equals(str2));
System.out.println(str1.equalsIgnoreCase(str2));
在String类中compareTo()方法是一个非常重要的方法,该方法返回一个整型,该数据会根据大小关系返回三类内容:
- 相等:返回0。
- 小于:返回内容小于0。
- 大于:返回内容大于0。
代码示例:
System.out.println("A".compareTo("a"));
System.out.println("a".compareTo("A"));
System.out.println("A".compareTo("A"));
System.out.println("AB".compareTo("AC"));
System.out.println("刘".compareTo("杨"));
compareTo()是一个可以区分大小关系的方法,是String方法里是一个非常重要的方法。字符串的比较大小规则,总结成三个字 “字典序” 相当于判定两个字符串在一本词典的前面还是后面。先比较第一个字符的大小(根据 unicode 的值来判定),如果不分胜负,就依次比较后面的内容。
字符串查找
从一个完整的字符串之中可以判断指定内容是否存在,对于查找方法有如下定义:
NO. | 方法名称 | 类型 | 描述 |
---|
1. | public boolean contains(Char Sequence) | 普通 | 判断一个子字符串是否存在 | 2. | public int indexOf(String str) | 普通 | 从头开始找指定字符串的位置,查到了返回位置的开始索引,如果查不到返回-1 | 3. | public int lndexOf(String str,int fromIndex) | 普通 | 从指定位置开始查找子字符串位置 | 4. | public int lastlndexOf(String str) | 普通 | 由后向前查找子字符串位置 | 5. | public int lastlndexOf(String str,int fromIndex) | 普通 | 从指定位置由后向前查找子字符串 | 6. | public boolean startsWith(String prefix) | 普通 | 判断是否以指定字符串开头 | 7. | public boolean startsWith(String prefix,int toffset) | 普通 | 从指定位置开始判断是否以指定字符串开头 | 6. | public boolean endsWith(String suffix) | 普通 | 判断是否以指定字符串结尾 |
代码示例: 字符串查找,最好用最方便的就是contains()。
String str = "helloworld" ;
System.out.println(str.contains("world"));
代码示例: 使用indexOf()方法进行位置查找。
String str = "helloworld" ;
System.out.println(str.indexOf("world"));
System.out.println(str.indexOf("bit"));
if (str.indexOf("hello") != -1) {
System.out.println("可以查到指定字符串!");
}
注意事项: 使用indexOf()需要注意的是,如果内容重复,它只能返回查找的第一个位置。例如:
String str = "helloworld" ;
System.out.println(str.indexOf("l"));
System.out.println(str.indexOf("l",5));
System.out.println(str.lastIndexOf("l"));
代码示例: 判断开头和结尾
String str = "**@@helloworld!!" ;
System.out.println(str.startsWith("**"));
System.out.println(str.startsWith("@@",2));
System.out.println(str.endsWith("!!"));
字符串替换
使用一个指定的新的字符串替换掉已有的字符串数据,可用的方法如下:
NO. | 方法名称 | 类型 | 描述 |
---|
1. | public String replaceAll(String regex,String repalcement) | 普通 | 替换所有的指定内容 | 2. | public String replaceFirst(String regex,String repalcement) | 普通 | 替换首个内容 |
代码示例: 字符串的替换处理。
String str = "helloworld" ;
System.out.println(str.replaceAll("l", "_"));
System.out.println(str.replaceFirst("l", "_"));
注意事项: 由于字符串是不可变对象,替换不修改当前字符串, 而是产生一个新的字符串。
字符串拆分
可以将一个完整的字符串按照指定的分隔符划分为若干个子字符串,可用方法如下:
NO. | 方法名称 | 类型 | 描述 |
---|
1. | public String[] split(String regex) | 普通 | 将字符串全部拆分 | 2. | public String[] split(String regex,int limit) | 普通 | 将字符串部分拆分,该数组长度就是limit极限 |
代码示例: 实现字符串的拆分处理。
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);
}
另外,拆分是特别常用的操作。有些特殊字符作为分割符可能无法正确切分,需要加上转义。 代码示例: 拆分IP地址。
String str = "192.168.1.1" ;
String[] result = str.split("\\.") ;
for(String s: result) {
System.out.println(s);
}
代码示例: 多次拆分。
String str = "name=zhangsan&age=18" ;
String[] result = str.split("&") ;
for (int i = 0; i < result.length; i++) {
String[] temp = result[i].split("=") ;
System.out.println(temp[0]+" = "+temp[1]);
}
字符串截取
从一个完整的字符串之中截取出部分内容。可用方法如下:
NO. | 方法名称 | 类型 | 描述 |
---|
1. | public String substring(int beginIndex) | 普通 | 从指定索引截取到结尾 | 2. | public String substring(int beginIndex,int endIndex) | 普通 | 截取部分内容 |
代码示例: 观察字符串截取。
String str = "helloworld" ;
System.out.println(str.substring(5));
System.out.println(str.substring(0, 5));
注意事项:
- 索引从0开始。
- 注意前闭后开区间的写法, substring(0, 5) 表示包含 0 号下标的字符, 不包含 5 号下标。
其他操作方法
NO. | 方法名称 | 类型 | 描述 |
---|
1. | public String trim () | 普通 | 去掉字符串中的左右空格,保留中间空格 | 2. | public String toUpperCase() | 普通 | 字符串转大写 | 3. | public String toLowerCase() | 普通 | 字符串转小写 | 4. | public String concat(String str) | 普通 | 截取部分内容 | 5. | public int length() | 普通 | 取得字符串长度 | 6. | public boolean isEmpty | 普通 | 判断是否为空字符串,但不是null,而是长度为0 |
代码示例: 观察trim()方法的使用
String str = " hello world " ;
System.out.println("["+str+"]");
System.out.println("["+str.trim()+"]");
trim 会去掉字符串开头和结尾的空白字符(空格, 换行, 制表符等)。 代码示例: 大小写转化
String str = " hello%$$%@#$%world 哈哈哈 " ;
System.out.println(str.toUpperCase());
System.out.println(str.toLowerCase());
这两个方法只转换字母。 代码示例: 字符串length()
String str = " hello%$$%@#$%world 哈哈哈 " ;
System.out.println(str.length());
注意:数组长度使用数组名称.length属性,而String中使用的是length()方法。 好了,关于String类的相关知识点绝大部分都在这里了,如果对你有所帮助,别忘了点赞,有意见或者是想法,欢迎各位评论和私信,谢谢大家的支持!
|