字符串是什么?
String 是标准的不可变类(immutable),对它的任何改动,其实就是创建了一个新对象,再把引用指向该对象;String 对象赋值之后就会在常量池中缓存,如果下次创建会判定常量池是否已经有缓存对象,如果有的话直接返回该引用给创建者。
String 类的修饰符
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {}
字符串创建的方式有哪些?
String 的两种创建方式:
String str = "Cat"; // 在【常量池】中直接创建。
String str = new String("Dog"); // 在【常量池】创建后,再在【堆】上创建。
字符串拼加的三种方式:
String s1 = "Cat" + "Cat"; // 常量池
String s2 = s1 += "Cat"; // 堆
String s3 = s1 + "Cat"; // 堆
JVM 也会对 String 进行特殊处理,以此来提供程序的运行效率,比如以下代码:
String str = "A" + "Cat";
String str2 = "ACat";
System.out.println(str == str2); // true
字符串截取的方式有哪些?
字符串截取使用 substring()
方法,使用如下:
String str = "abcdef";
System.out.println(str.substring(2)); // cdef (2~length)
System.out.println(str.substring(2,str.length())); // cde (2~length-1)
字符串格式化的方式有哪些?
字符串格式化可以让代码更简洁更直观,如果使用 “+” 号拼接的话很容易出错,这个时候 静态方法 String.format()
就派上用场了,代码如下:
String str = String.format("我叫%s,今年%d岁,喜欢%s", "周杰伦", 40, "唱歌");
转换符说明列表:
转换符 | 说明 |
---|---|
%s | 字符串类型 |
%d | 整数类型(十进制) |
%c | 字符类型 |
%b | 布尔类型 |
%x | 整数类型(十六进制) |
%o | 整数类型(八进制) |
%f | 浮点类型 |
%e | 指数类型 |
%% | 百分比类型 |
%n | 换行符 |
字符串对比的方式有哪些?
根据前面的知识我们知道,使用 String 和 new String 声明的对象是不同的,那有没有简单的方法,可以忽略它们的创建方式(有没有 new)而只对比它们的值是否相同呢?
答案是肯定的,使用 equals()
方法可以直接比较字符串的值,代码如下:
String s1 = "A" + "Cat"; // 常量池
String s2 = "A"; // 常量池
s2 += "Cat"; // 堆
String s3 = "ACat"; // 常量池
System.out.println(s1.equals(s2));
System.out.println(s1.equals(s3));
System.out.println(s2.equals(s3));
以上使用 equals 对比的结果都为 true
。
如果要忽略字符串的大小写对比值可以使用 equalsIgnoreCase()
。
字符串的方法有哪些?
"a".compareTo("able to") // 首字母相同,比较长度:-6
"a".compareTo("z") // 首字母不相同,比较首字母Unicode值:-25
byte[] bytes = "cat".getBytes();
char[] chars = "cat".toCharArray();
char[] chars = {'A', 'B', 'C', 'D', 'E', 'F'};
String.valueOf(chars); // ABCDEF
String.valueOf(chars, 0, 3); // ABC
"ABCDEF".substring(1) // BCDEF
"ABCDEF".substring(1, 3) // BC
"It's a".concat(" orange cat") // It's a orange cat
"cat".replace("cat", "dog") // dog
" dog ".trim() // dog
String[] strings = "a`b`c`d`e`t".split("`");
System.out.println(strings[2]+strings[0]+strings[5]); // cat
"cat".charAt(1) // a
"cat".codePointAt(1) // 97
"cat cat".indexOf('c') // 0
"cat cat".lastIndexOf('c') // 4
"cat".hashCode() // 98262
"cat".length() // 3
"cat".contains(new String("a")) // true
"cat".contains(new StringBuilder("a")) //true
"cat".startsWith("c") // true
"cat".endsWith("t") // true
"".isEmpty() // true
String.format(" %s,%g块,你欠了%d年了", "王南", 1.25, 10)
String.format("%n %tc", new Date())
字符串:
1.String 属于基础数据类型吗?
答:String 不是基础数据类型,它是从堆上分配来的。基础数据类型有 8 个,分别为:boolean、byte、short、int、long、float、double、char。
2.什么是字符串常量池?
答:字符串常量池是存储在 Java 堆内存中的字符串池,是为防止每次新建字符串带的时间和空间消耗的一种解决方案。在创建字符串时 JVM 会首先检查字符串常量池,如果字符串已经存在池中,就返回池中的实例引用,如果字符串不在池中,就会实例化一个字符串放到池中并把当前引用指向该字符串。
3.String s=new String(“cat”) 创建了几个对象?
答:创建了一个或两个对象。字符串先出现在常量池,然后才出现在堆。
- 如果常量池中已经有了字符串 “cat”,就只会创建一个堆中的对象 ”cat“;
- 如果常量池中没有字符串 ”cat“,则先会在常量池中创建一个对象 ”cat“,再创建一个堆中的对象。
4. String 不可变性都有哪些好处?
答:不可变的好处如下。
- 多线程安全,因为字符串是不可变的,所以同一个字符串实例可以被多个线程共享,保证了多线程的安全性;
- 适合做缓存的 key,因为字符串是不可变的,所以在它创建的时候哈希值就被缓存了,不需要重新计算速度更快,所以字符串很适合作缓存的中的 key。
- 只有当字符串是不可变的,字符串常量池才能实现,字符串池的实现可以在运行时节约很多堆空间,因为不同的字符串变量都指向池中的同一个字符串;
- 可以避免一些安全漏洞,比如在 Socket 编程中,主机名和端口都是以字符串的形式传入,因为字符串是不可变的,所以它的值是不可改变的,否则黑客们可以钻到空子,改变字符串指向的对象的值,造成安全漏洞;
5. String 是否可以被继承?为什么?
答:String 不能被继承。因为 String 被声明为 final(最终类),所以不能被继承,源码如下(JDK 8)。
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
}
6.不可变对象 是什么?
不可变对象(Immutable Object):对象一旦被创建后,对象所有的状态及属性在其生命周期内不会发生任何变化。例如,String类和包装类。
以 String 类和 StringBuffer 类为例:
String s = "123";
s.replace("123", "999");
System.out.println(s);
StringBuffer sb = new StringBuffer("123");
sb.replace(0,3,"999");
System.out.println(sb);
输出结果:
123
999
我们可以看出来,String 执行 replace 方法后,String 对象本身没有发生改变;StringBuffer 执行 replace 方法后,StringBuffer 对象发生了改变。
字符串方法:
1.以下可以正确获取字符串长度的是?
A:str.length
B:str.size
C:str.length()
D:str.size()
答:C
题目解析:字符串没有 length 属性,只有 length()
方法。
2.以下代码输出的结果是?
String str = "cat";
str.substring(0,1);
System.out.println(str);
A:c
B:ca
C:cat
答:C
题目解析:因为 String 的 substring() 方法不会修改原字符串内容,所以结果还是 cat。
3.以下使用 substring 执行的结果什么?
String str = "abcdef";
System.out.println(str.substring(3, 3));
答:""(空)
4.判定字符串是否为空,有几种方式?
答:常用的方式有以下两种。
- str.equals("")
- str.length()==0
5.String 对象的 intern() 有什么作用?
答:intern() 方法用于查找常量池中是否存在该字符值,如果常量池中不存在则先在常量池中创建,如果已经存在则直接返回。
示例代码:
String s1 = "laowang";
String s2 = s1.intern();
System.out.println(s1 == s2); // true
比较:
1."==" 和 equals 的区别是什么?
① “==” 解读
- 基本数据类型进行值比较;
- 引用类型进行引用比较。
② equals 解读
- equals 本质上就是
==
- 不过 String 和 Integer 等类重写了 equals 方法,把引用类型的的引用比较改成了值比较。
看下面的代码就明白了:
Cat c1 = new Cat("喵~");
Cat c2 = new Cat("喵~");
System.out.println(c1.equals(c2)); // false(Object类默认的equals方法,本质是==)
String s1 = new String("喵~");
String s2 = new String("喵~");
System.out.println(s1.equals(s2)); // true(String类重写的equals方法)
2.以下字符串对比的结果是什么?
String s1 = "A" + "Cat";
String s2 = "A";
s2 += "Cat";
String s3 = "ACat";
System.out.println(s1 == s2);
System.out.println(s1 == s3);
System.out.println(s2 == s3);
答:false true false
题目解析:String s1 = “hi,” + “lao” + “wang” 代码会被 JVM 优化为:String s1 = “hi,laowang”,这样就和 s3 完全相同,s1 创建的时候会把字符 “hi,laowang” 放入常量池,s3 创建的时候,常量池中已经存在对应的缓存,会直接把引用返回给 s3,所以 s1==s3
就为 true,而 s2 使用了 +=
其引用地址就和其他两个不同。
StringBuffer:
1.以下 String 传值修改后执行的结果是什么?
public static void main(String[] args) {
String str = new String("ACat");
change(str);
System.out.println(str);
}
public static void change(String str) {
str = "ADog";
}
答:ACat ,如果输出change方法返回的变量str会得到“ADog”。
2.以下 StringBuffer 传值修改后的执行结果是什么?
public static void main(String[] args) {
StringBuffer sf = new StringBuffer("A");
change(sf);
System.out.println(sf);
}
public static void change(StringBuffer sf){
sf.append("laowang");
}
答:ACat
题目解析:String 为不可变类型,在方法内对 String 修改的时候,相当修改传递过来的是一个 String 副本,所以 String 本身的值是不会被修改的;而 StringBuffer 为可变类型,参数传递过来的是对象的引用,对其修改它本身就会发生改变。
3.String、StringBuffer、StringBuilder 的区别?
答:字符串相关类型主要有这三种:String、StringBuffer、StringBuilder,其中 StringBuffer、StringBuilder 都是可以变的字符串类型,StringBuffer 在字符串拼接时使用 synchronized 来保障线程安全,因此在多线程字符串拼接中推荐使用 StringBuffer。
以下是 String、StringBuffer、StringBuilder 的区别:
- String
- 不可变对象;
- 使用 不可变对象 来保证线程安全;
- 性能最低,每次修改都创建新对象;
- 重写了 equals 方法。
- StringBuffer
- 可变对象;
- 使用 synchronized 来保证线程安全;
- 性能优于 String,但不如 StringBuilder,因为使用了同步。
- StringBuilder
- 可变对象;
- 非线程安全;
- 性能最高。