【effective Java读书笔记】方法(一)

【effective Java读书笔记】方法(一)

前言:

依然是从安全性的角度讲述方法:主要讲述两点:一、有效性检查的两种方式:主动抛出异常,断言。二、保护性拷贝的意义和使用

第38条:检查参数的有效性

一、违反参数值限制主动抛出异常:

代码一:

public class Sample {
	public BigInteger mod(BigInteger m) {
		if (m.signum() <= 0) {
			throw new ArithmeticException("Modulus <= 0:" + m);
		}
		return null;
	}
}
代码一执行类:

public class Test {
	@org.junit.Test
	public void test() {
		Sample sample = new Sample();
		sample.mod(new BigInteger("-1"));
	}
}
结果:运行时异常。

java.lang.ArithmeticException: Modulus <= 0:-1
at com.function.Sample.mod(Sample.java:8)
at com.function.Test.test(Test.java:12)


二、断言检查有效性(-ea运行参数):

不了解断言的小伙伴看这里
代码二:

public class Sample2 {
	public static void sort(long a[],int offset,int length){
		assert a!=null;
		assert offset >=0 && offset<= a.length;
		assert length >=0 && length<=a.length-offset;
		System.out.println("success exec");
	}
}
代码二执行类:

public class Test {
	@org.junit.Test
	public void test2(){
		long[] a = new long[5];
		for (int i = 0; i < a.length; i++) {
			a[i]=(long) (i/1.0);
		}
		Sample2.sort(a, -1, 3);
	}
}
-ea命令执行结果:

Exception in thread "main" java.lang.AssertionError

at com.function.Sample2.sort(Sample2.java:6)

at com.function.Test.main(Test.java:28)

提示这一行代码有问题,因为offset = -1,不满足断言。

assert offset >=0 && offset<= a.length;

注意:以上两种限制判断方法,均写在方法开头处。

第39条:必要时进行保护性拷贝:

为什么要写保护性拷贝?保护性拷贝是什么?

第一个问题:面对客户的不良行为仍然能保持健壮性的类,需要采用保护性拷贝。

第二个问题,保护性拷贝是什么,我们先看一个举例如下:

代码三:

public class Period {
	private final Date start;
	private final Date end;

	public Period(Date start, Date end) {
		if (start.compareTo(end) > 0) {
			throw new IllegalArgumentException(start + "after" + end);
		}
		this.start = start;
		this.end = end;
	}

	public Date start() {
		return start;
	}

	public Date end() {
		return end;
	}

	@Override
	public String toString() {
		return "Period [start=" + start + ", end=" + end + "]";
	}
}
代码三的执行方法:

@org.junit.Test
	public void test3(){
		Date start = new Date();
		Date end = new Date();
		Period p = new Period(start, end);
		System.out.println(p.toString());
		end.setYear(78);
		System.out.println(p.toString());
	}
执行结果:

Period [start=Sun Jun 18 22:59:06 CST 2017, end=Sun Jun 18 22:59:06 CST 2017]

Period [start=Sun Jun 18 22:59:06 CST 2017, end=Sun Jun 18 22:59:06 CST 1978]

说好的约束呢?第二行结果显示,开始时间大于结束时间,因为被引用型变量修改了原始对象。

那么此处需有保护性拷贝出场了。为避免内部信息受到攻击,对构造器每个参数进行保护性拷贝。修改后代码如下:

public class Period2 {
	private final Date start;
	private final Date end;

	public Period2(Date start, Date end) {
		//先保护性拷贝
		this.start = new Date(start.getTime());
		this.end = new Date(end.getTime());
		//再检查有效性,避免多线程问题
		if (start.compareTo(end) > 0) {
			throw new IllegalArgumentException(start + "after" + end);
		}
	}

	public Date start() {
		return start;
	}

	public Date end() {
		return end;
	}

	@Override
	public String toString() {
		return "Period [start=" + start + ", end=" + end + "]";
	}
}
代码执行类

@org.junit.Test
	public void test4(){
		Date start = new Date();
		Date end = new Date();
		Period2 p = new Period2(start, end);
		System.out.println(p.toString());
		end.setYear(78);
		System.out.println(p.toString());
	}
执行结果:

Period [start=Sun Jun 18 23:39:06 CST 2017, end=Sun Jun 18 23:39:06 CST 2017]

Period [start=Sun Jun 18 23:39:06 CST 2017, end=Sun Jun 18 23:39:06 CST 2017]

这回虽然修改了引用型变量的值,但是一段时间的Period对象不变。依然受到开始时间《结束时间的约束。完美?nonono!差点忘了,还可以通过这种方式攻击,看代码:

@org.junit.Test
	public void test5(){
		Date start = new Date();
		Date end = new Date();
		Period2 p = new Period2(start, end);
		System.out.println(p.toString());
		end.setYear(78);
		p.end().setYear(78);
		System.out.println(p.toString());
	}
倒数第二句新增代码:

p.end().setYear(78);

由于提供了访问这个对象的引用,那么便能访问到保护性拷贝后的对象,对它进行操作,得到下面的结果:

Period [start=Sun Jun 18 23:44:07 CST 2017, end=Sun Jun 18 23:44:07 CST 2017]

Period [start=Sun Jun 18 23:44:07 CST 2017, end=Sun Jun 18 23:44:07 CST 1978]

当然,这样的结果我们不能忍,修改如下代码即可:

public Date start() {
		return new Date(start.getTime());
	}

	public Date end() {
		return new Date(end.getTime());
	}
现在我们的一段时间的Period对象才能算的上是安全,完美!

这个问题告诉我们,当一个类中包含引用型变量的时候一定要认真考虑清楚它的作用,然后考虑是否需要进行保护性拷贝处理,使得它安全可靠。









  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值