zoukankan      html  css  js  c++  java
  • Java基础(五)——String

    一、String

    1、介绍

      String是一个final类,不可被继承,代表不可变的字符序列。是一个类类型的变量,Java程序中的所有字符串字面量(如"abc")都作为此类的实例实现,"abc"是一个对象。
      字符串是常量,创建之后不能更改。String 类复写 了Object类中的equals方法。
      String对象的字符内容是存储在一个字符数组value[]中的。

      JDK8部分源码:

      说明:实现了Serializable接口,表示字符串是支持序列化的。实现了Comparable<String>接口,表示字符串可以比较大小。
      内部定义了final char[] value用于储存字符串数据,final体现不可变性。
      通过字面量(区别于new)给一个字符串赋值,此时的字符串声明在字符串常量池中。
      字符串都在字符串常量池中,字符串常量池是不会存储相同内容的字符串。
      总结:最后对String的考查都基于以上最后两点原理。

      不难理解:
      row1:首先常量池什么也没有,就创建了字符串"abc",然后将它的地址赋给了s1。
      row2:常量池已经有了字符串"abc",所以直接将它的地址赋给了s2,此时s1==s2为true。
      row3:常量池没有"hello",就创建了字符串"hello",然后将它的地址赋给了s1。
      深刻理解常量池的概念及字符串的不可变性,不难得出:

    1 String s1 = "abc"; // 字面量的定义方式
    2 String s2 = "abc";
    3 System.out.println(s1 == s2); // true
    4 
    5 String s3 = s1.replace('a', 'm');
    6 System.out.println(s1); // abc
    7 System.out.println(s3); // mbc
    8 
    9 System.out.println(s1 == s3); // false

    2、考查点

      代码示例:

     1 String s1 = "javaEE";
     2 String s2 = "javaEE";
     3 String s3 = new String("javaEE");
     4 String s4 = new String("javaEE");
     5 
     6 System.out.println(s1 == s2); // true
     7 System.out.println(s1 == s3); // false
     8 System.out.println(s1 == s4); // false
     9 System.out.println(s3 == s4); // false
    10 
    11 Person p1 = new Person("Tom", 12);
    12 Person p2 = new Person("Tom", 12);
    13 System.out.println(p1.name == p2.name); // true
    14 System.out.println(p1.name.equals(p2.name)); // true
      说明:

      面试题:String s = new String("abc")的方式,在内存中创建了几个对象?
      答:两个。一个是堆空间中new结构,另一个是char value[]对应的常量池中的数据:"abc"。
      有的答案是一个或两个。原因是如果在字符串常量池已经存在"abc"字符串,就不会再创建了,只会在堆内存创建一个对象。

      代码示例:
     1 String s1 = "java";
     2 String s2 = "mqsql";
     3 String s3 = "javamqsql";
     4 
     5 String s4 = "java" + "mqsql";
     6 
     7 String s5 = s1 + "mqsql";
     8 String s6 = "java" + s2;
     9 String s7 = s1 + s2;
    10 
    11 // 1.不同拼接的对比
    12 System.out.println(s3 == s4); // true
    13 System.out.println(s3 == s5); // false
    14 System.out.println(s3 == s6); // false
    15 System.out.println(s3 == s7); // false
    16 
    17 System.out.println(s5 == s6); // false
    18 System.out.println(s5 == s7); // false
    19 
    20 System.out.println(s6 == s7); // false
    21 
    22 // 2.intern().返回常量池里的地址
    23 String intern = s5.intern();
    24 System.out.println(s3 == intern); // true
    25 
    26 // 3.final.会使String成为一个常量
    27 final String finalStr = "java";
    28 String s8 = finalStr + "mqsql";
    29 System.out.println(s3 == s8); // true

      内存结构:

      结论:
      ①常量与常量的拼接结果在常量池。且常量池中不会存在相同内容的常量。
      ②只要有变量参与的连接,结果就在堆中。s5,存放的是堆空间的地址值。
      ③intern()是返回String对象在常量池中的引用。

      代码示例:

     1 public class Main {
     2     String str = new String("good");
     3     char[] ch = {'t', 'e', 's', 't'};
     4 
     5     public static void main(String[] args) {
     6         Test test = new Test();
     7         test.change(test.str, test.ch);
     8 
     9         System.out.println(test.str); // good
    10         System.out.println(test.ch); // best
    11     }
    12 
    13     public void change(String str, char[] ch) {
    14         str = "test ok";
    15         ch[0] = 'b';
    16     }
    17 }

    3、字符串的内存结构

      JDK 1.6:字符串常量池存储在方法区(永久区)
      JDK 1.7:字符串常量池存储在堆空间
      JDK 1.8:字符串常量池存储在方法区(元空间)
      详细介绍见JVM。JVM中涉及字符串的内存结构。

    4、常用方法

      获取
      int length():获取字符串长度
      char charAt(int index):获取指定位置的字符
      int indexOf(int ch):返回字符ch第一次出现的位置
      int indexOf(String str):!=-1 可判断包含str
      int indexOf(int ch, int fromIndex):从指定位置字符ch第一次出现的位置
      int indexOf(String str, int fromIndex)
      int lastIndexOf(int ch):反向索引,返回字符ch第一次出现的位置
      int lastIndexOf(String str)
      int lastIndexOf(int ch, int fromIndex)
      int lastIndexOf(String str, int fromIndex)

      判断
      boolean startWith(str):是否以指定字符串开头
      boolean endsWith(str):是否以指定字符串结尾
      boolean isEmpty():字符串长度是否为空("" true. null 报空指针异常)
      boolean contains(str):是否包含str
      boolean equals(str):复写了Object类中的equals方法
      boolean equalsIgnoreCase(str)

      转换
      byte[] getBytes():String---->byte[]
      new String(byte[] bytes):byte[]---->String
      char[] toCharArray():String---->char[]
      new String(char[] arr):char[]---->String
      new String(char[] arr, int offset, int count):char[]---->String
      String valueOf():将基本数据类型转换成String
      String toUpperCase()
      String toLowerCase()

      替换
      String replace(char oldChar, char newChar)
      String replace(charSequence tar, charSequence rel)

      切割
      String[] split(regex):按指定规则切割字符串
      String substring(int beginIndex):从指定位置开始获取子串
      String substring(int beginIndex, int endIndex):获取子串[begin,end)

    二、StringBuffer

    1、介绍

      线程安全可变字符序列,可将字符串缓冲区安全地用于多个线程。
      StringBuffer可以对字符串内容进行增删,是字符串缓冲区,此时不会产生新的对象。是一个容器,返回的还是原缓冲区对象。很多方法与String相同。
      字符串的组成原理就是通过该类实现的。
      StringBuffer是可变长度的。

    2、源码分析

     1 String str1 = new String(); // final char[] value = new char[0];
     2 String str2 = new String("abc"); // final char[] value = new char[]{'a', 'b', 'c'};
     3 
     4 // char[] value = new char[16]; 底层创建了一个长度16的数组
     5 StringBuilder sb1 = new StringBuilder();
     6 sb1.append('a'); // char[0] = 'a';
     7 sb1.append('b'); // char[1] = 'b';
     8 
     9 // char[] value = new char["abc".length() + 16];
    10 StringBuffer sb2 = new StringBuffer("abc");

      总结:sb.length() == 3
      扩容:从源码中,可以看到,默认空参数的构造器StringBuffer容器大小为16。默认情况下,扩容为原容量2倍 + 2,同时将原有数组中的元素copy到新数组中。
      指导意义:数组copy效率低,在知道需要多少容量时,建议用StringBuffer(int capacity)或StringBuilder(int capacity)

    3、常用方法

      存储
      StringBuffer append("abc"):将指定参数追加到末尾
      StringBuffer insert(index, "qq"):将指定参数追加到index处

      删除
      StringBuffer delete(int start, int end):删除缓冲区数据[start,end)
      StringBuffer deleteCharAt(int index):删除缓冲区指定位置字符

      获取
      char charAt(int index)
      int length()
      int indexOf(String str)
      int lastIndexOf(String str)
      String substring(int start, int end)
      void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin):将缓冲区指定数据存储到指定字符数组中

      修改
      StringBuffer replace(int start, int end, string str):把[start,end)替换为str
      void setCharAt(int index, char ch):将缓冲区指定位置换成指定字符

      反转
      StringBuffer reverse():返回的依然是原容器

    三、StringBuilder

    1、介绍

      线程不安全可变字符序列,将StringBuilder的实例用于多个线程是不安全的。如果需要这样的同步,则建议使用StringBuffer。
      JDK1.5以后,一个可变的字符序列,此类提供一个与StringBuffer兼容的API,但不保证同步。该类被设计用作StringBuffer的一个简易替换。用在字符串缓冲区被单个线程使用的时候(这种情况很普遍)。如果可能,建议优先采用该类,因为在大多数实现中,它比StringBuffer要快。
      每个字符串生成器都有一定的容量。只要字符串生成器所包含的字符序列的长度没有超出此容量,就无需分配新的内部缓冲区。如果内部缓冲区溢出,则此容量自动增大。

    2、考查点

     1 String s1 = "abc";
     2 StringBuffer s2 = new StringBuffer("abc");
     3 StringBuffer s3 = s2.reverse();
     4 
     5 System.out.println(s1); // abc
     6 System.out.println(s2); // cba
     7 System.out.println(s3); // cba
     8 
     9 System.out.println(s2 == s3); // true
    10 
    11 System.out.println(s1.equals(s2)); // false
    12 System.out.println(s1.equals(s3)); // false
    13 System.out.println(s2.equals(s3)); // true

      记住:StringBuffer是一个容器。返回的还是原缓冲区对象。就不难理解上述答案。

    3、String、StringBuffer、StringBuilder异同

      StringBuffer是线程同步。StringBuilder是线程不同步。实际开发中建议使用StringBuilder,效率高。
      String:不可变的字符序列,底层使用char[]存储。
      StringBuffer:可变的字符序列,线程安全的,效率低。底层使用char[]存储。
      StringBuilder:可变的字符序列,线程不安全的,效率高。底层使用char[]存储。

    4、String、StringBuffer、StringBuilder效率对比

      从高到低:StringBuilder > StringBuffer > String

    作者:Craftsman-L

    本博客所有文章仅用于学习、研究和交流目的,版权归作者所有,欢迎非商业性质转载。

    如果本篇博客给您带来帮助,请作者喝杯咖啡吧!点击下面打赏,您的支持是我最大的动力!

  • 相关阅读:
    How can i use iptables on centos 7?
    Running Jenkins behind Nginx
    GPG入门教程
    HTML5 canvas标签-4 灰度直方图的实现
    [转载]手把手用C++解密Chrome80版本数据库
    Delphi
    7-zip Delphi API
    cef对本地web资源打包加密
    CEF3资源重定向、读取加密资源、读取zip资源
    axios设置withCredentials导致“跨域”的解决方案
  • 原文地址:https://www.cnblogs.com/originator/p/13901360.html
Copyright © 2011-2022 走看看