Effective Java 2nd 阅读 第二章 创建和销毁对象

转载请注明来自:http://blog.csdn.net/huntersjm/article/details/43114241

本文持续更新中。。。

第一条:考虑用静态工厂方法代替构造器

1:静态工厂方法可以根据不同的需要来定义方法名称,而公用的构造方法因为命名一致,所以不容易区分差异性

2:当参数一致时,如果使用公用的构造方法,则会冲突,当改变构造参数的顺序时,则会产生误会,而静态工厂方法可以很便捷的更换名称来实现

package com.sjm.hunter.effectivejava;

import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

/**
 * Created by jiamin.sjm on 15/1/25.
 */
public class StaticFactoryMethod {

    private String name;
    private Map<String,String> map;
    private boolean def;

    /**
     * 如果我需要两种构造器,构造器参数一致,怎么解决问题?
     * 或许我会增加一个参数,比如def来帮助我决定
     * 本例子中假设内部定义一个Map,需要不同的构造方法决定,是HashMap还是TreeMap
     * 毕竟,他们有各自的左右
     */
    private StaticFactoryMethod(String name, boolean def){
        this.name  = name;
        this.def = def;
        if(def){
            map = new HashMap<String, String>();
        }else{
            map = new TreeMap<String, String>();
        }
    }

    /**
     * 而如果我讲公用方法定义为私有的,使用静态工厂方法来访问,就更加清晰明确了~
     */
    public static StaticFactoryMethod newHashInstance(String name){
        return new StaticFactoryMethod(name,true);
    }

    public static StaticFactoryMethod newTreeInstance(String name){
        return new StaticFactoryMethod(name,false);
    }
}
3:而当如果我提供的是不可变类或者单例,那么就可以使用如下的方法

    private static final StaticFactoryMethod STATIC_FACTORY_METHOD_HASH = new StaticFactoryMethod("Hash",true);
    private static final StaticFactoryMethod STATIC_FACTORY_METHOD_TREE = new StaticFactoryMethod("tree",false);

    private StaticFactoryMethod setName(String name){
        this.name = name;
        return this;
    }
    /**
     * 而如果我讲公用方法定义为私有的,使用静态工厂方法来访问,就更加清晰明确了~
     */
    public static StaticFactoryMethod getHashInstance(String name){
        return STATIC_FACTORY_METHOD_HASH.setName(name);
    }

    public static StaticFactoryMethod getTreeInstance(String name){
        return STATIC_FACTORY_METHOD_TREE.setName(name);
    }

    public void put(String key,String value){
        this.map.put(key,value);
    }

    public String get(String key){
        return this.map.get(key);
    }

    public static void main(String []args){
        StaticFactoryMethod staticFactoryMethod = StaticFactoryMethod.newHashInstance("MyHash");
        staticFactoryMethod.put("1","111");
        System.out.println(staticFactoryMethod.get("1"));
    }
4:顺便提一句,如果上述的Map类型改为String,Object就是一个简单的本地Session,如果使用泛型的方式来定义key,value会更好玩哦,有兴趣的可以自行尝试一下~~~

5:Java中有接口和父类,这个时候,静态工厂方法再次有他的优势了,因为使用构造方法返回的肯定是当前的类实例,而静态工厂方法可以返回接口或者类的子类型对象,可能说的不清楚,看代码~

package com.sjm.hunter.effectivejava;

/**
 * Created by jiamin.sjm on 15/1/25.
 */
public interface ISession<K,V>{
    public void put(K k,V v);
    public V get(K k);
}
package com.sjm.hunter.effectivejava;

import java.util.HashMap;
import java.util.TreeMap;

/**
 * Created by jiamin.sjm on 15/1/25.
 */
public class SessionFactory<K,V>{
    public static ISession newHashSession(){
        return new HashSession();
    }

    public static ISession newTreeSession(){
        return new TreeSession();
    }

    private static class HashSession<K,V> implements ISession<K,V>{
        private HashMap<K,V> map = new HashMap<K, V>();

        @Override
        public void put(K k, V v) {
            this.map.put(k,v);
        }

        @Override
        public V get(K k) {
            return this.map.get(k);
        }
    }

