2012-03-01 25 views
3

有沒有辦法將非核心Java類返回到新加載的狀態?我想要相當於卸載一個類並從頭開始重新加載它。我最關心的是靜態初始化器和變量。尋找重新運行類靜態初始值設定項的方法

問題背景:我正在爲學生代碼編寫機器人分級程序。我見過的一個常見學生錯誤是不恰當地使用靜態變量。例如,考慮一個包含靜態數量的元素的集合。該集合在第一次創建和使用時會正常工作,但在下一個實例化時失敗。如果我希望我的測試儘可能模塊化,那麼我需要一種方法在測試後恢復乾淨狀態。

現在我正在加載類或多或少像這樣,我已經勾畫出我想要如何使用它。

String classUnderTest = "package.class"; 
    Class unitUnderTest; 
    try { 
     unitUnderTest = Class.forName(classUnderTest); 
    } catch (ClassNotFoundException e) { 
     System.out.println("Class \"" + classUnderTest + "\" was not found, can't continue."); 
     printGradeAndExit(); 
    } 
    // Run foundation tests (stuff on which later tests depend) using unitUnderTest.newInstance() 
    runFoundationTests(unitUnderTest); 
    // Now reset unitUnderTest for a static variable detection test 
    lookForCommonStaticVariableMistakes(unitUnderTest); 

顯然,機器人平地機不能完美,但我想抓住常見的錯誤,這是其中之一。

根據Java Language Specification, Section 12.7,支持類的卸載是可選的(但會做我想要的)。有沒有辦法做到這一點,而不依賴於非標準的功能?

最後的手段是做一個Ant構建,它在單獨的程序中運行一系列測試,但如果可能的話,我想在一個過程中完成這項工作。

+0

我沒有看到您發佈的代碼中使用靜態初始值設定項的任何內容,更不用說「重新運行」它們。 – Perception 2012-03-01 03:29:05

+0

當unitUnderTest加載並調用newInstance()時,該類的靜態初始值設定項將運行。 – Mike 2012-03-01 03:31:02

回答

3

您不必爲每次運行只卸載類就使用一個新的類加載器。您可以創建URLClassLoader的新實例並使用該實例加載學生代碼(而不是將學生代碼放在常規應用程序類路徑中)。使用該ClassLoader加載目標類,然後使用返回的Class對象找到您選擇的方法並調用它。如果您希望調用的方法是非靜態的,則必須先採取一些額外的步驟,通過反射來創建對象的實例。

使用典型的public static void main(String[])方法進行輸入的示例。

String[] args = new String[0]; //Add arguments for main call. 

//Add whatever classpath elements you need. 
String[] classpath = {"file:///example/path/to/studentProjectJarFile.jar", 
         "file:///example/path/to/studentProjectDir/"}; 

//Create classloader. 
ClassLoader cl = 
      new URLClassLoader(classpath); 

Class<?> mainClazz; 
Method mainMethod; 

//Find Class. 
mainClazz = cl.loadClass("com.example.MyClass"); 
//Find the target method. 
mainMethod = mainClazz.getMethod("main", String[].class); 
//Invoke method. 
mainMethod.invoke(null, (Object) args); 

在這個例子中com.example.MyClass不應該在常規的應用程序類路徑上。如果是那麼這個版本將被使用,而不是由自定義類加載器找到的版本,因爲標準類加載器使用父代優先代理來加載類。

+0

我想我明白你要去哪裏。它會帶我一堆小心的API讀數來弄清楚,但我得到了一般想法。我應該能夠使用新的類加載器(URL或其他)創建新實例,並且每個實例都是獨立的靜態初始化。這應該適合我在這裏的具體需求。這也會導致依賴類的重新加載嗎?例如,靜態內部類?我現在不需要它,但我可以看到將來需要它。 – Mike 2012-03-01 03:35:47

+0

@Mike我更新了我的答案,也包括一個例子。回答你的問題:是的,只要該類未被類加載器加載,該類加載器是自定義類加載器的父類,例如系統類加載器(應用程序類路徑),擴展類加載器(jre擴展類)或引導類加載器(jre類)。 – Dev 2012-03-01 03:50:12

1

坦率地說,我會開始一個新的JVM來評估每個學生的代碼。如果您嘗試在一個JVM實例中執行所有操作,那麼對於一個學生的代碼來說,干擾其他人的機會太多。像一個無限循環一樣簡單...

作爲一個副作用,這在很大程度上消除了對自定義類加載器等的需求。

+0

這已經是計劃了。每個學生獲得一個新的JVM。但是在單個學生項目上啓動一個新的JVM用於多個模塊化測試?這有點多。 – Mike 2012-03-01 04:28:38

+1

我想說明的是,JVM啓動時間在100 ms範圍內,這對於運行數百次測試來說會非常緩慢。 – Nayuki 2012-03-01 04:45:20

+0

@Mike - 「有點多」 - 這取決於你是否重視你的時間或計算機的時間更高。非常緩慢是相對的。 (FWIW,我爲150多名學生做了這樣的事情,但我承認自己實現了自動標記,因爲JUnit測試的結果是學生們沒有事先看到......這種方法有其自身的問題。 ) – 2012-03-01 05:46:58

相關問題