《Effective Java》第2章 创建和销毁对象

学习一门语言有三个重要的部分必须要掌握:语法(该门语言的结构如何),词汇(如何用该语言命名你想要讨论的事物),用法(如何用惯用和高效的方式表达日常事物)。 那么《Effective Java》就是描述Java语言的用法很不错的一本书,阅读学习本书能够为程序员提供很多很好的编写Java代码的建议,从写代码的时候就避免掉进一些不必要的陷阱,减轻后续的debug的工作量。 也就是借鉴牛人们的经典建议,避免耗费大量时间在前人已经经历的弯路上。


本文以及之后的读书笔记内容均来自于《Effective Java》Second Edition Joshua Bloch 书中的建议。


首先简单温习下Java语言最基本的一些概念:

Java语言支持四种类型:接口(interface),类(class),数组(array)和基本类型(primitive)。前三种类型通常被称为引用类型(Reference type),类实例和数组是对象(object),而基本类型的值则不是对象。类的成员(member)由它的域(field)、方法(method)、成员类(member class)和成员接口(member interface)组成。方法的签名(signature)由它的名称和所有参数类型组成,签名不包括它的返回类型。


类,接口,构造器,成员以及序列化形式被统称为API元素(API element)。从一个包导出API由API实现所属包内所有可以被从包外访问的API元素组成,包含包中的每个共有(public)类或者接口中所有公有(public)的或者受保护(protected)的成员和构造器。


第2章 创建和销毁对象


1.考虑用静态工厂方法代替构造器(public static $CLASS_TYPE newInstance())【Item 1】

  静态工厂方法获取类的实例对象相较于直接通过类的构造器直接创建实例对象的优势有:

  1) 静态工厂方法的名字比构造器的参数更加清晰明了的描述了当前创建的是一个什么了作用的对象

  2) 不必每次调用静态工厂方法时都创建一个新对象,而构造器调用一次创建一个新对象

  3) 静态工厂方法增加创建对象类型的灵活性,可以返回原返回类型的任何子类型的对象,这种方式为未来代码实现调整提供了方便,并且隐藏了API的具体实现

  4) 静态工厂方法在创建参数化类型实例的时候,使得代码变得更加简洁,这里需要用到泛型

  而静态工厂方法的缺陷在于:

  1) 类如果不含有public或者protected的构造器,就不能被子类化,因为子类必须隐式或显式的调用父类的构造方法

  2) 静态工厂方法其实与其它静态方法没有任何区别,这降低了此类的易读性,因为不知道该静态方法的设计初衷的维护人员不能直观的明白其作用,建议增加注释说明

2.构造方法需要有较多的参数时建议考虑构建器(builder)【Item 2】

  1) 对于有类的构造需要较多参数时,可以通过构造方法的重叠来处理,第一层构造方法可以包含必要的几个参数,下一层构造方法可以在第一层的基本上增加几个可选参     数....知道最后一层构造方法包含所有必选参数以及可选参数。这种方式的缺点在于当类的构造需要很多参数时,构造方法的层层重叠使得客户端的代码较难写,并且代码可读性差

  2) JavaBeans方式,类仅提供无参数的构造方法,客户端通过setter方法来设置每个必要的参数以及每个可选参数,这种方式看起来解决了类构造方法的重叠的问题,但是这样使得类可变,可变就意味着多线程安全性需要我们付出额外的努力

  3) Builder模式,不直接生成想要的对象,而是让客户端利用所有必要的参数调用构造方法或者静态工厂方法获得一个Builder对象,通过Builder的setter方法来设定可选参数,设定完成之后调用无参数的build方法获取类的实例对象。Builder的每个setter方法均返回Builder类型对象,这样保证setter方法可以连续调用(builder.setA().setB()),这样可读性和多线程安全性都能得到保证。但需要注意,builder的setter方法需要进行参数校验,如果发现设定了非法参数需要抛出相关异常。

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

  1) 将构造器访问权限设置为private,通过public static final修饰静态成员变量,该变量new出类的对象,保证该类实例的单一,这种方式需要在private构造器中通过check只被new了一个实例,如果有程序通过反射来直接调用private构造器时需要抛出异常提示错误

  2) 通过public静态工厂方法控制类实例的单一性,但本方式如果在实现序列化时需要声明所有实例的成员变量为transient的,并提供一个readResolve方法,否则每次在反序列化时都会重新创建一个实例。

  3) JDK 1.5之后只需编写一个包含单个元素的枚举类型即可保证singletone属性,同时在面对负责的序列化以及反射攻击都可以防止多实例化,其为实现Singletone最佳方式。

