簡體   English   中英

從Junit測試中調用靜態Singleton類會引發StackOverflow錯誤

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM