杰瑞科技汇

Java中char与String的本质区别是什么?

char 是一个“字符”,而 String 是一串“字符”,它们是 Java 中处理文本数据最基础也是最重要的两个类型,但它们在本质、用法和性能上有天壤之别。

Java中char与String的本质区别是什么?-图1
(图片来源网络,侵删)

核心区别概览

特性 char String
本质 基本数据类型 引用数据类型 (对象)
单个 Unicode 字符 零个或多个字符的序列
内存分配 上分配内存,空间固定(2字节) 上分配内存,空间可变
默认值 '\u0000' (空字符) null
不可变性 可变 不可变
声明方式 char c = 'A'; String s = "Hello";
常用方法 无,是一个基本值 非常多,如 .length(), .charAt(), .substring()
性能 存取速度快,性能高 创建和修改有性能开销(因为涉及对象创建和垃圾回收)

详细解释

char - 字符

char 是 Java 的八种基本数据类型之一,它专门用来存储单个字符

关键点:

  • 单引号char 类型的字面量必须用单引号 括起来。
    char grade = 'A';
    char ch = '好'; // Java 使用 Unicode,所以可以存储中文字符
    char escape = '\n'; // 转义字符,代表换行
  • 内存占用char 在内存中占用 2 个字节(16位),这是因为它使用的是 UTF-16 编码来表示 Unicode 字符集中的任意一个字符。
  • 可变性char 是一个基本类型,它的值是可变的。
    char ch = 'A';
    System.out.println(ch); // 输出 A
    ch = 'B'; // 直接修改其值
    System.out.println(ch); // 输出 B
  • 用途:通常用于表示单个符号,比如性别、单个字母、单个汉字等。

String - 字符串

String 在 Java 中是一个,属于引用数据类型,它用来表示一个字符的序列,也就是我们常说的“字符串”。

关键点:

Java中char与String的本质区别是什么?-图2
(图片来源网络,侵删)
  • 双引号String 类型的字面量必须用双引号 括起来。
    String name = "张三";
    String message = "Hello, World!";
    String emptyString = ""; // 空字符串,长度为0
    String nullString = null; // 空值,表示没有指向任何字符串对象
  • 内存分配String 对象存储在堆内存中,当你创建一个 String 对象时,JVM 会在堆上分配空间来存储这些字符。
  • 不可变性:这是 String 最重要的特性,一个 String 对象被创建后,它的内容不能被改变
    String s = "Hello";
    s = "World"; // 这不是修改了 "Hello" 这个对象,而是让引用 s 指向了一个新的 "World" 对象

    任何看起来像是在修改字符串的操作(如 concat(), substring(), replace() 等),实际上都是:

    1. 基于原始字符串创建一个新的 String 对象。
    2. 将修改后的内容存入这个新对象。
    3. 返回这个新对象的引用。 原始的 String 对象如果没有任何引用指向它,就会被垃圾回收器回收。
  • 性能影响:由于 String 的不可变性,在循环或需要频繁修改字符串内容的场景中,反复创建新对象会带来显著的性能开销。
  • 用途:用于处理任何长度的文本,如句子、段落、文件路径、URL、用户输入等。

代码示例与对比

示例1:创建与修改

public class CharStringExample {
    public static void main(String[] args) {
        // --- char 示例 ---
        char c1 = 'A';
        System.out.println("初始 char 值: " + c1);
        c1 = 'B'; // 直接修改,没有创建新对象
        System.out.println("修改后的 char 值: " + c1);
        System.out.println("--------------------");
        // --- String 示例 ---
        String s1 = "Hello";
        System.out.println("初始 String 值: " + s1);
        System.out.println("s1 的内存地址 (hash): " + s1.hashCode());
        // 看似是修改,实际上是创建新对象
        s1 = s1 + ", Java!";
        System.out.println("拼接后的 String 值: " + s1);
        System.out.println("s1 修改后的内存地址 (hash): " + s1.hashCode());
        // 使用 concat 方法也是一样,它会返回一个新字符串
        String s2 = s1.concat(" Welcome!");
        System.out.println("使用 concat 后的值: " + s2);
        System.out.println("s2 的内存地址 (hash): " + s2.hashCode());
    }
}

输出分析: 你会看到 s1 在拼接前后的 hashCode 不同,这证明了 s1 引用指向了两个完全不同的 String 对象,原始的 "Hello" 对象如果不再被引用,就会被回收。

示例2:性能对比(在循环中)

public class PerformanceComparison {
    public static void main(String[] args) {
        // 使用 char 拼接 (伪代码,char本身不能拼接,这里用+=模拟)
        // char ch = 'A';
        // for (int i = 0; i < 10000; i++) {
        //     ch++; // 性能极高,只是一个简单的值修改
        // }
        // 使用 String 拼接 (性能差)
        String str = "";
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            str += i; // 在循环中,每次循环都创建一个新的 String 对象,性能极差!
        }
        long endTime = System.currentTimeMillis();
        System.out.println("使用 + 拼接耗时: " + (endTime - startTime) + " ms");
        // 使用 StringBuilder 拼接 (性能好)
        StringBuilder sb = new StringBuilder();
        startTime = System.currentTimeMillis();
        for (int i = 0; i < 100000; i++) {
            sb.append(i); // 在同一个对象上进行操作,不创建新对象,性能高
        }
        str = sb.toString();
        endTime = System.currentTimeMillis();
        System.out.println("使用 StringBuilder 拼接耗时: " + (endTime - startTime) + " ms");
    }
}

输出分析: 你会发现 的方式耗时非常长,而 StringBuilder 的方式几乎瞬间完成,这直观地展示了 String 不可变性带来的性能问题。StringBuilder 是 Java 提供的专门用于高效处理可变字符串的工具类。


总结与最佳实践

  1. 选择 char 还是 String

    • 如果你的数据永远只有一个字符,比如性别('M', 'F')、单个符号、单个字母,那么使用 char 是最合适的,它更轻量、更高效。
    • 除此之外,几乎所有需要处理文本的场景都应该使用 String,即使只有一个字符,用 String 也是完全可行的,String s = "A";
  2. String 不可变性的最佳实践

    • 不要在循环中使用 或 来拼接字符串,这会导致性能灾难。
    • 当你需要频繁修改字符串内容时(例如循环拼接、读取文件内容等),请使用 StringBuilderStringBuffer
      • StringBuilder:非线程安全,但性能更高,是绝大多数单线程环境下的首选。
      • StringBuffer:线程安全,方法有 synchronized 修饰,性能稍差,用于多线程环境。
  3. String 的常量池优化 Java 为了提高性能和节省内存,对 String 字面量做了一个优化,叫做字符串常量池

    String s1 = "hello";
    String s2 = "hello";
    System.out.println(s1 == s2); // 输出 true,因为它们指向常
分享:
扫描分享到社交APP
上一篇
下一篇