Java常用类详解两万字精华

1 Math类

1.1 概述

查看API文档,我们可以看到API文档中关于Math类的定义如下:

Math类是Java提供的一个工具类,位于java.lang包中,因此在使用时不需要显式导包。Math类被final修饰,意味着它不能被继承。它提供了一系列用于基本数学运算的静态方法,包括算术运算、指数运算、对数运算、三角函数等。由于所有方法都是静态的,调用这些方法时可以直接使用类名,而无需实例化对象。

1.2 常见方法

Java中Math类的常见方法如下:

public static int abs(int a)					// 返回参数的绝对值
public static double ceil(double a)				// 返回大于或等于参数的最小整数
public static double floor(double a)			// 返回小于或等于参数的最大整数
public static int round(float a)				// 按照四舍五入返回最接近参数的int类型的值
public static int max(int a, int b)				// 获取两个int值中的较大值
public static int min(int a, int b)				// 获取两个int值中的较小值
public static double pow(double a, double b)	// 计算a的b次幂的值
public static double random()					// 返回一个[0.0,1.0)的随机值

1.2.1 方法说明

  • abs(int a):返回参数的绝对值。如果参数为负数,则返回它的正值。
  • ceil(double a):返回大于或等于a的最小整数值,结果以double类型返回。即向上取整
  • floor(double a):返回小于或等于a的最大整数值,结果以double类型返回。即向下取整
  • round(float a):按照四舍五入的方式返回最接近aint类型的值。如果是double类型的值,则返回long类型的值。
  • max(int a, int b):返回两个数中的较大值。重载的方法还可以处理longfloatdouble等类型。
  • min(int a, int b):返回两个数中的较小值。重载的方法同样可以处理其他类型。
  • pow(double a, double b):返回ab次幂的值。结果以double类型返回。
  • random():返回一个[0.0, 1.0)之间的随机double值。

1.2.2 案例演示

以下是常见方法的示例代码:

public class MathDemo01 {
    
    
    public static void main(String[] args) {
    
    
        // 绝对值
        System.out.println("-2的绝对值为:" + Math.abs(-2));
        System.out.println("2的绝对值为:" + Math.abs(2));

        // 向上取整
        System.out.println("大于或等于23.45的最小整数为:" + Math.ceil(23.45));
        System.out.println("大于或等于-23.45的最小整数为:" + Math.ceil(-23.45));

        // 向下取整
        System.out.println("小于或等于23.45的最大整数为:" + Math.floor(23.45));
        System.out.println("小于或等于-23.45的最大整数为:" + Math.floor(-23.45));

        // 四舍五入
        System.out.println("23.45四舍五入的结果为:" + Math.round(23.45));
        System.out.println("23.55四舍五入的结果为:" + Math.round(23.55));

        // 最大值
        System.out.println("23和45的最大值为:" + Math.max(23, 45));

        // 最小值
        System.out.println("12和34的最小值为:" + Math.min(12, 34));

        // 幂运算
        System.out.println("2的3次幂计算结果为:" + Math.pow(2, 3));

        // 随机数
        System.out.println("获取到的0-1之间的随机数为:" + Math.random());
    }
}

输出结果:

-2的绝对值为:2
2的绝对值为:2
大于或等于23.45的最小整数为:24.0
大于或等于-23.45的最小整数为:-23.0
小于或等于23.45的最大整数为:23.0
小于或等于-23.45的最大整数为:-24.0
23.45四舍五入的结果为:23
23.55四舍五入的结果为:24
2345的最大值为:45
1234的最小值为:12
23次幂计算结果为:8.0
获取到的0-1之间的随机数为:0.7322484131745958

明白了,我会基于你提供的内容进行补充和完善。


2 System类

2.1 概述

查看API文档,我们可以看到API文档中关于System类的定义如下:

System类所在包为java.lang包,因此在使用的时候不需要进行导包。同时System类被final修饰了,因此该类不能被继承。

System类包含了系统操作的一些常用方法,比如获取当前时间所对应的毫秒值、终止当前JVM、进行数组复制等。这些方法为我们提供了对操作系统和Java虚拟机的直接控制。

2.2 常见方法

常见方法介绍

我们需要重点学习的System类中的常见方法如下所示:

