Java中String类

Java中的String详解

1. String类核心概念

1.1 不可变性(Immutability)

String类是不可变的(Immutable),这是其最重要的特性:

public final class String implements Serializable, Comparable<String>, CharSequence {
    // 字符数组被final修饰,且没有提供修改方法
    private final char[] value;  // JDK 8
    // private final byte[] value; // JDK 9+
}

不可变性的实现原理:

  • String类被final修饰,不能被继承
  • 内部字符数组valuefinal修饰
  • 没有提供任何修改内部状态的方法
  • 所有"修改"操作都返回新的String对象

示例代码:

public class StringImmutableDemo {
    public static void main(String[] args) {
        String str1 = "Hello";
        String str2 = str1.concat(" World");
        
        System.out.println("str1: " + str1);        // 输出: Hello
        System.out.println("str2: " + str2);        // 输出: Hello World
        System.out.println("str1 == str2: " + (str1 == str2)); // false
        
        // str1的值没有改变,concat()返回了新的String对象
    }
}

1.2 不可变性的优势

  1. 线程安全:多线程环境下无需同步
  2. 缓存HashCode:String的hashCode只需计算一次
  3. 字符串常量池:相同内容的字符串可以共享内存
  4. 安全性:防止恶意修改,适合作为HashMap的key

2. String的内存模型

2.1 字符串存储位置

public class StringMemoryDemo {
    public static void main(String[] args) {
        // 1. 字符串字面量 - 存储在字符串常量池
        String str1 = "Hello";
        String str2 = "Hello";
        System.out.println(str1 == str2); // true - 指向同一个对象
        
        // 2. new关键字 - 在堆中创建新对象
        String str3 = new String("Hello");
        String str4 = new String("Hello");
        System.out.println(str3 == str4); // false - 不同对象
        System.out.println(str1 == str3); // false - 不同位置
        
        // 3. 内容比较
        System.out.println(str1.equals(str3)); // true - 内容相同
    }
}

2.2 JVM内存分布

JVM内存
方法区(元空间)
堆内存
栈内存
字符串常量池
'Hello' (str1, str2指向)
str1 (引用)
str2 (引用)
str3 (引用)
String对象1 (str3指向)
str4 (引用)
String对象2 (str4指向)
类信息、常量等
其他对象

3. String常用方法详解

3.1 字符串创建和基本操作

public class StringMethodsDemo {
    public static void main(String[] args) {
        String str = "Hello World Java";
        
        // 长度和判空
        System.out.println("长度: " + str.length());           // 16
        System.out.println("是否为空: " + str.isEmpty());       // false
        System.out.println("是否为空白: " + str.isBlank());     // false (JDK 11+)
        
        // 字符访问
        System.out.println("第6个字符: " + str.charAt(5));      // 'W'
        System.out.println("字符数组: " + Arrays.toString(str.toCharArray()));
        
        // 子字符串
        System.out.println("子串(6,11): " + str.substring(6, 11)); // "World"
        System.out.println("从索引6开始: " + str.substring(6));      // "World Java"
    }
}

3.2 字符串查找和匹配

public class StringSearchDemo {
    public static void main(String[] args) {
        String text = "Java is great, Java is powerful";
        
        // 查找方法
        System.out.println("包含'great': " + text.contains("great"));        // true
        System.out.println("以'Java'开头: " + text.startsWith("Java"));       // true
        System.out.println("以'powerful'结尾: " + text.endsWith("powerful")); // true
        
        // 索引查找
        System.out.println("'Java'首次出现位置: " + text.indexOf("Java"));      // 0
        System.out.println("'Java'最后出现位置: " + text.lastIndexOf("Java"));  // 15
        System.out.println("从索引5开始查找'is': " + text.indexOf("is", 5));    // 5
        
        // 正则匹配
        System.out.println("匹配模式: " + text.matches(".*Java.*"));           // true
    }
}

3.3 字符串转换和处理

public class StringTransformDemo {
    public static void main(String[] args) {
        String original = "  Hello World Java  ";
        
        // 大小写转换
        System.out.println("大写: " + original.toUpperCase());
        System.out.println("小写: " + original.toLowerCase());
        
        // 去除空白
        System.out.println("去除首尾空白: '" + original.trim() + "'");
        System.out.println("去除所有空白: '" + original.strip() + "'"); // JDK 11+
        
        // 替换操作
        System.out.println("替换空格为'-': " + original.replace(" ", "-"));
        System.out.println("替换首个'l': " + original.replaceFirst("l", "L"));
        System.out.println("正则替换: " + original.replaceAll("\\s+", "_"));
        
        // 分割
        String[] words = original.trim().split("\\s+");
        System.out.println("分割结果: " + Arrays.toString(words));
    }
}

