java安全编码指南之:方法编写指南
简介
java程序的逻辑是由一个个的方法组成的,而在编写方法的过程中,我们也需要遵守一定的安全规则,比如方法的参数进行校验,不要在assert中添加业务逻辑,不要使用废弃或者过期的方法,做安全检查的方法一定要设置为private等。
今天我们再来深入的探讨一下,java方法的编写过程中还有哪些要注意的地方。
不要在构造函数中调用可以被重写的方法
一般来说在构造函数中只能调用static,final或者private的方法。为什么呢?
如果父类在执行构造函数的时候调用了一个可以被重写的方法,那么在该方法中可能会使用到未初始化的数据,从而导致运行时异常或者意外结束。
另外,还可能到方法获取到未初始化完毕的实例,从而导致数据不一致性。
举个例子,我们定义了一个Person的父类:
public class Person { public void printValue(){ System.out.println("this is person!"); } public Person(){ printValue(); } }
然后定义了一个Boy的子类,但是在Boy子类中,重新了父类的printValue方法。
public class Boy extends Person{ public void printValue(){ System.out.println("this is Boy!"); } public Boy(){ super(); } public static void main(String[] args) { Person persion= new Person(); Boy boy= new Boy(); } }
输出结果:
this is person! this is Boy!
可以看到Boy调用了自己重写过的printValue方法。
注意,这里并不是说会产生语法错误,而是这样会导致业务逻辑看起来非常混乱。
怎么解决呢?简单办法就是将Person中的printValue置位final即可。
不要在clone()方法中调用可重写的方法
同样的,我们在定义clone方法的时候也不要调用可重写的方法,否则也会产生意想不到的变化。
还是上面的例子,这次我们添加了clone方法到Person类:
public Object clone() throws CloneNotSupportedException { Person person= (Person)super.clone(); person.printValue(); return person; }
接下来我们添加clone方法到Boy类:
public Object clone() throws CloneNotSupportedException { Boy clone = (Boy) super.clone(); clone.printValue(); return clone; }
因为在clone方法中调用了可重写的方法,从而让系统逻辑变得混乱。不推荐这样使用。
重写equals()方法
考虑一下父类和子类的情况,如果在父类中我们定义了一个equals方法,这个方法是根据父类中的字段来进行比较判断,最终决定两个对象是否相等。
如果子类添加了一些新的字段,如果不重写equals方法,而是使用父类的equals方法,那么就会遗漏子类中新添加的字段,最终导致equals返回意想不到的结果。
所以一般来说,子类需要重写equals方法。
如果重新equals方法,需要满足下面几个特性:
- reflexive反射性
对于一个Object a来说,a.equals(a)必须成立。
- symmetric对称性
对于一个Object a和Object b来说,如果a.equals(b)==true,那么b.equals(a)==true一定成立。
- transitive传递性
对于Object a,b,c来说,如果a.equals(b)==true,b.equals(c)==true,那么a.equals(c)==true一定成立。
- consistent一致性
对于Object a,b来说,如果a和b没有发生任何变化,那么a.equals(b)的结果也不能变。
- 对于非空的引用a,a.equals(null) 一定要等于false
具体代码的例子,这里就不写了,大家可以自行练习一下。
hashCode和equals
hashCode是Object中定义的一个native方法:
@HotSpotIntrinsicCandidate public native int hashCode();
根据Oracle的建议,如果两个对象的equals方法返回的结果是true,那么这两个对象的hashCode一定要返回同样的int值。
为什么呢?
我们看下下面的一个例子:
public class Girl { private final int age; public Girl(int age) { this.age = age; } @Override public boolean equals(Object o) { if (o == this) { return true; } if (!(o instanceof Girl)) { return false; } Girl cc = (Girl)o; return cc.age == age; } public static void main(String[] args) { HashMap<Girl,Integer> hashMap= new HashMap<>(); hashMap.put(new Girl(20), 20); System.out.println(hashMap.get(new Girl(20))); } }
上面的Girl中,我们定义了equals方法,但是并没有重写hashCode,最后返回的结果是null。
因为我们new了两次Girl这个对象,最后导致native方法中两个不同对象的hashCode是不一样的。
我们可以给Girl类中添加一个hashCode方法:
public int hashCode() { return age; }
这样就可以返回正确的值。
compareTo方法的实现
我们在实现可比较类的时候,通常需要实现Comparable接口。Comparable接口定义了一个compareTo方法,用来进行两个对象的比较。
我们在实现compareTo方法的时候,要注意保证比较的通用规则,也就是说,如果x.compareTo(y) > 0 && y.compareTo(z) > 0 那么表示 x.compareTo(z) > 0.
所以,我们不能使用compareTo来实现特殊的逻辑。
最近看了一个日本的电影,叫做dubo默示录,里面有一集就是石头,剪刀,布来判断输赢。
当然,石头,剪刀,布不满足我们的通用compareTo方法,所以不能将逻辑定义在compareTo方法中。
本文的代码:
learn-java-base-9-to-20/tree/master/security
本文已收录于 http://www.flydean.com/java-security-code-line-method/
最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现!
欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
java安全编码指南之:死锁dead lock
简介 java中为了保证共享数据的安全性,我们引入了锁的机制。有了锁就有可能产生死锁。 死锁的原因就是多个线程锁住了对方所需要的资源,然后现有的资源又没有释放,从而导致循环等待的情况。 通常来说如果不同的线程对加锁和释放锁的顺序不一致的话,就很有可能产生死锁。 不同的加锁顺序 我们来看一个不同加锁顺序的例子: public class DiffLockOrder { private int amount; public DiffLockOrder(int amount){ this.amount=amount; } public void transfer(DiffLockOrder target,int transferAmount){ synchronized (this){ synchronized (target){ if(amount< transferAmount){ System.out.println("余额不足!"); }else{ amount=amount-transferAmount; target.amount=target.amount+transfe...
- 下一篇
看动画学算法之:linkedList
简介 linkedList应该是一种非常非常简单的数据结构了。节点一个一个的连接起来,就成了linkedList。今天我们使用动画的方法一起来看看linkedList是怎么插入和删除的。 linkedList的构建 linkedList是由一个一个的节点构成的。而每个节点只需要存储要保存的数据和下一个节点的引用即可。 linkedList本身需要一个head节点,所以我们的linkedList可以这样构建: public class LinkedList { Node head; // head 节点 //Node表示的是Linked list中的节点,包含一个data数据和下一个节点的引用 class Node { int data; Node next; //Node的构造函数 Node(int d) { data = d; } } } linkedList的操作 先看一下linkedList怎么插入数据,插入数据有三种方式,头部插入,尾部插入,中间插入。 头部插入 先看一个头部插入的例子: 头部插入的逻辑是什么呢? 新插入的节点作为head节点,然后将原来的head节点指向当前h...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- CentOS7,CentOS8安装Elasticsearch6.8.6
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- Linux系统CentOS6、CentOS7手动修改IP地址
- CentOS7安装Docker,走上虚拟化容器引擎之路
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- SpringBoot2全家桶,快速入门学习开发网站教程
- CentOS7设置SWAP分区,小内存服务器的救世主
- Docker快速安装Oracle11G,搭建oracle11g学习环境