views:

342

answers:

7

I have a Java class that looks like this:

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);
  }
}

Because the static block is run when the class is loaded, how can I detect if it's called from it's own main() or by another class to conditionally set variables?


I understand when some of you said "All sorts of bells go off!" Well, it's because I got a situation: I'm designing a class that needs to load lots of data to the limit of my PC (4G Ram), and I'm running 32-bit version of Java which can only use 1.5G of Ram max; so when I test this class by itself, I need to load as much data as possible to test all possible situations, but when it is called from multiple other classes, it can't do that (would cause out of heap space error), so it should only load min. data needed. And yet since all the data should only be loaded once at start up, it should be in the static block; otherwise I need to implement extra logic to detect if it's being loaded the first time (need to load data), or 2nd, 3rd time (shouldn't load data again and again). And if I implement extra logic to do that and move the data load code out of the static block, it would cause unnecessary complexity because if I move to 64-bit version of java (hopefully soon), that extra complexity would be extra burden (I'll have enough space to load all data even when being called from other classes). So the temp quick fix is to detect it in the static block and handle the difference accordingly, when I have enough space, just comment them out without the need to change programming logic structure.

Thanks for all the answers and advices, I tried the "StackTraceElement" approach, it works great! It solved my problem.

+1  A: 

Using static initializers to load data is dicey at best. Perhaps an alternate approach would be to make this a single-instance class (either through use of the Singleton Pattern or simply by ensuring in your code that it's only instantiated once). Then you could call the constructor or a load method with a flag to indicate how to set the variables.

FWIW, I think that even loading the data using a static method (to which, again, you could pass a flag), while not a great solution, would be preferable to using a static initializer.

MattK
This has prompted me to read some posts on static initializers, but I'm not sure I agree. Would you still consider static initializers dicey that did nothing but create guaranteed-immutable state and did not access outside resources?
Steve B.
@MattK: Why are static initializers so dicey? IMHO, they are very appropriate for some kinds of things that you want initialized at the time a class is loaded. They can be abused, yes, but that doesn't mean they're all bad.
Eddie
Not necessarily. This post refers to "loading data", so I assumed the poster was accessing outside resources. On the other hand, I can't think of very many situations when you would need to do this in a static initializer rather than in the constructor of a Singleton.
MattK
@Eddie: I did say "for loading data." :-)
MattK
Ah, you're referring to filesystem or database access in a static initializer. Yes, dicey. MUCH better would be a Singleton.
Eddie
+1: A Singleton is definitely the way to go here.
R. Bemrose
+2  A: 

Technically your static initializer cannot be called from it's own main method because it will always run before the main method:

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

So you're trying to test the impossible ;-)

Josef
+2  A: 

a VERY DIRTY solution would be throwing and catching an exception in the static initializing block. When catching the exception, make it print the stack trace to a ByteArrayOutputStream, transform the byte array into a String and parse the trace to see if the static initializer was called from the main method you expect.

However, doing this sounds like black magic and must avoided in favor of better designs . . .

Reginaldo
There is no need to THROW an exception. Just instantiate one. But better yet, Java 1.5 has methods you can call to get the current Thread's call stack, without bothering to instantiate or throw an Exception. But yes, a very dirty (and slow) solution.
Eddie
It would be better to use getStackTrace() and examine the StackTraceElements directly. For very small values of "better".
Michael Myers
+1 for incredible dirtiness. I could never have even imagined anything like this.
Joonas Pulakka
Thread.currentThread().getStackTrace() accomplishes this without the throw/catch....but, dang....that's still dirty.
Jared
Depends. If you need to know the caller of a method or a static initializer, say for logging/debugging purposes, then you don't have many alternatives.
Andreas_D
+3  A: 

Just look at the actual Stack. Test with the following implementation of your static block. The printout will be different whether you 'executed' the My_ABC class or the class has been loaded later:

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
}
Andreas_D
A: 

I don't know if it's possible to absolutely know whether a class is being loaded because its own main() is being invoked, or because another class made a reference to it -- or just decided to load it. You can investigate the stack trace, but this would result in very brittle code. Code that might work on one JVM but not another. Or code that works in a desktop application but not an Applet or Web Start application. Not a good idea.

Better would be to rethink your design. If a class needs to know who loaded it, the class is doing too many things. Factor that part out into a configuration class (or whatever else is appropriate) so whoever is loading the class can provide the correct information.

Here's a cute trick to create a singleton: Use a single-valued enum. The JVM guarantees that there cannot be more than one instance created. In the enum's constructor (not static initializer block), do whatever database or file system access you need, throwing appropriate RuntimeExceptions as appropriate if things fail. The enum will be instantiated when the class is loaded, so you don't need to use a static init block.

Eddie
+3  A: 

I think you should definitely change your approach.

But since you asked something concrete here it is ( summarizing from others ) .

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");
    }
}

Use this to test it:

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

p.s. I don't know why Josef delete his answer it was in the right track.

OscarRyz
I don't believe this would work in a Web Start application. Maybe it would, but I don't think so.
Eddie
Actually, the initializer in X will be invoked *before* X.main(). That's another puzzling thing about this question.
Michael Myers
Not puzzling at all. The class has to be loaded before main can be called.
Eddie
@Eddie: I mean the question is puzzling, not the behavior. ;)
Michael Myers
@mmeyers: Ah, sorry about that! :)
Eddie
My guess it that Frank comes from another programming language background where this could "potentially" be correct ( I don't know if such idiom is correct somewhere else ) but the coding style shown, it is definitely not java.
OscarRyz
@Eddie: No problem--but you're now the 12th (at least) person on SO to misspell my name while looking right at it. :)
Michael Myers
@mmyers: D'oh! Sorry about that. I'll blame SO by making names a link so I can't double click and cut-and-paste. Actually, it's that I know folks named "Meyers" so my fingers type that spelling automatically.
Eddie
A: 

Maybe I'm confused, but this seems like a much simpler issue than people are making it out to be...

Create a private static boolean which defaults to false, and is set true in your class's main method. Create an accessor, and you're all set.

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

  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) 
  {
    launched = true;
    My_ABC my_abc = new My_ABC(3,true);
  }

  public static boolean launchedLocally()
  {
    return launched;
  }
}
dimo414
The problem with this is that the static block is executed before main() is, so `launched` will be false either way.
Michael Myers
mmyers is right, I tried it, didn't work.
Frank
Ah, now I get it. This is far more unpleasant a problem than I first realized.
dimo414