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方法
     
    失败原子性
    方法调用失败,对象仍保持在被调用之前的状态。
    同时,调用者应考虑捕获异常后,对象的状态是否被破坏。
     
     
  • 相关阅读:
    Java实现 LeetCode 400 第N个数字
    Java实现 LeetCode 400 第N个数字
    Java实现 LeetCode 399 除法求值
    Java实现 LeetCode 399 除法求值
    Java实现 LeetCode 399 除法求值
    Java实现 LeetCode 398 随机数索引
    Java实现 LeetCode 398 随机数索引
    Java实现 LeetCode 398 随机数索引
    linux中的cd ..和cd -命令有什么区别?
    GCC使用
  • 原文地址:https://www.cnblogs.com/night1989/p/9525713.html
Copyright © 2011-2022 走看看