2011-03-17 92 views
3
MyInterface intf = (MyInterface) Class.forName(className).newInstance(); 

我有一段代碼,它將使用上述調用按需創建新接口並調用某個方法。所有的實現類通常包含大量的final static變量和靜態初始化代碼,我只想在其生命週期中只觸發一次。如何防止某個類的多個對象實例化

但是,由於我使用newInstance()調用,我感到舊對象獲取GCed並且類被再次初始化,因此所有的靜態變量。

爲了避免這種情況,我想把它放在一個緩存中,以便這些類不會再次重新構造,因此會在其生命週期中初始化一次。 (注意:我的界面是線程安全的)。

我應該把它放在Hashtable中,只是查看它還是有更好的方法來處理緩存?

回答

6

所有實現類 通常含有大量的最後靜態 變量和靜態初始化代碼 ,我想在其一生中火只 一次。

但由於我使用newInstance()調用 ,我是 老對象獲取GCed和類 再次,因此初始化所有 靜態變量的印象。

不,這是不正確的。靜態字段和靜態塊只能運行一次 - 當類加載時。因此反覆創建實例不會重新運行它們。而且,他們不會被垃圾收集。所以你不必擔心靜態字段被GCed和重複重新創建:-)。

附加說明:

以上僅適用於僅使用默認類加載器的情況。如果您使用多個類加載器並卸載類加載器,那麼它加載的所有類以及它們的靜態字段變爲符合GC的條件。此外,每個類加載器都存在3靜態字段,不僅每類,所以如果使用不同的類加載器加載相同的類,靜態初始化將確實運行多次。但是,這可能不是你cocern ...

也看到這裏的討論:

Are static fields open for garbage collection?

+1

或者反過來說:由不同的類加載器加載的相同字節碼導致不同的類(具有單獨的靜態特性)。 – 2011-03-17 12:43:27

0

這就是所謂的Singleton
使用此模式將只有一個類的實例。
有一些例外,但是在大多數情況下,您只有一個實例。

+0

單身人士,而不是單身噸:-) – 2011-03-17 11:34:14

+0

感謝您指出:) – Padmarag 2011-03-17 11:58:40

2

怎麼是這樣的:

private static Map<String, Object> cache = 
    new ConcurrentHashMap<String, Object>(); 

@SuppressWarnings("unchecked") 
public static <T> T getImplementation(final String implementationClass){ 
    T result; 
    synchronized(cache){ 
     result = (T) cache.get(implementationClass); 
     if(result == null){ 
      try{ 
       result = 
        (T) Class.forName(implementationClass).newInstance(); 
      } catch(final Exception e){ 
       // error handling here 
      } 
      cache.put(implementationClass, result); 
     } 
    } 
    return result; 
} 

這將保證各只有一個類的實例被使用(只要你只能使用此方法)

實例應用:

FooService service = getImplementation("com.mycompany.FooServiceImpl"); 

使用Guava ,這個代碼可以改善with a Computing Map

private static Map<String, Object> cache = 
    new MapMaker().makeComputingMap(
     new Function<String, Object>(){ 

      @Override 
      public Object apply(final String input){ 
       try{ 
        return Class.forName(input).newInstance(); 
       } catch(final Exception e){ 
        // error handling here 
       } 
      } 
     }); 

@SuppressWarnings("unchecked") 
public static <T> T getImplementation(final String implementationClass){ 
    return (T) cache.get(implementationClass); 
} 
3

如果我理解正確的話,你需要創建類的新實例,你要確保每次調用時執行一些初始化代碼只有一次,而不是newInstance()。我認爲,如果你把初始化代碼放在一個靜態部分,那麼你很好:AFAIK靜態初始化塊中的代碼在加載類時由虛擬機執行一次。另請參閱此question

+1

「在靜態初始化程序塊中的代碼在加載類時由虛擬機執行一次」。真正;如果你使用多個類加載器,一個類可能會被加載多次。但是如果你這樣做,你通常應該知道你在做什麼...... – sleske 2011-03-17 11:43:39

2

這個怎麼樣。

static final Map<String, Object> instances = new HashMap<String, Object>(); 

public static Object getInstance(String name) { 

    if (name == null) { 
     return null; 
    } 
    Object obj = null; 

    synchronized (ClassName.class) { 
     try { 
      obj = instances.get(name); 
      if (obj == null) { 
       obj = Class.forName(name).newInstance(); 
       instances.put(name, obj); 
      } 
     } catch (ClassNotFoundException e) { 
     } catch (InstantiationException e) { 
     } catch (IllegalAccessException e) { 
     } 
    } 
    return obj; 
} 
+1

+0:你不更新任何實例映射;) – 2011-03-17 11:45:23

+0

而且你也不是安全的競爭條件,其中多個線程創建新的實例 – 2011-03-17 11:48:04

+0

@peter:更新 – 2011-03-17 11:48:13