    private static class TreeSession<K,V> implements ISession<K,V>{
        private TreeMap<K,V> map = new TreeMap<K, V>();

        @Override
        public void put(K k, V v) {
            this.map.put(k,v);
        }

        @Override
        public V get(K k) {
            return this.map.get(k);
        }
    }
}
注意:此处使用了静态的内部类,至于内部类以及相关知识,请自行百度~
这里的要点便是,静态工厂方法,可以构建一些类,使用预定义的实现方式,来提供某个接口或者方法的对象实例,同时,该类可以对外部匿名,很多Java库中使用了该方式,比如:concurrent包中的Executor

java.util.concurrent;
public interface Executor {
    void execute(Runnable command);
}

package java.util.concurrent;
public class Executors {

    
    public static ExecutorService newFixedThreadPool(int nThreads) 
   
    public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) 

    
    public static ExecutorService newSingleThreadExecutor() 

    
    public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) 

    
    public static ExecutorService newCachedThreadPool() 

    
    public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) 

    
    public static ScheduledExecutorService newSingleThreadScheduledExecutor() 

    
    public static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory) 

    
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) 

    
    public static ScheduledExecutorService newScheduledThreadPool(
            int corePoolSize, ThreadFactory threadFactory) 

    public static ExecutorService unconfigurableExecutorService(ExecutorService executor) 

    public static ScheduledExecutorService unconfigurableScheduledExecutorService(ScheduledExecutorService executor) 

    
    public static ThreadFactory defaultThreadFactory() 

    
    public static ThreadFactory privilegedThreadFactory() 

    
    public static <T> Callable<T> callable(Runnable task, T result) 

    
    public static Callable<Object> callable(Runnable task) 

    
    public static Callable<Object> callable(final PrivilegedAction<?> action) 

    
    public static Callable<Object> callable(final PrivilegedExceptionAction<?> action) 

    
    public static <T> Callable<T> privilegedCallable(Callable<T> callable) 

    
    public static <T> Callable<T> privilegedCallableUsingCurrentClassLoader(Callable<T> callable) 

    
    static final class RunnableAdapter<T> implements Callable<T> 

    
    static final class PrivilegedCallable<T> implements Callable<T> 

    
    static final class PrivilegedCallableUsingCurrentClassLoader<T> implements Callable<T> 

    
    static class DefaultThreadFactory implements ThreadFactory 

    
    static class PrivilegedThreadFactory extends DefaultThreadFactory 

    
    static class DelegatedExecutorService extends AbstractExecutorService 

    static class FinalizableDelegatedExecutorService
        extends DelegatedExecutorService 

    
    static class DelegatedScheduledExecutorService
            extends DelegatedExecutorService
            implements ScheduledExecutorService 
    
    private Executors() 
}
6:服务提供者框架,通过服务提供者注册服务,使用者获得服务之后进行使用,非常灵活的通过简单的传入name来实现获取

package com.sjm.hunter.effectivejava;

/**
 * Created by jiamin.sjm on 15/1/25.
 */
public interface IServer{
    public void server();
}
package com.sjm.hunter.effectivejava;

/**
 * Created by jiamin.sjm on 15/1/25.
 */
public class Server implements IServer {
    @Override
    public void server() {
        System.out.println("我是服务者!");
    }
}
package com.sjm.hunter.effectivejava;

import java.util.HashMap;
import java.util.TreeMap;

/**
 * Created by jiamin.sjm on 15/1/25.
 */
public class Servers{
    private static final HashMap<String,IServer> servers = new HashMap<String, IServer>();

    public static IServer getServer(String name){
        return servers.get(name);
    }

    public static void registryServer(String name,IServer server){
        servers.put(name,server);
    }
}
package com.sjm.hunter.effectivejava;

/**
 * Created by jiamin.sjm on 15/1/25.
 */
public class Main {
    public static void main(String []args){
        Servers.registryServer("server",new Server());
        Servers.getServer("server").server();
    }
}
7:静态工厂方法,如果Java以后的版本中可以做到类型推断,那么可以直接使用类型推断获取,自动根据返回的参数,判断需要的 (如果是编译器主动将返回的类型传入方法,或许就可以)

