【effective Java读书笔记】类和接口(二)
来源:互联网 发布:ipa 反编译源码 编辑:程序博客网 时间:2024/06/05 05:18
前言:
这一章有干货!不可变类、代码结构的梳理通过采用接口和抽象类结合搭架子,通过抽象类实现适配器,通过接口处理策略模式等。
正文:
第15条:使可变性最小化
第一个概念:不可变类;
1.不可变类举例:
String,基本类型的包装类,BigInteger和BigDecimal
2.关于不可变类的五要素,
见具体代码如下:
//1.final 保证类不会被扩展public final class Complex {//2.使所有的域都是final的,一旦初始化不能改变//3.使所有的域都是私有的。虽然final域不可改变,但是非私有意味着始终提供维护//5.不提供任何修改对象属性的方法private final double re;private final double im;public Complex(double re,double im) {this.re = re;this.im = im;}public Complex add(Complex c) {//4.返回对象引用,需要保护性拷贝return new Complex(c.re+re,c.im+im);}public Complex substract(Complex c){return new Complex(re-c.re, im-c.im);}}
注:关于第一点,BigInteger,BigDecimal是可被继承的,不是final的;
注:关于注释第四点,又叫函数式做法。例如BigDecimal的add方法:
private static BigDecimal add(longxs,long ys, int scale){
long sum = add(xs,ys);
if (sum!=INFLATED)
return BigDecimal.valueOf(sum,scale);
return new BigDecimal(BigInteger.valueOf(xs).add(ys),scale);
}
3.不可变类的优点:
本质上是线程安全的,不要求同步。即并发访问对象时,对象不会受到其他干扰导致对象内部改变。
4.不可变类应该尽可能重用类的实例。
为什么要重用?因为创建对象的开销。因此基本类型的包装类和BigInteger都有这样的静态工厂。俗称常量池。代码如下
private static class IntegerCache {
static finalintlow = -128;
static final int high;
static final Integercache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue !=null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatExceptionnfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j =low;
for(intk = 0;k < cache.length;k++)
cache[k] =new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
因此,面试中常常有这样的面试题:@Testpublic void test(){Integer integer = new Integer(2);Integer integer2 = new Integer(2);System.out.println(integer == integer2);Integer integer3 = 2;Integer integer4 = 2;System.out.println(integer3==integer4);Integer integer5 = 128;Integer integer6 = 128;System.out.println(integer5==integer6);}结果是什么?
false
true
false
原因不再多说。往上翻。。。5.不可变类唯一的缺点,
对于每个不同的值都需要一个单独的对象。例如:
BigInteger bigInteger = new BigInteger("1000000");System.out.println(bigInteger);bigInteger = bigInteger.flipBit(0);System.out.println(bigInteger);执行结果:
1000000
1000004
两个不同的对象。BigInteger bigInteger = new BigInteger("1000000");for (int i = 0; i < 10000; i++) {bigInteger = bigInteger.flipBit(0);}System.out.println(bigInteger);假如这样,则产生了10000个对象。这个时候,当然就回到了之前讲的问题。
用一句话总结就是:基本类型的包装类则转化为基本类型运算。其他类型例如String,BigInteger则使用辅助类StringBuilder,BitSet。
6.通过静态工厂方法使不可变类final
静态工厂的优势不再多说,看代码第6点:
//1.final 保证类不会被扩展public class Complex2 {//2.使所有的域都是final的,一旦初始化不能改变//3.使所有的域都是私有的。虽然final域不可改变,但是非私有意味着始终提供维护//5.不提供任何修改对象属性的方法private final double re;private final double im;private Complex2(double re,double im) {this.re = re;this.im = im;}//6.保证类不会被扩展的第二种方式,采用静态工厂public static Complex2 valueOf(double re,double im){return new Complex2(re, im);}public Complex2 add(Complex2 c) {//4.返回对象引用,需要保护性拷贝return new Complex2(c.re+re,c.im+im);}public Complex2 substract(Complex2 c){return new Complex2(re-c.re, im-c.im);}}
第16条,复合优先于继承
复合其实也就是包装类的意思。
先看一个段继承的代码:
public class InstrumentedHashSet<E> extends HashSet<E>{private int addCount = 0;public InstrumentedHashSet() {}@Overridepublic boolean add(E e) {addCount++;return super.add(e);}@Overridepublic boolean addAll(Collection<? extends E> c) {addCount += c.size();return super.addAll(c);}public int getAddCount() {return addCount;}}单元测试如下:
public class Test2 {@Testpublic void test(){InstrumentedHashSet<String> strs = new InstrumentedHashSet<>();strs.addAll(Arrays.asList("Snap","Crackle","Pop"));System.out.println(strs.getAddCount());}}执行结果如下:
6
为什么不是3?明明只加了3个元素。原因HashSet的addAll是在add基础上实现的。
public boolean addAll(Collection<?extends E>c) {
boolean modified =false;
for (E e :c)
if (add(e))
modified = true;
return modified;
}
那么相当于add方法执行了3次,addAll方法加了个3。因此一切的罪魁祸首是覆盖操作的时候,没有考虑父类的方法之间的关联关系。解决方案即是包装类,对父类代码无修改,代码如下:
public class ForWardHashSet<E>{private HashSet<E> hashSet;private int addCount = 0;public ForWardHashSet(HashSet<E> hashSet) {this.hashSet = hashSet;}public boolean add(E e) {addCount++;return hashSet.add(e);}public boolean addAll(Collection<? extends E> c) {addCount += c.size();return hashSet.addAll(c);}public int getAddCount() {return addCount;}}单元测试如下:
public class Test2 {@Testpublic void test(){ForWardHashSet<String> strs2 = new ForWardHashSet<>(new HashSet<>());strs2.addAll(Arrays.asList("Snap","Crackle","Pop"));System.out.println(strs2.getAddCount());}}执行结果:
3
总结:包装类的好处之前也说过,扩展性好,无侵入。只有当存在实际的包含情况才需要用到继承extends。
第17条,要么为继承而设计并提供文档说明,要么禁止继承;
此处仅仅有个原则需要注意:构造器不能调用可被覆盖的方法.
父类代码:
public class Super {public Super() {overRideMe();}public void overRideMe() {}}
子类代码:
public class Sub extends Super {private final Date date;public Sub() {date = new Date();}@Overridepublic void overRideMe() {System.out.println(date);}}单元测试:
@Testpublic void test(){Sub sub = new Sub();sub.overRideMe();}执行结果:第一条是父类执行的overRideMe,父类的overRideMe被子类覆盖后,应该会直接执行子类的overRideMe,然而date为null,所以第一条其实是null。打印出来的是第二条。
null
Wed Jul 19 14:40:55 CST 2017
总结:覆盖这个方法会导致异常,然而父类并没有说明或者禁止。
第18条 接口优先于抽象类;
略,接口、抽象类提供骨架,可以参考AbstractList,AbstractCollection,Collection。同时可以通过抽象类,写适配器Adapter。
第19条 接口只用于定义类型;
略
第20条 类层次优先于标签类
略,一般能用标签类(通过枚举判断,进行各种构造方法初始化),都可以改造为接口类层次。
第21条 用函数对象表示策略
略,计划写一篇策略模式;
第22条 优先考虑静态成员类;
略;
- 【effective Java读书笔记】类和接口(二)
- Effective Java读书笔记(4 类和接口)
- Effective Java读书笔记(第4章-类和接口)
- 【读书笔记】《Effective Java》(3)-- 类和接口
- 【effective Java读书笔记】类和接口(一)
- Effective Java 读书笔记(三):类和接口
- Effective Java 读书笔记(二)
- Effective Java读书笔记(二)
- effective java读书笔记 (二)
- Effective Java读书笔记——第四章 类和接口
- Effective Java读书笔记二:枚举和注解(30-37)
- Effective Java读书笔记二:枚举和注解
- Effective Java读书笔记、感悟——3.类和接口(一)
- Effective Java读书笔记二
- effective java读书笔记二
- 《Effective Java》读书笔记二
- Effective java 读书笔记( 二 )
- Effective Java读书笔记(一):类、接口、对象
- easyui-tree相关
- 2017 google code jam D轮
- 修改后缀名工具
- iOS 光标默认获取当前文本框输入位置
- Unity插件EasyToucu安卓平台的个人开发实例
- 【effective Java读书笔记】类和接口(二)
- 无题
- tensorflow学习——numpy
- Web报表系统葡萄城报表:Java 报表
- 英语语法5-现在完成时(续)
- nexus3.3.1上传第三方jar包
- Linux 驱动编译报错:error: macro "__DATE__" might prevent reproducible builds [-Werror=date-time]
- jQuery获取元素位置、滚动条高度
- mybatis 中哪些数据库支持 useGeneratedKeys="true"