Java:String类再回顾

字符串广泛应用 在 Java 编程中,在 Java 中字符串属于对象,Java 提供了 String 类来创建和操作字符串。 String
类是不可改变的,所以一旦创建了 String 对象,那它的值就无法改变了。 如果需要对字符串做很多修改,那么应该选择使用
StringBuffer & StringBuilder 类

为什么String类是不可改变的?

String s = "AAAA";
System.out.println("s = " + s);

s = "BBBB";
System.out.println("s = " + s);

输出结果:

AAAA
BBBB

从结果上看是改变了,但为什么说String对象是不可变的呢?

原因在于实例中的 s 只是一个 String 对象的引用,并不是对象本身,当执行 s = “BBBB”; 创建了一个新的对象 “BBBB”,而原来的 “AAAA” 还存在于内存中。

如图:

或者根据 jdk 的源码来分析。

字符串实际上就是一个 char 数组,并且内部就是封装了一个 char 数组。

并且这里 char 数组是被 final 修饰的:

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];

并且 String 中的所有的方法,都是对于 char 数组的改变,只要是对它的改变,方法内部都是返回一个新的 String 实例。


Java:String、StringBuffer 和 StringBuilder 的区别

String:字符串常量,字符串长度不可变。Java中String 是immutable(不可变)的。用于存放字符的数组被声明为final的,因此只能赋值一次,不可再更改。

StringBuffer:字符串变量(Synchronized,即线程安全)。如果要频繁对字符串内容进行修改,出于效率考虑最好使用 StringBuffer,如果想转成 String 类型,可以调用 StringBuffer 的 toString() 方法。Java.lang.StringBuffer 线程安全的可变字符序列。在任意时间点上它都包含某种特定的字符序列,但通过某些方法调用可以改变该序列的长度和内容。可将字符串缓冲区安全地用于多个线程。

StringBuilder:字符串变量(非线程安全)。在内部 StringBuilder 对象被当作是一个包含字符序列的变长数组。

基本原则:

  • 如果要操作少量的数据用 String ;
  • 单线程操作大量数据用StringBuilder ;
  • 多线程操作大量数据,用StringBuffer。

