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

      最近闲着花费了接近两个多月的时间,把《java effective》看完了,收益颇多,《Java effective》这本书确实值得java程序员细读,可以尝试思考一下如何编写更好的代码、java语言如何进行设计和java规范潜在意义。这些都是值得思考和探究的。

    创建和销毁对象:

    1.静态工厂方法:考虑用静态工厂方法代替构造器
       优点:
        1.可以自由命名;
        2.不必在每次调用它们的时候都创建一个新对象;
        3.可以返回原返回类型的任何子类的对象;
        4.在创建参数化实例的时候,可以简化代码
       缺点:
        1.类如果不含有公有或受保护的构造器,就不能被子类化;
        2.它们与其他的静态方法实际上没有任何区别

    2.构建器:如果类的构造器或者静态工厂中遇到多个参数时要考虑用构建器。在类的内部定义内部类作为参数载体,通过build构建。

    3.用私有构造器或者枚举类型强化Singleton属性。

    4.通过私有构造器强化不可实例化的能力,特别是帮助类,工具类等添加私有构造方法。

    5.避免创建不必要的对象。对象的重用和基本类型的装箱拆箱。

    6.消除过期的对象引用。例如:关闭数据流,断开数据库连接,置空大对象。

    7.避免使用终结方法finalizer()。对于所有对象都通用的方法。

    8.覆盖equals时请遵守通用约定。equals约定:自反性,对称性,传递性,一致性。

    9.覆盖equals时总要覆盖hasCode。

    10.始终要覆盖toString。

    11.谨慎地覆盖clone。

    12.考虑实现Comparable接口。

    类和接口

    13.使类和成员的可访问性最小化。访问权限:private,default,protected,public。

    14.在公有类中使用访问方法而非公有域。公有类中字段设为私有,通过方法访问。

    15.使可变性最小化:不要提供任何修改对象状态的方法;保证类不会被扩展;使所有的域都是final的;
    使所有的域都成为私有的;确保对于任何可变组件的互斥访问。

    16.复合优于继承。为继承而设计的必须提供文档说明,否则就禁止继承。
    禁止继承方法有:加final修饰;构造器私有化,对外提供静态工厂。

    17.接口优于抽象。接口只用于定义类型,方便作为参数传递。

    18.类层次优于标签类。抽取公共部分,去除类型条件,采用子类实现不同策略。

    19.优先考虑静态成员类,对外提供公有的字段或属性。
    公有静态成员类:作用规则跟其他的静态成员相似,常做为外部类的公有辅助类。
    私有静态成员类:依赖外部类实例。当不需要访问外部类时,在外部类内作为组件存在。
    匿名类:在使用时同时被声明和实例化,只有在非静态环境中才有外部类实例。常用作:动态创建函数对象;创建过程对象;用在静态工厂方法的内部。
    局部类:作用规则跟其他的局部变量相似,只有在非静态环境中才有外部类实例。

    泛型

    20.请不要在新代码中使用原生态类型。如果使用原生态类型,就失掉了泛型在安全性和表述性方面的所有优势。泛型有子类化的规则。无限制通配符类型为 Set<?>,代表任何元素类型,除了null之外。

    21.列表优先于数组。数组是协变且可以具体化的。泛型是不可变且可以被擦除的。因此泛型只在编译时强化类型信息,并在运行时丢弃类型信息。擦除就是使泛型可以与没有使用泛型的代码随意进行互用。

    22.优先考虑泛型,优先考虑泛型方法。
    数组与泛型数组:第一种创建一个Object的数组,将它转换成泛型数组;第二种创建一个Object的数组存储数据,在使用时强制转换成泛型类型;第三种通过Array.newInstance(Class<?> componentType, int length);创建泛型类型数组。

    23.利用有限制通配符来提升API的灵活性。PECS:producer-extends,consumer-super。

    24.优先考虑类型安全的异构容器。用Class对象作为类型令牌。Class.cast();检验参数是否为Class对象表示的类型实例。Class.asSubclass();将调用它的Class 对象转换成其参数表示的类的一个子类,失败抛出ClassCastException。

    枚举和注解

    25.用enum代替int常量,用实例域代替序数,用EnumSet代替位域,用EnumMap代替序数索引,用接口模拟可伸缩的枚举。
    枚举类型要用在集合(Set)中使用EnumSet存储操作。
    位域(bit field):用OR位运算将几个常量合并到一个集合中。例如:public static final int BOLD=1<<3;

    26.注解优先于命名模式,坚持使用Override注解,用标记接口定义类型。
    命名模式(naming pattern):使用名称来表明程序元素需要通过某种工具或者框架进行特殊处理。
    标记接口(marker interface):是指没有包含方法声明的接口,而是指明或者标明一个类实现了具有某种属性的接口。
    例如:JUnit测试方法以test开头。
    命名模式的缺点:1.名称拼写错误;2.无法确保它们只用到相应的程序元素上;3.没有提供将参数值与程序元素关联起来的好方法。

    方法

    27.检查参数的有效性,必要时进行保护性拷贝,谨慎设计方法签名,慎用重载,慎用可变参数。

    28.返回零长度的数组或者集合,而不是null。为所有导出的API元素编写文档注释。

    通用程序设计

    29.将局部变量的作用域最小化。
    局部变量声明:在第一次使用它的地方声明。

    30.for-each循环优先于传统的for循环。

    31.如果需要精确的答案,避免使用float和double,而是使用BigDecimal。

    32.基本类型优先于装箱基本类型。装箱和拆箱基本类型的混合类型计算时,会进行拆箱,当程序进行拆箱时会抛出NullPointerException异常。当程序装箱基本类型时会导致高开销和不必要的对象创建。

    33.当心字符串连接的性能。通过接口引用对象。接口优先于反射机制。

    34.谨慎地使用本地方法。谨慎地进行优化。遵守普遍接受的命名惯例。

    异常

    35.只针对异常的情况才使用异常。

    36.对可恢复的情况使用受检异常,对编程错误使用运行时异常。

    37. 避免不必要地使用受检的异常。

    38.优先使用标准的异常。抛出与抽象相对应的异常。每个方法抛出的异常都要有文档。

    39.在细节消息中包含能捕获失败的信息。努力使失败保持原子性。不要忽略异常。
    失败原子性(failure atomic):失败的方法调用应该使对象保持在被调用之前的状态,具有这种属性的方法被称为具有失败原子性。

    并发

    40.同步访问共享的可变数据。避免过度同步。executor和task优先于线程。并发工具优先于wait和notify。
    并发工具分三类:ExecutorFrameWork、并发集合(ConcurrentCollection)、同步器(Synchronizer)。
    应该优先使用并发集合(ConcurrentXXMap,ConcurrentXXList,ConcurrentXXQueue),再使用外部同步集合(Collections.synchronizedMap)或者并发集合(Hashtable)。
    在使用wait和notify时,务必确保从while循环内部调用wait。一般情况优先使用notifyAll,而不是使用notify。

    41.线程安全性的文档化
    线程安全性的级别: 不可变的(immutable),String;无条件的线程安全(unconditionally thread-safe);有条件线程安全(conditionally thread-safe);非线程安全(not thread-safe);线程对立(thread-hostile)。

    42.慎用延迟初始化,不要依赖于线程调度器,避免使用线程组(ThreadGroup,存在很多问题)。

    序列化
    对象序列化:将对象编码成字节流。反序列化:将字节流编码重新构建成对象。

    43.谨慎地实现Serializable接口。考虑使用自定义的序列化形式。保护性地编写readObject方法。
    对象反序列化是通过readObject生成实例的,而不是通过构造函数。内部类不应该实现Serializable。编写显式序列化readObject方法,必须执行构造器所有有效性检查和保护性拷贝,如果检查失败,则抛出InvalidObjectException异常。无论是直接方式还是间接方式,都不要调用类中任何可被覆盖的方法。

    44.对于实例控制,枚举类型优先于readResolve。考虑用序列化代理代替序列化实例。
    尽可能地使用枚举类型来实施实例控制。如果非要用,必须提供一个readResolve方法,并确保该类的所有实例域都为基本类型或者是transient的。序列化代理模式的精髓就是反序列化不再是通过不可控的readObject()途径,而是通过正常的构造函数途径。

    记于2018年3月20日

    备注:
    作者:Shengming Zeng
    博客:http://www.cnblogs.com/zengming/

    本文是原创,欢迎大家转载;但转载时必须注明文章来源,且在文章开头明显处给明链接。
    <欢迎有不同想法或见解的同学一起探讨,共同进步>

  • 相关阅读:
    Linux中应用程序如何使用系统调用syscall
    Spring Service、Dao进行Junit单元测试
    spring service层单元测试
    java-随机生成用户名(中文版及英文版)
    Java获取随机数
    JUnit单元测试框架的使用
    Mock以及Mockito的使用
    Spring Boot 各Starter介绍
    编写自己的SpringBoot-starter
    Spring Boot使用单元测试
  • 原文地址:https://www.cnblogs.com/zengming/p/8593797.html
Copyright © 2011-2022 走看看