【Java】常用类

字符串相关

⭐不可变字符序列------String

特点
  • java.lang.String 类用于表示字符串。 在 Java 程序中,所有字符串字面量 (例如 "hello")都被视为 String 类的实例。

  • 🌠String 对象表示的是不可变的字符序列 。一旦一个 String 对象被创建,其值将无法被更改。任何对字符串内容的"修改"操作,实际上都会创建一个新的 String 对象

  • String 类被声明为 final,因此:不能被继承、其行为在语言层面是固定的,不允许通过子类方式改变字符串的语义。这保证了 String 不可变性的安全性和一致性。

  • 在 JDK 8 及之前,String 的字符内容存储在一个 char[] 数组中,该数组用于保存字符串的 UTF-16 编码字符:

    • value 被声明为 private,外部无法直接访问,也没有getset方法
    • value 被声明为 final,其引用不可被修改,String 类未提供任何方法用于修改数组中的单个字符

    因此,字符串内容在逻辑和物理层面上都保持不可变。

java 复制代码
public final class String
        implements Serializable, Comparable<String>, CharSequence {

    /** The value is used for character storage. */
    private final char value[];

    /** Cache the hash code for the string */
    private int hash; // Default to 0
    
    ...
  • 从 JDK 9 开始,String 类的底层实现进行了优化,改为使用 byte[] 数组。

官方说明指出:

大多数字符串对象只包含 Latin-1(ISO-8859-1)字符,这些字符只需要一个字节存储,而使用 char[] 会造成一半的空间浪费。

因此:如果字符串仅包含 Latin-1 字符,则使用 单字节编码;如果包含其他字符,则使用 UTF-16 编码(双字节);内部通过一个编码标志来区分使用的字符编码方式。该优化在不影响外部行为的前提下显著降低了内存占用。

java 复制代码
public final class String
        implements Serializable, Comparable<String>, CharSequence {

    @Stable
    private final byte[] value;

    private final byte coder;

    static final byte LATIN1 = 0;
    static final byte UTF16  = 1;

    private int hash; // Default to 0
    
    ...
  • 使用 + 运算符可进行字符串拼接;其他类型的对象在与字符串拼接时,会自动调用其 toString() 方法。
🌠内存结构

由于 String 对象被设计为不可变 ,相同内容的字符串在程序运行过程中可以被安全地共享。为了避免重复创建内容相同的字符串对象、降低内存开销并提升性能,JVM 引入了**字符串常量池(String Constant Pool)**这一机制,用于统一存储字符串字面量以及通过 String#intern() 方法维护的字符串实例。

JDK 6 及之前版本 中,字符串常量池属于 方法区(Method Area) 的一部分,方法区通常由 永久代(PermGen) 实现,其内存大小固定、垃圾回收能力有限,容易引发 OutOfMemoryError: PermGen space

JDK 7 开始 ,JVM 对内存结构进行了调整,将 字符串常量池从方法区移至 Java 堆(Heap) 中。这样做的好处包括:

  • 字符串常量池可以享受堆内存更灵活的扩展能力;
  • 垃圾回收机制更加完善,减少内存溢出风险;
  • 有利于提升整体内存管理的稳定性与可维护性。

需要注意的是,字符串常量池的位置变化并不影响 String 不可变性的语义,而是 JVM 内部实现层面的优化调整。

java 复制代码
String s1 = "hello";
String s2 = "hello";
System.out.println(s1 == s2);      // true
// 内存中只有一个"hello"对象被创建,同时被s1和s2共享

s1 = "hi";
java 复制代码
class Person {
    String name;
}

public class Main {
    public static void main(String[] args) {
        Person p1 = new Person();
        p1.name = "Tom";

        Person p2 = new Person();
        p2.name = "Tom";

        System.out.println(p1.name.equals(p2.name)); // 内容比较                 true
        System.out.println(p1.name == p2.name);      // 引用地址比较              true
        System.out.println(p1.name == "Tom");        // 引用地址比较              true
    }
}

p1.name.equals(p2.name)true

  • .equals() 比较字符串内容是否一致。
  • "Tom".equals("Tom") → 内容相同 → true

p1.name == p2.nametrue

  • == 比较的是对象在内存中的引用地址
  • Java 对字符串字面量(如 "Tom")会进行字符串常量池优化
  • 所以虽然是两次赋值,实际上 "Tom" 指向的是同一个字符串对象 → true

p1.name == "Tom"true

  • 同上,p1.name 指向字符串常量 "Tom" → 同一个对象引用 → true

若改为:

java 复制代码
p1.name = new String("Tom");
p2.name = new String("Tom");

结果:

java 复制代码
true
false
false
  • .equals() 依然比较内容 → true
  • 但两个 new String("Tom") 创建了两个不同的对象 → 引用不同 → ==false

✨有关new

java 复制代码
String s1 = "abc";
String s2 = new String("abc");
System.out.println(s1 == s2);      // false
  • 没有在堆中创建新对象;s1 指向的是字符串常量池中的String对象(如果 "abc" 不存在则在常量池创建String("abc")字符串对象;有则复用)
  • s2指向在堆中创建的一个新的String对象,堆中String对象的value数组指向常量池中常量对象的value数组(同样会检查字符串常量池,无则创建,有则复用)

总结:"abc" 形式会直接使用字符串常量池中的对象;new String("abc") 会在堆中创建一个新的 String 对象,该对象的 value 数组指向常量池中已有字符串对象的 value 数组,因此内容相同但引用不同。

Tips:String s = new String("hello"); 可能会在内存中创建 2 个对象

java 复制代码
String s1 = "a";
String s2 = "a";
String s3 = new String("a");
String s4 = new String("a");

System.out.println(s1 == s2);//true
System.out.println(s1 == s3);//false
System.out.println(s1 == s4);//false
System.out.println(s3 == s4);//false

⭐有关intern()

  • String s1 = "a";

说明:在字符串常量池中创建了一个字面量为"a"的字符串。

  • s1 = s1 + "b";

说明:实际上原来的"a"字符串对象已经丢弃了,现在在堆空间中产生了一个字符串s1+"b"(也就是"ab")。如果多次执行这些改变串内容的操作,会导致大量副本字符串对象存留在内存中,降低效率。如果这样的操作放到循环中,会极大影响程序的性能。

最初 s1 指向 "a"(常量池)。拼接 "b" 后,s1 指向堆中新的 "ab" 对象。

  • String s2 = "ab";

说明:直接在字符串常量池中创建一个字面量为"ab"的字符串。

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

说明:s3指向字符串常量池中已经创建的"ab"的字符串。

  • String s4 = s1.intern();

说明:堆空间的s1对象在调用intern()之后,会将常量池中已经存在的"ab"字符串赋值给s4。

练习:

java 复制代码
public class Main {
    public static void main(String[] args) {
        String s1 = "hello";
        String s2 = "world";
        String s3 = "hello" + "world";  // 编译期常量拼接
        String s4 = s1 + "world";       // 运行期拼接
        String s5 = s1 + s2;            // 运行期拼接
        String s6 = (s1 + s2).intern(); // intern() 返回常量池中的引用(如果有)

        System.out.println(s3 == s4); // false
        System.out.println(s3 == s5); // false
        System.out.println(s4 == s5); // false
        System.out.println(s3 == s6); // true
    }
}

详解:

String s3 = "hello" + "world";

  • 编译器在编译阶段 就会将它优化为 "helloworld",所以 s3 指向字符串常量池 中的 "helloworld"

String s4 = s1 + "world";

  • s1 是一个变量,虽然值是常量 "hello",但由于不是编译期常量,拼接发生在运行时 ,生成的新对象不在常量池中 → new String("helloworld")

String s5 = s1 + s2;

  • 同样,s1s2 都是变量,拼接在运行时发生 → 新的堆对象

String s6 = (s1 + s2).intern();

  • s1 + s2 是运行时生成的 "helloworld"(堆中对象)
  • 调用 .intern() 会去常量池中查找 是否已有 "helloworld"
    • 有的话返回常量池中的引用(即 s3
    • 所以 s6 == s3true

😊结论:

(1)常量+常量:结果是常量池。且常量池中不会存在相同内容的常量。

(2)常量与变量 或 变量与变量:结果在堆中

(3)拼接后调用intern方法:返回值在常量池中

示例

java 复制代码
public class Main {
    public static void main(String[] args) {
        String s1 = "hello";
        String s2 = "world";
        String s3 = "helloworld";

        String s4 = s1 + "world";//s4字符串内容也是helloworld,s1是变量,"world"常量,变量 + 常量的结果在堆中
        String s5 = s1 + s2;//s5字符串内容也helloworld,s1和s2都是变量,变量 + 变量的结果在堆中
        String s6 = "hello" + "world";//常量+ 常量 结果在常量池中,因为编译期间就可以确定结果

        System.out.println(s3 == s4);//false
        System.out.println(s3 == s5);//false
        System.out.println(s3 == s6);//true
    }
}

final

java 复制代码
public class Main {
    public static void main(String[] args) {
        final String s1 = "hello";
        final String s2 = "world";
        String s3 = "helloworld";

        String s4 = s1 + "world";//s4字符串内容也helloworld,s1是常量,"world"常量,常量+常量结果在常量池中
        String s5 = s1 + s2;//s5字符串内容也helloworld,s1和s2都是常量,常量+ 常量 结果在常量池中
        String s6 = "hello" + "world";//常量+ 常量 结果在常量池中,因为编译期间就可以确定结果

        System.out.println(s3 == s4);//true
        System.out.println(s3 == s5);//true
        System.out.println(s3 == s6);//true
    }
}

intern()返回常量池中的引用(若有)

java 复制代码
public class Main {
    public static void main(String[] args) {
        String s1 = "hello";
        String s2 = "world";
        String s3 = "helloworld";

        String s4 = (s1 + "world").intern();//把拼接的结果放到常量池中
        String s5 = (s1 + s2).intern();

        System.out.println(s3 == s4);//true
        System.out.println(s3 == s5);//true
    }
}

concat方法拼接,哪怕是两个常量对象拼接,结果也是在堆。

java 复制代码
public class Main {
	public static void main(String[] args) {
		String s1 = "hello";
		String sr2 = "world";
		String s3 ="helloworld";
		
		String s4 = "hello".concat("world");
		String s5 = "hello" + "world";
		
		System.out.println(s3 == s4);//false
		System.out.println(s3 == s5);//true
	}
}
java 复制代码
public class StringTest {

    String str = new String("good");
    char[] ch = { 't', 'e', 's', 't' };

    public void change(String str, char ch[]) {
        str = "test ok";
        ch[0] = 'b';
    }
    
    public static void main(String[] args) {
        StringTest ex = new StringTest();
        ex.change(ex.str, ex.ch);
        System.out.println(ex.str + " and ");// good and 
        System.out.println(ex.ch); // best
    }
}
常用API

问AI

⭐可变字符序列------StringBuffer、StringBuilder

在 Java 中之所以要引入 StringBufferStringBuilder ,是因为 String 类是不可变的。当对 String 进行拼接、修改等操作时,实际上都会创建新的 String 对象,原有对象不会被改变,这在频繁频繁修改或拼接字符串的场景下会产生大量临时对象,造成内存浪费和性能下降。为了解决这一问题,Java 提供了 StringBuffer 和 StringBuilder,它们是可变字符串类 ,可以在原对象上直接进行修改,从而提高效率。其中,StringBuffer 是线程安全的 ,适用于多线程环境;StringBuilder 是非线程安全的,但性能更高。

  • StringBuilderStringBuffer 均继承AbstractStringBuilder
java 复制代码
abstract class AbstractStringBuilder implements Appendable, CharSequence {

    /** 用于存储字符的数组 */
    char[] value;                // 没有 final,value 可以指向新数组

    /** 当前字符序列的长度 */
    int count;
    
    ...
}
  • StringBuilderStringBufferAPI 一致,与 String 大致相同

Java比较器

目的

引用数据类型是不能像基本数据类型直接使用比较运算符来比较大小的,而Java 比较器是一种定义对象之间大小关系(排序规则)的机制。

Java 提供了两套比较机制:

比较器 所在包 特点
Comparable java.lang 自然排序(类自身定义)
Comparator java.util 外部排序(临时/多策略)

Comparable------自然排序(内置排序规则)

java 复制代码
public interface Comparable<T> {
    int compareTo(T o);
}
  • 实现 Comparable 接口的类必须实现 compareTo 方法,两个对象即通过方法的返回值来比较大小。如果当前对象大于形参对象,则返回正整数,小于则返回负整数,等于则返回零。
  • 实现 Comparable 接口的对象列表或数组可以通过 Collections.sort 或 Arrays.sort 等进行自动排序。实现此接口的对象可以用作有序映射中的键或有序集合中的元素,无需指定比较器。
  • 如果对类 C 的任意两个对象 e1 和 e2 来说:e1.compareTo(e2) == 0 的结果,和 e1.equals(e2) 的结果永远相同(要么都为 true,要么都为 false),那么就称类 C 的自然排序与 equals 方法是一致的。Java 建议(但不强制)这样设计。

示例

java 复制代码
public class Student implements Comparable<Student> {
    private String name;
    private int age;

    @Override
    public int compareTo(Student o) {
        return this.age - o.age; // 按年龄升序
    }
}
java 复制代码
List<Student> list = new ArrayList<>();
Collections.sort(list); // 不需要额外比较器

缺点

  • 必须修改类源码,不适合第三方类(如 StringInteger 已写死)
  • 只能有一种排序规则

⭐Comparator------外部比较器(灵活排序)

  • 适用于不方便修改类代码的场景
  • 比较器独立于类,可以随时指定不同排序规则,也不会影响其他地方实现了Comparable接口的排序规则使用
java 复制代码
public interface Comparator<T> {
    int compare(T o1, T o2);
}
  • 重写方法,比较o1和o2的大小:如果方法返回正整数,则表示o1大于o2;如果返回0,表示相等;返回负整数,表示o1小于o2。
  • 可将 Comparator 传递给 sort 方法(如 Collections.sort 或 Arrays.sort),从而允许在排序顺序上实现精确控制。

示例

java 复制代码
Comparator<Student> ageComparator = new Comparator<Student>() {
    @Override
    public int compare(Student o1, Student o2) {
        return o1.getAge() - o2.getAge();
    }
};

Collections.sort(list, ageComparator);

Lambda

java 复制代码
Collections.sort(list, (o1, o2) -> o1.getAge() - o2.getAge());

工具方法

java 复制代码
list.sort(Comparator.comparingInt(Student::getAge));

👉 先按年龄,再按姓名

java 复制代码
list.sort(
    Comparator.comparing(Student::getAge)
              .thenComparing(Student::getName)
);

总结

Comparable 用于定义类的"自然排序",Comparator 用于定义"灵活、多策略"的外部排序规则;实际开发中,Comparator 使用更频繁。

系统相关

java.lang.System

java.lang.System 是 Java 的核心类之一,位于 java.lang 包中,无需导包即可使用。

它主要用于:

  • 标准输入 / 输出
  • 获取系统时间
  • 操作 JVM(退出、垃圾回收)
  • 读取系统属性(如 OS、Java 版本等)

System 类中的成员几乎都是 static 的 ,通常通过 System.xxx 直接调用。

System 的 3 个成员变量

1️⃣ System.in ------ 标准输入流

java 复制代码
public static final InputStream in;

作用:

  • 表示标准输入流
  • 默认对应键盘输入
  • 常用于 ScannerBufferedReader

📌 Scanner 中的 System.in

java 复制代码
Scanner sc = new Scanner(System.in);

示例:

java 复制代码
import java.util.Scanner;

public class TestIn {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        System.out.print("请输入一个整数:");
        int num = sc.nextInt();
        System.out.println("你输入的是:" + num);
    }
}

2️⃣ System.out ------ 标准输出流

java 复制代码
public static final PrintStream out;

作用:

  • 表示标准输出流
  • 默认输出到控制台
  • 常用于打印信息

示例:

java 复制代码
public class TestOut {
    public static void main(String[] args) {
        System.out.println("Hello Java");
        System.out.print("不换行输出");
    }
}

📌 System.out.println() 是最常用的调试和输出方式。


3️⃣ System.err ------ 标准错误输出流

java 复制代码
public static final PrintStream err;

作用:

  • 表示标准错误输出流
  • 用于输出错误或警告信息
  • 默认也是输出到控制台,但在日志或重定向时与 out 可区分

示例:

java 复制代码
public class TestErr {
    public static void main(String[] args) {
        System.out.println("普通信息");
        System.err.println("错误信息");
    }
}

📌 实际项目中常用于:

  • 错误日志
  • 异常提示

System 的 4 个成员方法

1️⃣ currentTimeMillis() ------ 获取当前时间戳

java 复制代码
public static long currentTimeMillis()

作用:

  • 返回当前时间与 1970-01-01 00:00:00 UTC 之间的毫秒数
  • 常用于:
    • 计算程序运行时间
    • 生成时间戳

示例:

java 复制代码
public class TestTime {
    public static void main(String[] args) {
        long start = System.currentTimeMillis();

        for (int i = 0; i < 1000000; i++) {
            // 模拟耗时操作
        }

        long end = System.currentTimeMillis();
        System.out.println("耗时:" + (end - start) + " ms");
    }
}

2️⃣ exit(int status) ------ 终止 JVM

java 复制代码
public static void exit(int status)

作用:

  • 立即终止 Java 虚拟机(JVM)
  • 参数含义:
    • 0:正常退出
    • 0:异常退出

示例:

java 复制代码
public class TestExit {
    public static void main(String[] args) {
        System.out.println("程序开始");
        System.exit(0);
        System.out.println("这行不会执行");
    }
}

📌 常用于:

  • 程序出现严重错误
  • 控制台工具程序

3️⃣ gc() ------ 建议 JVM 进行垃圾回收

java 复制代码
public static void gc()

作用:

  • 通知 JVM 进行垃圾回收
  • 不保证立即执行(只是建议)

示例:

java 复制代码
public class TestGC {
    public static void main(String[] args) {
        System.out.println("请求垃圾回收");
        System.gc();
    }
}

📌 实际开发中:

  • 很少手动调用
  • 垃圾回收主要由 JVM 自动管理

4️⃣ getProperty(String key) ------ 获取系统属性

方法签名:

java 复制代码
public static String getProperty(String key)

作用:

  • 获取 Java 运行环境或操作系统相关信息
  • 常用于环境判断、路径获取
属性名 说明
java.version Java 版本
java.home Java 安装路径
os.name 操作系统名称
os.version 操作系统版本
user.name 当前用户名
user.home 用户主目录
user.dir 当前工作目录
file.separator 文件分隔符(/\
java 复制代码
public class TestProperty {
    public static void main(String[] args) {
        System.out.println("Java版本:" + System.getProperty("java.version"));
        System.out.println("操作系统:" + System.getProperty("os.name"));
        System.out.println("用户目录:" + System.getProperty("user.home"));
        System.out.println("当前路径:" + System.getProperty("user.dir"));
    }
}

java.lang.Runtime

Runtime 类表示 Java 程序的运行环境,主要用于:

  • JVM 运行时环境 交互
  • 获取 JVM 内存信息
  • 主动触发垃圾回收
  • 执行外部程序(如 .exe、脚本)

📌 特点:

  • Runtime 是一个单例类
  • 不能通过 new Runtime() 创建对象
  • 只能通过 Runtime.getRuntime() 获取实例

✨常用方法

1️⃣ getRuntime() ------ 获取 Runtime 实例

java 复制代码
public static Runtime getRuntime()

作用:

  • 获取当前 Java 程序唯一的 Runtime 对象

示例:

java 复制代码
public class TestRuntime {
    public static void main(String[] args) {
        Runtime runtime = Runtime.getRuntime();
        System.out.println(runtime);
    }
}

2️⃣ exit(int status) ------ 终止 JVM

java 复制代码
public void exit(int status)

作用:

  • 终止 Java 虚拟机
  • System.exit(int) 效果相同

示例:

java 复制代码
public class TestExit {
    public static void main(String[] args) {
        Runtime.getRuntime().exit(0);
        System.out.println("不会执行");
    }
}

📌 通常更推荐使用 System.exit(),语义更直观。


3️⃣ gc() ------ 请求垃圾回收

java 复制代码
public void gc()

作用:

  • 请求 JVM 进行垃圾回收
  • 不保证立即执行

示例:

java 复制代码
public class TestGC {
    public static void main(String[] args) {
        Runtime.getRuntime().gc();
        System.out.println("已请求GC");
    }
}

4️⃣ freeMemory() ------ 获取空闲内存

java 复制代码
public long freeMemory()

作用:

  • 返回 JVM 中可用的空闲内存
  • 单位:字节(byte)

5️⃣ totalMemory() ------ 获取 JVM 已分配内存

java 复制代码
public long totalMemory()

作用:

  • 返回 JVM 当前已向操作系统申请的内存总量

6️⃣ maxMemory() ------ 获取 JVM 最大内存

java 复制代码
public long maxMemory()

作用:

  • 返回 JVM 能使用的最大内存
  • -Xmx 参数相关

示例:查看 JVM 内存情况

java 复制代码
public class TestMemory {
    public static void main(String[] args) {
        Runtime runtime = Runtime.getRuntime();

        System.out.println("最大内存:" + runtime.maxMemory() / 1024 / 1024 + " MB");
        System.out.println("已分配内存:" + runtime.totalMemory() / 1024 / 1024 + " MB");
        System.out.println("空闲内存:" + runtime.freeMemory() / 1024 / 1024 + " MB");
    }
}

📌 常用于:

  • 性能调优
  • 排查内存问题

7️⃣ exec(String command) ------ 执行外部程序(⚠️重点)

方法签名:

java 复制代码
public Process exec(String command) throws IOException

作用:

  • 在 Java 程序中执行 操作系统命令
  • 返回 Process 对象

示例 1:打开记事本(Windows)

java 复制代码
public class TestExec {
    public static void main(String[] args) throws Exception {
        Runtime.getRuntime().exec("notepad");
    }
}

示例 2:执行系统命令

java 复制代码
public class TestExecCmd {
    public static void main(String[] args) throws Exception {
        Runtime.getRuntime().exec("cmd /c dir");
    }
}

数学相关

java.lang.Math

数学工具类

java.math.BigInteger

超大整数运算,long 不够用(超过 ±9×10¹⁸),需要精确的大整数计算

java.math.BigDecimal

高精度小数运算,金融计算(金额、利率、税费)

java.util.Random

生成伪随机数

相关推荐
雨中飘荡的记忆2 小时前
MyBatis类型处理模块详解
java·mybatis
金牌归来发现妻女流落街头2 小时前
【线程池 + Socket 服务器】
java·运维·服务器·多线程
wanghowie2 小时前
01.03 Spring核心|事务管理实战
java·后端·spring
Chen不旧2 小时前
Java模拟死锁
java·开发语言·synchronized·reentrantlock·死锁
千寻技术帮2 小时前
10356_基于Springboot的老年人管理系统
java·spring boot·后端·vue·老年人
最贪吃的虎2 小时前
Redis 除了缓存,还能干什么?
java·数据库·redis·后端·缓存
崎岖Qiu2 小时前
【设计模式笔记24】:JDK源码分析-Comparator中的「策略模式」
java·笔记·设计模式·jdk·策略模式
萧曵 丶2 小时前
Java 安全的单例模式详解
java·开发语言·单例模式
Qiuner2 小时前
Spring Boot 全局异常处理策略设计(一):异常不只是 try-catch
java·spring boot·后端