1、下面程序的运行结果是什么?
String s1 = "java";
StringBuffer s2 = new StringBuffer(s1);
System.out.println(s1.equals(s2)); //1
StringBuffer s3 = new StringBuffer("java");
System.out.println(s3.equals("java")); //2
System.out.println(s3.toString().equals(s1)); //3
- 注释 1 打印为 false,主要考察 String 的 equals 方法,String 源码中 equals 方法有对参数进行 instance of String 判断语句,
StringBuffer 的祖先为 CharSequence,所以不相等。 - 注释 2 打印为 false,因为 StringBuffer 没有重写 Object 的 equals 方法,所以 Object 的 equals 方法实现是 == 判断,故为 false。
- 注释 3 打印为 true,因为 Object 的 toString 方法返回为 String 类型,String 重写了 equals 方法实现为值比较。
2、String、StringBuffer、StringBuilder 的区别是什么?
- String 是字符串常量,StringBuffer 和 StringBuilder 都是字符串变量,后两者的字符内容可变,而前者创建后内容不可变。
- StringBuffer是线程安全的,而StringBuilder是非线程安全的,线程安全会带来额外的系统开销,所以StringBuilder的效率比 StringBuffer高;
- String的每次修改操作都是在内存中重新 new 一个对象出来,而StringBuffer、StringBuilder则不用,且提供了一定的缓存功能,默认16个字节数组的大小,超过默认的数组长度时扩容为原来字节数组的长度 * 2 + 2,所以使用StringBuffer和StringBuilder 时可以适当考虑下初始化大小,以便通过减少扩容次数来提高代码的高效性。
StringBuffer的扩容机制?
3、怎样将 GB2312 编码的字符串转换为 ISO-8859-1 编码的字符串?
如下代码即可实现。2处打印为乱码,因为编码字符集和解码字符集不一样导致的。
String s1 = "哈喽";
System.out.println(s1); //1,哈喽
String s2 = new String(s1.getBytes("GB2312"),"ISO-8859-1"); //2,¹þà¶
System.out.println(s2);
4、String 为什么是不可变的?
- String 不可变是因为在 JDK 中 String 类被声明为一个 final 类,且类内部的 value 字节数组也是 final 的。
- 只有当字符串是不可变时字符串池才有可能实现,字符串池的实现可以在运行时节约很多 heap 空间,因为不同的字符串变量都指向池中的同一个字符串。
- 因为字符串是不可变的,所以是多线程安全的,同一个字符串实例可以被多个线程共享,这样便不用因为线程安全问题而使用同步,字符串自己便是线程安全的。
- 因为字符串是不可变的所以在它创建的时候 hashcode 就被缓存了,不变性也保证了 hash 码的唯一性,不需要重新计算,这就使得字符串很适合作为 Map 的键,字符串的处理速度要快过其它的键对象,这就是 HashMap 中的键往往都使用字符串的原因。
5、说说 String str = "hello world"; 和 String str = new String("hello world"); 的区别?
- 在 java 的 class 文件中有专门的部分用来存储编译期间生成的字面常量和符号引用,这部分叫做 class 文件常量池,在运行期间对应着方法区的运行时常量池,所以 String str = "hello world"; 在编译期间生成了 字面常量和符号引用,运行期间字面常量 "hello world" 被存储在运行时常量池(只保存了一份)。
- 通过 new 关键字来生成对象是在堆区进行的,堆区进行对象生成的过程是不会去检测该对象是否已经存在的,所以通过 new 来创建的一定是不同的对象,即使字符串的内容是相同的。
6、下面程序的运行结果是什么?
String s1 = "java";
System.out.println("java" == s1.substring(0)); //1
System.out.println("ava" == s1.substring(1)); //2
substring方法的源码如下
根据传入的开始索引是否为0,来决定是返回当前对象
还是新对象
。
public String substring(int beginIndex) {
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
int subLen = value.length - beginIndex;
if (subLen < 0) {
throw new StringIndexOutOfBoundsException(subLen);
}
return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
}
- 注释1结果是true,因为传入0,所以返回当前对象,==比较的是地址,同一个对象的地址相等。
- 注释2结果是false,因为传入1,返回新对象,比较地址不相等。
7、下面程序的运行结果是什么?
String s1 = "JAVA";
String s2 = "java".toUpperCase();
System.out.println(s1==s2); //1
System.out.println(s1.equals(s2)); //2
- 注释1结果为false,toUpperCase方法返回新字符串对象。
- 注释2结果为true,equals方法比较的是值。
8、下面程序的运行结果是什么?
String s1 = "JavaEE";
String s2 = "EE";
String s3 = "Java"+s2;
System.out.println(s1==s2); //1
- 注释1结果为false。字符串常量与字符串变量连接时得到的新字符串不再保存在常量池中,而是在堆内存中新建一个String对象来存放,很明显常量池中要求的存放的是常量,有String类型变量当然不能存在常量池中了。
9、下面程序的运行结果是什么?
String s1 = "javaEE";
final String s2 = "EE";
String s3 = "java"+s2;
System.out.println(s1 == s3); //1
- 注释1的结果为true。此处是字符串常量与String类型常量连接,得到的新字符串依然保存在常量池中。
因为对 final 变量的访问在编译期间都会直接被替代为真实的值。
10、下面程序的运行结果是什么?
private static String getMsg() {
return "world";
}
String s1 = "helloworld";
final String s2 = getMsg();
String s3 = "hello" + s2;
System.out.println(s1 == s3); //1
- final String s2 = getMsg() 其实与 final String s2 = new String(“world”) 是一样的,也就是说 return “world” 会在堆中创建一个 String 对象保存 ”world”。
- 虽然 s2被定义成了 final,但不代表是常量,因为虽然将 s2 用 final 修饰了,但是由于其赋值是通过方法调用返回的,那么它的值只能在运行期间确定,因此指向的不是同一个对象,所以可见并非定义为 final 的就保存在常量池中,很明显此处 s2 常量引用的 String 对象保存在堆中,因为 getMsg() 得到的 String 已经保存在堆中了,final 的 String 引用并不会改变 String 已经保存在堆中这个事实;对于 s2 换成 final String s2 = new String("world"); 一样会返回 false,原因同理。
11、下面程序的运行结果是什么?
String s1 = "javaEE";
String s2 = "java";
String s3 = "EE";
String s4 = s2+s3;
System.out.println(s4 == s1); //1、false
System.out.println(s4.intern() == s1); //2、true
System.out.println(s4 == s1.intern()); //3、false
System.out.println(s4.intern() == s1.intern()); //4、true
- 注释1结果是false。因为s4指向堆内存中的字符串对象,而s1指向常量池中的字符串常量,两者的地址不相等。
12、为什么针对安全保密高的信息,char[] 比 String 更好?
- 因为 String 是不可变的,一旦创建就不能更改,直到垃圾收集器将它回收才能消失,即使我们修改了原先的变量,实际上也是在内存中新建一个对象,原数据还是保留在内存中等待回收。
- 而字符数组 char[] 中的元素是可以更改的,也就是说像密码等保密信息用完之后我们可以马上修改它的值而不留痕迹,从而相对于 String 有更好的安全性。
13、用 java 代码实现字符串的反转?
1、使用 JDK 中 StringBuffer(并发安全)或者 StringBuilder 的反转方法,不仅速度快、效率高,代码如下:
public static String reverse(String string){
if (null ==string || string.length()<=1){
return string;
}
return new StringBuffer(string).reverse().toString();
}
2、使用递归方案实现,代码如下:
public static String reverse(String string){
if (null==string || string.length()<=1){
return string;
}
return reverseOther(string.substring(1))+string.charAt(0);
}
14、用 java 代码来检查输入的字符串是否回文(对称)?
1、使用 JDK 现有 API 实现,代码如下:
public static boolean is(String string){
if(string == null){
return false;
}
StringBuilder reverse = new StringBuilder(string).reverse();
return reverse.toString().equals(string);
}
2、自写判断算法
public static boolean is(String string){
if(string == null){
return false;
}
int length = string.length();
for (int i = 0; i < length/2; i++) {
if(string.charAt(i) != string.charAt(length-1-i)){
return false;
}
}
return true;
}
15、用 java 代码写一个方法从字符串中删除给定字符?
public static String remove(String string,char c){
if (string == null){
return null;
}
return string.replaceAll(Character.toString(c),"");
}