深入理解String类

时间:2021-11-22 15:57:30

String是java中的字符串。String类是不可变的,对String类的任何改变,都是返回一个新的String类对象。String不属于8种基本数据类型,String是一个对象。本文主要具体介绍一下String。

一、String在内存中的存储方式

例一:

public class TestString {

public static void main(String[] args) {
String str1 = "abc";
String str2 = "abc";
String str3 = "ab";
String str4 = "ab"+"c";
String str5 = str3 + "c";
String str6 = new String(str2);

System.out.println( str1 == str2 );
System.out.println( str1 == str4 );
System.out.println( str1 == str5 );
System.out.println( str1 == str6 );
}
}

答案:
True
True
False
False

  • 第一个断言很好理解,因为在编译的时候,”abc”被存储在常量池中,str1和str2的引用都是指向常量池中的”abc”。所以 str1str2 引用是相同的。
  • 第二个断言是由于编译器做了优化。在JVM中, 这个
    String str4 = "ab"+"c";
    其实就是:String str4 = "abc";再在常量池中查找这个字符串是否存在,如果存在,则让变量直接引用该字符串。所以 str1str4 引用也是相同的。
  • 第三个断言可以参考api中关于String类的说明:
    Java 语言提供对字符串串联符号(”+”)以及将其他对象转换为字符串的特殊支持。字符串串联是通过 StringBuilder(或 StringBuffer)类及其 append 方法实现的。字符串转换是通过 toString 方法实现的。
    所以当 str5 最终将是一个在堆中存储的String对象,而 str1 是存储在常量池里面的, 所以不同。
  • str4 的对象不是显式赋值的,编译器会在堆中重新分配一个区域来存储它的对象数据。所以* str1和str6* 的引用是不一样的。(堆、栈、常量池见批注1)
    深入理解String类

二、String类中的常用方法

  1. 字符串比较
    equals() ——判断内容是否相同。
    compareTo() ——判断字符串的大小关系。
    compareToIgnoreCase(String int) ——在比较时忽略字母大小写。
    == ——判断内容与地址是否相同。
    equalsIgnoreCase() ——忽略大小写的情况下判断内容是否相同。
    reagionMatches() ——对字符串中的部分内容是否相同进行比较(详情请参考API)。
  2. 字符串查找
    charAt(int index) ——返回指定索引index位置上的字符,索引范围从0开始。
    indexOf(String str)——从字符串开始检索str,并返回第一次出现的位置,未出现返回-1。
    indexOf(String str,int fromIndex);——从字符串的第fromIndex个字符开始检索str。
    lastIndexOf(String str)——查找最后一次出现的位置。
    lastIndexOf(String str,int fromIndex)—-从字符串的第fromIndex个字符查找最后一次出现的位置。
    starWith(String prefix,int toffset)—–测试此字符串从指定索引开始的子字符串是否以指定前缀开始。
    starWith(String prefix)——测试此字符串是否以指定的前缀开始。
    endsWith(String suffix)——测试此字符串是否以指定的后缀结束。
  3. 字符串截取
    public String subString(int beginIndex)——返回一个新的字符串,它是此字符串的一个子字符串。
    public String subString(int beginIndex,int endIndex)——返回的字符串是从beginIndex开始到endIndex-1的串。
  4. 字符串替换
    public String replace(char oldChar,char newChar)。
    public String replace(CharSequence target,CharSequence replacement)——把原来的etarget子序列替换为replacement序列,返回新串。
    public String replaceAll(String regex,String replacement)——用正则表达式实现对字符串的匹配。注意replaceAll第一个参数为正则表达式,鄙人曾经深受其害。
  5. 更多方法请参考API
    拓展阅读:字符串处理(将字符串“ABCD”按照规律顺序输出ABCD,ABC,BCD,AB,BC,CD,A,B,C,D批注2)

三、String,StringBuffer与StringBuilder的区别?

String是不可变而StringBuffer是可变的,但是这可变与不可变究竟是什么意思呢?如果你能用IDE进行debug的话,你就会发现,String实例化以后所有的属性都是final的,而StringBuffer确不是,这就是可变与不可变。下面引用SCJP的试题来解释一下这个例子:
例二:

public class Test {   
public static void stringReplace (String text) {
text = text.replace('j' , 'i');
}

public static void bufferReplace (StringBuffer text) {
text = text.append("C");
}

public static void main (String args[]) {
String textString = new String ("java");
StringBuffer textBuffer = new StringBuffer ("java");

stringReplace (textString);
bufferReplace (textBuffer);

System.out.println (textString + textBuffer);
}
}

答案:
javajavaC

解释:String 是不可以变的字符串,StringBuffer 是可变的字符串. 对StringBuffer进行操作,是在原来的对象之上进行改变. 而对String进行操作,是创建新的对象. 假设调用stringReplace方法的参数text的内存地址是0x0001的话,在执行 text = text.replace(‘j’ , ‘i’); 以后,text的内存地址就会变成类似0x0002的地址,但是肯定不再是0x0001了,原因就是String类是final的,任何修改都将创造一个 新的字符串。那么在stringReplace方法里面输出text,就应该是0x0002地址里的内容了,iava。
总结:
String:在String类中没有用来改变已有字符串中的某个字符的方法,由于不能改变一个java字符串中的某个单独字符,所以在JDK文档中称String类的对象是不可改变的。然而,不可改变的字符串具有一个很大的优点:编译器可以把字符串设为共享的。

**StringBuffer:**StringBuffer类属于一种辅助类,可预先分配指定长度的内存块建立一个字符串缓冲区。这样使StringBuffer类的append方法追加字符 比 String使用 + 操作符添加字符 到 一个已经存在的字符串后面有效率得多。因为使用 + 操作符每一次将字符添加到一个字符串中去时,字符串对象都需要寻找一个新的内存空间来容纳更大的字符串,这无凝是一个非常消耗时间的操作。添加多个字符也就意味着要一次又一次的对字符串重新分配内存。使用StringBuffer类就避免了这个问题.

StringBuilder:一个可变的字符序列是5.0新增的。此类提供一个与 StringBuffer 兼容的 API,但不保证同步。该类被设计用作 StringBuffer 的一个简易替换,用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)。如果可能,建议优先采用该类,因为在大多数实现中,它比 StringBuffer 要快。两者的方法基本相同。

批注1:
堆:由JVM分配的,用于存储对象等数据的区域。
栈:由JVM分配区域,用于保存线程执行的动作和数据引用。栈是一个运行的单位,Java中一个线程就会相应有一个线程栈与之对应。
常量池 :在编译的阶段,在堆中分配出来的一块存储区域,用于存储 显式 的String,float或者integer.例如String str=”abc”; abc这个字符串是显式声明,所以存储在常量池。
常量池拓展阅读:Java常量池详解之一道比较蛋疼的面试题
批注2:
Question:将字符串“ABCD”按照规律顺序输出ABCD,ABC,BCD,AB,BC,CD,A,B,C,D。
首先分析字符串规律,理清思路这样处理起来才会得心应手。根据规律可以看出字符串的长度是从4->3->2->1这个大致可以知道分组需要循环4次,然后再看每组的字符串结构,可以通过图例很明显的看出(具体就不用解释了吧,图解释的很详细。。。)
深入理解String类
代码:

String str = "ABCD";
int len = str.length();
for (int i = 1; i <= len; i++) {
for (int j = 0; j < i; j++) {
System.out.println(str.substring(j, len-i + j+1));
}
}

详细参考: 字符串处理
参考文章:
1
2