public static long currentTimeMillis()			// 获取当前时间的毫秒值
public static void exit(int status)				// 终止当前正在运行的JVM,0表示正常退出,非0表示异常退出
public static native void arraycopy(Object src,  int  srcPos, Object dest, int destPos, int length); // 进行数组元素复制

2.3 常见方法补充与解释

1. currentTimeMillis() 方法
  • 功能:获取当前时间的毫秒值。从1970年1月1日00:00:00 GMT到现在所经过的时间,返回long类型的毫秒数。
  • 应用场景:常用于统计代码运行时间、实现定时功能等。

示例:

public class SystemDemo {
    
    
    public static void main(String[] args) {
    
    
        long start = System.currentTimeMillis();
        // 执行一段代码,比如循环计算
        for (int i = 0; i < 100000; i++) {
    
    
            System.out.println(i);
        }
        long end = System.currentTimeMillis();
        System.out.println("执行时间:" + (end - start) + " 毫秒");
    }
}

通过这个方法,可以方便地进行代码的性能分析。

2. exit(int status) 方法
  • 功能:终止正在运行的JVM进程,参数status为0表示正常退出,非0表示异常退出。
  • 应用场景:通常在异常处理、需要强制终止程序时使用。

示例:

public class SystemDemo {
    
    
    public static void main(String[] args) {
    
    
        System.out.println("程序开始执行");
        
        // 强制终止JVM
        System.exit(0);  // 或者System.exit(1)表示异常退出

        // 以下代码不会执行
        System.out.println("程序已经终止");
    }
}
3. arraycopy(Object src, int srcPos, Object dest, int destPos, int length) 方法
  • 功能:用于将一个数组从指定位置复制到另一个数组的指定位置,效率比手动复制更高。

  • 应用场景:常用于数组的复制、元素删除、元素插入等操作。

  • 参数说明

    • src:源数组
    • srcPos:源数组起始位置
    • dest:目标数组
    • destPos:目标数组的起始位置
    • length:要复制的元素个数

示例:

public class SystemDemo {
    
    
    public static void main(String[] args) {
    
    
        int[] srcArray = {
    
    1, 2, 3, 4, 5};
        int[] destArray = new int[10];

        // 复制srcArray的前3个元素到destArray中,从索引1开始
        System.arraycopy(srcArray, 0, destArray, 1, 3);

        // 输出结果 [0, 1, 2, 3, 0, 0, 0, 0, 0, 0]
        System.out.println(Arrays.toString(destArray));
    }
}

注意

  • 源数组和目标数组必须兼容(比如都是整型数组,或者子类数组赋值给父类数组)。
  • 如果拷贝时的length超出了数组的长度范围,会抛出ArrayIndexOutOfBoundsException异常。

2.4 arraycopy 的应用扩展

除了复制,arraycopy()还可以用于实现数组元素的删除或插入操作。以下是删除操作的示例:

删除数组中的某个元素

public class SystemDemo {
    
    
    public static void main(String[] args) {
    
    
        int[] arr = {
    
    10, 20, 30, 40, 50};
        int deleteIndex = 2;  // 要删除的元素位置

        // 删除数组的第3个元素(值为30),即将30后的元素依次向前移动
        System.arraycopy(arr, deleteIndex + 1, arr, deleteIndex, arr.length - deleteIndex - 1);

        // 将数组的最后一个元素置为0
        arr[arr.length - 1] = 0;

        // 输出结果:[10, 20, 40, 50, 0]
        System.out.println(Arrays.toString(arr));
    }
}

2.5 arraycopy 的底层细节

arraycopy是一个本地方法,它是由JVM底层实现的,用于高效地进行数组复制。该方法能直接操作内存,性能优于通过循环来复制数组。

arraycopy 方法的注意事项:

  1. 如果数据源数组和目标数组都是基本数据类型,那么它们的类型必须完全一致。
  2. 如果是引用数据类型,那么子类类型可以赋值给父类类型。
  3. arraycopy()不会自动调整目标数组的大小,目标数组必须能够容纳被复制的元素,否则会抛出ArrayIndexOutOfBoundsException异常。

3 Runtime类

3.1 概述