4. String、StringBuffer、StringBuilder对比

4.1 核心特性对比

特性StringStringBufferStringBuilder
可变性不可变可变可变
线程安全是(同步方法)
性能低(频繁操作时)中等
内存开销高(创建新对象)
适用场景少量字符串操作多线程环境单线程环境

4.2 详细特性分析

4.2.1 String类
public class StringAnalysis {
    public static void main(String[] args) {
        // String的不可变性导致每次操作都创建新对象
        String str = "Hello";
        str = str + " World";  // 创建新的String对象
        str = str + " Java";   // 再次创建新对象
        
        // 线程安全:由于不可变性,天然线程安全
        // 适用场景:字符串内容不频繁变化的情况
    }
}
4.2.2 StringBuffer类
public class StringBufferAnalysis {
    public static void main(String[] args) {
        // StringBuffer是可变的,线程安全
        StringBuffer sb = new StringBuffer("Hello");
        sb.append(" World");   // 在原对象上修改,不创建新对象
        sb.append(" Java");    // 继续在原对象上修改
        
        // 所有方法都是synchronized的
        // 适用场景:多线程环境下的字符串操作
        System.out.println(sb.toString());
    }
}
4.2.3 StringBuilder类
public class StringBuilderAnalysis {
    public static void main(String[] args) {
        // StringBuilder是可变的,非线程安全,性能最好
        StringBuilder sb = new StringBuilder("Hello");
        sb.append(" World");   // 在原对象上修改
        sb.append(" Java");    // 性能最优
        
        // 方法不是synchronized的,性能更好
        // 适用场景:单线程环境下的字符串操作
        System.out.println(sb.toString());
    }
}

4.3 使用场景总结

public class StringUsageScenarios {
    
    // 场景1:少量字符串操作 - 使用String
    public String formatUserInfo(String name, int age) {
        return "用户: " + name + ", 年龄: " + age;  // 简单拼接,使用String
    }
    
    // 场景2:多线程环境下的字符串构建 - 使用StringBuffer
    public class LogBuffer {
        private StringBuffer buffer = new StringBuffer();
        
        public synchronized void addLog(String message) {
            buffer.append(System.currentTimeMillis())
                  .append(": ")
                  .append(message)
                  .append("\n");
        }
        
        public synchronized String getLogs() {
            return buffer.toString();
        }
    }
    
    // 场景3:单线程环境下的大量字符串操作 - 使用StringBuilder
    public String generateReport(List<String> data) {
        StringBuilder report = new StringBuilder();
        report.append("=== 报告开始 ===\n");
        
        for (String item : data) {
            report.append("- ").append(item).append("\n");
        }
        
        report.append("=== 报告结束 ===");
        return report.toString();
    }
}

5. 字符串常量池与intern()方法

5.1 字符串常量池机制

字符串常量池(String Pool)是JVM为了优化字符串存储而设计的特殊内存区域:

public class StringPoolDemo {
    public static void main(String[] args) {
        // 字面量直接进入常量池
        String s1 = "hello";
        String s2 = "hello";
        System.out.println(s1 == s2);  // true - 指向同一个对象
        
        // new创建的对象在堆中
        String s3 = new String("hello");
        System.out.println(s1 == s3);  // false - 不同位置
        
        // 编译时常量拼接会进入常量池
        String s4 = "hel" + "lo";
        System.out.println(s1 == s4);  // true - 编译时优化
        
        // 运行时拼接不会进入常量池
        String prefix = "hel";
        String s5 = prefix + "lo";
        System.out.println(s1 == s5);  // false - 运行时创建
    }
}

5.2 intern()方法详解

intern()方法用于手动将字符串加入常量池:

public class InternMethodDemo {
    public static void main(String[] args) {
        // 基本用法
        String s1 = new String("hello");
        String s2 = s1.intern();  // 将s1的内容加入常量池
        String s3 = "hello";
        
        System.out.println(s1 == s2);  // false - s1在堆中,s2指向常量池
        System.out.println(s2 == s3);  // true - 都指向常量池
        
        // intern()的返回值
        String s4 = new String("world");
        String s5 = s4.intern();
        String s6 = s4.intern();
        System.out.println(s5 == s6);  // true - intern()返回常量池中的引用
        
        // 性能考虑
        demonstrateInternPerformance();
    }
    
