Java基础(二)——常用类库之Object类、包装类、Math类

148 阅读10分钟
版本 说明 发布日期
1.0 发布文章第一版 2022-06-09

前言

  • 这篇文章不是面面俱到的基础知识集合,只是我个人的学习笔记。
  • 以下所有内容仅代表个人观点,不一定正确,请大家辩证地阅读。
  • 以下内容基于java 1.8。

常用类库总览

包名说明
java.lang核心包,包含了一些比较重要的类,该包中的所有内容由Java虚拟机自动导入。例如System、String。这个lang其实是language的缩写。
java.util工具包,提供了工具类和集合类。如Scanner、Random、List。
java.io输入输出包,提供了大量流相关的类。如FileInputStream。
java.net网络包,提供了网络编程相关的类。如ServerSocket、Socket。
java.sql数据包,提供了大量数据库操作相关的类。如DriverManager、Connection

Object类——这就是万物皆对象~

基本概念

  • 位于java.lang.Object。
  • 是java中所有类的根类,也就是说是java所有类的直接或者间接父类。
    • 一个人只能有一个对象,但是一个java它可以有很多很多个对象(说到这里,有些小伙伴流下了羡慕的口水)。
    • java表示自己对象太多,不好管啊!所以用一个Object类统一管理。这么一说Object有点像曾经的东厂哈哈哈。
  • 如果一个类定义的时候没有extends关键字,则默认会extends Object。这就是上面说的直接父类。

常用方法

方法声明功能介绍
Object()无参构造方法。
boolean equals(Object obj)用于判断两个对象是否相等。Object类中的逻辑是比较地址是否相等,与 == 运算符的结果一致。所以该方法通常需要重写。若该方法被重写,同时则应该重写hashCode方法,以保证结果的一致性。
int hashCode()用于获取调用对象的哈希码值(可通俗理解为内存地址的编号)。若两个对象调用equals方法相等,则各自调用该方法的结果必须相同;若两个调用对象equals方法不相等,则各自调用该方法的结果应该不相同。所以该方法通常需要与equals方法同时重写。
String toString()用于获取调用对象的字符串形式。该方法默认返回的字符串为:包名.类名@哈希码值的十六进制。为了返回更有意义的数据,该方法通常需要重写。使用print打印引用或使用字符串拼接引用时,都会自动调用该方法。
Class<?> getClass()用于获取调用对象的Class对象,反射机制使用。

boolean equals(Object obj)

  • Java API文档对于equals方法提出了几个特性,当我们重写equals时,应当自觉遵循这些特性。
  • 自反性:对于任何非空引用x,x.equals(x)应该返回true。
  • 对称性:对于任何非空引用x和y,x.equals(y)应该返回true,当且仅当y.equals(x)返回true。
  • 传递性:对于任何非空引用x、y和z。如果x.equals(y)返回true、y.equals(z)返回true。则x.equals(z)也应该返回true。
  • 一致性:对于任何非空引用x和y,如果未修改对象上用于equals比较的信息,则多次调用x.equals(y)应该始终返回true或始终返回false。
  • 非空性:对于任何非空引用x,x.equals(null)应该返回false。
  • 下面给一个最常见的equals重写模板,可以完全满足上面的所有特性。当然方法不是绝对的,只要能满足这些特性就OK啦~
public class Person {
    private String id;

    public Person(String id) {
        this.id = id;
    }

    public void setId(String id) {
        this.id = id;
    }

    //重写Object的equals方法
    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Person) {
            //比较特征
            return this.id.equals(((Person) obj).id);
        }
        return false;
    }

    //重写hashCode方法
    @Override
    public int hashCode(){
        return id.hashCode();
    }

    public static void main(String[] args) {
        Person yu = new Person("1996817");
        Person angel = new Person("1997414");
        System.out.println("angel.equals(yu):" + angel.equals(yu));
        System.out.println("更改id");
        yu.setId("1997414");
        System.out.println("angel.equals(yu):" + angel.equals(yu));
        System.out.println("yu.equals(angel):" + yu.equals(angel));
        System.out.println("angel.equals(angel):" + angel.equals(angel));
        System.out.println("angel.equals(null):" + angel.equals(null));
    }
}
  • 运行结果如下:
angel.equals(yu):false
更改id
angel.equals(yu):true
yu.equals(angel):true
angel.equals(angel):true
angel.equals(null):false

int hashCode()

  • hashCode是在使用哈希集合(例如HashMap)时,在键值散列表中用到,这一点从API文档中也能看到。
  • 因此,equals不等的对象,hashCode需要尽量不等;equals相等的对象,hashCode必须相等。
  • 上面说过,重写了equals方法,必然还需要重写hashCode方法。但是怎么重写呢?
    • 通常,我们会分别对equals中的比较特征值(例如上例的id)求hash,然后将他们的和作为hash结果。
    • Objects类提供了一个静态方法:hash(Object... values)。获取比较特征值的hash时,经常会用到。

自动生成equals、hashCode、toString

  • 如果你用的idea,恭喜你,这三个方法可以自动按照模板生成(默认快捷键:Alt + Insert)。
  • 上面的例子,如果使用idea的自动生成,代码将会如下。
@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    Person person = (Person) o;
    return Objects.equals(id, person.id);
}

@Override
public int hashCode() {
    return Objects.hash(id);
}

@Override
public String toString() {
    return "Person{" +
            "id='" + id + ''' +
            '}';
}

包装类

基本概念

  • 位于java.lang.Byte、java.lang.Short等。
  • 用于将基本数据类型包装成对象,以满足Java“万物皆对象”的理念。
  • 目前包装类有以下几种:Byte、Short、Integer、Long、Float、Double、Boolean、Character。分别对应一种基本数据类型。
  • 数字类型的包装类统一继承了抽象类Number。
  • 将基本数据类型转换为包装类,称为装箱;将包装类转换为基本数据类型,称为拆箱。从Java1.5开始,增加了大量自动拆箱和装箱的机制。
  • 下面讲解几种典型的包装类。

Integer

常用常量

常量声明功能
public static final int MAX_VALUE表示int类型可以描述的最大值,即2^31-1
public static final int MIN_VALUE表示int类型可以描述的最小值,即-2^31
public static final int SIZE表示int类型二进制形式的位数
public static final int BYTES表示int类型所占的字节个数
public static final Class TYPE表示int类型的Class实例

常用方法

方法声明功能介绍
int intValue()获取调用对象中的整数值并返回。作为拆箱的标准方法。
static Integer valueOf(int i)根据参数指定整数值得到Integer类型对象。作为装箱的标准方法。
boolean equals(Object obj)比较调用对象与参数指定的对象是否相等
String toString()返回描述调用对象数值的字符串形式
static int parseInt(String s)将字符串类型转换为int类型并返回。字符串不合理时会抛出NumberFormatException。
static String toString(int i)获取参数指定整数的十进制字符串形式。注意和上面的toString的区别:静态且参数为int。
static String toBinaryString(int i)获取参数指定整数的二进制字符串形式
static String toHexString(int i)获取参数指定整数的十六进制字符串形式
static String toOctalString(int i)获取参数指定整数的八进制字符串形式

自动装箱机制

  • Java1.5之后有了自动装箱机制,从而让下面两行代码的效果是等价的。也就是说Integer对整数类型的赋值运算,会自动调用valueOf。
Integer a = 100;
Integer b = Integer.valueOf(100);
  • 如果想看下自动装箱池是什么样的,可以去看源码:Integer类的静态内部类IntegerCache。

自动装箱池(常量池)

  • 猜猜下面的结果是啥
public class IntegerTest {
    public static void main(String[] args){
        Integer a = 1;
        Integer b = 1;
        Integer c = Integer.valueOf(1);
        Integer d = new Integer(1);
        Integer e = 128;
        Integer f = 128;

        System.out.println(a==b);
        System.out.println(c==b);
        System.out.println(d==b);
        System.out.println(f==e);
    }
}
  • 结果是
true
true
false
false
  • 有的小伙伴又懵了:怎么着?说好的==比地址呢?怎么结果看起来如此木有规律!?
  • 但是这个真的比的是地址!为什么呢?因为包装类有一个叫自动装箱池的东西,对于-128~127的整数,对应的包装类都作为常量存放在了内存当中。所以a、b、c都是直接指向的内存中的这些常量,地址也就理所当然是相同的了。
  • 需要注意的是,如果是通过直接调用构造方法,则不会用到自动装箱池,而是指向的新new出来的内存。

自动拆箱机制

  • 猜猜下面的结果是啥
Integer a = 1;
Integer b = 2;
Long g = 3L;
System.out.println(g == (a + b));
System.out.println(g.equals(a + b));
  • 结果如下:
true
false
  • 为什么呢?
    • 对于第一个比较:大家可能第一时间会想到==比较的是地址,所以理应为false。但是包装类在进行运算符运算的时候,会触发“自动拆箱机制”,也就是说a + b这东西,计算结果会从Integer变为int。而Long与int进行==比较,Long也会变为long。所以最终就变成了基本数据类型的比较,结果自然是true。
    • 对于第二个比较:这个比较简单,因为Long类重写了equals方法,自己去看一下就知道了。如果比较的是非Long类型,则直接为false。

Double

  • Double和Integer基本大同小异,只是有一个方法需要提及一下:boolean isNaN()
  • 这个方法是非静态的,所以肯定是Double的引用来调用,也就是说调用这个方法的肯定是一个数字。所以为啥还要判断是否是数字呢?
  • 然后看一下下面这个例子就明白了哈哈哈。此非数字不是彼非数字。
public class DoubleTest {
	public static void main(String[] args) {
		Double a = 0/0.0;
		System.out.println(a);
		System.out.println(a.isNaN());
	}
}
  • 运行结果如下。也就是说这个方法是用来判断是否存在0/0.0的情况的。顺道提一句,0/0直接编译器报算术运算错误哈。之所以0.0可以除,0不能除,终究还是因为误差的问题。
NaN
true
  • 还有一个boolean isInfinite()方法也是同理。无穷大嘛,一个非零数字除以0.0的情况。

Boolean

  • 同样是大同小异,但是依然有一个小东东需要说明一下,这个东东就是boolean parseBoolean(String s)方法
  • 这个方法通过查看源码可以看到,当s是"true"时(且不区分大小写),则返回true;否则,其他任何情况下都是返回false。
  • 还有一个小小细节。别的包装类都有常量SIZE和BYTES,但Boolean没有哦~看过我写的《Java基础(一)》的小伙伴肯定都明白是怎么肥四。

Character

  • 大家猜猜我要说什么?没错!同样是大同小异!但是Character有几个方法还是比较常用的,下面列一下:
方法声明功能
static boolean isUpperCase(char ch)判断是否为大写字符
static boolean isLowerCase(char ch)判断是否为小写字符
static boolean isDigit(char ch)判断是否为数字字符
static char toUpperCase(char ch)转换为大写字符
static char toLowerCase(char ch)转换为小写字符

总而言之~

  • 总而言之,手动装箱方式:valueOf()
  • 总而言之,手动拆箱方式:xxxValue()
  • 总而言之,字符串转换为基本数据类型的方式:parseXxx()

Math

  • 位于java.lang.Math。提供了各种和数学相关的方法,例如对数、幂、根、三角函数等。
  • 这个包其实没啥好讲的,因为用不用得好,和编程水平没啥关系,主要取决于数学水平。哈哈哈哈!!!不过还是放几个常用的方法来感受一下吧:
方法声明功能
static int max(int a, int b)返回两个参数中的最大值
static int min(int a, int b)返回两个参数中的最小值
static double pow(double a, double b)返回a的b次幂
static int abs(int a)返回参数的绝对值
static long round(double a)返回参数四舍五入到整数的结果
static double sqrt(double a)返回参数的平方根
static double random()返回0.0到1.0的随机数。不过不推荐用这个方法,因为Random类更加强大。
  • 哦对了,提一句。这个类除了构造方法,其他的所有东西都是static修饰的哦!