簡體   English   中英

如何檢測Java類是由其自己的main()調用還是從另一個類調用?

[英]How can I detect if a Java class is called by its own main() or from another class?

我有一個看起來像這樣的Java類:

public class My_ABC
{
  int a=0;
  boolean B=true;

  static  // Initialize and load existing data only once at start-up
  {
     // need to know if it's called from its own main()
     // or by another class to conditionally set veriables
  }

  public My_ABC(int AA,boolean BB)
  {

  }

  public static void main(String[] args) 
  {
    My_ABC my_abc = new My_ABC(3,true);
  }
}

因為靜態塊是在加載類時運行的,所以如何檢測它是從自己的main()調用還是由另一個類調用以有條件地設置變量?


我知道你們中的一些人說:“各種鍾聲響起!” 好吧,這是因為我遇到了一種情況:我正在設計一個類,需要將大量數據加載到PC的極限(4G Ram)中,並且我正在運行只能使用1.5G的Java 32位版本的Ram max; 因此,當我單獨測試該類時,我需要加載盡可能多的數據以測試所有可能的情況,但是當從多個其他類中調用它時,它將無法執行此操作(將導致堆空間不足錯誤),因此只能加載最小 需要的數據。 但是,由於所有數據在啟動時僅應加載一次,因此應將其保存在靜態塊中。 否則,我需要實現額外的邏輯來檢測它是第一次加載(需要加載數據)還是第二次,第三次加載(不應該一次又一次地加載數據)。 而且,如果我實施額外的邏輯來做到這一點並將數據加載代碼移出靜態塊,則會導致不必要的復雜性,因為如果我移至64位版本的Java(希望很快),那么額外的復雜性將是額外的負擔(我將有足夠的空間來加載所有數據,即使從其他類調用時也是如此。 因此,臨時的快速修復方法是在靜態塊中檢測出該異常並相應地處理差異,當我有足夠的空間時,只需將它們注釋掉,而無需更改編程邏輯結構。

感謝您提供所有答案和建議,我嘗試了“ StackTraceElement”方法,效果很好! 它解決了我的問題。

只看實際的堆棧。 使用靜態塊的以下實現進行測試。 無論您是“執行” My_ABC類還是稍后加載該類,打印輸出都會有所不同:

static // Initialize and load existing data only once at start-up
{
    StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
    for (StackTraceElement el : stackTrace) {
        System.out.println(el);
    }
    // in real life you wouldn't print but use the stackTrace array
    // to identify why the class has been loaded and do your initialisation
}

我認為您絕對應該改變方法。

但是由於您在這里問的是具體的東西 (總結)。

public class X { 
    static { 
        System.out.println("Static free block");
        StackTraceElement [] st  = new RuntimeException("").getStackTrace();
        if( st.length == 1 ) {
            System.out.println("Invoked from main");
        } else { 
            System.out.println("Invoked from somewhere else");
        }
    }
    public static void main( String [] args ) { 
        System.out.println("Main");
    }
}

使用它來測試它:

public class Y  { 
    public static void main( String [] args ) { 
        X x = new X();
    }
}

ps我不知道為什么約瑟夫刪除他的答案是正確的。

從技術上講,您不能從其自己的main方法調用靜態初始化程序,因為它總是 main方法之前運行:

// output: 'foobar'
public class Foobar {
    static { System.out.print("foo"); }
    public static void main(String[] args) { System.out.print("bar"); }
}

因此,您正在嘗試測試不可能的事情;-)

一個非常臟的解決方案將在靜態初始化塊中引發並捕獲異常。 捕獲異常時,使它將堆棧跟蹤打印到ByteArrayOutputStream上,將字節數組轉換為String並分析跟蹤以查看是否從所需的主方法調用了靜態初始化程序。

但是,這樣做聽起來像是不可思議的事情,必須避免采用更好的設計。

最好使用靜態初始化程序加載數據。 也許另一種方法是使它成為單實例類(通過使用Singleton Pattern或僅通過在代碼中確保只實例化一次)即可。 然后,您可以調用構造函數或帶有標志的load方法來指示如何設置變量。

FWIW,我認為即使使用靜態方法(再次可以向其傳遞標志)加載數據,雖然不是一個很好的解決方案,但也比使用靜態初始化器更好。

我不知道是否有可能絕對地知道某個類是否正在被加載,是因為其自身的main()被調用,還是因為另一個類對其進行了引用-或只是決定加載它。 您可以調查堆棧跟蹤,但這會導致代碼很脆弱。 可能在一個JVM上運行但不在另一個JVM上運行的代碼。 或在桌面應用程序中有效但在Applet或Web Start應用程序中無效的代碼。 這不是一個好主意。

最好是重新考慮您的設計。 如果一個類需要知道是誰加載的,則該類正在做太多事情。 將部分因素分解到配置類中(或其他合適的方法),以便任何人加載類都可以提供正確的信息。

這是創建單例的一個可愛技巧:使用單值枚舉。 JVM保證創建的實例不能超過一個。 在枚舉的構造函數(不是靜態初始化程序塊)中,進行所需的任何數據庫或文件系統訪問,如果失敗,則拋出適當的RuntimeException 枚舉將在加載類時實例化,因此您不需要使用靜態init塊。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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