欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Effective Java读书笔记、感悟——1.创建和销毁对象 博客分类: Java_SE effectivejava对象创建对象销毁 

程序员文章站 2024-02-15 08:54:58
...

    每天看点Effective Java,挺好的,至今觉着Java小菜,多学基础知识,喜欢因为做喜欢的事忘记时间的感觉。

    不多吐槽了。直奔主题,这里只是笔记和一些感触,选取了一些我熟悉的类和方法举个例子,因为很久了做Android比较多,我可能会选择一些Android的类库说明android相关的设计理念,对于常用到的就不再举例,一时想不到有些,也不必要非要找出实例,自己做时候注意就好了。如果细致了解建议看原文,原文中提到了一些例子,除了很重要的、以前忽略的这里都不再重复。


 

一:考虑静态工厂方法代替构造器
->静态工厂方法,不全同设计模式中的工厂模式。
->静态工厂方法可以解决构造函数只能参数类型顺序不同的问题,比如可以两组相同的序列可以用方法名表示不同的含义。
->可以返回子类型,类似工厂模式了。到这里可以屏蔽子类,如针对不同参数返回不同子类优化性能,如果后来发展无法满足需求了,大可增加或删除子类,而构造方法不行。
->对于编码阶段禁忌第一反应是构造方法
->静态工厂方法缺点,无可继承构造方法时无法子类化,还有在java doc中和其他方法没有区别,而构造方法会单独列出。

二:遇到多个构造器参数时考虑用构造器
->构建器builder。这里很有代表性的就是Android中Dialog的Builder,详见API,提供了先传入参数后构造Dialog的方法。
->多个参数的解决方案中:
重叠构造器包含了用户不期望的默认值。Java bean非线程安全,可能处于不一致状态,且阻止了把类做成不可变的可能。
->构建器使用抽象工厂解决了两者,且可以在对象域(解释下,产生对象的方法)进行约束检查,违反抛出illegalstateexception,或在每个setter进行。
->Java中传统的抽象工厂实现是Class对象,用newInstance方法充当build方法的一部分,这种用法隐含着许多问题。因为newInstance方法总是企图调用类的无参数构造器,这个构造器甚至可能根本不存在。如果类没有可以访问的无参构造器不会出现编译错误,但是client code中必须catchInstantiationException or IllegalAccessException和一切因无参数构造器带来的问题,即使无throws子句。Class newInstantce破坏了编译时的异常检查,而Builder可以弥补。
->缺点:需要多建立一个构造器,代码显得冗长,一般用于多余4个参数调用,但如果你将来有添加参数的可能,那么请用构造器,因为同时存在这些就比较不协调了,对于API的设计来讲是非常不合理的。

三:用私有构造器或枚举类型强化Singleton属性
Singleton属性一般采取的两种方式:
1. 静态final instance
2. 私有final instance,提供静态getInstance方法返回,我们一般采取这种方式,因为它可以保证在API不便的情况下选定是否为Singleton或修改为其他形式。
这里提到这两种方式主要要说明两个没注意的问题:反射和序列化
->反射:这种方式反射都可以访问到构造方法,可以对构造方法做修改,当产生第二个实例抛出异常。
->序列化:仅仅普通方法实现接口是不够的,以为为了保证Singleton,必须要声明所有实例域都是瞬时的,并提供一个readResolve方法。否则存在的问题是每次反序列化一个序列化实例,都会创建一个新的实例。
private Object readResolve(){
      return instance;
}

书中提到了1.5增加枚举后的第三种方法,
public enum Elvis{
     INSTANCE;

     methods......
}
此方法直接解决以上两个问题。Android很重要的就是工具库编写的DB工具类使用了Singleton生成Android自身的dbhandler来处理,虽然不序列化,但是反射是个很严重的问题,毕竟是工具包,所以快点重构吧亲。同样,也许你在HttpHandler中也需要用到。如果没有制作这两个包,建议使用,因为可重用性非常高,也需要解决很多问题,几乎每个项目都使用,同样,保证包的使用的稳定性,不多说了,下一条。

四:通过私有构造器强化不可实例化的能力
->对只有静态方法和域的类,主要用于把基本类型的值或数据类型上的相关方法组织起来(Math,Arrays),可以通过Collections的方法把实现特定接口的对象上的惊天方法组织起来,可以利用这种类把final类上的方法组织起来,以取代扩展该类的做法。
->此工具类是不希望被实例化的,实例化对他么有任何意义。然后我们如果不提供构造器,jvm会自己提供,那还是会被实例化,那么我们只要在类中提供一个私有的构造器就可以了,并添加注释说明。
这样带来的问题是不能子类化,因为子类要求要求super父类的构造函数。

五:避免创建不必要的对象
->若一个方法频繁调用且每次生成相同的内部实例,可以作为static,如Map的keyset。
->维护自己的对象池来避免重复创建对象不是很好的做法,除非对象是非常重量级的,Object pool也增加了内存占用。

六:消除过期的对象引用
存在过期引用会导致无法GC,但清空对象引用应该是一种例外,而不是一种规范行为。
一下三种情况要考虑会发生内存泄露
1.类自己申请内存管理
2.缓存,易忘记管理,如WeakHashMap可以自动处理没有被外部引用的缓存项。一般利用后台线程定时清理,也可以类似LinkedHashMap使用removeEldestEntry在添加条目时清理。对于复杂的缓存,必须直接使用java.lang.ref
3.监听器和其他回调,回调此时可以做成弱引用。

七:避免使用终结方法
这里先说一下finalize的执行方式,再来说effective java中学到的东西。JVM采用跟搜索算法进行GC对象选择,这里不细说,JVM发现需要回收的对象(当然直观的来看就是没有引用的对象了)会做一次标记并进行一次筛选,筛选的条件就是此对象是否要执行finalize方法(对象未覆盖或finalize已被JVM执行过一次都被视为不需要执行),需要执行的放在F-Queue中,并稍后由一条由虚拟机自动建立的、低优先级的Finalizer线程去执行,但执行仅指虚拟机会触发这个方法,但不承诺会等待此方法运行结束,原因很简单,不可能因为等待这一个进程而使得其他要回收的资源一直等待,这样会导致GCC崩溃。当然一般也没有人会去这样做,毕竟教材没有这样教的,这里effective java中提到了两个用途很重要去掌握,回收资源一般作为一种显示的stop方法。finalize方法的作用:
1. 当用户没有调用stop,这时候在finilize中回收资源总比不回收强,相当于补充一个安全网。(Android中最典型的是MediaPlayer,如果不回收资源将会带来很大的问题,可以查看Android确实是这样做的,它调用的方法如下:
protected void finalize() { native_finalize(); } 
具体含义不是这里应该继续讨论的问题,但是需要关注的是如果去做,这里MediaPlayer做的不是realse一样的内容,实际需要考虑finalize执行的问题,它不能耗时太长且不保证执行)
2. 普通对象通过本地方法委托给一个本地对象(本地对等体,因为本地对等体不是普通对象,gC不知道它,当本地对等体不拥有关键资源时finilize是最合适的工具。
需要注意的是,子类如果不super父类的finalize那么不会执行父类的代码,为了防止子类恶意不调可以使用放入一个匿名类,创建一个终结方法守卫者,即当他finalize时会释放外面的资源,与子类调用与否无关。但是此方法不推荐使用,因为方法的本意只是为了让c++程序员更快习惯java的编码而做的妥协,并不是java的析构函数