String 类的概述和特点,面试当中的要点总结
参考文献:Java中String字符串面试详解
java常见的字符串(String)面试问题
文章目录
前言
API当中说,Java程序中所有的字符串字面值,都作为此类的实列实现,
其实就是说,程序中所有的字符串,都是string类的对象。(就算没有new也照样是)。
字符串的特点:
1、字符串是常量 内容不可改变 【重点】
2、正是应为字符串不可以改变,所以字符串可以共享使用
3、字符串效果上相当于是 char[] 字符数组,但是底层原理是 byte[] 字节数组。 8位就是一个字节。
一、创建一个字符串
创建字符串的常见3+1种方式:
三种构造方法:
public String(); 创建一个空白字符串,没有内容
public String(char[] array) ;根据字符数组的内容来创建对应的字符串
public String(byte[] array) ; 根据字节数组的内容,来创建字符串
最后一种则是直接创建。
注意,直接写上双引号,就是字符串对象。
public class Demo9String {
public static void main(String[] args) {
//通过三种构造方法创建数组
String str1=new String();//创建一个空白数组
System.out.println(str1);
//创建一个char【】 数组的字符串
char[] array={'a','b'};
String str2=new String(array);
System.out.println(str2);
//创建一个byte【】 字节数组的字符串
byte[] array2={97,98,67};
String str3=new String(array2);
System.out.println(str3);
//直接创建一个字符串
String str4="Arvin";
System.out.println(str4);
}
}
注意:使用 new String(“XXX”)创建出来的字符串,实际上创建的是两个。
一个是通过双引号创建的,另一个是通过new创建的.只不过他们创建的时期不同,一个是编译期(双引号创建,放进字符串常量池),一个是运行期(实列)!
java对String类型重载了+操作符,可以直接使用+对两个字符串进行连接。运行期调用String类的intern()方法可以向String Pool中动态添加对象。
字符串的常量池:(字符串常量池在堆当中)
String使用private final char value[]来实现字符串的存储
也就是说String对象创建之后,就不能再修改此对象中存储的字符串内容,就是因为如此,才说String类型是不可变的(immutable)。
不可变实现方式:
JVM验证是否已经有一个字符串“abc”(相同的char序列)。如果存在这样的字符串,JVM只是将现有对象的引用赋给变量str,否则,将创建一个新对象“abc”,并将其引用赋给变量str。
字符串常量池,程序当中直接 String="" 写上双引号的字符串,就在字符串的常量池中。
二、字符串中的常用方法
public int length()来获取字符串当中含有的字符个数,拿到字符串长度。
public String concat(String str),当前字符串和参数字符串拼接成返回值新的字符串。
public char charAt(int index),返回指定索引位置的单个字符。(索引从0开始)
public int indexOf(String str),查找参数字符串在本字符串中首次出现的索引位置,如果没有返回-1值。
public class Demo12StringGet {
public static void main(String[] args) {
String str1="Arvin";
char[] array={'r','v'};
String str2=new String(array);
System.out.println("返回字符串的长度是:"+str1.length());
System.out.println("把当前字符串和参数字符串连接:"+str1.concat(str2));
System.out.println("返回指定索引位置的字符串:"+str1.charAt(0));
System.out.println("查询参数字符串在当前字符串当中的位置索引:"+str1.indexOf(str2));
}
}
关键字’实习生’用法 intern()
这是java docs最好的描述:
intern()调用该方法时,如果池已包含String与equals(Object)方法确定的此对象相等的字符串,则返回池中的字符串。否则,将此String对象添加到池中,并String返回对此对象的引用。
String str = new String(“abc”);
str.intern();
它遵循对于任何两个字符串s和t,s.intern() == t.intern()是true当且仅当s.equals(t)是true。意味着如果s和t都是不同的字符串对象并且具有相同的字符序列,则在两者上调用intern()将导致由两个变量引用的单个字符串池文字。
三、字符串的比较
对于基本类型来说,== 比较是数值的比较
对于引用类型来说,== 比较是地址值比较
equals比较的也是字符串的地址值,但是因为String拥有享元模式,相同的字符串直接在共享池中共享了地址值。
==是进行对象的地址 值比较。
如果确实需要进行【字符串】的内容比较,可以使用两个方法
一、public boolean equals(Object obj)只有参数是一个字符串并且内容相同时才会给true,否则就是false
注意事项:
1、任何对象都可以使用Object进行接收
2、equals方法具有对称性,也就是a.equals(b)和b.equals(a)效果一样
3、如果比较方是一个常量一个变量,推荐把常量字符串写在前面,应为如果常量位null时调用equals方法会发生空指针异常
二、public boolean equalsIgnoreCase(String str)//这个比较方法会忽略大小写直接进行内容的比较
四、为什么Char数组比String更适合存储密码?
我们知道字符串存储在Java中的常量池中。在字符串池中创建字符串后,它将保留在池中,直到收集垃圾为止。此时,任何恶意程序都可以访问物理内存位置中的内存位置并访问该字符串。
如果我们将密码存储为字符串,那么它也将存储在弹簧池中,并且将在内存中可用的持续时间超过所需的时间,因为垃圾收集周期是不可预测的。这使得敏感密码字符串容易受到黑客攻击和数据窃取。
使用后我们可以将String变成空白吗?不,我们不可以。我们知道,一旦创建了一个String,我们就无法操纵它,例如你不能改变它的内容。字符串是最终的,不可变的。
但是char数组是可变的,它们的内容在使用后可以被覆盖。因此,您的应用程序应使用char []存储密码文本,并在使用密码后,用空白替换数组内容。
五、Java中的字符串是否是线程安全的?
是的,字符串是线程安全的。它们是不可变的,默认情况下,java中的所有不可变实例都是线程安全的。
六、为什么String是Java中流行的HashMap键?
在Java中,必须使用的密钥Map应该是不可变的,并且应该遵守equals()和hashCode()方法之间的契约。String全部满足两个条件。
此外,String类提供了许多有用的方法来比较,排序,标记化或低级大小写。在执行CRUD操作时可以使用这些方法Map。它使它成为一个非常有用的类,Map而不是创建自己的类。
七、String,StringBuffer和StringBuilder之间的区别?
Stringclass表示一系列字符,并提供了处理字符的有用方法。String类实例是不可变的。因此,每次使用字符串类执行字符串连接时,都会使用连接字符串创建一个新对象。
StringBuilderclass用于以更高效的内存方式执行字符串连接操作。它在内部维护char array并仅操作此数组中的内容。
在执行所有操作后需要获取完整的连接字符串时,它会创建一个包含字符数组内容的新String。
StringBuffer与StringBuilder 非常相似。唯一的区别是它是线程安全的。这都是方法synchronized。
测试题
package StringTest;
public class test1 {
public static void main(String[] args){
String a = "a1";//“a1”在编译的时候就能确定,所以编译的时候,a1被放进了常量池中,同时a指向常量池中的a1对象
String b = "a"+ 1;//a和1这两个常量都能在编译时确定,所以他们相加的结果也能确定,因此编译器检查常量池中是否有值为a1的String对象,发现有了,因此b也指向常量池中的a1对象
System.out.println(a==b);//==判断的是a和b是否指向同一个对象,也就是同一块内存区域
}//true
}
package StringTest;
public class test2 {
public static void main(String[] args){
String a = "ab";
String bb = "b";
String b = "a"+ bb; //编译器不能确定为常量
System.out.println(a==b);
}//false
}
package StringTest;
public class test3 {
public static void main(String[] args){
String a = "ab";
final String bb = "b";
String b = "a"+ bb; //bb加final后是常量,可以在编译器确定b
System.out.println(a==b);
}//true
}
package StringTest;
public class test4 {
public static void main(String[] args){
String a = "ab";
final String bb = getBB();
String b = "a"+ bb;//bb是通过函数返回的,虽然知道它是final的,但不知道具体是啥,要到运行期才知道bb的值
System.out.println(a==b);
}//false
private static String getBB(){ return "b"; }
}
package StringTest;
public class test5 {
private static String a = "ab";
public static void main(String[] args){
String s1 = "a";
String s2 = "b";
String s = s1 + s2;//+的用法
System.out.println(s == a);
System.out.println(s.intern() == a);//intern的含义
}//flase true
}
package StringTest;
public class test6 {
private static String a = new String("ab");
public static void main(String[] args){
String s1 = "a";
String s2 = "b";
String s = s1 + s2;
System.out.println(s == a);
System.out.println(s.intern() == a);
System.out.println(s.intern() == a.intern());
}//flase false true
}