Server server = Servers.getServer();
或许等价于:

Server server = Servers.getServer(Server.class);
8:常见静态工厂方法:

valueOf
of
getInstance
newInstance
getType
newType
突然想到,log4j,log4j就是使用了这个方法,感兴趣的可以去看看源代码哦~

第二条:遇到多个构造器参数时要考虑用构建器

第一眼看到构建器的时候,没看懂这个意思。突然感觉,Effective Java是让我见到了好多的新知识啊!!!

通常,有些类是需要很多参数的 JavaBeans,最常见的就是Hibernate中的Entity类,这个时候,有些参数可能是有默认值的,所以,如果只提供一个构造器,那就很麻烦,是不是,要写一长串,而如果定义好各种构造器,容易弄混乱,同时,就需要别的方案了,这个时候,构造器就出现了。

当然,弄过hibernate的人可能会说,hibernate很好很方便啊,因为他提供了最大化的构造器方法,最小的构造器方法,还有一系列的Setter方法之类的,可能很多人觉得好方便,的确,蛮方便的,但是,我个人觉得不是很好,因为这么随意的把成员变量暴露给外部来修改,这是很不好的编程习惯(当然,我这种很Low的人。。。哎。。。)

那么,EJ里面推荐了的方法是,构建器。什么是构建器呢,英文原版中是 builder,这下,我理解了~我感觉是不是叫建造者更好呢,通过一个builder去build()一个对象出来,这个模式就是这么简单~

package com.sjm.hunter.effectivejava;

/**
 * Created by jiamin.sjm on 15/1/26.
 */
public class Person {
    private String name;
    private int age;
    private boolean gender;

    private Person(Builder builder) {
        this.name = builder.name;
        this.age = builder.age;
        this.gender = builder.gender;
    }

    // 年龄总得告诉别人吧,所以可以获取,年龄什么的很多人都是保密的
    public String getName() {
        return this.name;
    }

    public String getGender(){
        return this.gender?"男":"女";
    }
    public static class Builder{
        private String name;
        private int age;
        private boolean gender;
        //一个人,姓名必须要的吧
        public Builder(String name){
            this.name = name;
        }
        //年龄性别什么的可选好了~
        public Builder age(int age){
            this.age = age;
            return this;
        }
        public Builder gender(boolean gender) {
            this.gender = gender;
            return this;
        }
        public Person build(){
            return new Person(this);
        }
    }
}
看到没有,这里定义中包含最小的构造器,保证足够的参数,同时,还可以保证,对象创建之后,不会被胡乱修改

同时,我想,强调一点,我们还可以通过建造者来进行参数限制,可以在set时,或者build()时,如果出现不合理的情况,直接抛出异常,这样比在构建对象之后,抛出异常好多了,那个时候,对象都可以被使用了啦,是不是感觉很好啊!

//年龄大于40的不适合干啥啥啥的~
        public Builder age(int age) throws IllegalArgumentException{
            if(age>40){
                throw new IllegalArgumentException();
            }
            this.age = age;
            return this;
        }

也就是说,可以通过建造者进行参数的约束限制。

顺便说一下,这里的参数设定时,返回的时this,也就是Builder对象,使用的是,chainning 也就是链式调用,记得之前使用http请求jar的时候,用过数次,然后特别地jQuery里面用的蛮多的。

当然了,建造者也有不好的地方,那就是多了些工作量,要写额外的代码啊,当然,这也为你带来好处,安全,有效,方便。


第三条:用私有构造器或者枚举类型强化singleton属性

这一条是意思是:如果需要使用单例模式,那么推荐使用私有的构造器,或者枚举类型,其实,学过设计模式的人都知道,单例模式的实现方式

</pre><pre style="font-family: Menlo; background-color: rgb(255, 255, 255);"><span style="font-size:12px;"><span style="font-family: Arial, Helvetica, sans-serif;"></span></span><pre name="code" class="java"><span style="font-size:12px;"><span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);"></span></span><pre name="code" class="java"><span style="font-size:12px;">package com.sjm.hunter.effectivejava;

/**
 * Created by jiamin.sjm on 15/1/26.
 */
