Java常用的API_01 (重写`equals()`方法时必须重写`hashCode()`方法)

本文介绍了Java中的常用API,如Math库函数、System.currentTimeMillis获取时间、质数判断算法,以及System.arraycopy用于数组复制。还讨论了浅克隆和深克隆的区别,特别是在对象拷贝时对基本数据类型和引用数据类型的处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

跟着黑马的Java学习视频学者本篇常用的API

一、Math

在这里插入图片描述

public static int abs(int a) {
    return (a < 0) ? -a : a;
}

public static double ceil(double a) {
    return StrictMath.ceil(a);
}

public static double floor(double a) {
    return StrictMath.floor(a);
}


public static double pow(double a, double b) {
    return StrictMath.pow(a, b); 
    // default impl. delegates to StrictMath}
}

public static int max ( int a, int b){
    return (a >= b) ? a : b;
}

二、System

(1)虚拟机停止 System.exit(0);

System.out.println("hello1");
System.exit(0);
System.out.println("hello2");

在这里插入图片描述

(2) 返回当前系统的时间毫秒值形式 public static long currentTimeMillis()

public class TestCurrentTimeMillis {

    public static void main(String[] args) {
        long start1 = System.currentTimeMillis();
        for (int i = 2; i < 10000; i++) {
            if (isPrimer1(i)) {
                System.out.println(i);
            }
        }
        long end1 = System.currentTimeMillis();
        System.out.println("isPrimer1 execution time: " + (end1 - start1) + "ms");

        long start2 = System.currentTimeMillis();
        for (int i = 2; i < 10000; i++) {
            if (isPrimer2(i)) {
                System.out.println(i);
            }
        }
        long end2 = System.currentTimeMillis();
        System.out.println("isPrimer2 execution time: " + (end2 - start2) + "ms");
    }

    // 判断一个数是否为质数(未优化)
    public static boolean isPrimer1(int number) {
        if (number <= 1) return false;
        for (int i = 2; i < number; i++) {
            if (number % i == 0) {
                return false;
            }
        }
        return true;
    }

    // 判断一个数是否为质数(优化,只需检查到其平方根)
    public static boolean isPrimer2(int number) {
        if (number <= 1) return false;
        for (int i = 2; i <= Math.sqrt(number); i++) {
            if (number % i == 0) {
                return false;
            }
        }
        return true;
    }


}

(3)System.arraycopy方法进行数组拷贝

对于基本数据类型数组(如int[]):

   int[] arr1 = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
   int[] arr2 = new int[10];
   System.arraycopy(arr1, 0, arr2, 4, 3);
  

对于引用数据类型数组(这里假设有一个父类Person和子类Student的关系):

   Student s1 = new Student("张三", 23);
   Student s2 = new Student("李四", 24);
   Student s3 = new Student("王五", 25);

   Student[] brr1 = {s1, s2, s3};
   Person[] brr2 = new Person[3];

   System.arraycopy(brr1, 0, brr2, 0, 3);
   
 for (int i = 0; i < brr2.length; i++) {
       // 强转如果数据源数组和目的地数组都是引用数据类型,那么子类类型可以赋值给父类类型
       Student stu=(Student) brr2[i];
       System.out.println(stu.getName() + " " + stu.getAge());
   }

三、Object是顶级父类,只有无参构造

/*object是顶级父类,只有无参构造*/
public class APi_03Object {
    public static void main(String[] args) {
        // Student类的实例化和equals()方法使用部分已注释掉

        String str = "abc";
        StringBuilder sb = new StringBuilder("abc");
        
        // 比较String对象和StringBuilder对象的内容是否相等
        System.out.println(str.equals(sb.toString())); // true,因为sb.toString()返回"abc"
        System.out.println(sb.toString().equals(str)); // true,同样比较的是字符串内容

        // 以下两行代码会抛出编译错误,因为StringBuilder的equals()方法不会直接与String比较
        // System.out.println(str.equals(sb));
        // System.out.println(sb.equals(str));
    }
}

(1)默认情况下,因为Object类中的toString方法返回的是地址值

当打印一个对象,想要看到属性值的话,那么就重写toString方法就可以了。需要在重写的方法中,把对象的属性值进行拼接。

(2)equals 判断对象是否相等,默认使用“==”来比较

重写equals()方法时必须重写hashCode()方法

是因为在Java中,如果两个对象相等(即equals()方法返回true), 它们的哈希码(hash code)必须相等。
如果两个对象的equals()方法相等但哈希码不相等, 这会导致在使用基于哈希的集合(如HashMap、HashSet)时出现问题。
因此,为了保持一致性,必须同时重写这两个方法。

