• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Jeanne Boyarsky
  • Ron McLeod
  • Paul Clapham
  • Liutauras Vilda
Sheriffs:
  • paul wheaton
  • Rob Spoor
  • Devaka Cooray
Saloon Keepers:
  • Stephan van Hulst
  • Tim Holloway
  • Carey Brown
  • Frits Walraven
  • Tim Moores
Bartenders:
  • Mikalai Zaikin

The CASE of the STATIC initializer

 
Ranch Hand
Posts: 63
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I'm working my way through Kathy and Bert's cert book (Java 2 Sun Certified Programmer and Developer by Kathy Sierra and Bert Bates), and read about the switch statement. Since the text stated that the case criteria must be final AND ASSIGNED, I thought, "Hmmm...I wonder what would happen if I made it a constant (ie. public static final), but did not initialize it until the static {} block which is called before the constructor?
I figured since the case statement MUST have a value before being compiled this would mean:
1. if it works, then the switch is actually processed at compile time---otherwise, the public static final var would not have a value and the class wouldn't compile
2. if it doesn't work, then the switch is indeed executed upon creation of the class object (not an instance)---this would indicate it likely occurs at runtime as opposed to at compile time.
The code was this:

As you may or may not have guessed, this FAILS to compile at variable d in the statement "case d:" with an error that the static variable may not be initialized.
This indicates to me that the static {} initializer must run only once the class (not the instance of the class) comes into existence in the JVM (is this assumption correct?) as opposed to at compile time. Otherwise, you would think it would be able to "see" the initialization and pass on the compilation.
Incidentally, I have so far figured out only one purpose for the static initializer...to keep constant values centrally located in one static class, for example. We are actually using it for the serialVer constant so that we can keep the serialVer IDs in one file. However, the only way to get a true new serialVer is to go back into each module using this technique and commenting out (or renaming) the serialVer so that the class can generate a new one. (serialVer is used to trick the class into allowing deserialization when it otherwise wouldn't, provided other certain rules have not been violated....it's VERY cool and I read about it in the Head First Java book by the same authors I mentioned at the top of this message).
Ross
[ July 21, 2003: Message edited by: Ross Goldberg ]
 
Ranch Hand
Posts: 1392
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Let's review the rule for the value in a case label.

the case criteria must be final AND ASSIGNED


switch ( x ) { case expression : statements }
Rule: The expression in the case label must be a constant expression.
Example 1: A literal of primitive type is a constant expression. 0 1 -2 'a' 'B' '\u0065'
Example 2: A simple name that refers to a final variable whose initializer is a constant expression is a constant expression.
"initializer" in the above sentence means a variable initializer. It occurs in the variable declaration. Variable initializers are different from static initializers and instance initializers, which are blocks of code.
// i, j, k, l are compile-time constants
final int i = 1; //1 is a literal
final int j = i; //i is a constant
final int k = j; //j is a constant
final int l = i + j + k; //i + j + k is a constant expressions
//x, y, z are not compile-time constants
final int x; //no initializer
final int y = x; //x is not a constant expression
int z = 1; //not final
Notice I did not declare any of these static. Of course they could be declared static.
[ July 21, 2003: Message edited by: Marlene Miller ]
 
Marlene Miller
Ranch Hand
Posts: 1392
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
This chapter from Bill Venner's Inside the Java Virtual Machine discusses the initialization of static variables.
Chapter 7 The Lifetime of a Type
See also JLS 12.4 Initialization of Classes and Interfaces.
 
