In java, say I have the following
==fileA.java==
class A
{
public static final int SIZE = 100;
}
Then in another file I use this value
==fileB.java==
import A;
class b
{
Object[] temp = new Object[A.SIZE];
}
When this gets compiled does SIZE get replaced with the value 100, so that if I were to replace the FileA.jar but not FileB.jar, would the object array get the new value or would it have been hardcoded to 100 because that's the value when it was originally built?
Yes, the Java compiler does replace static constant values like SIZE in your example with their literal values.
So, if you would later change SIZE in class A but you don't recompile class b, you will still see the old value in class b. You can easily test this out:
file A.java
public class A {
public static final int VALUE = 200;
}
file B.java
public class B {
public static void main(String[] args) {
System.out.println(A.VALUE);
}
}
Compile A.java and B.java. Now run: java B
Change the value in A.java. Recompile A.java, but not B.java. Run again, and you'll see the old value being printed.
You can keep the constant from being compiled into B, by doing
class A
{
public static final int SIZE;
static
{
SIZE = 100;
}
}
Another route to proving that the behavior is to looking at the generated bytecode. When the constant is "small" (presumably < 128):
public B();
Code:
0: aload_0
1: invokespecial #10; //Method java/lang/Object."<init>":()V
4: aload_0
5: bipush 42
7: anewarray #3; //class java/lang/Object
10: putfield #12; //Field temp:[Ljava/lang/Object;
13: return
}
(I used 42 instead of 100 so it stands out more). In this case, it is clearly substituted in the byte code. But, say the constant is "bigger." Then you get byte code that looks like this:
public B();
Code:
0: aload_0
1: invokespecial #10; //Method java/lang/Object."<init>":()V
4: aload_0
5: ldc #12; //int 86753098
7: anewarray #3; //class java/lang/Object
10: putfield #13; //Field temp:[Ljava/lang/Object;
13: return
When it is bigger, the opcode "ldc" is used, which according to the JVM documentation "an unsigned byte that must be a valid index into the runtime constant pool of the current class".
In either case, the constant is embedded into B. I imagine, since that in opcodes you can only access the current classes runtime constant pool, that this the decision to write the constant into the class file is independent of implementation (but I don't know that for a fact).
Woo - you learn something new everyday!
Taken from the Java spec...
Note: If a primitive type or a string
is defined as a constant and the value
is known at compile time, the compiler
replaces the constant name everywhere
in the code with its value. This is
called a compile-time constant. If the
value of the constant in the outside
world changes (for example, if it is
legislated that pi actually should be
3.975), you will need to recompile any classes that use this constant to get
the current value.
The important concept here is that the static final field is initialised with a compile-time constant, as defined in the JLS. Use a non-constant initialiser (or non-static or non-final) and it wont be copied:
public static final int SIZE = null!=null?0: 100;
(null is not a *compile-time constant`.)
Actually I ran into this bizarreness a while ago.
This will compile "100" into class b directly. If you just recompile class A, this will not update the value in class B.
On top of that, the compiler may not notice to recompile class b (at the time I was compiling single directories and class B was in a separate directory and compiling a's directory did not trigger a compile of B)
As an optimization the compiler will inline that final variable.
So at compile time it will look like.
class b
{
Object[] temp = new Object[100];
}
One thing should note is: static final value is known at compile time
if the value is not known at compile time, compiler won't replaces the constant name everywhere in the code with its value.
public class TestA {
// public static final int value = 200;
public static final int value = getValue();
public static int getValue() {
return 100;
}
}
public class TestB {
public static void main(String[] args) {
System.out.println(TestA.value);
}
}
first compile TestA and TestB, run TestB
then change TestA.getValue() to return 200, compile TestA, run TestB, TestB will get the new value
enter image description here
There is an exception to this:-
If static final field is null at the time of compiling then it doesn't get replaced with null (which is actually its value)
A.java
class A{
public static final String constantString = null;
}
B.java
class B{
public static void main(String... aa){
System.out.println(A.constantString);
}
}
Compile both A.java and B.java and run java B
Output will be null
Now Update A.java with following code and compile only this class.
class A{
public static final String constantString = "Omg! picking updated value without re-compilation";
}
Now run java B
Output will be Omg! picking updated value without re-compilation
Java does optimise these sorts of values but only if they are in the same class. In this case the JVM looks in A.SIZE rather than optimizing it because of the usage case you are considering.
Related
I am trying to check for default value of instance variables (i.e. 0 here) in the generated bytecode.
I can see <init>() getting called and if I print myvar instance variable inside constructor then I see getfield called for myvar but then where was this default set first?
Please answer on following:
When is default value being set in myvar? (after compilation or object creation time)
Who(compiler or jvm) is initializing(or let's say setting default value) in the instance variable?
public class FieldInit {
int myvar;
public static void main(String[] args) {
new FieldInit(); // and what would happen if I comment out this
}
}
I am trying to deassemble bytecode using javap but not able to see the <clinit>() method, I guess here this may be happening. Please let me know if it is possible to see <clinit>() method and if so, how?
In JVM, an object instantiation is split into two bytecode instructions:
new allocates a new uninitialized object;
invokespecial calls a constructor that initializes the object.
The JVM Specification for the new bytecode says:
Memory for a new instance of that class is allocated from the
garbage-collected heap, and the instance variables of the new object
are initialized to their default initial values
The JVM sets all instance fields to zeroes when executing new instruction. So, by the time a constructor is invoked, all fields are already set to their default values. You will not find this "zeroing" in the bytecode - it's done implicitly by the JVM during object allocation.
Your question is not answerable because it's more complicated than you think it is.
fields in java are encoded in a field datastructure and this datastructure includes room for the initial value. However, the only possible initial values in this data structure are numbers and strings.
Let's see it in action! (and note that L is just a java syntax thing to tell java: This number is a long, not an int. 5 is a constant, so is 5L).
class Test {
public static final long mark = 5L;
}
javac Test.java
javap -v c Test
.....
public static final long mark;
descriptor: J
flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
ConstantValue: long 5l
Hey look there it is, constant value 5L.
But what if it isn't constant?
Ah, that's a problem. You can't encode that here.
So, instead, it's syntax sugar time!
You can write a special method in any class that is run during a class's static initialization. You can also write a special method that is run as a new instance is made. That one is almost entirely the same as a constructor, with only exotic differences. It looks like this:
public class Test {
static {
System.out.println("What voodoo magic is this?");
}
public static void main(String[] args) {
System.out.println("In main");
}
}
Let's see it in action!
javac Test.java
java Test
What voodoo magic is this?
In main
javap -c -v Test
...
static {};
descriptor: ()V
flags: (0x0008) ACC_STATIC
Code:
stack=2, locals=0, args_size=0
0: getstatic #7 // Field >java/lang/System.out:Ljava/io/PrintStream;
3: ldc #21 // String Voodoo...
5: invokevirtual #15 // Method >java/io/PrintStream.println:(Ljava/lang/String;)V
8: return
LineNumberTable:
line 3: 0
line 4: 8
}
As you can see, that weird static{} thing was compiled to something that looks exactly like a method, bytecode and all, but the name of the method is bizarre, it's just static{}.
Here comes the clue
But what happens if we make that a little more complicated. Let's initialize this field to the current time, instead!
class Test {
public static final long mark = System.currentTimeMillis();
}
This is just syntax sugar. That explains how this works, because as I told you, at the class file level, you cannot initialize fields with non-constants. Thus, that compiles to the same thing as:
class Test {
public static final long mark;
static {
mark = System.currentTimeMillis();
}
}
and you can javap to confirm this.
One is called a 'compile time constant'. The other isn't. This shows up in various ways. For example, you can pass a CTC as annotation parameter. Try that: try using static final long MARK = 5L; (you will be able to), then static final long MARK = System.currentTimeMillis(); - that won't be allowed.
So, where is that initial value? If it's a constant value, javap -c -v will show it. If it isn't, it's stuck in the static block.
The following simple code snippet is working fine and is accessing a static field with a null object.
final class TestNull
{
public static int field=100;
public TestNull temp()
{
return(null);
}
}
public class Main
{
public static void main(String[] args)
{
System.out.println(new TestNull().temp().field);
}
}
In the above code, the statement System.out.println(new TestNull().temp().field); in which the static field field is being associated with a NULL objectnew TestNull().temp(), still it is returning a correct value of it which is 100 instead of throwing a null pointer exception in Java! Why?
As opposed to regular member variables, static variables belong to the class and not to the instances of the class. The reason it works is thus simply because you don't need an instance in order to access a static field.
In fact I'd say it would me more surprising if accessing a static field could ever throw a NullPointerException.
If you're curious, here's the bytecode looks for your program:
// Create TestNull object
3: new #3; //class TestNull
6: dup
7: invokespecial #4; //Method TestNull."<init>":()V
// Invoke the temp method
10: invokevirtual #5; //Method TestNull.temp:()LTestNull;
// Discard the result of the call to temp.
13: pop
// Load the content of the static field.
14: getstatic #6; //Field TestNull.field:I
This is described in the Java Language Specification, Section 15.11.1: Field Access Using a Primary. They even provide an example:
The following example demonstrates that a null reference may be used to access a class (static) variable without causing an exception:
class Test {
static String mountain = "Chocorua";
static Test favorite(){
System.out.print("Mount ");
return null;
}
public static void main(String[] args) {
System.out.println(favorite().mountain);
}
}
It compiles, executes, and prints:
Mount Chocorua
Static variables are shared among every object of a class. So while the actual object reference you are returning is null (In C++: TestNull* temp = null;), you have an object of type TestNull which Java can use to find static values of that class.
Remember in Java objects are really pointers. Pointers have a type. From that type Java can discern certain information, even if its pointing to null.
Static fields are associated with the class not an instance of that class. Therefore you don't need an instance of an object to access the static field.
You could also access the field by calling TestNull.field
Given the following enum:
enum Repeat {
Daily,
Weekly,
Yearly
}
I realize we are able to write it this way:
Repeat repeat = Repeat.Daily.Weekly.Yearly.Weekly;
which is equivalent to:
Repeat repeat = Repeat.Weekly;
May I know why such syntax is allowed? Is there a way to let the compiler warn us against this?
This is allowed as Daily, Weekly, Yearly are the static field by default inside the enum and holds the object of Repeat. Also, you will get a warning from the compiler "The static field Repeat.Weekly should be accessed in a static way". It is similar to below lines of code.
class Foo{
public static Foo obj1 = new Foo();
public static Foo obj2 = new Foo();
public static Foo obj3 = new Foo();
}
Foo f = Foo.obj1.obj2.obj3; // will work fine but you will get a warning from the compiler.
Here is some part of bytecode inspection of Repeat enum and from this, it is clear that Enum variable is static and holds the Object of Enum itself.
0: new #1 // class com/java8/demo/Repeat
3: dup
4: ldc #14 // String Daily
6: iconst_0
7: invokespecial #15 // Method "<init>":(Ljava/lang/String;I)V
10: putstatic #19 // Field Daily:Lcom/java8/demo/Repeat;
13: new #1 // class com/java8/demo/Repeat
Enum instance are just static instance of the enum class.
We have two way to access static field of a class:
Via class itselft: Repeat.Daily
Via instance of class: Repeat.Daily.Daily
When you chain your enum:
Repeat repeat = Repeat.Daily.Weekly.Yearly.Weekly;
It's just like get a static field from an instance of a class.
Is there a way to let compiler warn us against this?
Yes, use a good IDE and turn on warning. That way, you'll be notified as soon as your write the code, before you even compile it.
E.g. in Eclipse, it is called "Non-static access to static member":
Enum literals are static members, and with each static member, one can access them either using the class reference:
TypeName.staticMember
TypeName.staticMethod()
Or on an instance:
new TypeName().staticMember
new TypeName().staticMethod()
The second approach is discouraged (and the compiler will issue a warning)
As enum literals are just static members, Repeat.Daily.Weekly.Yearly.Weekly is like the second code snippet above, accessing static members on instance references.
With a class, that would be:
class Type {
static Type INSTANCE1, INSTANCE2, INSTANCE3;
}
And one can get a reference to INSTANCE3 using Type.INSTANCE1.INSTANCE2.INSTANCE3. It's valid, but it's bad practice.
This question already has answers here:
Use of uninitialized final field - with/without 'this.' qualifier
(4 answers)
Closed 8 years ago.
I wrote this piece of code and it seems compiler allows accessing uninitialized blank final field when accessed using 'this' keyword:
public class TestClass
{
public final int value1;
public int value2;
TestClass(int value) {
value2 = 2 + this.value1; // access final field using 'this' before initialization gives no compiler error
//value2 = 2 + value1; // uncomment it gives compile time error - variable value1 might not have been initialized
value1 = value;
}
public static void main(String args[]) {
TestClass tc = new TestClass(10);
System.out.println("\nTestClass Values : value1 = " + tc.value1 + " , value2 = " + tc.value2);
}
}
I tried compiling it on 1.5, 1.6, & 1.7 and got same result in all three of them.
To me it looks like compiler bug because compiler must throw error in this case but with 'this' keyword it doesn't and thus creates scope of coding error as it will go unnoticed by the programmer since no compile-time or run-time error will be thrown.
FEW POINTS WHY IT IS NOT A DUPLICATE
- all answers are explaining how it works and what JLS says, fine, but my real intent here is should that be allowed at the first place?
- my question here is more from programmer's point of view and not language semantics
This is not a bug. This a feature for Java Specification 1.6 and lower.
The final field can be accessed at any part of the code. There is no restriction about it.
The only restriction in case of final is that it has to be initialized before class instance is created.
When you use this, you express that it is element of the object that is being constructed.
Since the specification 1.7 because of change we may say that this is bug in some implementation of compilers.
But since 1.8 the code will produce same error for this.value1 or value1.
In Java ints have a default value of 0, so even if you did not initialize it, the compiler has still assigned 0 as value.
Fields that are declared but not initialized will be set to a reasonable default by the compiler. Generally speaking, this default will be zero or null, depending on the data type.
Source: http://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html
Note that this does not apply to local variables, only field variables have a default value.
I used the following code
public class HelloWorld {
public final int value1;
public int value2;
public HelloWorld(int value){
System.out.println(this.value1);
System.out.println(this.value2);
value1 = value;
}
public static void main(String args[]) {
}
}
and the bytecode it generates is
Compiled from "HelloWorld.java"
public class test.HelloWorld extends java.lang.Object{
public final int value1;
public int value2;
public test.HelloWorld(int);
Code:
0: aload_0
1: invokespecial #11; //Method java/lang/Object."<init>":()V
4: getstatic #14; //Field java/lang/System.out:Ljava/io/PrintStream;
7: aload_0
8: getfield #20; //Field value1:I
11: invokevirtual #22; //Method java/io/PrintStream.println:(I)V
14: aload_0
15: iload_1
16: putfield #20; //Field value1:I
19: return
public static void main(java.lang.String[]);
Code:
0: return
}
If you notice JVM does a getstatic call in case of final and getfield in case of normal instance variable. Also now if you look at the specs for getstatic it says
On successful resolution of the field, the class or interface that declared the resolved field is initialized (§5.5) if that class or interface has not already been initialized.
So if you use this with a final variable then it is initialize it. Not it will initialize this.value1 and not value1 which is final. You still have to initialize it before using.
The following code fails with a NullPointerException in main (map==null).
The issue occurs only if I define 2001 or more Enum constants, 2000 work fine.
Why isn't the static code block not executed?
Do we hit any silent limit of the compiler (no warnings, no errors) or JVM?
The compiled class file exceeds 172KB,
import java.util.HashMap;
public enum EnumTest {
E(1),E(2),...,E(2001);
private static HashMap<Integer, EnumTest> map = new HashMap<Integer, EnumTest>();
static {
for ( EnumTest f : EnumTest.values() ) {
map.put( (int) f.id, f );
}
}
short id;
private EnumTest(int id) {
this.id = (short) id;
};
public short getId() {
return id;
}
public static final EnumTest fromInt(int id) {
EnumTest e = map.get( id );
if ( e != null ) {
return e;
}
throw new IllegalArgumentException( "" + id );
}
public static void main(String[] args) {
System.out.println( "size:" + map.size() );
}
}
Runtime Environment:
java version "1.7.0_01"
Java(TM) SE Runtime Environment (build 1.7.0_01-b08)
Java HotSpot(TM) 64-Bit Server VM (build 21.1-b02, mixed mode)
also happens with:
java version "1.6.0_32" Java(TM) SE Runtime Environment (build
1.6.0_32-b05) Java HotSpot(TM) Client VM (build 20.7-b02, mixed mode, sharing)
These kinds of problems come from the fact that some (often compiler-generated) initializer code exceeds 65536 bytes of byte code. A single method can not contain more than that many bytes of bytecode to be executed (due to restrictions in the class file format).
A common source of problems like this are large arrays like this:
byte someBytes = { 1, 2, 3, ..., someBigValue };
The problem here is that such fields are actually initialized with someBigValue assignment statements in a generated initializer (constructor or static initializer).
Enum values are actually initialized in a similar fashion.
Given the following enum class:
public enum Foo {
CONSTANT(1);
private Foo(int i) {
}
}
We look at the output of javap -v and see the following code block:
static {};
flags: ACC_STATIC
Code:
stack=5, locals=0, args_size=0
0: new #4 // class Foo
3: dup
4: ldc #7 // String CONSTANT
6: iconst_0
7: iconst_1
8: invokespecial #8 // Method "<init>":(Ljava/lang/String;II)V
11: putstatic #9 // Field CONSTANT:LFoo;
14: iconst_1
15: anewarray #4 // class Foo
18: dup
19: iconst_0
20: getstatic #9 // Field CONSTANT:LFoo;
23: aastore
24: putstatic #1 // Field $VALUES:[LFoo;
27: return
As you can see there are quite a lot of bytecode operations that handle instantiating CONSTANT with the correct values. If you have many such enum values, then the size of that static initializer block can easily exceed 64k bytes of code and thus make the class uncompilable.
A possible workaround is to reduce the size of the initializing code by reducing the number of arguments (for example by calculating the number passed in based on the index of the enum value instead of using an argument). That might just give you enough wiggle room to extend this a bit further.
Alternatively you could try splitting your enum into several enums connected by implementing a common interface. The enums could be grouped by area/intention/category/...:
public interface MessageType {
int getId();
}
public enum ConnectionMessage implements MessageType {
INIT_CONNECTION(1),
LOGIN(2),
LOGOUT(3),
CLOSE_CONNECTION(4);
// getId code, constructor, ...
}
public enum FrobnicationMessage implements MessageType {
FROBNICATE_FOO(5),
FROBNICATE_BAR(6),
DEFROB_FOO(7),
DEFROB_BAR(8),
...
// getId code, constructor, ...
}
I'm assuming that the enum values are actually referenced somewhere in your code and not just pure value holders, if they only hold values and individual values are not treated differently in your code, then replacing them with a single class instantiated once per data item stored in a central resource is probably the best approach.
I suspect what you should be seeing is an error when you compile
error: code too large
Perhaps your version of the compiler has a bug and doesn't show this.
When I create 2500 enums values it fails with this error but with 2400 enum values it runs correctly.
There is a limit of 64 KB for the byte code of any method and the enums are initialised in the one method for the static initializer block.
The problem is that many byte code instructions use the byte offset as a 16-bit value which places this limitation on the whole method (even if there is no such instruction at the end of a method)
The javac is not smart enough to break up the static initialiser block into multiple sub-methods, but then again having thousands of enums suggest you could do what is required another way.