String类支持的方法:

  • 获取字符串长度 用于获取有关对象的信息的方法称为访问器方法。

    • String 类的一个访问器方法是 length() 方法
      • 1、length() 方法是针对字符串来说的,要求一个字符串的长度就要用到它的length()方法;
      • 2、length 属性是针对 Java 中的数组来说的,要求数组的长度可以用其 length 属性;
      • 3、Java 中的 size() 方法是针对泛型集合说的, 如果想看这个泛型有多少个元素, 就调用此方法来查看!
  • 连接字符串

    • concat() 方法

    • '+'操作符

      • String a = "a";
        String b = "b";
        String c = a + b;

        相当于:

        String c = new StringBuffer().append(a).append(b).toString();

        对于字符串的加运算,当编译成 class 文件时,会自动编译为 StringBuffer 来进行字符串的连接操作。

        同时对于字符串常量池:

        当一个字符串是一个字面量时,它会被放到一个常量池中,等待复用。

        String a = "saff";
        String b = "saff";
        String c = new String("saff");
        System.out.println(a.equal(b));  // true
        System.out.println(a.equal(c));  // true

        这个就是字符串的常量池。

  • 创建格式化字符串

    • String 类的静态方法 format() 能用来创建可复用的格式化字符串,而不仅仅是用于一次打印输出。
  • 返回指定索引处的 char 值

    • charAt() 方法用于返回指定索引处的字符。索引范围为从 0 到 length() - 1。
  • 字符串与对象进行比较按字典顺序比较两个字符串

    • int compareTo(Object o)

    • compareTo(String anotherString)

      • 关于这个方法,不管参数是对象还是字符串,最终要比较的都是两个字符串的不同,以下称调用方法那边的为原字符串,方法参数里的为参数字符串。

        这个方法分为两种比较方式:

        1、不同的字符在较短字符串长度之内时

        2、不同的字符在较短字符串长度之外时

        1. 看下 compareTo 的源码:

          /*
          *如果参数字符串等于此字符串,则返回值 0;
          *如果此字符串按字典顺序小于字符串参数,则*返回一个小于 0 的值;
          *如果此字符串按字典顺序大于字符串参数,则返回一个大于 0 的值。
          */
          public int compareTo(String anotherString) {
              int len1 = value.length;
              int len2 = anotherString.value.length;
              //取数组长度里面最小的
              int lim = Math.min(len1, len2);
              // 获得两个数组,这两个数组就是string的属性
              char v1[] = value;
              char v2[] = anotherString.value;
              int k = 0;
              while (k < lim) {
              //获取第K的字符,进行比较
                  char c1 = v1[k];
                  char c2 = v2[k];
                  if (c1 != c2) {
                  //Java使用的是Unicode编码,因此返回这两个字符的Unicode差值。
                      return c1 - c2;
                  }
                  k++;
              }
              //如果前lim个字符都相同,那么就返回长度差。
              return len1 - len2;
          }
          • 1、取得string的value数组
          • 2、取得value数组里面的元素
          • 3、按照unicode值进行比较
          • 4、返回比较的值
          String a = "a";
          String b = "b";
          System.out.println(a.compareTo(b));

          输出值 -1

          String a = "b";
          String b = "a";
          System.out.println(a.compareTo(b));

          输出值 1

          String a = "a";
          String b = "a";
          System.out.println(a.compareTo(b));

          输出 0

          两个字符串首字母不同,则该方法返回首字母的 asc 码的差值。

          String a = "abc";
          String b = "bcdfg";
          System.out.println(a.compareTo(b));

          输出 -1。

          参与比较的两个字符串如果首字符相同,则比较下一个字符,直到有不同的为止,返回该不同的字符的 ascii 码差值。

          String a = "abc";
          String b = "abedfg";
          System.out.println(a.compareTo(b));

          输出 -2。

          两个字符串不一样长,可以参与比较的字符又完全一样,则返回两个字符串的长度差值。

          String a = "abc";
          String b = "abcdefg";
          System.out.println(a.compareTo(b));

          输出 -4。

          string a = "abcde";
          String b = "abcd";
          System.out.println(a.compareTo(b));

          输出 1

          目前 compareTo 项目中的用途是比较版本号的高低。

          String a = "1.0.0";
          String b = "1.0.1";
          System.out.println(a.compareTo(b));

          输出 -1

  • 按字典顺序比较两个字符串,不考虑大小写

    • compareToIgnoreCase()

      • 关于这个方法,不管参数是对象还是字符串,最终要比较的都是两个字符串的不同,以下称调用方法那边的为原字符串,方法参数里的为参数字符串。

        这个方法分为两种比较方式:

        1、不同的字符在较短字符串长度之内时

        返回值=原字符串与参数字符串中第一个不同字符相差的ASCII码值,为原减参。

        例子如下:

        String str1="javDscrspt";
        String str2="jAvascript";
        str1.compareToIgnoreCase(str2);

        此时返回值为3,是d的ASCII码(100)减去了a的ASCII码值(97)得到或者D与A相差得到的。

        注意:只比较第一个不同(这个方法里不考虑字母大小写)的字符,后面的s和i也不一样但不会进行比较了,无关字母大小写所以只比较相同格式下相差的ASCII码值。

        2、不同的字符在较短字符串长度之外时

        返回=原字符串与参数字符串相差的字符个数,原字符串长度大时为正,反之为负。

        例子如下:

        String str1="javAScript";
        String str2="JaVa";
        str1.compareToIgnoreCase(str2);

        此时返回值为6,是str1相比str2多出来的字符个数。

        注意:此时只比较位数,而无关ASCII码值,并非是S(s)的ASCII码值减去0的ASCII码值,在参数字符串前面字符和原字符串一样时,返回值就是两者相差的字符个数,即使改变后面的字符也不会影响到返回的值,比如String str1=“jAva233666”,此时结果仍是6。

  • 当且仅当字符串与指定的StringBuffer有相同顺序的字符时候返回真

    • boolean contentEquals(StringBuffer sb)
  • 返回指定数组中表示该字符序列的 String

    • static String copyValueOf(char[] data)
    • static String copyValueOf(char[] data, int offset, int count)
  • 测试此字符串是否以指定的后缀结束

    • boolean endsWith(String suffix)
  • 将字符串与指定的对象比较,不考虑大小写

    • boolean equalsIgnoreCase(String anotherString)
  • 使用平台的默认字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中

    • byte[] getBytes()

      • 可用如下方法查看字符串用 getBytes() 方法处理后返回的 byte[] 数组中的内容:

        class Main {
            public static void main(String[] args) {
            String str = "make a fortune";
            byte[] byt = str.getBytes();
                for (byte b : byt) {
                    System.out.println(b);
                }
            }
        }

        以上程序运行结果为:

        109
        97
        107
        101
        32
        97
        32
        102
        111
        114
        116
        117
        110
        101

        可见 byte[] 数组中存放的是字符串响应位置对应的字母的哈希值,如字符串中的字母 a 对应 byte[] 数组中的 97 。

        另外:返回的 byte[] 数组的长度,与原字符串的长度相等

  • 使用指定的字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中

    • byte[] getBytes(String charsetName)
  • 将字符从此字符串复制到目标字符数组

    • void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin)
  • 返回此字符串的哈希码

    • int hashCode()
  • 返回指定字符在此字符串中第一次出现处的索引

    • int indexOf()
  • 返回字符串对象的规范化表示形式

    • String intern()

      • 尽管在输出中调用intern方法并没有什么效果,但是实际上后台这个方法会做一系列的动作和操作。在调用”ab”.intern()方法的时候会返回”ab”,但是这个方法会首先检查字符串池中是否有”ab”这个字符串,如果存在则返回这个字符串的引用,否则就将这个字符串添加到字符串池中,然会返回这个字符串的引用。

        可以看下面一个范例:

        String str1 = "a";
        String str2 = "b";
        String str3 = "ab";
        String str4 = str1 + str2;
        String str5 = new String("ab");
         
        System.out.println(str5.equals(str3));
        System.out.println(str5 == str3);
        System.out.println(str5.intern() == str3);
        System.out.println(str5.intern() == str4);

        得到的结果:

        true
        false
        true
        false

        为什么会得到这样的一个结果呢?我们一步一步的分析。

        • 第一、str5.equals(str3)这个结果为true,不用太多的解释,因为字符串的值的内容相同。
        • 第二、str5 == str3对比的是引用的地址是否相同,由于str5采用new String方式定义的,所以地址引用一定不相等。所以结果为false。
        • 第三、当str5调用intern的时候,会检查字符串池中是否含有该字符串。由于之前定义的str3已经进入字符串池中,所以会得到相同的引用。
        • 第四,当str4 = str1 + str2后,str4的值也为”ab”,但是为什么这个结果会是false呢?先看下面代码:
        String a = new String("ab");
        String b = new String("ab");
        String c = "ab";
        String d = "a" + "b";
        String e = "b";
        String f = "a" + e;
        
        System.out.println(b.intern() == a);
        System.out.println(b.intern() == c);
        System.out.println(b.intern() == d);
        System.out.println(b.intern() == f);
        System.out.println(b.intern() == a.intern());

        运行结果:

        false
        true
        true
        false
        true

        由运行结果可以看出来,b.intern() == a和b.intern() == c可知,采用new 创建的字符串对象不进入字符串池,并且通过b.intern() == d和b.intern() == f可知,字符串相加的时候,都是静态字符串的结果会添加到字符串池,如果其中含有变量(如f中的e)则不会进入字符串池中。但是字符串一旦进入字符串池中,就会先查找池中有无此对象。如果有此对象,则让对象引用指向此对象。如果无此对象,则先创建此对象,再让对象引用指向此对象。

        当研究到这个地方的时候,突然想起来经常遇到的一个比较经典的Java问题,就是对比equal和的区别,当时记得老师只是说“”判断的是“地址”,但是并没说清楚什么时候会有地址相等的情况。现在看来,在定义变量的时候赋值,如果赋值的是静态的字符串,就会执行进入字符串池的操作,如果池中含有该字符串,则返回引用。

        执行下面的代码:

        String a = "abc";
        String b = "abc";
        String c = "a" + "b" + "c";
        String d = "a" + "bc";
        String e = "ab" + "c";
                
        System.out.println(a == b);
        System.out.println(a == c);
        System.out.println(a == d);
        System.out.println(a == e);
        System.out.println(c == d);
        System.out.println(c == e);

        运行的结果:

        true
        true
        true
        true
        true
        true
  • 返回指定字符在此字符串中最后一次出现处的索引

    • lastIndexOf()
  • 检测字符串是否匹配给定的正则表达式

    • matches()
  • 测试两个字符串区域是否相等

    • boolean regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len)
  • 替换为新字符串

    • replace()
  • 使用给定的 replacement 替换此字符串所有匹配给定的正则表达式的子字符串

    • replaceAll()
  • 使用给定的参数 replacement 替换字符串第一个匹配给定的正则表达式的子字符串

    • replaceFirst()
  • 根据给定正则表达式的匹配拆分此字符串

    • split()

      • . 必须得加转义字符**\**,以下是拆分 IP 地址的实例:

        public class Demo {
            public static void main(String args[]) {
                String str = "192.168.1.1";
                // . 必须得加 转义字符\\
                for(String s : str.split("\\.")){
                    System.out.println(s);
                }
            }
        }
  • 检测字符串是否以指定的前缀开始

    • startsWith()
  • 返回一个新的字符序列,它是此序列的一个子序列

    • subSequence() (返回的是String)
    • substring() (返回的是实现了CharSequence接口的类,可以直接下转为String对象)
  • 将字符串转换为字符数组

    • toCharArray()
  • 将字符串转换为小写

    • toLowerCase()
  • 返回此对象本身

    • toString()
  • 将字符串小写字符转换为大写

    • toUpperCase()
  • 用于删除字符串的头尾空白符

    • trim()
  • 返回给定data type类型x参数的字符串表示形式

    • valueOf()

System.out.println(s);
}
}
}
```

  • 检测字符串是否以指定的前缀开始

    • startsWith()
  • 返回一个新的字符序列,它是此序列的一个子序列

    • subSequence() (返回的是String)
    • substring() (返回的是实现了CharSequence接口的类,可以直接下转为String对象)
  • 将字符串转换为字符数组

    • toCharArray()
  • 将字符串转换为小写

    • toLowerCase()
  • 返回此对象本身

    • toString()
  • 将字符串小写字符转换为大写

    • toUpperCase()
  • 用于删除字符串的头尾空白符

    • trim()
  • 返回给定data type类型x参数的字符串表示形式

    • valueOf()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章