在笔试面试的算法题中,String是重要考点,在实际项目中,字符串也是十分常用的数据类型,因此要熟练掌握对String的运用。
使用
初识
如下代码:
- str是一个字符串常量,与C/C++不同的是,Java并没有规定字符串末尾要包含’\0’。
- String重写了toString方法,因此其输出的不是str的地址,而是str字符串的内容。
String str = "hello";
System.out.println(str);
常见new创建String方式
- 每次new一个对象的时候,都会在堆区申请一段空间。
//创建一个空的
String str1 = new String();
System.out.println(str2);// 输出为空
//创建一个指定字符串的
String str2 = new String("head");
System.out.println(str3);// 输出为head
//创建一个由字符数组指定字符串的
char[] array = {'h','e','a','d'};
String str4 = new String(array);
//指定需要字符数组中的哪些字符
System.out.println(str4);// 输出为head
String str5 = new String(array,1,2);// 第二个参数为偏移量,第三个参数为字符的数量
System.out.println(str5);// 输出为ea
“”(空)和null
- str6的意思为str6指向了一个对象,但里面的字符为空,因此可以正常调用String的方法
- str7的意思为不指向任何对象,对空指针执行方法调用时会报错
String str6 = ""; //指向的对象为空(里面的字符为空)
String str7 = null;//不指向任何对象
System.out.println(str6.length()); // 0
System.out.println(str6.isEmpty()); // true
System.out.println(str7.length()); // 报错
System.out.println(str7.isEmpty());// 报错
补充:字符串常量可以直接调用String的方法,如下所示
System.out.println("hello".length());
//结果为5
查找
char charAt(int index);
- 返回index位置上字符,如果index为负数或者越界,抛出IndexOutOfBoundsException异常。
int indexOf();
String str = "hello,iamhead";
int ret = str.indexOf('i');
System.out.println(ret); // 6
int ret2 = str.indexOf('i',3);//从指定位置开始查找
System.out.println(ret2); // 6
int ret3 = str.indexOf("am");
System.out.println(ret3); // 7
int ret4 = str.indexOf("am",2);//从指定位置开始查找
System.out.println(ret4); // 7
int lastIndexOf();
倘若逆序查找时还指定了位置,是指从顺序(从左到右)指定位置后,从指定位置逆序(从右向左)进行查询,获取到的位置是顺序(从左到右)得到的,而非逆序!!
String str = "hello,iamhead";
int ret = str.lastIndexOf('i');
System.out.println(ret); // 6
int ret2 = str.lastIndexOf('i',3);//从指定位置开始向前查找
System.out.println(ret2); // -1
int ret3 = str.lastIndexOf("am");
System.out.println(ret3); // 7
int ret4 = str.lastIndexOf("am",11);//从指定位置开始向前查找
System.out.println(ret4); // 7
比较
== —> 等号
对于八大基本数据类型,== 比较的是变量中的值,对于引用数据类型,== 比较的是引用中的地址,即是否引用了同一对象。
注:Java八大基本数据类型分别为byte、short、int、long、float、double、boolean、char,除此之外的数据均为引用数据类型,如本文的String。
int a = 10;
int b = 20;
int c = 10;
// 对于基本类型变量,==比较两个变量中存储的值是否相同
System.out.println(a == b); // false
System.out.println(a == c); // true
// 对于引用类型变量,==比较两个引用变量引用的是否为同一个对象
String s1 = new String("hello");
String s2 = new String("hello");
String s3 = new String("world");
String s4 = s1;
System.out.println(s1 == s2); // false,因为比较的是地址
System.out.println(s2 == s3); // false,地址和值都不相同
System.out.println(s1 == s4); // true,因为地址相同
== 比较的返回类型为boolean,不是false就是true。
equals —> boolean equals(Object object);
已知Object是所有类的超类,其中包含了euqals方法,而String类重写了equals方法,因此若要比较两个字符串的值是否相等,就使用equals方法。String中的equals基于以下规则:
- 先检测要比较的两个对象是否为同一个对象,如果是则直接返回true,如果不是则返回。
- 检测object是否为String类型的对象,如果是则继续比较,如果不是则返回false。
- this和object两个字符串的长度是否相同,是继续比较,否则返回false。
- 按照字典序,从前往后逐个字符进行比较。
// 示例代码如下,即为String对equals的重写
public boolean equals(Object object) {
if (this == object) {
return true;
}
if (object instanceof String) {
// 将anObject向下转型为String类型对象
String anotherString = (String)object;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
}
while (n-- != 0) {
if (v1[i] != v2[i]) return false;
i++;
}
return true;
}
}
return false;
重要面试题:==和equals都是干嘛的,异同?
Java | “==”和 equals() 的异同
int compareTo(String s)
- 将s1和s2两个字符串按照字典序进行比较,如果s1大于s2,则返回正数,如果s1小于s2,返回负数,等于返回0。
String s1 = new String("hello");
String s2 = new String("hello");
System.out.println(s1.compareTo(s2)); //返回 0
int compareToIgnoreCase(String s)
- 使用办法同上,唯一的区别是忽略了大小写。
转换
数值转字符串
String.valueOf()可以将许多对象转换为String。
String s = String.valueOf(19.9);
System.out.println(s);
字符串转数字
int data1 = Integer.parseInt("198");
double data2 = Double.parseDouble("19.9");
大小写转换
String str7 = "hello";
String str8 = str7.toUpperCase();
String str9 = str8.toLowerCase();
//注:不是在原有的基础上进行转化,而是转换后变成新的对象,因为Java中String不可修改
字符串转数组、数组转字符串
//字符串转数组
String str1 = "hello";
char[] array1 = str1.toCharArray();
//数组转字符串
char[] array2 = {'a','b','c'};
String str2 = new String(array3);
替换
replace()、replaceAll()
- replace()既支持字符的替换,也支持字符串的替换。
- replaceAll()仅支持字符串的替换,但是它还支持给予规则表达式的替换,如replaceAll(“\d”, “*”)把一个字符串所有的数字字符都换成星号。
- 因为字符串是不可变对象,所以字符串一经创建就无法改变了。因此替换时并==不是在原有字符串上进行替换==,而是产生出一个新的字符串。
String str = "hello,iamhellohello";
String ret1 = str.replace("hello","head");// str.replaceAll("hello","head");
System.out.println(ret1); // head,iamheadhead
String ret2 = str.replace('h','a');
System.out.println(ret2); // aead,iamaeadaead
//规则表达式的替换
String str2 = "1head2head3head";
String ret3 = str2.replaceAll("\\d","*");
System.out.println(ret3); // *head*head*head
replaceFirst()
- 仅对第一个匹配的字符串进行替换,随后结束,使用可以参考replace(),但是不支持单个字符的替换(其实写成replaceFirst(“x”,“y”)类似的样子就行了,没有必要纠结于替换单个字符嘞)。
拆分
- String[] split(String regex) :将字符串全部拆分。
- String[] split(String regex, int limit):将字符串以指定的格式拆为limt组。
String str = "hello head world";
String[] ret = str.split(" ");//以空格进行拆分:[hello,head,world]
String[] ret2 = str.split(" ", 2);//分为两组 : [hello,head world]
截取
- String substring(int beginIndex):从指定索引截取到结尾
- String substring(int beginIndex, int endIndex):截取部分内容,注意前闭后开
String str = "abcdefg";
String ret = str.substring(0,3); // abc 注意左闭右开
String ret2 = str.substring(3); // defg
StringBuilder和StringBuffer
比较
-
由于String的不可更改特性,为了方便字符串的修改(主要是为了加快修改的时间效率),Java中又提供StringBuilder和StringBuffer类。
-
和 String 类不同的是,StringBuffer和StringBuilder类的对象能够被多次的修改,并且不会产生新的对象。
-
这两个类大部分功能是相同的,但是StringBuilder是线程不安全的,它的方法不安全,不能够同步访问。而StringBuffer是线程安全的。
-
然而,StringBuilder相较于StringBuffer执行速度更快,因此在多数情况下仍然可以使用StringBuilder。
-
这是StringBuilder的部分源码
-
-
这是StringBuffer的部分源码
-
-
通过观察我们可以发现,StringBuilder的方法均没有添加synchronized,而StringBuffer的方法均添加了synchronized来保证线程安全。
来点小题
(以下均不考虑常量池的存在)
String str = new String(“ab”); 会创建多少个对象?
- "ab"是一个字符串字面量,会在编译时生成一个字符串对象。
- new String(“ab”) 会创建一个新的字符串对象,并且这个对象的内容是 “ab”。
- 共创建两个对象。
String str = new String(“a”) + new String(“b”); 会创建多少个对象?
- "a"和"b"是两个字符串字面量,会在编译时生成两个字符串对象。
- new String(“a”) 会创建一个新的字符串对象,并且这个对象的内容是 “a”。
- new String(“b”) 会创建一个新的字符串对象,并且这个对象的内容是 “b”。
- new String(“a”) + new String(“b”) 会进行字符串拼接,字符串拼接会生成一个新的字符串对象,其内容是拼接后的结果 “ab”。这个新对象是通过 StringBuilder 或者 StringBuffer拼接后再通过 toString()方法生成的。
- 共创建五个对象。