Java基础学习(八)

Java基础学习(八):字符串

本文为个人学习记录,内容学习自 黑马程序员


概述

  • java.lang 包是 Java 的核心包,其下的类都是无需手动导入即可使用的
  • java.lang.String 是字符串类,Java 程序中所有字符串都为此类的对象

字符串的创建

创建 String 对象有两种方式:

  • 直接赋值:String name = "victoria";

  • 使用 new 创建:

    构造方法 说明
    public String() 创建空白字符串,不含任何内容
    public String(String original) 根据传入的字符串,创建字符串对象
    public String(char[] chs) 根据字符数组,创建字符串对象
    public String(byte[] chs) 根据字节数组,查ASCII码表,创建字符串对象

字符串内存分析

  • 存储位置:

    • 直接赋值得到的字符串存储在字符串常量池(StringTable)中,在 JDK7 之前字符串常量池位于方法区中,后来移到了堆中
    • 通过 new 创建的字符串存储在堆中
  • 底层机制:

    • 通过直接赋值创建字符串时,如果字符串常量池中没有该常量,则创建一个新常量,并将地址传给字符串变量;如果字符串常量池中已经有了该常量,则会复用该常量

      String a = "abc";						// a存储的地址值为 0x0011
      String b = "abc";						// b存储的地址值为 0x0011
      
    • 使用 new 创建的字符串和其他对象的创建一样,会在堆中开辟新内存存储数据,不存在相同字符串的复用

      char[] chs = {'a', 'b', 'c'};
      String a = new String(chs);				// a存储的地址值为 0x0011
      String b = new String(chs);				// b存储的地址值为 0x0022
      
  • 总结:使用直接赋值不仅简单,还节约内存


String 类常用方法

  1. 字符串的比较

    由于引用数据类型的变量存储的是地址值,因此会出现下面的比较问题:

    String a = "abc";
    String b = "abc";
    System.out.println(a == b);					// 输出为 true
    
    String a = new String("abc");
    String b = new String("abc");
    System.out.println(a == b);					// 输出为 false
    

    小知识:使用 Scanner 从键盘接收的字符串也是 new 创建的

    为了实现对字符串本身的比较,String 类提供了两种方法:

    • equals():完全一样结果为true,否则为false
    • equalsIgnoreCase():不区分大小写
    String a = new String("abc");
    String b = new String("abc");
    boolean result = a.equals(b);
    boolean result2 = a.equalsIgnoreCase(b);
    
  2. 字符串按索引获得字符

    通过调用 charAt() 方法实现

    String str = "abc";
    char ch = str.charAt(1);
    System.out.println(ch);						// 输出为 'b'
    
  3. 获取字符串长度

    通过调用 length() 方法实现

    String str = "abc";
    int len = str.length();
    System.out.println(len);					// 输出为 3
    
  4. 截取字符串

    通过调用 substring() 方法实现

    两种实现方法:

    • String substring(int beginIndex, int endIndex) :第一个参数表示起始索引,第二个参数表示结束索引(不包含)
    • String substring(int beginIndex):给定起始索引,默认截取到末尾

    注意:1. 包头不包尾,包左不包右,2. 需要通过返回值接收截取后的字符串,因为字符串本身是不可修改的

    String str = "abc";
    String subStr1 = str.substring(0, 2);
    String subStr2 = str.substring(1);
    System.out.println(subStr1);				// 输出为 "ab"
    System.out.println(subStr2);				// 输出为 "bc"
    
  5. 字符串替换

    通过调用 replace() 方法实现

    注意:需要通过返回值接收替换后的结果

    String str = "abcaaacba";
    String str2 = str.replace("aaa", "bbb");
    System.out.println(str2);					// 输出为 "abcbbbcba"
    
  6. 字符串提取字符数组

    通过调用 toCharArray() 方法实现

    用途:常用于修改字符串内容

    String str = "abc";
    char[] chs = str.toCharArray();				// 得到的chs为['a', 'b', 'c']
    

字符串不可变

  • 字符串本身是不可变的,一旦创建,字符串的内容就无法被修改,任何对字符串的修改操作实际上都会创建一个新的字符串对象

    String str = "Hello";
    str.substring(0, 2);						// 虽然对字符串进行了截取操作,但原字符串并没有被修改
    System.out.println(str);  					// 输出为 "Hello"
    
  • 字符串本身不可变,但指向字符串的引用类型变量是可以改变的

    String str = "Hello";						// 创建了一个字符串对象
    str = str + " World";						// 等式右侧创建了一个新的字符串对象,再将地址值赋值给左侧的引用类型变量
    System.out.println(str);					// 输出为 "Hello World"
    