在Java中,equals() 方法和 hashCode() 方法的关系非常密切,主要体现在使用哈希表(如 HashMap, HashSet 等)时的数据一致性。这两个方法必须遵守以下规则:

  1. 如果两个对象相等(即 equals() 方法返回 true),那么它们的 hashCode() 方法必须返回相同的整数值。
  2. 如果两个对象的 hashCode() 方法返回相同的值,这并不要求它们一定相等(即 equals() 方法返回 true)。

如果你重写了 equals() 方法但没有重写 hashCode() 方法,可能会违反上述的第一条规则,导致在使用哈希表时出现问题。例如,如果两个对象根据 equals() 是相等的,但它们的 hashCode() 返回不同的值,那么在哈希表中,这两个对象可能被错误地存储在不同的位置,从而导致数据的不一致性。

因此,为了保证哈希表等数据结构的正确性和性能,当你重写 equals() 方法时,也应该相应地重写 hashCode() 方法。这样可以确保相等的对象能够有相同的哈希码,从而在哈希表中正确地归类和查找。

例子

让我们通过一个简单的Java类来说明为什么需要同时重写 equals()hashCode() 方法。

假设我们有一个 Person 类,其中包含两个属性:nameage。我们希望两个 Person 对象在 nameage 都相同时,被视为相等。

public class Person {
    private String name;
    private int age;

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

    // 未重写 hashCode()
}

如果我们只重写了 equals() 而没有重写 hashCode(),那么可能会在使用哈希表时遇到问题。例如:

HashSet<Person> set = new HashSet<>();
Person p1 = new Person("John", 30);
Person p2 = new Person("John", 30);

set.add(p1);
set.add(p2);

System.out.println(set.size());  // 输出可能是 2 而不是预期的 1

由于 p1p2 是相等的,我们期望 HashSet 只包含一个元素。但是,如果没有重写 hashCode()p1p2 可能会有不同的哈希码,导致它们被视为不同的元素。

现在,让我们重写 hashCode() 方法:

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

这样,当 nameage 相同时,hashCode() 会返回相同的值,确保 HashSet 正确地识别出 p1p2 是相同的元素,从而 set.size() 将输出 1,符合我们的预期。

未重写
在这里插入图片描述

重写了的
在这里插入图片描述

四、Clone

把A对象的属性值完全拷贝给B对象,
也叫对象拷贝,对象复制

基本数据类型: 存储的是真实的值
引用数据类型: 存储的是另一个空间的地址值

(1)浅克隆

不管对象内部的属性是基本数据类型还是引用数据类型,都完全拷贝过来
public static void main(String[] args) {
    int[] data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0};
    User u1 = new User(1, "张三", "123", "CC", data);

    User u2 = (User) u1.clone();
    System.out.println(u1.arrToString());
    System.out.println(u2.arrToString());


//        验证浅克隆
    int[] arr = u1.getData();
    arr[0] = 100;
    System.out.println(u1.arrToString());
    System.out.println(u2.arrToString());
}

(2)深克隆

基本数据类型拷贝过来字符串复用引用数据类型会重新创建新的

@Override
protected Object clone() throws CloneNotSupportedException {

//        先把克隆对象中的数组获取出来
    int[] data = this.data;
//        创建新的数组
    int[] newdata = new int[data.length];

    for (int i = 0; i < data.length; i++) {
        newdata[i] = data[i];
    }
    User u = (User) super.clone();
    u.data = newdata;
    return u;
}
public static void main(String[] args) {
    int[] data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0};
    User u1 = new User(1, "张三", "123", "CC", data);

    User u2 = (User) u1.clone();
    System.out.println(u1.arrToString());
    System.out.println(u2.arrToString());


//        验证浅克隆
    int[] arr = u1.getData();
    arr[0] = 100;
    System.out.println(u1.arrToString());
    System.out.println(u2.arrToString());
}

(3)深克隆 – Gson

import com.google.gson.Gson;

public class Main {
    public static void main(String[] args) {
        // 创建一个Gson对象
        Gson gson = new Gson();

        // 将Java对象转换为JSON字符串
        String json = gson.toJson(new YourObject());

        // 将JSON字符串转换为Java对象
        YourObject obj = gson.fromJson(json, YourObject.class);
    }

    // 定义一个示例Java对象
    static class YourObject {
        String name;
        int age;

        public YourObject() {
            this.name = "Alice";
            this.age = 30;
        }
    }
}

五、Objects

在Java中,Objects类是一个实用类,提供了一些静态方法来操作对象。它包含方法如equals(), hash(), isNull()等。您可以使用这些方法来执行对象的比较、哈希计算和空检查等操作。

equals
如果没有重写,比较地址值,如果重写了,就比较属性值。

// 示例代码
Object obj1 = new Object();
Object obj2 = new Object();

