問題描述
最近在寫一些單元測試用例,為了避免連接外界服務,所有選擇mock了數據庫Dao層,計划將數據庫所需要的數據存在List中,在類加載的時候初始化List並且填充數據。代碼如下:
1 public class UserDaoMock extends UserDao { 2 @Override 3 public List<UserInfo> selectUserInfo() { 4 return getUserInfo(); 5 } 6 7 static { 8 initUserInfo(); 9 } 10 11 public static UserInfo getUserInfo() { 12 return userInfos; 13 } 14 15 16 private static List<UserInfo> userInfos = new ArrayList<UserInfo>(); 17 private static void initUserInfo() { 18 UserInfo userInfo1 = new UserInfo(); 19 userInfo1.setId(1L); 20 userInfo1.setPin("user1"); 21 userInfo1.setUserId(1L); 22 userInfo1.setDataBillingType(1); 23 userInfo1.setErp("operator1"); 24 userInfo1.setCreatedTime("2019-01-01"); 25 userInfo1.setModifiedTime("2019-05-01"); 26 userInfo1.setYn(1); 27 userInfos.add(userInfo1); 28 29 UserInfo userInfo2 = new UserInfo(); 30 userInfo2.setId(2L); 31 userInfo2.setPin("user2"); 32 userInfo2.setUserId(2L); 33 userInfo2.setDataBillingType(1); 34 userInfo2.setErp("operator2"); 35 userInfo2.setCreatedTime("2019-01-01"); 36 userInfo2.setModifiedTime("2019-05-01"); 37 userInfo2.setYn(1); 38 userInfos.add(userInfo2); 39 40 UserInfo userInfo3 = new UserInfo(); 41 userInfo3.setId(3L); 42 userInfo3.setPin("user3"); 43 userInfo3.setUserId(3L); 44 userInfo3.setDataBillingType(1); 45 userInfo3.setErp("operator1"); 46 userInfo3.setCreatedTime("2019-01-01"); 47 userInfo3.setModifiedTime("2019-05-01"); 48 userInfo3.setYn(1); 49 userInfos.add(userInfo3); 50 } 51 }
結果在new對象的時候:
1 UserDao userDao = new UserDaoMock();
一直報錯:
java.lang.ExceptionInInitializerError at com.jd.ads.afa_index.indexes.task.TestUserServiceTask.setUp(TestUserServiceTask.java:37) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44) at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24) at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:74) at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:83) at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:72) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:231) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:88) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229) at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71) at org.junit.runners.ParentRunner.run(ParentRunner.java:309) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:174) at org.apache.maven.surefire.junit4.JUnit4TestSet.execute(JUnit4TestSet.java:53) at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:119)
起初以為是init方法出錯了,嘗試了多種方式,才發下原來是類加載時,當執行到static{}語句塊時開始調用userInfos,但userInfos這個list由於是在后面而還未定義和初始化,所以導致了錯誤。還是因為基礎知識不扎實啊!!!
問題解決
將static對象提到最前面,即解決了此問題,簡直愚昧的我啊:
1 public class UserDaoMock extends UserDao { 2 private static List<UserInfo> userInfos = new ArrayList<UserInfo>(); 3 4 @Override 5 public List<UserInfo> selectUserInfo() { 6 return getUserInfo(); 7 } 8 9 static { 10 initUserInfo(); 11 } 12 13 public static UserInfo getUserInfo() { 14 return userInfos; 15 } 16 17 private static void initUserInfo() { 18 UserInfo userInfo1 = new UserInfo(); 19 userInfo1.setId(1L); 20 userInfo1.setPin("user1"); 21 userInfo1.setUserId(1L); 22 userInfo1.setDataBillingType(1); 23 userInfo1.setErp("operator1"); 24 userInfo1.setCreatedTime("2019-01-01"); 25 userInfo1.setModifiedTime("2019-05-01"); 26 userInfo1.setYn(1); 27 userInfos.add(userInfo1); 28 29 UserInfo userInfo2 = new UserInfo(); 30 userInfo2.setId(2L); 31 userInfo2.setPin("user2"); 32 userInfo2.setUserId(2L); 33 userInfo2.setDataBillingType(1); 34 userInfo2.setErp("operator2"); 35 userInfo2.setCreatedTime("2019-01-01"); 36 userInfo2.setModifiedTime("2019-05-01"); 37 userInfo2.setYn(1); 38 userInfos.add(userInfo2); 39 40 UserInfo userInfo3 = new UserInfo(); 41 userInfo3.setId(3L); 42 userInfo3.setPin("user3"); 43 userInfo3.setUserId(3L); 44 userInfo3.setDataBillingType(1); 45 userInfo3.setErp("operator1"); 46 userInfo3.setCreatedTime("2019-01-01"); 47 userInfo3.setModifiedTime("2019-05-01"); 48 userInfo3.setYn(1); 49 userInfos.add(userInfo3); 50 } 51 }
注:
類加載時不會為實例變量賦值,對象創建時不會為靜態變量賦值。我們調用靜態方法時,此類就開始加載,加載的時候不會為實例變量賦值,但是會按順序給靜態變量賦值。
類加載特性 :
*在虛擬機的生命周期中一個類只被加載一次。
*類加載的原則:延遲加載,能少加載就少加載,因為虛擬機的空間是有限的。
*類加載的時機:
1)第一次創建對象要加載類.
2)調用靜態方法時要加載類,訪問靜態屬性時會加載類。
3)加載子類時必定會先加載父類。
4)創建對象引用不加載類.
5) 子類調用父類的靜態方法時
(1)當子類沒有覆蓋父類的靜態方法時,只加載父類,不加載子類
(2)當子類有覆蓋父類的靜態方法時,既加載父類,又加載子類
6)訪問靜態常量,如果編譯器可以計算出常量的值,則不會加載類,例如:public static final int a =123;否則會加載類,例如:public static final int a = math.PI。
轉載請注明:https://www.cnblogs.com/fnlingnzb-learner/p/10615516.html