标签:
@throws
标签在文档中说明违反参数值限制时抛出的异常。/**
* Returns a BigInteger whose value is {@code (this mod m}). This method
* differs from {@code remainder} in that it always returns a
* <i>non-negative</i> BigInteger.
*
* @param m the modulus.
* @return {@code this mod m}
* @throws ArithmeticException {@code m} ≤ 0
* @see #remainder
*/
public BigInteger mod(BigInteger m) {
if (m.signum <= 0)
throw new ArithmeticException("BigInteger: modulus not positive");
BigInteger result = this.remainder(m);
return (result.signum >= 0 ? result : result.add(m));
}
private static void sort(long array[],int offset,int length){
//assert 断言
assert array!=null;
assert offset>=0&&offset<=array.length;
assert length>=0&&length<= array.length-offset;
}
sort(null,0,0);
//Exception in thread "main" java.lang.AssertionError
断言默认是关闭的
Java编译中启用断言:-enableassertions
,简写为-ea
IDEA启用断言:Run-->Edit Configuration-->VM Options-->添加-ea
保护性地设计程序(假设类的客户端会尽其所能破坏类的约束条件)
//下面的类声称可以表示一段不可变的时间周期
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 getStart(){
return start;
}
public Date getEnd() {
return end;
}
}
上面那个Period类表面上看是不可变类,并且加了约束条件:周期的起始时间不能再结束时间之后。但由于Date类是可变的,很容易就违反这个约束条件。如下。
public static void main(String[] args) {
Calendar calendar = Calendar.getInstance();
calendar.set(2008,Calendar.JULY,8);
Date start = calendar.getTime();
calendar.set(2016,Calendar.JULY,3);
Date end = calendar.getTime();
Period period = new Period(start,end);
start.setYear(2017);//修改了Period类
Date start1 = period.getStart();
System.out.println(start1.getYear());//2017
}
为避免类的实例的内部信息受到攻击,可对构造器中的每个可变参数进行保护性拷贝。
public Period(Date start,Date end){
// if(start.compareTo(end)>0)
// throw new IllegalArgumentException(start+"after"+end);
// this.start = start;
// this.end = 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);
}
虽然替换构造方法能避免上述的攻击,但仍然可以改变Period实例。它的访问方法提供了对其可变内部成员的访问能力。
period.getEnd().setYear(1998);//修改了Period实例
对于后一种攻击,只需修改方法,使它返回可变内部域的保护性拷贝即可:
public Date getStart(){
return new Date(start.getTime());
}
public Date getEnd() {
return new Date(end.getTime());
}
参数的保护性拷贝:
下面的例子就是使用可变对象作为Map的键,导致Map的约束条件被破坏。
Calendar calendar = Calendar.getInstance();
calendar.set(2008,Calendar.JULY,8);
Date start = calendar.getTime();
Map<Date,String> map = new HashMap<>();
map.put(start,start.getYear()+"");
start.setYear(1999);
String s = map.get(start);
System.out.println(s);
//修改后,使用Date.getTime()返回的long值作为Map内部时间的表示方法
Map<Long,String> newMap = new HashMap<>();
newMap.put(start.getTime(),start.getYear()+"");
结论:
最好使用不可变对象作为对象内部的组件。
类具有从客户端得到或返回客户端的可变组件,类就必须保护性地拷贝这些组件。
拷贝的成本受到限制,且类信任它的客户端不会不恰当地修改组件,就可在文档中指明客户端的职责是不得修改受到影响的组件。
可参看Google Java Style中的方法命名标准
目标是4个参数,或者更少。 相同类型的长参数序列格外有害。 容易弄错顺序,但程序仍可以编译和运行。
缩短过长的参数列表的三种办法:
- 1.分解成多个方法
- 2.创建辅助类
- 3.Builder模式
对于参数类型,优先使用接口而不是类。
methodA(Map<K,V> map);
//methodA(HashMap<K,V> map);
对于boolean参数,优先使用两个元素的枚举类型。
public enum TemperatureScale{
F,C
}
Thermometer.newInstance(TemperatureScale.C);
public class CollectionClassifier {
public static String classify(Set<?> set){
return "Set";
}
public static String classify(List<?> list){
return "List";
}
public static String classify(Collection<?> collection){
return "Unknown Collection";
}
public static void main(String[] args) {
Collection<?> [] collections = {
new HashSet<String>(),
new ArrayList<BigInteger>(),
new HashMap<String,Object>().values()
};
for(Collection collection:collections){
System.out.println(classify(collection));
}
}
}
//
//Unknown Collection
//Unknown Collection
//Unknown Collection
classify方法被重载(overloaded)了,而调用哪个重载方法是在编译时做出决定的。for循环中的三次循环,参数的编译时类型都是相同的:Collection<?>
。即使每次循环的运行时类型都是不同的。
重载方法(overloaded method)的选择是静态的,被覆盖的方法(overridden method)的选择是动态的。调用哪个被覆盖的方法是在运行时做出决定的。
public class Overriding {
public static void main(String[] args) {
Wine [] wines = {
new Wine(),
new SparklingWine(),
new Champagne()
};
for(Wine wine:wines){
System.out.println(wine.name());
}
}
}
class Wine{
String name(){
return "wine";
}
}
class SparklingWine extends Wine{
@Override
String name() {
return "sparkling wine";
}
}
class Champagne extends SparklingWine{
@Override
String name() {
return "champagne";
}
}
//output:
//wine
//sparkling wine
//champagne
标签:
原文地址:http://www.cnblogs.com/JohnTsai/p/5369225.html