Java中自定义异常类以及对象流自定义类中为什么要声明一个全局常量?

问题描述

在开发中我们会经常使用自定义异常类,或者使用对象流,在自定义异常类中,我们需要继承ExceptionRuntimeException,并且需要提供serialVersionUID的属性,而在使用对象流中自定义类需要实现Serializable(标识)接口,也同要需要提供一个serialVersionUID的属性,那么这是为什么呢?让我们看一下源码:


1.Exception类中的serialVersionUID属性

Exception类中的serialVersionUID属性


2.Serializable接口中的注释

Serializable接口中的注释

提示:其实在Serializable接口的注释中已经对问题进行了解释,但是我们留到后面去说

错误演示:

我们先演示如果不按照规则进行会出现什么问题。
注意:开发中,需要使用try-catch-finally方式处理流的异常

1.我们随便定义一个JavaBean

import java.io.Serializable;

public class Person implements Serializable {//Serializable:属于一个标识接口
    transient String name;
    static int age;
    int id;
//    String nationality = "China";//国籍

//    static final long serialVersionUID = 423216156564656L;

    public Person() {
    }

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

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getId() {
        return id;
    }

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

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

2.再定义一个测试类

import org.junit.Test;

import java.io.*;

public class PersonTest {

    /**
     * 演示自定义类的对象的序列化和反序列化过程
     * 序列化过程
     */
    @Test
    public void test1() throws IOException {
        //1.创建File类的对象
        File file = new File("object.txt");
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file));

        //2.写出数据即为序列化过程
        Person p1 = new Person("Tom", 18);
        oos.writeObject(p1);
        oos.flush();

        Person p2 = new Person("Jerry", 23, 1221);
        oos.writeObject(p2);
        oos.flush();

        //3.关闭资源
        oos.close();
    }

    /**
     * 演示自定义类的对象的序列化和反序列化过程
     * 反序列化过程
     */
    @Test
    public void test2() throws IOException, ClassNotFoundException {
        //1.创建File类的对象
        File file = new File("object.txt");
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));

        //2.读取文件中的对象(或反序列化的过程)
        Person person = (Person) ois.readObject();
        System.out.println(person);

        Person person1 = (Person) ois.readObject();
        System.out.println(person1);

        //3.关闭资源
        ois.close();
    }
}

先运行test1
test1运行结果再运行test2
test2运行结果

test1中我们将数据写入到Objtct.txt类中,再通过test2反序列化将Objtct.txt中的数据转换成对象流,可以看到运行结果没有问题,非常正常,这是因为在序列化过程中,会自动给自定义类创建一个serialVersionUID,而在反序列化中也会使用这个id来找到这个类进行反序列化,而我们一旦对原本的Person类进行修改(将国籍属性取消注释),那么再次运行又会重新生成一个serialVersionUID,从而导致原有的Objtct.txt中的数据因为找不到原来的serialVersionUID而不能再转换成对象流。


将国籍注释打开
在这里插入图片描述

再次运行test2从Objtct.txt读取文件中的对象
在这里插入图片描述
可以看到报错信息显示:

java.io.InvalidClassException: java05_objectstream.demo.Person; local class incompatible: stream classdesc serialVersionUID = 1027002611310758675, local class serialVersionUID = -135851562499732227

意思是:
本地类不兼容:
流classdesc serialVersionUID = 1027002611310758675,
本地类serialVersionUID = -135851562499732227
因为对象流中保存的serialVersionUID 与本地类的serialVersionUID 不一致导致


显示定义serialVersionUID :

static final long serialVersionUID = 423216156564656L;//数值随便输

如果声明了serialVersionUID,即使在序列化完成之后修改了类导致类重新编译,则原来的数据也能正常反序列化,只是新增的字段值是默认值而已。

在这里插入图片描述

总结

static final long serialVersionUID = 42L;

无论是自定义异常类还是对象流自定义类,都建议显式声明

在SonarQube中分析Java项目时,统一声明常量是符合代码规范和最佳实践的重要一环。以下是一些推荐的做法: 1. **常量命名规范**:常量名称应该全部使用大字母,并用下划线分隔单词,例如 `MAX_COUNT` 或 `DEFAULT_TIMEOUT`。这样的命名方式能够清晰地表明该变量是一个常量[^3]。 2. **常量类设计**:创建专门的类或接口来存放相关的常量组。这样可以将逻辑上相关的常量集中管理,并且可以通过静态导入的方式方便地访问这些常量。例如: ```java public final class Constants { private Constants() { /* 防止实例化 */ } public static final int MAX_COUNT = 100; public static final long DEFAULT_TIMEOUT = 5000L; // milliseconds } ``` 这种做法有助于避免全局命名空间污染,并且提高了代码的可维护性[^1]。 3. **可变性**:确保常量一旦被初始化后就会改变其值。这通常通过将字段声明为 `final` 来实现。此外,对于包含多个元素的数据结构(如集合),应使用可变的实现或者在初始化之后再修改它们。 4. **文档注释**:为每个常量提供详细的Javadoc注释,说明其用途以及可能的取值范围等信息。这对于其他开发者理解和正确使用这些常量非常重要。 5. **SonarQube规则配置**:利用SonarQube提供的质量模型和自定义规则功能,可以对项目的编码标准进行定制化检查。比如设置关于常量命名、位置等方面的特定规则,以保证整个项目中的常量都遵循一致的标准。可以通过安装插件如Checkstyle、PMD来增强这方面的能力[^2]。 6. **工具支持**:采用IDE插件或者其他辅助工具帮助自动格式化代码并提示违反编码规范的情况。很多现代IDE都支持与SonarQube集成,在编代码的同时就能得到即时反馈。 7. **团队共识与培训**:最后但同样重要的是,所有团队成员都需要了解并遵守这套统一的常量声明指南。定期组织内部分享会或培训课程可以帮助加强这一点。 通过上述措施,可以在很大程度上提升Java项目中常量管理的专业性和一致性,同时也更容易让SonarQube等工具有效地识别出潜在的问题点。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Leighteen

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

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

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

打赏作者

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

抵扣说明:

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

余额充值