项目升级啦

公司要新做一个医疗行业的业务,经过业务端和产品端的评估该业务与公司已有的产品线关联不大,用户后续也不想在老系统那台老爷车上继续使用,话说老系统到现在差不多10年了,中间经历过的前后端开发者形形色色,维护者换了一轮又一轮,系统已经日渐臃肿,页面UI也不大美观,作为技术研发人员也表示能够理解。而且从技术的角度考虑,新系统中除了要研发的新业务外,它所依赖的基础模块与传统的权限管理模式也大不相同,强行在原系统上研发的话,基础模块的重新搭建也是必不可少,到时候难免把原系统改的面目全非。经过综合考虑觉得重新搭建一套系统,前后端应用均从0开始研发。

新搭建的项目那java8肯定是不能再用了,技术人员也要跟上时代进步的步伐,果断安排上了springboot3和java17。说来也惭愧,我虽然是研发的老司机了,可新版本的jdk还没有在项目中实际使用过,这次研发新项目方才真正实践其中的一些新特性。

Records(数据类)

Records 是 Java 14 中作为预览功能引入,并在 Java 16 中正式成为永久特性的新功能。它的主要目的是简化不可变数据载体类(Data Carrier Class)的创建。这类类通常只包含数据字段,并提供访问这些字段的方法,而没有复杂的行为。

Records 出现之前,我们通常需要手动编写一个 POJO(Plain Old Java Object)类,包含私有字段、构造函数、getter 方法、equals()hashCode()toString() 方法。这不仅繁琐,而且容易出错。Records 正是为了解决这个问题而设计的。

1. 什么是 Record?

Record 是一种特殊的类,它代表其状态是其唯一有意义的特征的不可变数据。它自动为你生成:

  • 私有、final 的字段:对应于 record 头部中声明的组件。
  • 公共构造函数:参数与 record 组件的顺序和类型相同,用于初始化字段。
  • 公共的 accessor 方法(访问器):对于每个组件 x,会生成一个名为 x() 的方法(注意:不是 getX()),返回该字段的值。
  • equals(Object o) 方法:基于所有字段的值进行比较。
  • hashCode() 方法:基于所有字段的值生成哈希码。
  • toString() 方法:生成包含类名和所有字段名及其值的字符串表示。

2. 基本语法

public record Person(String name, int age) {
    // record body (可选)
}

这个简单的声明等价于以下传统类:

// 等价的传统写法
public final class Person {
    private final String name;
    private final int age;

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

    public String name() { // 注意:是 name() 而不是 getName()
        return name;
    }

    public int age() { // 注意:是 age() 而不是 getAge()
        return age;
    }

    // 自动生成的 equals, hashCode, toString...
    // ... (代码省略)
}

3. 使用 Record

public class RecordUseDemo {
    public static void main(String[] args) {
        // 创建 record 实例
        Person person = new Person("Alice", 30);

        // 访问字段 (使用 accessor 方法)
        System.out.println(person.name()); // 输出: Alice
        System.out.println(person.age());  // 输出: 30

        // toString()
        System.out.println(person); // 输出: Person[name=Alice, age=30]

        // equals() 和 hashCode()
        Person person2 = new Person("Alice", 30);
        System.out.println(person.equals(person2)); // 输出: true
        System.out.println(person.hashCode() == person2.hashCode()); // 输出: true
    }
}

4. Record 的特性与限制

  • 隐式 finalRecord 类是隐式 final 的,不能被继承。
  • 隐式 publicRecord 类和其组件(字段)是隐式 public 的。
  • 不可变性:所有字段都是 private final 的,确保了 Record 实例的不可变性。
  • 无继承Record 不能 extends 其他类(因为它隐式继承了 java.lang.Record)。
  • 只能实现接口Record 可以 implements 接口。
  • 紧凑的构造函数:可以编写“紧凑构造函数”来对参数进行验证或预处理,但不能声明自己的字段。
public record Person(String name, int age) {
    // 紧凑构造函数 - 用于验证
    public Person {
        if (age < 0) {
            throw new IllegalArgumentException("Age cannot be negative");
        }
        // 注意:这里不能写 this.name = name; 或 this.age = age;
        // 字段的赋值由编译器自动完成
        // 你只能进行验证或修改组件值(通过 this(...) 调用其他构造函数,但 record 通常只有一个构造函数)
        // 或者调用 super(...),但 record 没有显式父类构造函数可调用。
    }

    // 你也可以添加静态方法或实例方法
    public boolean isAdult() {
        return age >= 18;
    }
}
  • 可以添加方法:可以在 Record 中添加静态方法、实例方法、甚至 getter/setter(尽管 setter 会破坏不可变性,不推荐)。
public record Point(int x, int y) {
    // 静态方法
    public static Point origin() {
        return new Point(0, 0);
    }

    // 实例方法
    public double distanceToOrigin() {
        return Math.sqrt(x * x + y * y);
    }

    // 可以重写自动生成的方法
    @Override
    public String toString() {
        return "Point(" + x + ", " + y + ")";
    }
}

实践示例

在数据层的Maaper中定义数据类User, 用于查询接口的方法返回对象。

public interface MessageMapper {

    /**
     * 用户对象
     * @param id
     * @param name
     */
    record User(String id, String name) {}

    /**
     * 获取发信人
     * @param messageId
     * @return
     */
    User getSendUser(Long messageId);
}

Text Blocks(多行字符串)


使用三重引号(""")定义多行字符串,避免转义字符。简便了大段的文本处理效率,不用再检查字符串“+”拼接中的引号是否对称等特殊字符字符转义问题啦。
示例:

  // java8 之前的写法
    String s1 = "{\"name\":\"John\",\"age\":30}";
    // java 14 引入的写法
    String s2= """
          {
               "name": "John",
               "age": 30
           }
       """;
  

应用场景:JSON/XML配置、SQL语句拼接,提升可读性。

Switch表达式


支持模式匹配和表达式返回值,替代传统switch语句。真是三目运算符的好帮手,写过三目运算符的都知道,有时候仅仅是多一个值选项就不得不转换为各种if else的嵌套写法,可以这样使用switch之后,写法真的简便很多。
示例

    int days=0;
    //java8 之前的写法
     switch (month) {
            case "JANUARY":
            case "MARCH":
            case "MAY":
            case "JULY":
            case "AUGUST":
            case "OCTOBER":
            case "DECEMBER":
                days = 31;
                break;
            case "APRIL":
            case "JUNE":
            case "SEPTEMBER":
            case "NOVEMBER":
                days = 30;
                break;
            case "FEBRUARY":
                days = 28;
                break;
            default:
                System.out.println("Invalid month.");
                break;
        }


    //java14之后的写法
     days = switch (month) {
      case "JANUARY", "MARCH", "MAY", "JULY", "AUGUST", "OCTOBER", "DECEMBER" -> 31;
      case "APRIL", "JUNE", "SEPTEMBER", "NOVEMBER" -> 30;
      case "FEBRUARY" -> 28;
      default -> throw new IllegalArgumentException("Invalid month: " + month);
    };

应用场景:状态机处理(如订单状态流转)。

以上就是我这边的使用过程和经历啦,后面项目中有其他特性应用再行补充和说明了。关于本文的内容和个人的使用经历,也欢迎大家在评论区留言和交流。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值