[英]Calling a Static Singleton Class from Junit test throws StackOverflow error
當我們運行DrawerOpenTest.java時,它會引發StackOverflow錯誤,而我們希望測試用例能夠通過。 當DrawerOpen類為單例時,assertThat(actualState,is(expectedState)); 在測試用例中也應如此。
請注意,“狀態”是3種基本方法的接口。
DrawerOpenTest類
import static org.hamcrest.CoreMatchers.*;
import org.junit.*;
public class DrawerOpenTest {
@Test
public void openCloseButtonPushedPositiveTest(){
DVDPlayer cut = DVDPlayer.getInstance(DrawerOpen.getInstance());
State expectedState = DrawerClosedNotPlaying.getInstance();
State actualState = cut.openCloseButtonPushed();
assertThat(actualState, is(sameInstance(expectedState)));
}
}
DrawerOpen類
public class DrawerOpen implements State {
private DVDPlayer player = DVDPlayer.getInstance(DrawerOpen.getInstance());
private static State state;
private DrawerOpen() {}
public static State getInstance() {
if(state == null)
state = new DrawerOpen();
return state;
}
@Override
public void openCloseButtonPushed() {
player.close();
player.changeState(DrawerClosedNotPlaying.getInstance());
}
@Override
public void playButtonPushed() {
player.close();
player.play();
player.changeState(DrawerClosedPlaying.getInstance());
}
@Override
public void stopButtonPushed()
}
}
DVDPlayer類
public class DVDPlayer {
private DVDPlayer() {}
private static DVDPlayer player = null;
private State state;
public State getState() {
return state;
}
public static DVDPlayer getInstance(State stateParam) {
//making it singleton
if(player == null)
{
player = new DVDPlayer();
player.state = DrawerClosedNotPlaying.getInstance();
}
else
player.state = stateParam;
return player;
}
public void changeState(State newState) {
this.state=newState;
}
public State openCloseButtonPushed(){
state.openCloseButtonPushed();
return state;
}
public State playButtonPushed() {
state.playButtonPushed();
return state;
}
public State stopButtonPushed() {
state.stopButtonPushed();
return player.state;
}
public void open() {
System.out.println("DVDPlayer is opening.....");
}
public void close() {
System.out.println("DVDPlayer is closing.....");
}
public void play() {
System.out.println("DVDPlayer is playing.....");
}
public void stop() {
System.out.println("DVDPlayer is stopping.....");
}
}
結果:由於不斷初始化自身,因此會導致StackOverflowError。 請幫助如何使此測試通過。
java.lang.StackOverflowError
at DrawerOpen.<init>(DrawerOpen.java:5)
at DrawerOpen.getInstance(DrawerOpen.java:13)
at DrawerOpen.<init>(DrawerOpen.java:5)
at DrawerOpen.getInstance(DrawerOpen.java:13)
at DrawerOpen.<init>(DrawerOpen.java:5)
at DrawerOpen.getInstance(DrawerOpen.java:13)
at DrawerOpen.<init>(DrawerOpen.java:5)
at DrawerOpen.getInstance(DrawerOpen.java:13)
at DrawerOpen.<init>(DrawerOpen.java:5)
at DrawerOpen.getInstance(DrawerOpen.java:13)
at DrawerOpen.<init>(DrawerOpen.java:5)
at DrawerOpen.getInstance(DrawerOpen.java:13)
at DrawerOpen.<init>(DrawerOpen.java:5)
at DrawerOpen.getInstance(DrawerOpen.java:13)
at DrawerOpen.<init>(DrawerOpen.java:5)
at DrawerOpen.getInstance(DrawerOpen.java:13)
at DrawerOpen.<init>(DrawerOpen.java:5)
這個:
public class DrawerOpen implements State {
private DVDPlayer player = DVDPlayer.getInstance(DrawerOpen.getInstance());
// ...
}
意味着您每次嘗試創建DrawerOpen
時都會調用DrawerOpen.getInstance()
,因為player
是成員變量。
為了在DrawerOpen.getInstance()
創建實例,您必須創建DrawerOpen
的實例; 但這又再次調用DrawerOpen.getInstance()
,要求您創建一個DrawerOpen
,它調用DrawerOpen.getInstance()
...
您可以通過將DrawerOpen.getInstance()
分配給靜態字段來避免此遞歸調用:
public class DrawerOpen implements State {
private static DrawerOpen INSTANCE = DrawerOpen.getInstance();
private DVDPlayer player = DVDPlayer.getInstance(INSTANCE);
// ...
}
但是,還有另一個問題,那就是您在調用DVDPlayer.getInstance(INSTANCE)
時沒有分配INSTANCE
,因此最終將調用DVDPlayer.getInstance(null)
。
解決此問題的一種方法是調用:
private DVDPlayer player = DVDPlayer.getInstance(this);
但是,這是所謂的“ 不安全發布”的示例,其中您在完全初始化DrawerOpen
類之前泄漏了this
引用。 這可能會導致其他意外問題。
您的代碼有些混亂。 您應該仔細考慮為什么要使用單例,因為在我看來,這些似乎不像單例屬性。
例如,您有一個依賴於參數的“單身”有點狡猾:如果您使用其他State
實例調用DVDPlayer.getInstance
,則當前將忽略新的State實例,並使用先前的State實例。 這很可能導致混亂或令人驚訝的行為:我給您提供了該狀態實例,但DVDPlayer正在使用該狀態實例。
更改這兩行:
private DVDPlayer player = DVDPlayer.getInstance(DrawerOpen.getInstance());
private DrawerOpen() {}
對此:
private DVDPlayer player;
private DrawerOpen() {
player = DVDPlayer.getInstance(this);
}
這將擺脫遞歸初始化循環。
說明:構造函數中的this
表示正在構造的對象。 這是一個將被返回的單DrawerOpen.getInstance()
...如果該對象已在代碼中的那一點被構造。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.