Runtime类表示 Java 程序的运行时环境,可以获取与当前 JVM 运行相关的系统资源信息,例如 CPU 核心数、内存信息等。Runtime类属于 java.lang 包,因此不需要显式导入。该类不能被实例化,只能通过静态方法 getRuntime() 获取 Runtime 对象,然后调用其方法来执行各种操作,如内存管理、执行命令等。

3.2 常见方法

下面列出了 Runtime 类中常用的方法,提供了对 JVM 运行时环境的访问和控制能力。

public static Runtime getRuntime()   // 获取当前 Java 程序的运行时环境对象
public void exit(int status)         // 终止当前正在运行的 Java 虚拟机,0 表示正常退出,非 0 表示异常退出
public int availableProcessors()     // 获取当前系统中可用的 CPU 核心数
public long maxMemory()              // 获取 JVM 可以从操作系统中申请的最大内存,单位为字节
public long totalMemory()            // 获取 JVM 已经从操作系统申请的内存总量,单位为字节
public long freeMemory()             // 获取 JVM 剩余可用内存,单位为字节
public Process exec(String command)  // 执行操作系统命令

方法说明:

  1. getRuntime():返回与当前 Java 应用程序关联的 Runtime 对象。通过它可以调用 JVM 环境相关的方法。
  2. exit(int status):终止当前 Java 虚拟机的运行。参数 status 为退出状态码,0 表示正常退出,非零表示异常退出。
  3. availableProcessors():返回 Java 虚拟机可用的处理器核心数,通常用于多线程编程时决定任务的并行度。
  4. maxMemory():返回 JVM 能从操作系统获取的最大内存量,通常与启动时的 -Xmx 选项相关。
  5. totalMemory():返回 JVM 当前已从操作系统分配的内存总量,单位为字节。
  6. freeMemory():返回 JVM 中未使用的可用内存,单位为字节。
  7. exec(String command):运行指定的操作系统命令,并返回一个 Process 对象,用于处理进程的输入输出。

代码示例:

public class RunTimeDemo {
    
    
    public static void main(String[] args) throws IOException {
    
    
        // 获取 Runtime 实例
        Runtime runtime = Runtime.getRuntime();

        // 获取 CPU 核心数
        System.out.println("可用的处理器核心数:" + runtime.availableProcessors());

        // 获取 JVM 的最大内存、总内存、空闲内存
        System.out.println("JVM 最大内存:" + runtime.maxMemory() / 1024 / 1024 + " MB");
        System.out.println("JVM 已分配的内存:" + runtime.totalMemory() / 1024 / 1024 + " MB");
        System.out.println("JVM 可用内存:" + runtime.freeMemory() / 1024 / 1024 + " MB");

        // 执行系统命令
        // 关闭计算机(此处为 Windows 命令,慎重执行)
        // runtime.exec("shutdown -s -t 3600");
    }
}

运行结果(部分输出示例):

可用的处理器核心数:8
JVM 最大内存:4064 MB
JVM 已分配的内存:254 MB
JVM 可用内存:251 MB

3.3 恶搞好基友

  1. Runtime.exec() 执行系统命令:

    Runtime.getRuntime().exec("shutdown -s -t 7200");// 7200s 后关机
    
    Runtime.getRuntime().exec("shutdown -a"); // 取消关机
    
    • shutdown -s -t N:在 N 秒后关机。
    • shutdown -a:取消关机。
  2. 当点击任何一个按钮时,调用 Runtime.getRuntime().exec() 方法来执行关机命令,并启动倒计时。点击取消按钮可终止关机操作。

  • 代码中涉及到的 shutdown 命令是 Windows 特有的命令,在不同操作系统上可能会有所不同。

4 Object类

4.1 概述

Object 类是 Java 中所有类的祖先类,也就是说,所有的类都直接或间接继承自 Object 类。它位于 java.lang 包中,因此无需导入即可使用。在 Java 中,每个对象都是 Object 类的实例或者其子类的实例。

查看API文档我们可以看到,在Object类中提供了一个无参构造方法,如下所示:

Object 类提供了基本的行为方法,如对象的比较、克隆、哈希值生成等,这些方法可以被子类重写以适应特定的需求。

4.2 常见方法

常用的 Object 类方法如下:

