🌸个人主页:https://blog.csdn.net/2301_80050796?spm=1000.2115.3001.5343
🏵️热门专栏:🍕 Collection与数据结构 (92平均质量分)https://blog.csdn.net/2301_80050796/category_12621348.html?spm=1001.2014.3001.5482
🧀线程与网络(96平均质量分) https://blog.csdn.net/2301_80050796/category_12643370.html?spm=1001.2014.3001.5482
🍭MySql数据库(93平均质量分)https://blog.csdn.net/2301_80050796/category_12629890.html?spm=1001.2014.3001.5482
🍬算法(97平均质量分)https://blog.csdn.net/2301_80050796/category_12676091.html?spm=1001.2014.3001.5482
感谢点赞与关注~~~
目录
- 1. String类
- 1.1 常用方法
- 1.1.1 字符串构造
- 1.1.2 字符串的常规方法
- 1.1.3 String对象的比较
- 1.1.4 字符串查找
- 1.1.5 转化
- 1.1.6 字符串替换
- 1.1.7 字符串的拆分
- 1.1.8 字符串截取
- 1.1.9 大小写转换
- 1.1.10 字符串的不可变性
- 1.1.11 字符串的修改
- 1.2 StringBuilder, StringBuffer与OJ例题
- 1.2.1 StringBuilder和StringBuffer
- 1.2.2 String类OJ
1. String类
1.1 常用方法
1.1.1 字符串构造
String类的常用构造方法只有以下三种
public class Main {public static void main(String[] args) {String s1 = "hello";//使用常量串进行构造String s2 = new String("hello");//创建String对象char[] array = {'h','e','l','l','o'};//使用字符数组String s3 = new String(array);System.out.println(s1);System.out.println(s2);System.out.println(s3);}
}
如要使用其他String方法,大家可以查看源码或者是jdk帮助手册
【注意】
- String类是引用类型的对象,内部其实并不存在字符串本身,我们可以查看String类的部分源码:
public final class Stringimplements java.io.Serializable, Comparable<String>, CharSequence,Constable, ConstantDesc {private final byte[] value;//用于存字符串的数组,字符存储在这个数组中形成了字符串private int hash; // 默认为0public String(String original) {//对以上两个成员变量进行构造this.value = original.value;this.hash = original.hash;}}
下面我们用一段代码来说明String是引用类型
public class Main {public static void main(String[] args) {String s1 = "hello";String s2 = new String("world");char[] array = {'h','i'};//创建了3个不同的引用对象String s3 = new String(array);System.out.println(s1);System.out.println(s2);System.out.println(s3);String s4 = s3;//s4和s3指向了同一个引用System.out.println(s4);}
}
字符串的引用方式其实和我们前面提到的数组很类似,因为底层就是数组实现的,我们下面通过画图说明
2. 在Java中,直接用双引号引起来的对象也是String类
System.out.println("hello");
1.1.2 字符串的常规方法
- 计算长度
public class Main {public static void main(String[] args) {String s1 = "hello";String s2 = new String("world");char[] array = {'h','i'};//创建了3个不同的引用对象String s3 = new String(array);System.out.println(s1);System.out.println(s2);System.out.println(s3);String s4 = s3;//s4和s3指向了同一个引用System.out.println(s4);System.out.println(s3.length());}
}
我们在这里需要注意的一点是,Java中的字符串不想c语言一样有以‘\0’这样的转义字符结尾的说法,有几个字母,字符串就是多长,在上面的运行结果我们可以看到,s3的长度为2
- 判空
public class Main {public static void main(String[] args) {String s1 = "hello";String s2 = new String("");//s2为空字符char[] array = {'h','i'};//创建了3个不同的引用对象String s3 = new String(array);System.out.println(s1);System.out.println(s2);System.out.println(s3);String s4 = s3;//s4和s3指向了同一个引用System.out.println(s4);System.out.println(s3.length());System.out.println(s2.isEmpty());}
}
从上述结果我们可以看出,isEmpty方法,如果字符串为空,返回ture,如果不为空,返回false
1.1.3 String对象的比较
字符串的比较也是常见的操作之一,比如:字符串的排序,Java中总共提供了4种方式
- == 比较是否引用同一个对象
注意:对于内置类型,比较的是变量中的值,对于引用类型比较的是引用中的地址
public class Main {public static void main(String[] args) {int a = 1;int b = 1;int c = 2;String s1 = "hello";String s2 = "hi";String s3 = "hello";String s4 = s1;System.out.println(a == b);//两个值相等System.out.println(a == c);//两个值不相等System.out.println(s1 == s2);//两个字符串不一样System.out.println(s3 == s1);//虽然两个字符串相同,但是指向的引用不同System.out.println(s1 == s4);//指向同一个引用}
}
需要注意的是,由于String是引用类型,虽然s1和s3相同,但是它们的引用不同,所以返回false
那么我们如果想要比较两个字符串是否相同,我们有办法吗,当然有,我们下面来介绍它
- boolean equals(Object anObject) 方法:按照字典序比较
字典序:按照字符的大小顺序
String重写了父类Object方法,Objec中默认使用“==”进行比较,String重写后按照如下规则比较:
public boolean equals(Object anObject) {// 1. 先检测this 和 anObject 是否为同一个对象比较,如果是返回trueif (this == anObject) {return true;}// 2. 检测anObject是否为String类型的对象,如果是继续比较,否则返回falseif (anObject instanceof String) {// 将anObject向下转型为String类型对象String anotherString = (String)anObject;int n = value.length;// 3. this和anObject两个字符串的长度是否相同,是继续比较,否则返回falseif (n == anotherString.value.length) {char v1[] = value;char v2[] = anotherString.value;int i = 0;// 4. 按照字典序,从前往后逐个字符进行比较while (n-- != 0) {if (v1[i] != v2[i])return false;i++;}return true;}}return false;
}
public class Main {public static void main(String[] args) {int a = 1;int b = 1;int c = 2;String s1 = "hello";String s2 = "hi";String s3 = "hello";String s4 = s1;System.out.println(a == b);//两个值相等System.out.println(a == c);//两个值不相等System.out.println(s1 == s2);//两个字符串不一样System.out.println(s3 == s1);//虽然两个字符串相同,但是指向的引用不同System.out.println(s1 == s4);//指向同一个引用System.out.println(s1.equals(s3));//两个字符串相同System.out.println(s2.equals(s1));//两个字符串不同}
}
上述代码我们可以看到,虽然s1和s3指向的是不同的引用,但是只要字符串相同,就返回true
3. int compareTo(String anotherString) 方法:按照字典序比较
与equals不同的是,equals返回的是boolean类型,而compareTo返回的是int类型。具体比较方式:
- 先按照字典次序大小比较,如果出现不等的字符,直接返回这两个字符的大小差值
- 如果前k个字符相等(k为两个字符长度最小值),返回值两个字符串长度差值
public class Main {public static void main(String[] args) {String s1 = "hello";String s2 = "hi";String s3 = "hello";String s4 = "helloaaa";System.out.println(s1 == s2);//两个字符串不一样System.out.println(s3 == s1);//虽然两个字符串相同,但是指向的引用不同System.out.println(s1 == s4);//指向同一个引用System.out.println(s1.equals(s3));//两个字符串相同System.out.println(s2.equals(s1));//两个字符串不同System.out.println(s1.compareTo(s2));//返回字典序差值System.out.println(s1.compareTo(s3));//相同返回0System.out.println(s1.compareTo(s4));//前几个字符相同,返回字符串长度只差}}
4. int compareToIgnoreCase(String str) 方法:与compareTo不同的是忽略大小写进行比较
public class Main {public static void main(String[] args) {String s1 = "Hello";String s2 = "hi";String s3 = "hello";String s4 = "helloaaa";System.out.println(s1 == s2);//两个字符串不一样System.out.println(s3 == s1);//虽然两个字符串相同,但是指向的引用不同System.out.println(s1 == s4);//指向同一个引用System.out.println(s1.equals(s3));//两个字符串相同System.out.println(s2.equals(s1));//两个字符串不同System.out.println(s1.compareTo(s2));//返回字典序差值System.out.println(s1.compareTo(s3));//相同返回0System.out.println(s1.compareTo(s4));//前几个字符相同,返回字符串长度只差System.out.println(s1.compareToIgnoreCase(s3));//忽略大小写进行比较}
}
1.1.4 字符串查找
字符串查找也是字符串中非常常见的操作,String类提供了如下查找方法:
public class Main {public static void main(String[] args) {String s = "aaabbbcccddd";System.out.println(s.charAt(2));System.out.println(s.indexOf('a'));System.out.println(s.indexOf('a',2));System.out.println(s.indexOf("bb"));System.out.println(s.indexOf("bb",4));System.out.println(s.lastIndexOf('d'));System.out.println(s.lastIndexOf('d',10));System.out.println(s.lastIndexOf("dd"));System.out.println(s.lastIndexOf("dd",10));}}
1.1.5 转化
- 数值和字符串转化
public class Main {public static void main(String[] args) {String s1 = String.valueOf(12);String s2 = String.valueOf(12.12);String s3 = String.valueOf(true);System.out.println(s1);System.out.println(s2);System.out.println(s3);int a = Integer.parseInt(s1);double b = Double.parseDouble(s2);System.out.println(s1);System.out.println(s2);}
}
2. 大小写转换
public class Main {public static void main(String[] args) {String s1 = "me";String s2 = "YOU";System.out.println(s1.toUpperCase());System.out.println(s2.toLowerCase());}
}
注意:这里在转化的是一个新的字符串,不是在原来的字符串上修改,因为字符串具有不可变性
3. 字符串转数组和字符串转数组
public class Main {public static void main(String[] args) {String s1 = "abcdefg";char[] array = s1.toCharArray();System.out.println(Arrays.toString(array));String s2 = new String(array);System.out.println(s2);}
}
4. 格式化字符串
public class Main {public static void main(String[] args) {String s = String.format("%d-%d-%d",2024,3,11);System.out.println(s);}
}
1.1.6 字符串替换
使用一个指定的新的字符串替换掉已有的字符串
方法 | 功能 |
---|---|
String replaceAll(String regex , String replacement) | 替换所有指定的内容 |
String replaceFrist (String regex , String replacement) | 替换第一个指定的内容 |
ublic class Main {public static void main(String[] args) {String str = "hello";System.out.println(str.replaceAll("l","_"));System.out.println(str.replaceFirst("l","_"));}}
注意事项:字符串是一个不可变对象,替换操作并没有修改当前字符串,而是产生了一个新的字符串
1.1.7 字符串的拆分
可将一个完整的字符串按照指定的分隔符划分为若干个子串
方法 | 功能 |
---|---|
String[] split (String regex) | 将字符串按指定分隔符全部拆分 |
String[] split (String regex , int limit) | 将字符串按指定分隔符,拆分为limit组 |
public static void main(String[] args) {String str2 = "hello world hello everyone";String[] a = str2.split(" ");System.out.println(Arrays.toString(a));String[] b = str2.split(" ",2);System.out.println(Arrays.toString(b));}
字符串的拆分是常见的操作,需要重点掌握,有时分隔符不可以被直接识别,需要加上转义符号,例如:
public class Main {public static void main(String[] args) {String str = "2004.04.05";String[] a = str.split("\\.");System.out.println(Arrays.toString(a));}}
注意事项:
- 字符“|”,“*”,“+”等字符都要加上转义符号,前面加上\
- 如果分割符号是“\”,就要写成“\\”
- 如果一个字符串中有多个分隔符,可以使用“|”作为连字符,下面是示例
public class Main {public static void main(String[] args) {String str = "2004-04.05"; String[] a = str.split("\\.|-");System.out.println(Arrays.toString(a));}
}
1.1.8 字符串截取
从一个完整的字符串中截取出部分内容
方法 | 功能 |
---|---|
String substring(int beginIndex) | 从指定索引内容截取到结尾 |
String substring(int beginIndex, int endIndex) | 截取部分内容 |
public class Main {public static void main(String[] args) {String str = "helloworld";System.out.println(str.substring(2) );System.out.println(str.substring(2,6) );}
}
下面我们观察第二个截取的字符串,我们发现截取时候,下标左闭右开的形式,并没有截取6下标这个位置的字符
1.1.9 大小写转换
方法 | 功能 |
---|---|
String toUpperCase() | 字符串转大写 |
String toLowerCase() | 字符串转大写 |
public class Main {public static void main(String[] args) {String str = "helloworld";String str1 = str.toUpperCase();System.out.println(str1);System.out.println(str1.toLowerCase());}
}
1.1.10 字符串的不可变性
String是一种不可变的对象,是因为:
- 在jdk的官方文档中,给出了说明
String instance is constant. (字符串是不可变的)Overwriting this field after construction will cause problems. Additionally, it is marked with Stable to trust the contents of the array. No other facility in JDK provides this functionality (yet). Stable is safe here, because value is never null.
- 下面我们从源码的角度来说明
private final byte[] value;
我们看见,上述储存字符的数组被private修饰,这说明在String类之外的其他类无法访问到value,所以在类外无法对该内容进行修改,又因为String类源码为只读文档,总不能对源码进行修改,在String类中直接修改,所以字符串便有了不可变性(不可修改性)
纠正:网上有的人说,不可修改是因为有String和value均有final的修饰,这种说法是错误的,数组用final修饰只是说明该不可以引用其他对象,但是其自身是可以修改的,类用final修饰只是说明该类不想被继承
那么我们要想对字符串进行修改,可以吗?当然可以,但是我们要尽量避免,因为效率非常低下,下面我们来说明
1.1.11 字符串的修改
public class Main {public static void main(String[] args) {String s = "hello";System.out.println(s + " " + "world");}}
我们不推荐字符串直接这样修改的原因是因为,由于字符串的不可变性,所以在中间会创建许多临时变量,会大大降低代码的效率
在这里我们可以看到每次创建的时候都会new一个新对象,每次都是一个不同的对象,我们应该避免对String的直接修改,使用StringBuilder和StringBuffer大大提高效率,我们下一篇介绍~
1.2 StringBuilder, StringBuffer与OJ例题
1.2.1 StringBuilder和StringBuffer
在上一篇博客我们提到,String类具有不可变性,在每次修改字符串的时候都是创建新的对象去修改,那么现在我们引入StringBuilder和StringBuffer类. 在用这两个类建立对象的时候, 字符串就是可变的, 可用类中的一些方法对字符串进行一系列操作,这两个大类的部分功能是相同的.这里介绍一些常用的方法,要是用到其他方法,大家可参考jdk帮助手册.
方法 | 功能 |
---|---|
StringBuff append(String str) | 在尾部添加字符串,相当于String的+= |
char charAt(int index) | 获取index位置的字符 |
int length() | 获取字符串的长度 |
int capacity() | 获取字符串在底层储存空间的大小 |
void ensureCapacity(int mininmumCapacity) | 扩容 |
int setCharAt(int index,char ch) | 将index位置的字符设置为ch |
int indexOf(String str) | 将index位置开始查找str第一次出现的位置 |
int indexOf(String str,int index) | 从index位置开始向后查找str,返回第一次出现的位置 |
int lastIndexOf(String str) | 从最后的位置开始查找指定字符串 |
int lastIndexOf(String str,int index) | 从index位置开始查找指定字符串 |
StringBuff insert(int index,String str) | 在index位置插入:八种基本类型,String类,Object类 |
StringBuffer delete(int start,int end) | 删除[start,end)区间的字符串 |
StirnBuffer deleteCharAt(int index) | 删除index位置的字符 |
StringBuffer replace(int start,int end,String str) | 替换[start,end)区间的字符串 |
String substring(int start) | 从start位置截取字符串,直到结尾,并以String类型返回 |
String substring(int start, int end) | 将[start,end)范围字符串截取下来,并以String类型返回 |
StringBuffer reverse() | 翻转字符串 |
String toString() | 将所有字符串以String的方式返回 |
下面进行上述方法的演示 |
public class Test {public static void main(String[] args) {StringBuilder sb1 = new StringBuilder("hello");sb1.append(" world");sb1.append(" !!!");//为sb1添加字符System.out.println(sb1);System.out.println(sb1.charAt(2));//获取2位置的字符System.out.println(sb1.length());sb1.setCharAt(0, 'H');//,第二个参数传入字符类型,字符用单引号引起来System.out.println(sb1);System.out.println(sb1.indexOf("ell"));//,传入字符串类型返回字符串第一次出现的位置System.out.println(sb1.indexOf("llo",1));//从指定位置开始查找字符串System.out.println(sb1.lastIndexOf("rld"));//从最后开始查找字符串System.out.println(sb1.lastIndexOf("wor",10));//从指定位置开始向前查找字符串sb1.insert(1,"uuu");//在指定位置插入指定字符串System.out.println(sb1);sb1.delete(1,4);//删除[1,4)位置的字符System.out.println(sb1);sb1.deleteCharAt(1);//删除指定位置的字符System.out.println(sb1);sb1.replace(2,5,"uuu");//把指定区间的字符串替换为指定字符串System.out.println(sb1);System.out.println(sb1.substring(4));//从指定位置开始截取字符串System.out.println(sb1.substring(4,7));//截取指定区间的字符串System.out.println(sb1.reverse());}
}
上述例子可以看出: String和StringBuilder最大的区别就是==String内容无法修改,而StringBuilder的内容可以修改,==频繁修改字符串考虑使用StringBuilder.
注意: String和StringBuilder类不可以直接转换,如果想要转换,可以采用如下方法:
- String变为StringBuilder: 利用StringBuiler的构造方法,或者append方法
- StringBuilder变为String: 利用toString方法
public class Test {public static void main(String[] args) {String s1 = "hello";StringBuilder sb2 = new StringBuilder(s1);//调用StringBuilder的构造方法,将String转换为StringBuilderStringBuilder sb3 = new StringBuilder();sb3.append(s1);System.out.println(sb2);System.out.println(sb3);String s2 = sb2.toString();//利用StringBuilder的toStirng方法,将sb转换为StringSystem.out.println(s2);}
}
面试题
- String类,StringBuilder和StringBuffer的区别
- String类的内容不可以修改,但是StringBuilder和StringBuffer可以修改
- StringBuffer采用同步处理,属于线程安全操作,而StringBuilder未采用同步处理,属于线程不安全操作
1.2.2 String类OJ
- 第一个只出现一次的字符
class Solution {public int firstUniqChar(String s) {int[] count = new int [256];//定义计数数组for(int i = 0;i < s.length();i++){count[s.charAt(i)]++;//存放每个字母出现的次数} for(int i = 0;i < s.length();i++){if(count[s.charAt(i)] == 1){//如果出现次数为1,则返回该下标return i;}}return -1;}
}
- 最后一个单词的长度
import java.util.Scanner;public class Main{public static void main(String[] args){// 循环输入Scanner sc = new Scanner(System.in);while(sc.hasNext()){// 获取一行单词String s = sc.nextLine();// 1. 找到最后一个空格// 2. 获取最后一个单词:从最后一个空格+1位置开始,一直截取到末尾// 3. 打印最后一个单词长度int len = s.substring(s.lastIndexOf(' ')+1, s.length()).length();System.out.println(len);}sc.close();}
}
- 判断字符串是否回文
class Solution {public static boolean isValidChar(char ch){if((ch >= 'a' && ch <= 'z') ||(ch >= '0' && ch <= '9')){return true;}return false;}public boolean isPalindrome(String s) {// 将大小写统一起来s = s.toLowerCase();int left = 0, right = s.length()-1;while(left < right){// 1. 从左侧找到一个有效的字符while(left < right && !isValidChar(s.charAt(left))){left++;}// 2. 从右侧找一个有效的字符while(left < right && !isValidChar(s.charAt(right))){right--;}if(s.charAt(left) != s.charAt(right)){return false;}else{left++;right--;}}return true;}
}