Java对象的数据安全

假如你是负责实现某个Java类的程序员(the implementator of a Java class),你会怎么做以防止客户端(the client)程序员篡改你的数据,干预你的程序的正常运行?

本篇随笔提供了几种方法。

使用private关键字修饰属性

出于数据的安全性考虑,在定义Java类的属性时,应当尽可能多地使用private关键字而非public关键字。private关键字使得外部类无法直接获取和更改本类的属性,所以private关键字一般要搭配getter方法和setter方法使用。

getter方法的命名一般以get开头,后接属性名,注意使用驼峰命名法。getter方法必须用public关键字修饰,为外部类提供了获取本类的属性值的接口。

setter方法的命名一般以set开头,后接属性名,注意使用驼峰命名法。setter方法必须用public关键字修饰,为外部类提供了修改本类的属性值的接口。

public class Person {
    private String name;

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

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

    public String getName() {
        return name;
    }
}

慎用mutator方法

我们一般将非静态的Java类的方法分为四种:

  1. creator or constructor
    即构造器,用于创建Java类的对象。根据Java语法,静态(static)类无需创建对象,即可直接调用其中的方法;非静态类需要先创建对象,再借助对象调用该类的方法。

  2. producer
    基于对象的数据和外部类提供的数据进行计算,返回生成的新数据,注意此过程中并未改变对象的数据。

  3. observer
    将对象的数据输出给外部类。getter方法即属于此种。

  4. mutator
    根据外部类的要求,改变对象的数据。setter方法即属于此种。

为防止外部程序随意更改对象的数据,除非必要,不要使用mutator方法。

使用不可变数据类型

不可变(immutable)数据类型的值是不可变的。在Java中,基本数据类型都是不可变的;对象数据类型有的可变,有的不可变。而且,提供某一特定功能的对象数据类型中,可能既有可变的,又有不可变的。如Date可变,LocalDateTime不可变;StringBuilder可变,String不可变。

注意,不可变数据类型只是值不可变,但地址依旧可变。所以,我们可以对一个String型变量多次赋值,每次赋值都会开辟一块新内存空间,存储一个新的String对象,再将变量的引用(reference)指向新对象,旧对象从此变为垃圾,等待回收。

        int n = 10;
        magicStr = "";
        for (i = 0; i < n; i++) {
            for (j = 0; j < n; j++)
                magicStr = magicStr + i * j + "\t";
        }

如果在IDEA中敲出上述代码,IDE会提示你换用可变的StringBuilder类型进行字符串累加。因为不可变类型每次开辟新内存进行数据复制的操作太过耗时。

        int n = 10;
        magicStr = "";
        StringBuilder stringBuilder = new StringBuilder(magicStr);
        for (i = 0; i < n; i++) {
            for (j = 0; j < n; j++)
                stringBuilder.append(i * j).append("\t");
            stringBuilder.append("\n");
        }
        magicStr = stringBuilder.toString();

设计不可变数据类型并不困难,只要保证外部类找不到更改对象数据的途径即可。可以从两个方面入手:

  1. 不提供mutator方法
  2. getter方法的返回值如果是可变的对象数据类型,要在return前进行对象拷贝,即借助该对象的构造器重新创建一个对象,使其数据与属性值相同,再将其返回。
public class Current{
    private Date cur;
    
    public Current(){
        this.cur = new Date();
    }

    public getCur(){
        return new Date(this.cur.getTime());
    }
}

使用final关键字修饰属性

与不可变数据类型不同的是,final关键字可使变量的地址不可改变。例如,对一个String型的变量加以final修饰,会使得无法对其进行再次赋值。

posted @ 2024-03-27 22:07  洺宝  阅读(94)  评论(0)    收藏  举报