假如你是负责实现某个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类的方法分为四种:
-
creator or constructor
即构造器,用于创建Java类的对象。根据Java语法,静态(static)类无需创建对象,即可直接调用其中的方法;非静态类需要先创建对象,再借助对象调用该类的方法。 -
producer
基于对象的数据和外部类提供的数据进行计算,返回生成的新数据,注意此过程中并未改变对象的数据。 -
observer
将对象的数据输出给外部类。getter方法即属于此种。 -
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();
设计不可变数据类型并不困难,只要保证外部类找不到更改对象数据的途径即可。可以从两个方面入手:
- 不提供mutator方法
- 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
修饰,会使得无法对其进行再次赋值。