JDK16新特性中的record类是什么以及如何使用

482 阅读4分钟

我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第2篇文章,点击查看活动详情

record 定义

javarecord 这个功能,是在 JDK 14 以预览版形式发布的,在 JDK 16 中才正式发布

record 档案类是用来表示 不可变数据的透明载体

不可变数据:是指档案类一旦创建就不能改变了

透明载体:是指档案类帮我们默认实现了其中的一些方法,这些方法可以直接使用,也可以自己替换掉

record 关键字的使用

public record User(String name, Integer age) {
    
}
  1. 上面这段代码创建的是一个类名为 User 的类,不是方法;
  2. 使用 record 关键字替换了 class 关键字,用来标识该类是档案类;
  3. 小括号内的是该类声明的参数,可以看成是构造方法;
  4. 大括号内的,就是该类的具体实现代码了,但是现在也不需要声明变量,也不需要写构造方法;

使用 User 对象,并赋值和读取数据

public class Main {

    public static void main(String[] args) {
        // 创建user对象
        User user = new User("张三", 20);
        System.out.println(user.name());
        System.out.println(user.age());
        System.out.println(user);
    }

}

输出的结果

张三
20
User[name=张三, age=20]

档案类一旦创建,就不能再改变里面的值了,所以除了构造方法外,就没有给参数赋值的地方了。

而且如果要声明其它的字段,也只能是 静态 的,声明方法也是 静态

下面是声明静态变量和方法的示例

public record User(String name, Integer age) {

    /**
     * 声明静态变量
     */
    static final Integer SEX = 1;

    /**
     * 声明静态方法
     *
     * @param name 姓名
     * @return User
     */
    static User noAgeUser(String name) {
        return new User(name, 0);
    }
}

使用的话,直接通过类名就可以调用静态成员

public class Main {

    public static void main(String[] args) {

        System.out.println(User.SEX);

        User user = User.noAgeUser("李四");
        System.out.println(user);
        
    }

}

到这里就解释了不可变数据的意思:一旦创建,里面的数据就不能改变了!

内置缺省方法(透明载体)

档案类已经帮我们写好了以下这些方法

  • 构造方法
  • equals() 方法
  • hashCode() 方法
  • toString() 方法
  • 读取不可变数据的方法

怎样验证上面这些方法是真的内置好的呢?

首先是构造方法,我们在上面创建 User 对象的时候就使用了,

如果没有有参构造方法的话,是无法直接 new 并传参的

User user = new User("张三", 20);

equals()hashCode() 方法,我们来验证一下

public static void main(String[] args) {

    User user1 = new User("张三", 20);
    User user2 = new User("张三", 20);

    System.out.println(user1.equals(user2));
    // 这里输出为 true
}

创建两个对象,入参一致,这个时候控制台输出的结果为 true

这就表明,档案类已经帮我们重写了 equals()hashCode() 这两个方法

如果没有重写,那这里输出的肯定是 false,因为创建的两个对象内存地址是不一样的

toString() 方法我们在上面直接输出对象的时候,也看到了

System.out.println(user);

输出的是

User[name=张三, age=20]

如果没有重写 toString() 方法的话,那输出的应该是该对象的内存地址,像下面这样

com.example.demo.User@2328c243

重写默认实现

同样的,上述几个内置的方法,我们也可以使用 @Override 进行重写,替换掉默认的实现

除了构造方法不用 @Override 注解(其实其它的方法也可以不用加,但是代码规范最好还是加上)

public record User(String name, Integer age) {

    /**
     * 重写构造方法,不需要 @Override 注解
     */
    public User {
        System.out.println("重写了构造方法");
    }

    /**
     * 重写 getName()
     *
     * @return
     */
    @Override
    public String name() {
        return name;
    }

    /**
     * 重写 getAge()
     *
     * @return
     */
    @Override
    public Integer age() {
        return age + 1;
    }

    /**
     * 重写 equals
     *
     * @param obj the reference object with which to compare.
     * @return
     */
    @Override
    public boolean equals(Object obj) {
        return false;
    }

    /**
     * 重写 hashCode
     *
     * @return
     */
    @Override
    public int hashCode() {
        return 0;
    }

    /**
     * 重写 toString
     *
     * @return
     */
    @Override
    public String toString() {
        return null;
    }
}

到这里解释了透明载体:默认实现了其中的一些方法,这些方法可以直接使用,也可以自己替换掉。

一般情况下,档案类给我们内置好的这些方法是可以满足需求的,不需要去进行重写替换。

常用的可能是重写构造方法,在重写的方法里,对数据做一些判断

public record User(String name, Integer age) {

    /**
     * 重写构造方法,不需要 @Override 注解
     */
    public User {
        System.out.println("重写了构造方法");
        if (age < 0) {
            throw new IllegalArgumentException("年龄不正确");
        }
    }

}

public class Main {

    public static void main(String[] args) {

        User user = new User("张三", -1);
        System.out.println(user);

    }

}

这样的话,就会抛出一个异常

重写了构造方法
Exception in thread "main" java.lang.IllegalArgumentException: 年龄不正确
	at com.example.demo.User.<init>(User.java:15)
	at com.example.demo.Main.main(Main.java:11)

总结一下档案类的一些限制

  • 档案类是 final 的,不支持继承
  • 档案类中声明的变量是不可变变量
  • 档案类中声明其它字段和方法只能是静态的(static
  • 档案类不能抽象(abstract

使用场景

使用场景的话,我觉得在多线程情况下,使用档案类,数据肯定是安全的,不用考虑并发下数据安全问题,因为数据不可变嘛

再有就是它是基于值的比较,省的我们再写一遍 equals()hashCode() 这两个方法

另外这也算是内置实现了 lombok 的一些功能

JDK 16 中 Record 文档