zoukankan      html  css  js  c++  java
  • 《Effective java》读书笔记

     第二章 创建和销毁对象
    何时用静态工厂方法代替构造器?
    1、获取有特殊含义的对象时,用有名称的静态工厂方法优于使用参数。
    2、不可变类希望复用实例时。
    3、不想暴露具体子类型(例如非公有内部类),或客户不需要关心具体子类型时。隐藏子类型便于系统扩展(可以随时删除或增加新的子类型),还可以增加系统灵活性(在运行时决定具体实现)。
    4、当实例化参数列表较长时。
     
    参数很多时,使用builder模式。
     
    单例singleton如何防止AccessibleObject.setAccessible攻击?
    让私有构造器检测实例是否存在,如存在则抛出异常。
     
    用枚举实现单例 mark
    只有单个元素的枚举,简洁,安全,天然可序列化。
     
    防止util类被误实例化 mark
    包含一个私有构造器,并无条件抛出异常。
     
    重用对象和创建新对象的权衡
    1、循环语句中反复创建新对象,可能影响性能。
    2、只有当创建新对象代价非常大时(比如数据库连接对象)才建议维护对象池。
    3、需要“保护性拷贝”的对象,重用有风险。
     
    消除过期对象的引用 mark
    书中的解说容易误解。其实原理很简单,一个数组逻辑上容量缩减,但虚拟机并不理解这个逻辑,因此不会回收边界之外的对象。只要数组对象不被回收,边界之外的引用就一直存在,并且这些对象所引用的对象也不会被回收。
    将过期的引用置为null即可。
    只要类是自己管理内存,就应该警惕内存泄漏问题。
    内存泄漏另一个常见来源是缓存。简单来说,就是缓存的部分数据可能不再有用,但不会自动过期。可以由一个后台线程实现定时失效。
    内存泄漏第三个来源是监听器和其他回调。这段没看懂。
     
    第三章 对于所有对象都通用的方法
    通用方法的通用约定(general contract)
    equals:
    自反性reflexive,对称性symmetric,传递性transitive,一致性consistent,非空性(不等于null)。
     
    覆盖equals时总要覆盖hashCode
    hashCode约定:equals为true,则hashCode必须相等;equals为false,则hashCode最好不等(提高查询性能)
    hashCode最佳实践:
    hc = 17;
    for field in fields
    do
        hc = 31*hc+hashCode(field); //或 (hc << 5)-hc+hashCode(field)
    end for
    return hc;
     
    对于不可变类,如果hashCode()开销比较大,考虑缓存。
     
    一个简单的hashCode()方法,其实有那么多可优化的细节。
     
    始终覆盖toString
    实际工作中,VO类应该覆盖toString,server类可以不覆盖。
     
    谨慎覆盖clone
    慎重使用cloneable接口。。
     
    compareTo约定:
    这里用数学符号>,<,=来表示顺序
    1、x>y则y<x, x=y则y=x;
    2、x>y>z则x>z
    3、x=y则x.compareTo(z)和y.compareTo(z)符号相等
     
    第四章 类和接口
    如果一个类适合设计成不可变的,就设计成不可变的。
    不可变类遵循五条规则:
    1.不要提供任何会修改对象状态的方法
    2.保证类不会被扩展。“final”修饰,或使用静态工厂方法。
    3.所有域都要是final,且为私有
    4.确保对于任何可变组件的互斥访问。不使用客户端提供的对象引用初始化这样的域;不要在任何访问方法(accessor)中返回这样的域,而是使用保护性拷贝(defensive copy)
     
    不可变对象本质是线程安全的,不要求同步
     
    组合优于继承
    除非是专门为了继承而设计的类,否则不要继承。
    要么为继承而设计,否则禁止继承。
    如何设计一个可以被继承的类?
    1、提供文档,说明某个方法调用了哪些可覆盖方法,覆盖这些方法会造成什么影响。(写给扩展类的程序员,而非用户)
    2、提供protected hook
    3、构造器决不能调用可被覆盖的方法,否则会造成父类构造器super()调用被子类覆盖的方法,造成不可预知的后果。
    4、慎用Cloneable 和 Serializable
     
    如何实现一个不可继承的类?
    1、final 2、静态工厂代替构造器
     
    继承要慎用,其使用场合仅限于你确信使用该技术有效的情况。一个判断方法是,问一问自己是否需要从新类向基类进行向上转型。如果是必须的,则继承是必要的。反之则应该好好考虑是否需要继承。《Java编程思想》
    只有当子类真正是超类的子类型时,才适合用继承。换句话说,对于两个类A和B,只有当两者之间确实存在is-a关系的时候,类B才应该继续类A。《Effective Java》
     
    接口优于抽象类
    可以混合实现多个接口,更灵活;可以运用decorator模式方便的扩展功能;
    想要同时具有抽象类的方便性,可以为接口提供一个抽象的骨架实现(例如AbstractList)。接口的实现可以选择扩展骨架类,也可以选择完全手动实现;完全手动实现时,可以使用“模拟多重继承(simulated muliple inheritance)”简化实现。
     
    接口的缺点在于不能随便增加新功能。
     
    用函数对象表示策略
    即策略模式。策略类应该是无状态的。
     
    嵌套类的使用
    嵌套类最好都是私有的。如果需要被外围引用,就应该声明为顶层类。
    静态成员类:最接近普通类,只能访问外围静态成员。
    非静态成员类:与外围类实例绑定。可访问外围类非静态成员。非静态成员类的实例必然归属于某个外围类的实例。例如集合类的Iterator。
    匿名类:常用语创建函数对象。
     
    第五章 泛型(generic)
    泛型的通配符类型
    通配符通常用于函数参数,使函数可接受不同类型的泛型对象。
    无限制的通配符类型(unbounded wildcard type)
    List<?> 和 List<Object>的区别:
    1、List<?>可接受其他类型List赋值,例如List<?> l = new ArrayList<String>();List<Object>不可以。因此,如果一个函数可以接受不同类型的列表做参数,可以写成 void func(List<?> arg)
    2、List<?>不能添加任何元素(null除外),因此更加安全。
     
    固定上边界的通配符(Upper Bounded Wildcards):
    <? extends E>,不能使用add(除了null),get返回E类型。
    固定下边界的通配符(Lower Bounded Wildcards):
    <? super E>,可以使用add(E类型或null),不能使用get。
     
    PECS:producer-extends, consumer-super
    如果泛型是一个生产者(使用get),使用<? extends E>;如果是一个消费者(使用add),使用<? super E>。
    如果函数的参数即是生产者,又是消费者,就不该用通配符,需要严格的类型匹配。
     
    尽可能消除每一个非受检警告!!!
     
    泛型的类型擦除(erasure)
    泛型信息只存在于代码编译阶段,在进入 JVM 之前,与泛型相关的信息会被擦除掉,专业术语叫做类型擦除。通俗地讲,泛型类和普通类在 java 虚拟机内是没有什么特别的地方。
    相对的,数组是具体化的(reified。个人理解即运行时,不同类型在虚拟机中的表示是有区别的,运行时表示包含了具体的类型信息)。
     
    优先考虑类型安全的异构容器
    有点累,以后再看
     
    第六章 枚举和注解
    java枚举的本质:通过公有的静态final域为每个枚举常量导出实例的类。是没有构造器的类,不能实例化也不能扩展。
    枚举代替int常量
    枚举的优势:
    1、实例受控
    2、类型安全
    3、可添加方法和域(例如一段描述性文本),实现了常见的通用接口(Object方法,Serializable,Comparable)
     
    用实例域代替序数
    即,如果要对枚举常量关联一个int值,不要使用ordinal方法,而是保存在一个int类型的域中,更好维护。
     
    用EnumSet代替位域,EnumMap代替序数索引
    累了,改天看.
     
    用接口模拟可伸缩的枚举
    当需要对枚举进行扩展时。客户端扩展接口定义自己的枚举,服务端用接口接受枚举实例。
    public interface EnumItf{
        void action();
    }
     
    public enum BasicEnum implements EnumItf{
        ...
    }
     
    public enum extendedEnum implements EnumItf{
        …
    }
     
    public void serverAPI(EnumItf concrEnum){
        concrEnum.action();
    }
     
    第七章 方法
    方法参数优先使用接口而不是类;优先使用枚举类型,而不是枚举的value
     
    最好避免定义两个有相同参数数目的重载方法,不要重载可变参数的方法。
     
    返回空数组或集合而不是null
     
    为所有API编写文档
     
    第八章 通用程序设计
    局部变量作用域最小化
    目的是增强代码的可读性。在第一次使用变量的地方声明。
     
    精确计算时,避免使用float和double
    建议使用BigDecimal。迟些看细节。
     
    优先使用接口引用对象,而不是类型
     
    第九章 异常
    只针对异常情况使用异常
    不要用异常代替业务校验。
     
    try-catch会阻止JVM的某些性能优化。
     
    自定义异常可以添加新的域和方法,以提供更多相关信息;
    toString方法应包含足够多的定位错误信息,可以写在message中,也可以重写toString方法
     
    失败原子性
    方法调用失败,对象仍保持在被调用之前的状态。
    同时,调用者应考虑捕获异常后,对象的状态是否被破坏。
     
     
  • 相关阅读:
    TextView 字数限制
    关于一个软件ipa包的其他图片资源
    查看一个软件ipa包的内容
    不断学习的博客
    高级iOS面试题
    CocoaPod出现-bash: pod: command not found 解决办法
    链表清空
    蛋疼的并查集
    再卖菜
    乒乓球男双输了
  • 原文地址:https://www.cnblogs.com/night1989/p/9525713.html
Copyright © 2011-2022 走看看