初始Java篇(JavaSE基础语法)(8)认识String类(下)
找往期文章包括但不限于本期文章中不懂的知识点:
个人主页:我要学编程(ಥ_ಥ)-CSDN博客
所属专栏:JavaSE
接上文 初始Java篇(JavaSE基础语法)(8)认识String类(上)-CSDN博客
目录
字符串截取
其他操作方法
字符串的不可变性
字符串修改
StringBuilder和StringBuffer
刷题练习
387.字符串中第一个唯一字符
HJ1 字符串最后一个单词的长度
125.验证回文串
字符串截取
从一个完整的字符串之中截取出部分内容。可用方法如下:
方法 | 功能 |
String substring(int beginIndex) | 从指定索引的位置截取到结尾 |
String substring(int beginIndex, int endIndex) | 截取部分内容(从beginIndex位置到endIndex位置) |
注意:
1. 索引从0下标开始。
2. 注意前闭后开区间的写法。substring(0, 5) 表示从0下标开始截取,一直到4下标,即[0,5) 。
示例:
其他操作方法
方法 | 功能 |
String trim() | 去掉字符串中的左右空格,保留中间空格 |
字符串的不可变性
String是一种不可变对象。字符串中的内容是不可改变。字符串不可被修改。下面就是String的源码:
这里可能会有小伙伴会说是因为value数组被 final 修饰了,因此这个数组的内容就不可变了,也就是说这个数组里面存放的字符就不可修改了。
其实不然,这个final修饰的数组,只是让这个数组名,也就是数组对象的引用不能被修改了,并不是说这个数组的内容不能被修改了。
final修饰引用类型表明该引用变量不能去引用其他对象了,但是其引用对象中的内容是可以修改的。
那既然如此,是什么不能让我们修改String的内容呢?其实是 private 修饰的整个数组。如果我们想要修改这个数组内容首先得拿到这个数组吧,但是 private 修饰就直接导致我们拿不到这个数组。因此我们就根本没有机会去修改这个数组。这也就是 String 不可修改的原因。
那可能小伙伴又有疑惑了:既然不能修改String,那前面我们学习的拆分字符串,字符串大小写转换……这些不都改变了字符串本身吗?这只是我们看到的表面现象。所有涉及到可能修改字符串内容的操作都是创建一个新对象,改变的是新对象 。这样我们每一次涉及修改字符串的操作都是创建一个新的对象。
为什么 String 要设计成不可变的?(不可变对象的好处是什么?) (目前简单了解)
1. 方便实现字符串对象池. 如果 String 可变, 那么对象池就需要考虑写时拷贝的问题了。
2. 不可变对象是线程安全的。
3. 不可变对象更方便缓存 hash code, 作为 key 时可以更高效的保存到 HashMap 中. 那如果想要修改字符串中内容,该如何操作呢?
字符串修改
注意:尽量避免直接对String类型对象进行修改,因为String类是不能修改的,所有的修改都会创建新对象,效率非常低下。例如:
public class Test { public static void main(String[] args) { String s = "hello"; System.out.println(s); s += " world"; System.out.println(s); } }
那么怎样才能使效率变得更高呢?
借助StringBuffer 和 StringBuilder。下面就来介绍一下。
StringBuilder和StringBuffer
由于String的不可更改特性,为了方便字符串的修改,Java中又提供StringBuilder和StringBuffer类。这两个类大部分功能是相同的。
看到这里,可能有小伙伴要问了:String不是不可修改吗?怎么又说为了方便修改提供了这两个类呢?这不就前后矛盾了吗?
不不不,并不矛盾。正是因为String不可修改,所以当我们想要修改String时,做不到。那么java开发人员也想到了这种情况,因此就设计出了StringBuffer和StringBuilder这两个类,来让我们想要修改时,可以做到。做法是通过StringBuffer和StringBuilder,修改传入的字符串,再把修改后的结果转换为字符串。这就是字符串的修改方式。
这里介绍 StringBuffer常用的一些方法。
StringBuff append(String str) 在尾部追加,相当于String的+=,可以追加:boolean、char、char[]、 double、float、int、long、Object、String、StringBuff的变量。
public class Test { public static void main(String[] args) { StringBuffer sb = new StringBuffer("Hello"); sb.append(" world"); System.out.println(sb); } }
char charAt(int index) 获取index位置的字符。
public class Test { public static void main(String[] args) { StringBuffer sb = new StringBuffer("Hello"); System.out.println(sb.charAt(0)); } }
int length() 获取字符串的长度 。
public class Test { public static void main(String[] args) { StringBuffer sb = new StringBuffer("Hello"); System.out.println(sb.length()); } }
int capacity() 获取底层保存字符串空间总的大小 。
这里的底层是指用 c/c++ 保存时的大小。不必关心,
void ensureCapacity(int mininmumCapacity) 扩容。
public class Test { public static void main(String[] args) { StringBuffer sb = new StringBuffer("Hello"); // 把空间扩充到原来的两倍 sb.ensureCapacity(sb.length()*2); } }
void setCharAt(int index, char ch) 将index位置的字符设置为ch 。
public class Test { public static void main(String[] args) { StringBuffer sb = new StringBuffer("Hello"); sb.setCharAt(4, 'O'); System.out.println(sb); } }
int indexOf(String str) 返回str第一次出现的位置。
public class Test { public static void main(String[] args) { StringBuffer sb = new StringBuffer("Hello"); // 这里要是字符串 System.out.println(sb.indexOf("o")); } }
int indexOf(String str, int fromIndex) 从fromIndex位置开始查找str第一次出现的位置 。
public class Test { public static void main(String[] args) { StringBuffer sb = new StringBuffer("Hello"); System.out.println(sb.indexOf("o", 2)); } }
StringBuff insert(int offset, String str) 在offset位置之前插入:八种基类类型 & String类型 & Object类型数据 。
public class Test { public static void main(String[] args) { StringBuffer sb = new StringBuffer("Hello"); // 在4位置之前,插入world System.out.println(sb.insert(4, " world")); } }
StringBuffer deleteCharAt(int index) 删除index位置字符。
public class Test { public static void main(String[] args) { StringBuffer sb = new StringBuffer("Hello"); System.out.println(sb.deleteCharAt(0)); } }
StringBuffer delete(int start, int end) 删除[start, end)区间内的字符。
public class Test { public static void main(String[] args) { StringBuffer sb = new StringBuffer("Hello"); // 删除的范围是[0,4) System.out.println(sb.delete(0, 4)); } }
StringBuffer replace(int start, int end, String str) 将[start, end)位置的字符替换为str。
public class Test { public static void main(String[] args) { StringBuffer sb = new StringBuffer("Hello"); // 把[0,4)内的字符串内容替换成空,相当于删除操作 System.out.println(sb.replace(0, 4, "")); } }
String substring(int start) 从start开始一直到末尾的字符以String的方式返回。
public class Test { public static void main(String[] args) { StringBuffer sb = new StringBuffer("Hello"); System.out.println(sb.substring(0)); } }
String substring(int start,int end) 将[start, end)范围内的字符以String的方式返回。
public class Test { public static void main(String[] args) { StringBuffer sb = new StringBuffer("Hello"); // 返回的是[0,4)之间的字符串 System.out.println(sb.substring(0, 4)); } }
StringBuffer reverse() 反转字符串。
public class Test { public static void main(String[] args) { StringBuffer sb = new StringBuffer("Hello"); System.out.println(sb.reverse()); } }
String toString() 将所有字符按照String的方式返回。
public class Test { public static void main(String[] args) { StringBuffer sb = new StringBuffer("Hello"); System.out.println(sb.toString()); } }
StringBuilder 的用法也和上面的类似。
StringBuffer 、StringBuilder 和String的区别:显而易见,前面两个是可以直接修改的,而String不行,只能通过创建出新的对象。而这个修改可变不可变主要是看是否可以在原对象上进行修改。其次,两者的方法也有些差异。
刷题练习
387.字符串中第一个唯一字符
给定一个字符串 s ,找到 它的第一个不重复的字符,并返回它的索引 。如果不存在,则返回 -1 。
示例 1:
输入: s = "leetcode" 输出: 0
示例 2:
输入: s = "loveleetcode" 输出: 2
示例 3:
输入: s = "aabb" 输出: -1
提示:
- 1