public String toString()             // 返回该对象的字符串表示形式(通常是类名 + 内存地址值)
public boolean equals(Object obj)    // 比较两个对象的地址值是否相同
protected Object clone()             // 对象克隆,实现浅拷贝
public int hashCode()                // 返回该对象的哈希码值

接下来通过案例演示 toString()equals() 的用法,以及对象克隆的实现。

4.2.1 toString 方法

toString() 方法返回该对象的字符串表示形式。在默认情况下,Object 类的 toString() 方法返回对象的类名加上对象的内存地址,但通常为了更有意义的输出,开发者会重写 toString() 方法以返回对象的成员信息。

示例:toString() 方法的使用

Student 类:

public class Student {
    
    

    private String name;
    private int age;

    public Student(String name, int age) {
    
    
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
    
    
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

测试类 ObjectDemo01:

public class ObjectDemo01 {
    
    

    public static void main(String[] args) {
    
    
        Student s1 = new Student("Alice", 20);
        System.out.println(s1);  // 调用 toString 方法
    }
}

输出结果:

Student{
    
    name='Alice', age=20}
解释:

Student 类中重写了 toString() 方法,使其返回学生的 nameage 信息。通过这种方式,打印对象时,可以清楚地查看对象的状态,而不是默认的内存地址表示。

4.2.2 equals 方法

equals() 方法用于比较两个对象是否相等。默认情况下,Object 类中的 equals() 方法比较的是两个对象的地址值。如果想要比较两个对象的内容是否相同,通常需要重写该方法。

示例:equals() 方法的使用

Student 类:

public class Student {
    
    

    private String name;
    private int age;

    public Student(String name, int age) {
    
    
        this.name = name;
        this.age = age;
    }

    @Override
    public boolean equals(Object obj) {
    
    
        if (this == obj) {
    
    
            return true;  // 如果两个对象的地址相同,则为相同对象
        }
        if (obj == null || getClass() != obj.getClass()) {
    
    
            return false;  // 如果对象为空或者类型不一致,则不相同
        }
        Student student = (Student) obj;  // 向下转型
        return age == student.age && name.equals(student.name);  // 比较属性值
    }
}

测试类 ObjectDemo02:

public class ObjectDemo02 {
    
    

    public static void main(String[] args) {
    
    
        Student s1 = new Student("Alice", 20);
        Student s2 = new Student("Alice", 20);
        System.out.println(s1.equals(s2));  // 调用 equals 方法
    }
}

输出结果:

true
解释:

通过重写 equals() 方法,我们实现了按属性值比较两个对象,而不是比较对象的内存地址。如果两个 Student 对象的 nameage 都相同,则返回 true,否则返回 false

4.2.3 clone 方法

clone() 方法用于创建对象的副本,通常称为“克隆”。Java 中默认的 clone() 方法实现浅拷贝(即对象中的基本类型会被复制,而引用类型只复制引用,不复制对象本身)。

示例:clone() 方法的使用

User 类(实现 Cloneable 接口):

public class User implements Cloneable {
    
    

    private String name;
    private int[] scores;

    public User(String name, int[] scores) {
    
    
        this.name = name;
        this.scores = scores;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
    
    
        User cloned = (User) super.clone();
        cloned.scores = this.scores.clone();  // 实现深拷贝
        return cloned;
    }

    @Override
    public String toString() {
    
    
        return "User{" +
                "name='" + name + '\'' +
                ", scores=" + Arrays.toString(scores) +
                '}';
    }
}

测试类 ObjectDemo03:

public class ObjectDemo03 {
    
    

    public static void main(String[] args) throws CloneNotSupportedException {
    
    
        int[] scores = {
    
    85, 90, 78};
        User user1 = new User("Bob", scores);

        // 克隆对象
        User user2 = (User) user1.clone();

        // 修改克隆对象的数组
        user2.scores[0] = 100;

        System.out.println("原始对象: " + user1);
        System.out.println("克隆对象: " + user2);
    }
}

输出结果:

原始对象: User{
    
    name='Bob', scores=[85, 90, 78]}
克隆对象: User{
    
    name='Bob', scores=[100, 90, 78]}
解释:

通过重写 clone() 方法,我们实现了深拷贝。克隆后修改 user2scores 数组并不会影响 user1scores 数组,因为我们在克隆时对数组进行了深拷贝。

4.2.4 hashCode 方法

hashCode() 方法用于返回对象的哈希码值,通常与 equals() 方法配合使用。当两个对象根据 equals() 方法相等时,它们的 hashCode() 值也必须相同。通常在使用 HashMapHashSet 等哈希表结构时会涉及 hashCode()

示例:重写 hashCode() 方法

Student 类中重写 hashCode() 方法,使其与 equals() 方法配合使用。

@Override
public int hashCode() {
    
    
    return Objects.hash(name, age);
}

通过这样重写,当两个对象的 nameage 相同时,它们的哈希码值也会相同,确保 equals()hashCode() 的一致性。

5 Objects类

5.1 概述

Objects 类位于 java.util 包下,是一个工具类,主要用于对象的常规操作,如比较、检查空值等。由于 Objects 类被 final 修饰,因此它不能被继承。同时,Objects 类中的所有方法都是静态方法,可以直接通过类名调用。

接下来我们来查看一下API文档,看一下Objects类中的成员,如下所示:

常见用途包括:

  • 判断对象是否为 null
  • 比较对象
  • 返回对象的 toString 表现形式

我们可以通过 Objects 类简化常见的对象操作,尤其是 null 检查。

5.2 常见方法

重点学习的 Objects 类方法如下:

public static String toString(Object o)                 // 返回对象的字符串表现形式
public static boolean equals(Object a, Object b)        // 比较两个对象是否相等
public static boolean isNull(Object obj)                // 判断对象是否为null
public static boolean nonNull(Object obj)               // 判断对象是否不为null

扩展方法(了解性方法):

public static <T> T requireNonNull(T obj)               // 检查对象是否不为null, 如果为null抛出异常
public static <T> T requireNonNullElse(T obj, T defaultObj) // 如果对象为null,返回默认对象;否则返回原对象
public static <T> T requireNonNullElseGet(T obj, Supplier<? extends T> supplier) // 如果对象为null,调用Supplier获取默认值

这些方法极大地简化了日常的对象操作,使代码更加简洁和安全,特别是在处理可能为 null 的对象时。

5.3 案例演示

5.3.1 基本方法演示

接下来通过一个案例演示 toStringequalsisNullnonNull 方法的使用。

Student 类:

public class Student {
    
    

    private String name;
    private int age;

    public Student(String name, int age) {
    
    
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
    
    
        return "Student{name='" + name + "', age=" + age + "}";
    }

    @Override
    public boolean equals(Object obj) {
    
    
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        Student student = (Student) obj;
        return age == student.age && Objects.equals(name, student.name);
    }
}

ObjectsDemo01 测试类:

public class ObjectsDemo01 {
    
    

    public static void main(String[] args) {
    
    
        method_01();
        method_02();
        method_03();
        method_04();
    }

    // 测试 toString 方法
    public static void method_01() {
    
    
        Student s1 = new Student("Alice", 20);
        System.out.println(Objects.toString(s1));  // 使用 Objects.toString 获取字符串表现形式
    }

    // 测试 equals 方法
    public static void method_02() {
    
    
        Student s1 = new Student("Alice", 20);
        Student s2 = new Student("Alice", 20);
        System.out.println(Objects.equals(s1, s2));  // 使用 Objects.equals 比较对象
    }

    // 测试 isNull 方法
    public static void method_03() {
    
    
        Student s1 = null;
        System.out.println(Objects.isNull(s1));  // 检查对象是否为 null
    }

    // 测试 nonNull 方法
    public static void method_04() {
    
    
        Student s1 = new Student("Alice", 20);
        System.out.println(Objects.nonNull(s1));  // 检查对象是否不为 null
    }
}

输出结果:

Student{
    
    name='Alice', age=20}
true
true
true
解释:
  • Objects.toString(): 返回 Student 对象的 toString() 表现形式。
  • Objects.equals(): 比较两个对象的属性是否相等。
  • Objects.isNull(): 判断对象是否为 null
  • Objects.nonNull(): 判断对象是否不为 null

5.3.2 扩展方法演示

接下来通过案例演示扩展方法 requireNonNullrequireNonNullElserequireNonNullElseGet 的用法。

ObjectsDemo02 测试类:

public class ObjectsDemo02 {
    
    

    public static void main(String[] args) {
    
    
        method_01();
        method_02();
        method_03();
    }

    // 演示 requireNonNull
    public static void method_01() {
    
    
        Student s1 = new Student("Bob", 21);
        Student student = Objects.requireNonNull(s1);  // 检查 s1 不为 null
        System.out.println(student);
    }

    // 演示 requireNonNullElse
    public static void method_02() {
    
    
        Student s1 = null;
        Student student = Objects.requireNonNullElse(s1, new Student("Tom", 22));  // 如果 s1 为 null,则返回默认对象
        System.out.println(student);
    }

    // 演示 requireNonNullElseGet
    public static void method_03() {
    
    
        Student s1 = null;
        Student student = Objects.requireNonNullElseGet(s1, () -> new Student("Jerry", 23));  // 使用 Supplier 获取默认对象
        System.out.println(student);
    }
}

输出结果:

Student{
    
    name='Bob', age=21}
Student{
    
    name='Tom', age=22}
Student{
    
    name='Jerry', age=23}
解释:
  • requireNonNull(): 检查对象不为 null,否则抛出 NullPointerException
  • requireNonNullElse(): 如果对象为 null,返回默认值。
  • requireNonNullElseGet(): 如果对象为 null,使用 Supplier 动态生成默认值。

6 BigInteger类

6.1 引入

在存储整数时,Java 提供的 int 类型的取值范围是 -2147483648 到 2147483647。如果要存储更大的整数,可以使用 long 类型,它的取值范围更大,但如果 long 类型也无法满足需求,Java 提供了 BigInteger 类,用于处理任意精度的整数。

BigInteger 能存储和操作非常大的整数

​ 有多大呢?理论上最大到42亿的21亿次方

​ 基本上在内存撑爆之前,都无法达到这个上限。

6.2 概述

BigInteger 类位于 java.math 包下,因此在使用时需要导包。它能够进行大整数的计算,涵盖四则运算、幂运算、比较等功能。

BigInteger 的构造方法:

public BigInteger(int num, Random rnd)       // 获取随机大整数,范围:[0 ~ 2^num - 1]
public BigInteger(String val)                // 获取指定的大整数
public BigInteger(String val, int radix)     // 获取指定进制的大整数

静态方法:

public static BigInteger valueOf(long val)   // 静态方法获取 BigInteger 对象,适用于 long 范围内的数值

构造方法小结:

  • 如果 BigInteger 表示的数字没有超出 long 的范围,可以用 valueOf() 方法创建对象。
  • 如果表示的数字超出了 long 的范围,可以使用构造方法创建对象。
  • BigInteger 是不可变对象,一旦创建,内部值不会发生改变。每次运算都会产生一个新的 BigInteger 对象。

6.3 常见方法

BigInteger 提供了丰富的运算方法,常用的包括:

public BigInteger add(BigInteger val)                    // 加法
public BigInteger subtract(BigInteger val)               // 减法
public BigInteger multiply(BigInteger val)               // 乘法
public BigInteger divide(BigInteger val)                 // 除法
public BigInteger[] divideAndRemainder(BigInteger val)   // 获取商和余数
public boolean equals(Object x)                          // 比较是否相等
public BigInteger pow(int exponent)                      // 幂运算
public BigInteger max(BigInteger val)                    // 返回较大值
public BigInteger min(BigInteger val)                    // 返回较小值
public int intValue()                                    // 转为 int 类型整数,超出范围会产生错误

示例代码 1:创建与基本运算

import java.math.BigInteger;

public class BigIntegerDemo1 {
    
    
    public static void main(String[] args) {
    
    
        // 获取一个指定的大整数,可以超出 long 的取值范围
        BigInteger bigInt1 = new BigInteger("123456789123456789123456789");

        // 进行四则运算
        BigInteger bigInt2 = new BigInteger("987654321987654321987654321");
        BigInteger sum = bigInt1.add(bigInt2);  // 加法
        BigInteger difference = bigInt1.subtract(bigInt2);  // 减法
        BigInteger product = bigInt1.multiply(bigInt2);  // 乘法
        BigInteger quotient = bigInt1.divide(bigInt2);  // 除法

        System.out.println("加法结果: " + sum);
        System.out.println("减法结果: " + difference);
        System.out.println("乘法结果: " + product);
        System.out.println("除法结果: " + quotient);

        // 获取商和余数
        BigInteger[] result = bigInt1.divideAndRemainder(bigInt2);
        System.out.println("商: " + result[0] + ", 余数: " + result[1]);
    }
}

示例代码 2:进制转换和其他运算

import java.math.BigInteger;

public class BigIntegerDemo2 {
    
    
    public static void main(String[] args) {
    
    
        // 创建一个二进制表示的 BigInteger
        BigInteger binaryInt = new BigInteger("110", 2);
        System.out.println("二进制 '110' 转换为十进制: " + binaryInt);

        // 幂运算:计算 10^3
        BigInteger ten = BigInteger.valueOf(10);
        BigInteger powerResult = ten.pow(3);
        System.out.println("10 的 3 次幂: " + powerResult);

        // 比较大小
        BigInteger big1 = new BigInteger("123456789");
        BigInteger big2 = new BigInteger("987654321");
        BigInteger max = big1.max(big2);
        BigInteger min = big1.min(big2);
        System.out.println("较大值: " + max);
        System.out.println("较小值: " + min);

        // 判断相等
        boolean isEqual = big1.equals(big2);
        System.out.println("big1 和 big2 是否相等: " + isEqual);
    }
}

输出结果:

加法结果: 1111111111111111111111111110
减法结果: -864197532864197532864197532
乘法结果: 1219326321033379059972231488171785450390620086789237045688774558783992481
除法结果: 0
商: 0, 余数: 123456789123456789123456789
二进制 '110' 转换为十进制: 6
10 的 3 次幂: 1000
较大值: 987654321
较小值: 123456789
big1 和 big2 是否相等: false

6.4 底层存储方式

BigInteger 的底层实现是基于二进制的位操作。实际上,BigInteger 的数据存储在一个整数数组中,每 32 位为一组来表示数值。理论上,BigInteger 的表示范围仅受内存限制,可以存储极其庞大的数值。

对于计算机而言,数据是以二进制的形式存储的,BigInteger 对应的数值被分割成多个 32 位的组来进行存储。由于 BigInteger 不可变,因此每次进行计算时,都会生成一个新的 BigInteger 对象。

数组中最多能存储元素个数:21亿多

数组中每一位能表示的数字:42亿多

理论上,BigInteger能表示的最大数字为:42亿的21亿次方。

但是还没到这个数字,电脑的内存就会撑爆,所以一般认为BigInteger是无限的。

存储方式如图所示:

6.5 小结

  • BigInteger 是 Java 提供的用于处理大整数的类,它支持任意精度的整数运算。
  • 它提供了常用的四则运算、幂运算、比较操作等方法,适合处理超出基本数据类型范围的整数计算。
  • BigInteger 是不可变类,每次运算都会生成新的对象。
  • 在内存允许的范围内,BigInteger 可以表示极其庞大的数值。

7 BigDecimal类

7.1 引入

在日常开发中,使用 floatdouble 类型进行浮点数运算时,可能会遇到精度丢失的问题。为了避免这种情况,Java 提供了 BigDecimal 类,来进行高精度的浮点数运算。下面是一个关于精度丢失的例子:

public class BigDecimalDemo01 {
    
    
    public static void main(String[] args) {
    
    
        System.out.println(0.09 + 0.01);
    }
}

运行结果:

0.09999999999999999

这个结果说明使用 floatdouble 进行浮点数运算可能会丢失精度,这是由于计算机底层将十进制浮点数转换为二进制时出现的误差。为了解决这个问题,BigDecimal 类应运而生,它能确保高精度的运算。

7.2 概述

BigDecimal 类位于 java.math 包下,提供了精确的浮点数运算,适用于金融、科学计算等对精度要求较高的场景。查看 API 文档中的定义:

7.3 常见方法

构造方法

public BigDecimal(String val)            // 通过字符串创建 BigDecimal 对象
public BigDecimal(double val)            // 通过双精度浮点数创建 BigDecimal 对象 (不推荐)

推荐使用 String 构造方法来创建 BigDecimal 对象,因为使用 double 构造时,仍然可能会丢失精度。

成员方法

BigDecimal 类提供了以下常见的数学运算方法:

public BigDecimal add(BigDecimal value)               // 加法运算
public BigDecimal subtract(BigDecimal value)          // 减法运算
public BigDecimal multiply(BigDecimal value)          // 乘法运算
public BigDecimal divide(BigDecimal value)            // 除法运算

案例演示

案例 1:四则运算
public class BigDecimalDemo01 {
    
    
    public static void main(String[] args) {
    
    
        BigDecimal b1 = new BigDecimal("0.3");
        BigDecimal b2 = new BigDecimal("4");

        System.out.println(b1.add(b2));        // 加法
        System.out.println(b1.subtract(b2));   // 减法
        System.out.println(b1.multiply(b2));   // 乘法
        System.out.println(b1.divide(b2));     // 除法
    }
}

输出结果:

4.3
-3.7
1.2
0.075
案例 2:除法特殊情况

当进行除法运算的结果为无限循环小数时,会抛出 ArithmeticException 异常。

public class BigDecimalDemo02 {
    
    
    public static void main(String[] args) {
    
    
        BigDecimal b1 = new BigDecimal("1");
        BigDecimal b2 = new BigDecimal("3");

        System.out.println(b1.divide(b2));   // 触发异常
    }
}

运行结果:

Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.

为了解决这个问题,我们可以使用带有舍入模式的 divide 方法:

BigDecimal divide(BigDecimal divisor, int scale, RoundingMode roundingMode)
  • scale:精确到小数点后的位数
  • roundingMode:舍入模式,如 RoundingMode.UPRoundingMode.FLOORRoundingMode.HALF_UP
案例 3:取舍模式
import java.math.BigDecimal;
import java.math.RoundingMode;

public class BigDecimalDemo03 {
    
    
    public static void main(String[] args) {
    
    
        BigDecimal b1 = new BigDecimal("1");
        BigDecimal b2 = new BigDecimal("3");

        // 向上舍入
        System.out.println(b1.divide(b2, 2, RoundingMode.UP));

        // 向下舍入
        System.out.println(b1.divide(b2, 2, RoundingMode.FLOOR));

        // 四舍五入
        System.out.println(b1.divide(b2, 2, RoundingMode.HALF_UP));
    }
}

运行结果:

0.34
0.33
0.33

小结

  • BigDecimal 是精确浮点数运算的首选,特别是在财务计算等对精度要求高的场景。
  • 在使用 BigDecimal 进行除法运算时,建议指定舍入模式以避免异常。

7.4 底层存储方式

BigDecimal 底层使用字符串形式存储数字,将每一位数字转换为数组中的元素,以便精确存储和计算。

小结

  • BigDecimal 的底层存储是基于字符串处理的,因此可以保证极高的精度。
    scale, RoundingMode roundingMode)

- `scale`:精确到小数点后的位数
- `roundingMode`:舍入模式,如 `RoundingMode.UP`、`RoundingMode.FLOOR`、`RoundingMode.HALF_UP` 等

#### 案例 3:取舍模式

```java
import java.math.BigDecimal;
import java.math.RoundingMode;

public class BigDecimalDemo03 {
    public static void main(String[] args) {
        BigDecimal b1 = new BigDecimal("1");
        BigDecimal b2 = new BigDecimal("3");

        // 向上舍入
        System.out.println(b1.divide(b2, 2, RoundingMode.UP));

        // 向下舍入
        System.out.println(b1.divide(b2, 2, RoundingMode.FLOOR));

        // 四舍五入
        System.out.println(b1.divide(b2, 2, RoundingMode.HALF_UP));
    }
}

运行结果:

0.34
0.33
0.33

小结

  • BigDecimal 是精确浮点数运算的首选,特别是在财务计算等对精度要求高的场景。
  • 在使用 BigDecimal 进行除法运算时,建议指定舍入模式以避免异常。

7.4 底层存储方式

BigDecimal 底层使用字符串形式存储数字,将每一位数字转换为数组中的元素,以便精确存储和计算。

[外链图片转存中…(img-G7CcHuxt-1730011704009)]

小结

  • BigDecimal 的底层存储是基于字符串处理的,因此可以保证极高的精度。
  • 在进行高精度计算时,推荐使用 BigDecimal 而不是 floatdouble,并使用合适的舍入模式。

猜你喜欢

转载自blog.csdn.net/PQ781826/article/details/143268822