// 使用Objects类的equals方法比较两个对象是否相等
boolean isEqual = Objects.equals(obj1, obj2);

// 使用Objects类的hash方法计算对象的哈希值
int hashValue = Objects.hash(obj1, obj2);

// 使用Objects类的isNull方法检查对象是否为null
boolean isNull = Objects.isNull(obj1);

六、精确的数(Biginteger、BigDecima)

6.1 BigInteger

对象一旦创建,内部记录的值就不能再发生改变
构造方法

通过指定基数和字符串值构造:
BigInteger(String value, int radix)

通过字符串值构造
BigInteger(String value)

获取随机大整数,范围: [0 ~ 2的num次方-1]
public BigInteger(int num, Random rnd)

在这里插入图片描述
BigInteger.valueOf(long val) 是一个静态方法,用于创建一个包含指定 long 值的 BigInteger 实例。这个方法可以方便地将基本数据类型转换为 BigInteger 对象。

6.1.1 静态方法

BigInteger类中的静态方法valueOf(long val)可以接受long类型的值作为参数,该值的范围在-2^632^63-1之间。如果超出这个范围,可以使用BigInteger的构造函数来处理更大范围的整数。

在内部对于常用的数字-16~16进行了优化,先行创建好的,多次获取的不会重新创建新的。

对象一旦创建内部数据不能发生改变

在这里插入图片描述

6.1.2 BigInteger构造方法小结

  1. 静态方法获取:如果BigInteger表示的数字没有超出long的范围,可以用静态方法获取。
  2. 构造方法获取:如果BigInteger表示的数字超出long的范围,可以用构造方法获取。
  3. 不可变性:对象一旦创建,BigInteger内部记录的值不能发生改变。
  4. 新对象生成:只要进行计算都会产生一个新的BigInteger对象。

6.1.3 常见方法

public BigInteger add(BigInteger val); // 返回两个 BigInteger 的和
public BigInteger subtract(BigInteger val); // 返回两个 BigInteger 的差

public BigInteger multiply(BigInteger val); // 返回两个 BigInteger 的乘积
public BigInteger divide(BigInteger val); // 返回两个 BigInteger 的商
public BigInteger mod(BigInteger val); // 返回两个 BigInteger 除法的余数
public BigInteger[] divideAndRemainder(BigInteger val) //商、余数
public BigInteger pow(int exponent); // 返回 BigInteger 的指数次幂

public BigInteger abs(); // 返回 BigInteger 的绝对值
public BigInteger negate(); // 返回 BigInteger 的负值

public int compareTo(BigInteger val); // 比较两个 BigInteger 的大小

public BigInteger max(BigInteger val); // 返回两个 BigInteger 中的最大值
public BigInteger min(BigInteger val); // 返回两个 BigInteger 中的最小值

public BigInteger and(BigInteger val); // 进行位与运算
public BigInteger or(BigInteger val); // 进行位或运算
public BigInteger xor(BigInteger val); // 进行位异或运算
public BigInteger not(); // 进行位非运算
public BigInteger shiftLeft(int n); // 进行左移位运算
public BigInteger shiftRight(int n); // 进行右移位运算

public int intValue(); // 将 BigInteger 转换为 int 类型
public long longValue(); // 将 BigInteger 转换为 long 类型
public float floatValue(); // 将 BigInteger 转换为 float 类型
public double doubleValue(); // 将 BigInteger 转换为 double 类型
        // 创建两个 BigInteger 实例
        BigInteger num1 = new BigInteger("3");
        BigInteger num2 = new BigInteger("2");

        // 加法
        BigInteger sum = num1.add(num2);
        System.out.println("加法结果: " + sum);

        // 减法
        BigInteger difference = num1.subtract(num2);
        System.out.println("减法结果: " + difference);

        // 乘法
        BigInteger product = num1.multiply(num2);
        System.out.println("乘法结果: " + product);

        // 除法
        BigInteger quotient = num1.divide(num2);
        System.out.println("除法结果: " + quotient);

6.1.4 原理

Java中的BigInteger类是用于表示任意精度整数的类。它的底层原理是使用数组来存储大整数的各个部分,并提供了相应的方法来执行整数运算,如加法、减法、乘法和除法。BigInteger类允许处理比基本整数类型更大范围的整数值,因为它不受固定位数限制。
在这里插入图片描述

6.2 BigDecima不可变的,任意精度有符号计算的十进制数

BigDecimal是Java中用于高精度计算的类,它提供了任意精度的浮点数运算。与基本数据类型如double或float不同,BigDecimal可以准确表示浮点数运算,避免了传统浮点数运算中的精度丢失问题。