public class Singleton {
    private static  boolean flag;
    private static final Singleton INSTANCE = new Singleton();

    private Singleton(){
        if(INSTANCE==null){
            INSTANCE = new Singleton();
        }
    }
    public static Singleton getInstance() {
        return INSTANCE;
    }
}</span>
 
 

这里面,我没有考虑并发的问题,加上并发,就可以变成单例模式,同时,也可以使用第一条中的静态工厂方法类似的实现这个问题

if(INSTANCE == null){
   synchronized(Singletom.class){
       if(INSTANCE == null){
           INSTANCE = new DoubleCheckedLockingSingleton();
       }
   }
}

可能有人说,为什么要加上null判断,那么就需要考虑EJ中说的,可能使用AccessibleObject.setAccessible()方法来获得私有构造方法的共享,也就是可以创建新的变量,虽然我没有试过

这里引用一段代码:http://stackoverflow.com/questions/4081227/singleton-pattern

import java.lang.reflect.Constructor;

public class PrivateInvoker {
    public static void main(String[] args) throws Exception{
        Constructor<?> con = Private.class.getDeclaredConstructors()[0];
        con.setAccessible(true);
        Private p = (Private) con.newInstance();
    } 
}

class Private {
    private Private() {
        System.out.println("Hello!");
    } 
}

EJ中提到,单例模式,反序列化的时候,会遇到一个问题,就是反序列化会创建新的实例,

EJ中推荐使用的方法是:增加readResolve方法,返回实例对象来避免这个问题,看的时候蛮不懂的,看了一下官方的文档

官方文档的意思是,如果该方法存在时,即用此方法来代替默认的反序列化处理类方法,可以细看官方JDK文档

当然了,Ej更推荐使用enum类型来解决问题,通过枚举,可以保证不会出现以上任何问题,包括反序列化的问题

public enum EnumEx {
    INSTANCE;
}


第四条:通过私有构造器强化不可实例化的能力

这里是说,如果不希望类被实例化,那么就定义一个私有的构造器,这样类就不会被实例化,这主要使用在工具类中,不需要被实例化,这包括不能被继承。

之前我知道,如果是final的类,不能被实例化,现在我才知道,原来,如果类有且只有私有的构造器,也不难被实例化,因为子类的构造方法,需要显示或隐式的调用父类的构造方法


第五条:避免创建不必要的对象

这句话相信大家都知道,创建不必要的对象,那肯定是不好的。那么怎么去避免呢?

a. 对于不可变的对象,重用对象,而不是去重复的创建,最常见的是字符串,比如 String s = new String("hunter"); 那就会创建新的对象,原因大家应该都知道的,而如果使用String s = "hunter";那么就可以重复利用对象的啦。

b. 使用静态工厂方法来获取对象,而不是创建新的对象

c. 重复利用的对象,比如在一个循环中频繁出现,而且不会改变,就定义到外部,就不必要每次都进行创建,当然有些情况下,编译器会自动进行调整结构。

d. 尽量使用基本类型代替封装的对象,如int-Integer,因为对象是可变的,而基本类型是不可变的,也就会被复用

e. 有使用,使用一个对象池,从对象池去对象,返回对象,也是很好的方式来重用对象,比如数据库连接池


第六条:消除过期的对象引用

 当变量不被使用时,应该被删除,如果其被对象引用,但是却不使用,那么就得注意。

注意的是缓存中,对象往往,忘记删除,而却不再使用或者过期,就会可能这个问题,当然,这可以在设定了有效时间的缓存中得到解决。

同时,在一些监听器或者回调中,也经常出现这种,可能已经不会被调用,然而还存在的引用,也会出现问题,小心OutOfMemory哦~


第七条:避免使用终结方法(也就是finalizer方法)

finalizer方法和c++中的析构函数有点类似,但是又不一样(不确定执行时间,也不一定执行),EJ中推举一般情况下不要使用该方法

finalizer方法,不确定执行时间,不确定是否会执行,其中的异常也不会被处理,性能上损失很严重

需要终结的可以显示的终结,比如使用close()类似的方法,当然有时候,try catch中可以使用finally块

虽然Ej中提到使用finalizer的两个场合,但是,其实我们基本上用不到。





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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值