4.通过私有构造器强化不可实例化的能力【Item 4】

   本方式可以防止设计初衷本类不需要被实例化,如所有类成员均为static的类,不需要被实例化。但是本方式会导致类不能被子类化。

5.避免创建不必要的对象【Item 5】

  1) Java中的8大基本类型(byte,char,boolean,int,short,long,float,double)不是引用类型,而Java定义针对八大基本类型的引用类型,在需要的时候会将基本类型包装为其对应的引用类型,这叫自动包装机制。而在仅需基本类型计算的程序中,如果使用基本类型的包装类会导致基本类型的包装类多次被实例化,创建一些不必要的对象,这种场景是不合适的编码方式,在编码时需要注意

  2) 要优先使用基本类型儿不是包装基本类型,当心无意识的自动包装的发生。

  3) 对一些小对象的创建与销毁的代价比较小,不必刻意使用重用对象机制(对象池),而对于创建销毁代价高昂的数据库/线程等对象的创建需要谨慎,能够重用的对象尽量重用。

6.消除过期的对象引用(内存泄露三个常见来源:无意识过期引用被长久持有,缓存,监听器和其它回调。内存分析工具:Heap剖析工具(Heap Profiler))【Item 6】

  1) Java虽然不用程序编写者负责内存的管理,其使用的GC机制来自动进行内存的回收,但是GC的规则是会回收不再被引用的对象实例,如果对象在我们编写代码是无意识的长期持有,则不会被GC机制回收,从而导致引用对象不断增长,出现内存泄露

  2) 如果对象引用已经没有用了,一定记得将其置为null,以便GC回收。

  3) 如果你在最紧凑的作用域内定义每一个变量,那么引用对象的生命周期会自然而然的结束,从而其占用内存得到回收

  4) 只要是类自己管理内存,程序员就应该警惕内存泄露问题,一旦元素被释放掉,则该元素中所包含的任何对象引用都应该被清空

  5)内存泄露一个常见来源为缓存,一旦你把对象引用放到缓存中,则它很容易被遗忘掉,从而使得它不再有用之后很长一段时间内仍然留在缓存中,这种问题可以用WeakHashMap等已于被回收的类型来代替缓存,其随着生命周期的结束被回收

  6) 内存泄露另一个来源则是监听器和其它回调,如果你实现了一个API,客户端在API中注册回调,却没有显式的取消注册,则这些注册会被聚集。确保回调立即被当做垃圾被回收的最佳方法是只保存他们的弱引用(weak reference)

7.避免使用finalize方法【Item 7】

  1) Java语言不能保证finalize被及时的执行,甚至不能保证被执行,因此想依附该方法保证被回收是不可靠的。

  2) 如果未被捕获的异常在finalize过程中被抛出来,那么该异常会被忽略,并且该对象的终结过程也会终止,不会像正常方法中发生异常会使得线程执行被终止,病发出Stack Trace,finalze过程中异常不会打印且警告都不会打印,从而增加了出问题后的debug难度,而如果你真的需要使用finalize方法,那么建议在异常时主动打印warning类型的log作为提示

  3) 不建议依赖finalize方法的调用进行必须释放资源的释放和销毁,那么像对象中封装的例如文件或线程等资源确实需要释放或终止,只需提供一个显式的终止方法,并要求客户端程序主动调用即可,如果在被终止之后,还有方法调用被终止的资源时需要抛出IllegalStateException。

  4) 显示的终止方法通常与try-finally结构结合起来使用,以确保及时终止。

  5) 那么finalize方法的作用是什么呢?其应该作为最后一道安全网进行使用,即在程序员做了任何主动终止和释放相关资源的努力下仍然有遗漏的可能,则可以以来finalize方法的调用进行最后的释放。

  6) finalize方法的调用不会像构造器一样能够被自动执行调用链,即子类构造方法被调用会自动调用父类的构造方法。finalize方法需要主动调用,如果子类中的finalize方法调用需要同步调用父类的finalize方法需要显式调用super.finalize方法。

  7) 如果使用finalize方法作为最后一道完全网,防止finalize方法的非法调用最好能够在finalize方法中添加warning类型的log打印


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值