6.2.1 构造方法

  1. 通过字符串构造:BigDecimal(String val) - 通过字符串形式的数字创建一个BigDecimal对象。–没有不可预知性。

  2. 通过整型构造:BigDecimal(int val) - 通过整型值创建一个BigDecimal对象。

  3. 通过双精度浮点数构造:BigDecimal(double val) - 通过双精度浮点数创建一个BigDecimal对象。—有些不接预知性

这些构造方法允许您以不同的方式初始化BigDecimal对象,根据您的需求选择合适的构造方法。

import java.math.BigDecimal;

public class Main {
    public static void main(String[] args) {
        String numberStr = "123.456";
        BigDecimal bigDecimal = new BigDecimal(numberStr);
        
        System.out.println("BigDecimal value: " + bigDecimal);
    }
}
  1. 如果表示的数字不大,没有超出double的取值范围,建议使用静态方法valueOf
  2. 如果表示的数字比较大,超出了double的取值范围,建议使用构造方法。
  3. 如果传递的是0到10之间的整数(包括0和10),valueOf方法会返回已经创建好的对象,不会重新创建新的对象。

6.2.2 静态方法

BigDecimal类的静态方法valueOf允许您通过传入基本数据类型值来创建一个BigDecimal对象。这个方法有几种重载形式,其中一种常见的是valueOf(long val),它接受一个长整型值作为参数并返回对应的BigDecimal对象。这种方法提供了一种方便的方式来将基本数据类型转换为BigDecimal对象。

import java.math.BigDecimal;

public class Main {
    public static void main(String[] args) {
        long longValue = 1000L;
        
        BigDecimal bigDecimal = BigDecimal.valueOf(longValue);
        
        System.out.println("BigDecimal value: " + bigDecimal);
    }
}

在这里插入图片描述

6.2.3 常用方法

1. `add(BigDecimal val)` - 将当前BigDecimal对象与另一个BigDecimal对象相加。
   
2. `subtract(BigDecimal val)` - 从当前BigDecimal对象中减去另一个BigDecimal对象。
   
3. `multiply(BigDecimal val)` - 将当前BigDecimal对象与另一个BigDecimal对象相乘。
   
4. `divide(BigDecimal val, int scale, RoundingMode roundingMode)` - 将当前BigDecimal对象除以另一个BigDecimal对象,可以指定保留小数位数和舍入方式。
   
5. `compareTo(BigDecimal val)` - 将当前BigDecimal对象与另一个BigDecimal对象进行比较。

在这里插入图片描述

例子

        BigDecimal num1 = new BigDecimal("10.5");
        BigDecimal num2 = new BigDecimal("5.2");

        // Addition
        BigDecimal sum = num1.add(num2);
        System.out.println("Sum: " + sum);

        // Subtraction
        BigDecimal difference = num1.subtract(num2);
        System.out.println("Difference: " + difference);

        // Multiplication
        BigDecimal product = num1.multiply(num2);
        System.out.println("Product: " + product);

        // Division
        BigDecimal quotient = num1.divide(num2, 2, RoundingMode.HALF_UP);
        System.out.println("Quotient: " + quotient);

        // Comparison
        int comparison = num1.compareTo(num2);
        System.out.println("Comparison result: " + comparison);

6.2.4 常见的舍入方式

  1. RoundingMode.UP - 向远离零的方向舍入,即向正无穷大方向舍入。

  2. RoundingMode.DOWN - 向零的方向舍入,即截断小数部分。

  3. RoundingMode.CEILING - 向正无穷大方向舍入,如果为负数则向零方向舍入。

  4. RoundingMode.FLOOR - 向负无穷大方向舍入,如果为负数则向负无穷大方向舍入。

  5. RoundingMode.HALF_UP - 四舍五入,当小数部分大于等于0.5时向正无穷大方向舍入。

  6. RoundingMode.HALF_DOWN - 四舍五入,当小数部分大于0.5时向正无穷大方向舍入。

  7. RoundingMode.HALF_EVEN - 银行家舍入规则,当小数部分为0.5时,向最接近的偶数方向舍入。

这些舍入方式可以根据需要选择,以控制小数部分的舍入行为。

6.2.5 底层原理

BigDecimal类的底层原理涉及使用BigInteger来表示整数部分,以及使用int变量来表示小数点的位置和标度。这种设计允许BigDecimal类支持任意精度的小数运算,避免了传统浮点数运算中的精度丢失问题。

在这里插入图片描述

当执行加减乘除等运算时,BigDecimal类会根据操作数的标度进行运算,并根据需要调整结果的标度。此外,BigDecimal还提供了控制舍入方式的选项,以确保精确的计算结果。

总的来说,BigDecimal类通过使用整数部分和标度来表示数字,以及提供精确的数学运算和舍入控制,实现了高精度的浮点数计算。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

你会魔法吗✧(≖ ◡ ≖✿)

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值