effectiveJava学习笔记:方法(一)

检查参数的有效性

1、一般在方法执行之前先检查参数的有效性,如果参数值无效,那么很快它就会失败,并且清楚的抛出合适的异常。

如果这个方法没有检查参数的异常,那么可能在方法处理中出现令人费解的异常。更糟糕的有可能是,方法可以正常返回,但是却使得某个对象处于被破坏的状态.

2、对于公有方法,可以在Javadoc中的@throw标签来说明违反异常时所抛出的异常类型

3、非公有方法通常应该使用断言(assert)来检查它们的参数(在生产环境中,一般是不支持assert的,因此这样可以提高效率,没有成本开销。所以,assert只在私有方法中使用,因为私有方法的调用者开发者,他和被调用者之间是一种弱契约关系,或者说没有契约关系,其间的约束是依靠开发者自己控制的,开发者应该有充分的理由相信自己传入的参数是有效的。所以,从某种角度上来说,assert只是起到一个预防开发者自己出错,或者是程序的无意出错)

	private 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;
	}

4、有一些参数暂时没有直接用到,只是保存起来供以后使用,这种参数的有效性检查也是尤其重要

    static List<Integer> intArrayAsList(final int[] a) {
        if (a == null)
            throw new NullPointerException();
 
        return new AbstractList<Integer>() {
            public Integer get(int i) {
                return a[i];  // Autoboxing (Item 5)
            }
 
            @Override public Integer set(int i, Integer val) {
                int oldVal = a[i];
                a[i] = val;     // Auto-unboxing
                return oldVal;  // Autoboxing
            }
 
            public int size() {
                return a.length;
            }
        };
    }

5、尽管在构造器中检查参数的有效性非常必要,但是也有例外,可能在有效情况下检查参数的有效性是及其昂贵的,甚至是不切实际的。

并非对参数的任何限制都是好事,一般来说要尽可能的通用, 符合实际的需要。假如方法对它能接受的参数都能完成合理的计算,那么对于参数的限制其实是越少越好的。因此,鼓励开发者把限制写到文档中,并在方法的开头显式的检查参数的有效性。

必要时进行保护性拷贝

容易被破坏的内部约束条件

虽然如果没有主动提供公共方法和变量,外部是无法修改类内部的数据的。但是,对象可能会在无意识的情况下提供帮助。例如,下面就是一个通过引用来修改类内部的数据,而破坏对象内部的约束条件的例子:

public final 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;
	}...

虽然没有p.setEnd()方法修改内部信息,但是通过Date的引用,我们可以修改Person内部的信息

Date start = new Date();
Date end = new Date();
Period p = new Period(start, end);
end.setYear(78);  // Modifies internals of p!
System.out.println(p);

对构造器的每个可变参数进行保护性拷贝

package com.ligz.Chapter7.Item39;

import java.util.Date;

/**
 * @author ligz
 */
public class Period {
	private final Date start;
	private final Date end;
	
	public Period(Date start, Date end) {
		this.start = new Date(start.getTime());
		this.end = new Date(end.getTime());
 
		if (this.start.compareTo(this.end) > 0)
			throw new IllegalArgumentException(start + " after " + end);

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

值得注意的是,这里用的是获取需要的Date数据来进行拷贝,才不是使用clone进行拷贝,是因为,Date本身不是final的,不能保证返回的一定是一个安全的java.util.Date类。

在这种方法中,构造器中不直接接受原对象的引用。而是,对原对象中的数据进行拷贝,使用备份对象作为Period实例的组件。

通过保护性拷贝,避免通过直接改变引用和获取返回值的引用,间接修改Period内的数据。

当编写方法和构造器的时候,如果允许客户端提供的对象进入到内部的数据,就需要考虑,客户端的对象是否是可变,它的可变是否会对内部数据产生影响?!!

谨慎设计方法签名

1、方法的名称易于理解、与其他的风格一致。

2、一个类当中的方法不要太多,当一项操作被频繁使用,才选择为他提供快捷方式。

3、避免使用参数过长的方法,参数的列表应该小于四个。使用三种方法可以避免参数过长(1.把方法分解成多个方法;2.创建辅助类来保存参数的分组,一般为静态成员;3.结合两种使用Builder模式,尤其适合可选参数,这个在我之前的博客里面有—类的创建)

3、对于参数类型优先使用接口而不是类。就像在方法中不会使用HashMap类作为输入,但是会使用Map接口作为参数。

4、对于Boolean参数,优先使用两个元素的枚举类型。(没看懂。。。下次有机会再挣扎一下)

猜你喜欢

转载自blog.csdn.net/qq_39071530/article/details/83340761