Ross Goldberg
Ranch Hand
Posts: 63
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
The puzzling part to me, however, is this:
Example 2: A simple name that refers to a final variable whose initializer is a constant expression is a constant expression.
Since the static method initializes uninitialized static variables BEFORE the class constructor, I would have (falsely) thought that it is interpreted at compile time. That's why I thought it would have worked, whereas it actually failed.
The part that confuses me is that if you do the serialVer method I describe, where it uses the static {} method to set the serialVer from a class containing constants, you'll notice that the class has already been stamped with the constant value (in another class that is assigned through the static {} method) once the class is compiled. This leads me to believe that static {} runs at compile time---or perhaps this is a special case?
Unfortunately, I have my code sample (a set of classes) on my machine at work so I cannot post it here. I will describe it, but if someone needs to see an example, I can post the code.
I know this is straying a bit from the original question, but I consider it to be interrelated (because the static initialization on the public final static (const) is then related to "case" due to the final. Yeah, yeah....odd, strange, but definitely interesting!!
Anyway, what I did was I created a serializable class, say AClass, another serializable class, say BClass, and declared a reference in AClass to BClass as well as a reference in AClass to an array of BClass. I also threw in a few variables including a transient for good measure.
Next, I compiled them and did a serialVer (just like java or javac) at the command line to get the serialVer. I then created a third class (a static one) where I defined constants for each serialVer value. Next, in the original two classes, I defined "public static final int serialVer;" (this is from memory so I apologize if I have the int part wrong...might be a long, but I think it is an int). Finally, in each of the two classes, I created a method as such:

Again, this is a similar example, but is basically what I did.
Next, I added a new instance variable to AClass and deleted an existing instance variable from AClass. Those are within the rules of allowable changes...the result is that when the object is deserialized, the new variable has its default value (depends on the data type) and the deleted one is simply not used.
However, if I hadn't defined serialVer in each serializable class, these changes would have caused AClass' "stamp" to be different (it would have a different serialVer) and a deserialization would throw an Exception. This is much like casting...basically, you're saying "I know what I'm doing." and it takes your word for it.
However, if you run serialVer (at the cmd prompt) on these compiled classes, you'll get whatever value you defined in your constants...that indicates to me that it is evaluated/assigned AT COMPILE TIME...not at RUN TIME. But if that is the case, then the static initialization on that final variable should have worked in the case statement (which brings us back to the initial point). Yet, it doesn't work, as it thinks the final var has not been initialized.
So how is this paradox explained? I realize this will probably not be on the exam in this form, but nevertheless, it certainly aids in deep understanding of several inter-related concepts.
Ross
 
Marlene Miller
Ranch Hand
Posts: 1392
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Method static {}
0 ldc2_w #3 <Long 1000>
3 putstatic #2 <Field long serialVersionId>
6 return
>jdb Test
> stop in Test.<clinit>
> run
Breakpoint hit: "thread=main", Test.<clinit>(), line=5 bci=0
5 serialVersionId = Constants.A;
main[1] print Test.serialVersionId
Test.serialVersionId = 0
main[1] step
Step completed: "thread=main", Test.<clinit>(), line=6 bci=6
6 }
main[1] print Test.serialVersionId
Test.serialVersionId = 1000
 
Marlene Miller
Ranch Hand
Posts: 1392
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Ross, may I suggest that you try to extract the essence of the question from the context. Try to make your question fit on one screen.
I think you will get better answers and more help that way. People like to solve little puzzles.
[ July 22, 2003: Message edited by: Marlene Miller ]
 