StringBuilder 类

  • java.lang.StringBuilder 可以看成一个容器,创建之后里面的内容是可变的

  • 作用:提高字符串的操作效率

  • 两种构造方式:无参和有参

    构造方法 说明
    public StringBuilder() 创建一个空白可变字符串对象,不含任何内容
    public StringBuilder(String str) 根据字符串的内容,来创建可变字符串对象
  • 常用方法:

    方法名 说明
    public StringBuilder append(任意类型) 添加数据,并返回对象本身
    public StringBuilder reverse() 反转容器中的内容
    public int length() 返回长度(字符出现的个数)
    public String toString() 把 StringBuilder 转换成 String
  • 返回值是自身 vs 无返回值

    • 相同点:都不需要创建一个新的变量用于接收返回值
    • 不同点:返回值是自身时可以用于链式编程
  • 方法示例:

    StringBuilder sb = new StringBuilder();
    // append方法
    sb.append(1).append('a').append("zz");					// 链式编程
    System.out.println(sb);									// 输出为 "1azz"(和 String 类似,Java 底层在实现打印输出时进行了特殊处理,不是输出地址值,而是属性值)
    // reverse方法
    sb.reverse();
    System.out.println(sb);									// 输出为 "zza1"
    // length方法
    int len = sb.length();
    System.out.println(len);								// 输出为 4
    // toString方法
    String str = sb.toString();
    System.out.println(str);								// 输出为 "zza1"
    

StringJoiner 类

  • java.util.StringJoiner 也可以看成一个容器,创建之后里面的内容是可变的

  • 作用:提高字符串的操作效率,而且代码编写简洁,但由于在 JDK8 才出现,因此使用不广

  • 两种构造方式:只有有参

    构造方法 说明
    public StringJoiner(String str) 创建一个StringJoiner对象,指定拼接时的间隔符号
    public StringJoiner(String str, String str, String str) 创建一个StringJoiner对象,指定拼接时间隔符号、开始符号、结束符号
  • 常用方法:

    方法名 说明
    public StringJoiner add(String str) 添加数据(只能添加字符串),并返回对象本身
    public int length() 返回长度(字符出现的个数)
    public String toString() 把 StringJoiner 转换成 String
  • 方法示例:

    StringJoiner sj = new StringJoiner(", ", "[", "]");
    sj.add("aaa").add("bbb").add("ccc");
    System.out.println(sj);										// 输出为 "[aaa, bbb, ccc]"
    int len = sj.length();
    System.out.println(len);									// 输出为 15
    String str = sj.toString();
    System.out.println(str);									// 输出为 "[aaa, bbb, ccc]"
    

字符串相关类底层原理

  1. 字符串存储的内存原理

    • 直接赋值:会复用字符串常量池中的
    • new创建:不会复用,而是开辟一个新的空间
  2. == 号比较的到底是什么?

    • 基本数据类型:比较数据值
    • 引用数据类型:比较地址值
  3. 字符串拼接的底层原理

    • 拼接时等号右边没有变量:会触发字符串的优化机制,在编译的时候就已经是最终结果了

      下例中,引用变量 s 中存储的是字符串常量池中 "abc" 的地址

      String s = "a" + "b" + "c";			===>			String s = "abc";
      
    • 拼接时等号右边有变量:

      • JDK8 之前:会在堆中创建新的 StringBuilder 对象和 String 对象

        下例中,引用变量 s2 中存储的是堆中新建的 String 对象的地址

        String s1 = "a";
        String s2 = s1 + "b";	===>	String s2 = new StringBuilder().append(s1).append("b").toString();
        
      • JDK8 之后:会先预估最终字符串长度,并创建一个数组存储该字符串,最后通过数组在堆中创建一个新的字符串

    • 总结:拼接时等号右边没有变量时,拼接后的新字符串存储在字符串常量池中;等号右边有变量时,拼接后新字符串存储在堆中

  4. StringBuilder 提高效率原理

    • 将所有要拼接的内容放到 StringBuilder 中,不会创建很多无用的空间,可以节约内存
  5. StringBuilder 扩容机制

    • StringBuilder 中的数据存储在 byte[] 数组中
    • StringBuilder 有 容量长度 两个概念,容量表征了最多容纳的字符个数,长度表征已经容纳的字符个数
    • 默认的容量为16,当长度超出了默认容量时,会进行一次扩容,将容量扩成34,如果还是超出容量,则容量大小以实际长度为准
posted @   victoria6013  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示