    private static void demonstrateInternPerformance() {
        long startTime = System.currentTimeMillis();
        
        // 大量使用intern()可能导致性能问题
        for (int i = 0; i < 100000; i++) {
            String s = new String("test" + i).intern();
        }
        
        long endTime = System.currentTimeMillis();
        System.out.println("intern()操作耗时: " + (endTime - startTime) + "ms");
    }
}

5.3 JDK版本差异

public class InternVersionDifference {
    public static void main(String[] args) {
        // JDK 6 vs JDK 7+ 的区别
        String s1 = new String("1") + new String("1");
        String s2 = s1.intern();
        String s3 = "11";
        
        // JDK 6: s1 != s2, s2 == s3
        // JDK 7+: s1 == s2, s2 == s3
        System.out.println("s1 == s2: " + (s1 == s2));
        System.out.println("s2 == s3: " + (s2 == s3));
        
        /*
         * JDK 6: 常量池在永久代,intern()会复制对象到常量池
         * JDK 7+: 常量池在堆中,intern()可能只存储引用
         */
    }
}

6. 性能优化最佳实践

6.1 避免在循环中进行字符串拼接

public class StringOptimization {
    
    // ❌ 错误做法:在循环中使用String拼接
    public String badConcatenation(List<String> items) {
        String result = "";
        for (String item : items) {
            result += item + ",";  // 每次都创建新对象
        }
        return result;
    }
    
    // ✅ 正确做法:使用StringBuilder
    public String goodConcatenation(List<String> items) {
        StringBuilder sb = new StringBuilder();
        for (String item : items) {
            sb.append(item).append(",");
        }
        return sb.toString();
    }
    
    // ✅ 更好的做法:使用String.join() (JDK 8+)
    public String bestConcatenation(List<String> items) {
        return String.join(",", items);
    }
}

6.2 合理使用StringBuilder的容量

public class StringBuilderOptimization {
    
    // ❌ 未指定初始容量,可能频繁扩容
    public String inefficientBuilder(List<String> items) {
        StringBuilder sb = new StringBuilder();  // 默认容量16
        for (String item : items) {
            sb.append(item);
        }
        return sb.toString();
    }
    
    // ✅ 预估容量,减少扩容次数
    public String efficientBuilder(List<String> items) {
        int estimatedLength = items.size() * 10;  // 预估总长度
        StringBuilder sb = new StringBuilder(estimatedLength);
        for (String item : items) {
            sb.append(item);
        }
        return sb.toString();
    }
}

6.3 字符串比较优化

public class StringComparisonOptimization {
    
    // ✅ 使用equals()进行内容比较
    public boolean safeEquals(String s1, String s2) {
        return Objects.equals(s1, s2);  // 处理null情况
    }
    
    // ✅ 常量放在前面,避免NullPointerException
    public boolean constantFirst(String input) {
        return "ADMIN".equals(input);  // 而不是 input.equals("ADMIN")
    }
    
    // ✅ 使用equalsIgnoreCase()进行忽略大小写比较
    public boolean caseInsensitiveEquals(String s1, String s2) {
        return s1 != null && s1.equalsIgnoreCase(s2);
    }
}

6.4 JDK 8+ 字符串优化特性

public class ModernStringOptimization {
    
    // JDK 8: 编译器自动优化字符串拼接
    public String compilerOptimization() {
        String name = "张三";
        int age = 25;
        // 编译器会自动转换为StringBuilder操作
        return "姓名: " + name + ", 年龄: " + age;
    }
    
    // JDK 8+: 使用String.join()
    public String joinStrings(String... parts) {
        return String.join(" - ", parts);
    }
    
    // JDK 11+: 使用新的字符串方法
    public void modernStringMethods() {
        String text = "  Hello World  ";
        
        // 重复字符串
        String repeated = "=".repeat(10);  // "=========="
        
        // 判断空白
        boolean isBlank = text.isBlank();
        
        // 去除空白(比trim()更强大)
        String stripped = text.strip();
        
        // 按行分割
        text.lines().forEach(System.out::println);
    }
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值