Ross Goldberg
Ranch Hand
Posts: 63
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Ok, but some topics simply are large...what follows was my proof-of-concept for the serialVer (my manager wanted proof prior to implementation in the project), so it had to demonstrate several different aspects.
Here is the sample code that compiles, btw. This is still a bit long as I had to test several concepts for the serializable test. Once I compiled it, I generated the serialVer IDs. I then ran the program to serialize my objects. Next, I added and removed some fields (you'll see comments in the code) and then deserialized it. Subsequently, I serialized a new object to show the different effects of loading the originally serialized object and the new serialized object, while proving that the technique did, indeed, work and thus allow me to make field changes while preserving the ability to deserialize data that had been serialized prior to the field changes (note only certain changes are allowed---for example, you can't change a variable's datatype).
 
Ross Goldberg
Ranch Hand
Posts: 63
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
One other aspect I should point out, however, is that even in the example preceding this message, we could have bypassed static {} and simply had the serialVer declaration be initialized to its constant's value...but I wanted to explore the aspects of static w/r to compilation and as you can read from above, I still am not certain exactly where/when the evaluation of the static block occurs. My assumption is that it would occur on class instantiation at run-time, but because of the behavior in the case keyword, it appears to occur at compile-time. This was my original question and the reason I brought serialVer and static {} into this discussion.
Ross
 
Ranch Hand
Posts: 247
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Ross,
Java language specification:


A compile-time constant expression is an expression denoting a value of primitive type or a String that is composed using only the following:
Literals of primitive type and literals of type String
Casts to primitive types and casts to type String
The unary operators +, -, ~, and ! (but not ++ or --)
The multiplicative operators *, /, and %
The additive operators + and -
The shift operators <<, >>, and >>>
The relational operators <, <=, >, and >= (but not instanceof)
The equality operators == and !=
The bitwise and logical operators &, ^, and |
The conditional-and operator && and the conditional-or operator ||
The ternary conditional operator ? :
Simple names that refer to final variables whose initializers are constant expressions
Qualified names of the form TypeName . Identifier that refer to final variables whose initializers are constant expressions


simply means this for your example:

and this:

Simply because in the second case, the initializer via static block is not considered as constant expression
I don't think there's much more to understand here..
I you want some more details, you may go have a glance there:
http://www.experts-exchange.com/Programming/Programming_Languages/Java/Q_20007543.html
especially response from jim cakalic
Hope this helps,
Regards,
Cyril.
 
Ross Goldberg
Ranch Hand
Posts: 63
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Cyril,
I've included the relevant portion from the link you provided:

I agree with your logic---that would mean that it is an uninitialized public static final and thus not a constant.
However, if that is the case, then why does the unique situation of using serialVer result in the value declared in the static initializer being returned after compiling?
In otherwords, if you have

then 1) it would not be initialized if it were not a constant because 2) if it were a constant, it would now have the value 1234567 after COMPILE.
If you do a serialVer <classname> on it, you'll see that you get 1234567...indicating that it has been defined as a constant.
The only other alternative I can think of is that serialVer is executing an instance of the class, which would run static {} before the constructor, and is thus getting the value then, as opposed to compile time. That would explain the behavior as expected and confirm what you stated above...the question is, is that what happens?
It is the paradox that I am questioning...the statement I made at the end of the last question is the only explanation I can find that would rationalize it, but I do not know whether or not that's what actually happens behind-the-scenes. Do you (or does someone else) know? I think it is required to understand where the role of static {} actually comes in. This is all based on the javac compiler.
Ross
 
cyril vidal
Ranch Hand
Posts: 247
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Ross,
You may make a difference between an initialiser considered as a constant (that case requires) and a variable considered as a constant.
Correct me if I'm wrong:
Initialiser as constant:

constant:

Yes, in the above example, serialVer is considered as a constant:as far as I know, such final variables that are not assigned a value at compile time are called blank final variables. And all blank final static variables must be assigned in a static initializer (because there's no constructor for class member and because a final variable must be initialized at last time by the constructor).
So, to resume, we can say that we can't use blank final static variables in case statements, but only static variables with constant initializer.
As far as I know, static variables are initialized during class loading. You don't need to instantiate any object.
If A owns a static member, only A declaration as follows:

should be sufficient for the static initialisation.
 
Ross Goldberg
Ranch Hand
Posts: 63
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Thanks Cyril...that distinction on the BLANK final static explained the logic behind it to me.
Ross
 
cyril vidal
Ranch Hand
Posts: 247
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I'm sorry. I've just read my precedent thread, and finally I think I was wrong by thinking a static blank final variable is a constant.
It's not.
That's why you can't use it in a 'case' statement.
Nevertheless, what I'm sure:
1�) A static blank final variable must be initialized in a static initializer so that compilation runs.
2�) a static blank final, as not considered as a constant, can't be used in a 'case' statement.
3�) Initialisation of static blocks execute at class-load time.
 
cyril vidal
Ranch Hand
Posts: 247
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
A piece of code that may help understand when static initialization is done, that is at class-load time, before main method is executed, and before the constructor is called:

Output:
5
6
7
8
9
main begins
main ends

Cyril.
 
cyril vidal
Ranch Hand
Posts: 247
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
In the case of static blank final variable (that are not assigned at compile time), we may test this through the following code:

Output:
5
main begins
main ends
Hope this helps,
Cyril.
 
If I'd had more time, I would have written a shorter letter. -T.S. Eliot such a short, tiny ad:
a bit of art, as a gift, the permaculture playing cards
https://gardener-gift.com
reply
    Bookmark Topic Watch Topic
  • New Topic