How do static methods actually run in Java? We usually invoke them using the class name, and do not create any object for them, but these are objects which actually can "run", not methods!
For static methods, a special keyword is available and added as part of the byte-code. It is called invokestatic.
From Oracle docs :
invokestatic
Operation
Invoke a class (static) method
Description :
The unsigned indexbyte1 and indexbyte2 are used to construct an index
into the run-time constant pool of the current class (§2.6), where the
value of the index is (indexbyte1 << 8) | indexbyte2. The run-time
constant pool item at that index must be a symbolic reference to a
method (§5.1), which gives the name and descriptor (§4.3.3) of the
method as well as a symbolic reference to the class in which the
method is to be found. The named method is resolved (§5.4.3.3). The
resolved method must not be an instance initialization method (§2.9)
or the class or interface initialization method (§2.9). It must be
static, and therefore cannot be abstract.
On successful resolution of the method, the class that declared the
resolved method is initialized (§5.5) if that class has not already
been initialized.
The operand stack must contain nargs argument values, where the
number, type, and order of the values must be consistent with the
descriptor of the resolved method.
Sample code :
public class Example {
static void myStaticMethod() {
System.out.println("Hello");
}
public static void main(String[] args) {
myStaticMethod();
}
}
Byte code :
public static void main(java.lang.String[]);
descriptor: ([Ljava/lang/String;)V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=0, locals=1, args_size=1
0: invokestatic #31 // Method myStaticMethod:()V
3: return
LineNumberTable:
line 8: 0
line 9: 3
LocalVariableTable:
Start Length Slot Name Signature
0 4 0 args [Ljava/lang/String;
Runtime constant pool of class :
...
#31 = Methodref #1.#32 // Example.myStaticMethod:()V
...
In case of static method Java loads the method when the class is loaded and shares the method with all the objects also and that's why an object is not necessary.
It's not always necessary to have an object in order to run a method.
And no, it's not the object that runs. It's the method that does the operation and objects or classes hold them as a property.
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
I'm little confused about static methods and object creation in java.
As we know we can access static members in static method as here.
public static void main(String[] args){
// only static method from outside ( without any object )
}
But my stupid question is that why java allow this?
`public static void main(String[] args){
Object o = new Object(); // is constructor implicitly static?
// I'm sure no but why java allow this to call here?
}
I know the above statement is similar to declare local variable in static method.
public static void main(String[] args){
int a = 3;
}
But I'm little confused about constructor.
In bytecode, your main() method looks like this (result of the javap -c Main.class command):
public static void main(java.lang.String[]);
Code:
0: new #3 // class java/lang/Object
3: dup
4: invokespecial #8 // Method java/lang/Object."<init>":()V
7: astore_1
8: return
As you can see, at location 0, the new instruction is performed. Then, at location 4, the constructor is invoked on the newly created object.
This is also specified in the Java Virtual Machine Specification:
4.10.2.4. Instance Initialization Methods and Newly Created Objects
Creating a new class instance is a multistep process. The statement:
...
new myClass(i, j, k);
...
can be implemented by the following:
...
new #1 // Allocate uninitialized space for myClass
dup // Duplicate object on the operand stack
iload_1 // Push i
iload_2 // Push j
iload_3 // Push k
invokespecial #5 // Invoke myClass.<init>
...
Constructors are not static. They are called on the instance, you just created. In byte code what happens.
An new object is created, but it is not inilialised.
The constructor is called on that object. In the constructor this is the object being initialised.
In my code, it appears convenient to use varargs when implementing a generic method when the type is an array:
public interface Codec<D,E> {
E encode(D decoded);
D decode(E encoded);
}
public class MyCodec implements Codec<byte[], char[]> {
#Override char[] encode(byte... decoded) {...}
#Override byte[] decode(char... encoded) {...}
}
When I write this, Eclipse shows a warning:
Varargs methods should only override or be overridden by other varargs
methods unlike MyCodec.encode(byte...) and
Codec.encode(byte[])
Should I just ignore the warning, or is this going to cause some unforeseen problems?
This is an Eclipse-specific warning. It has nothing to do with generics specifically and can be reproduced with this example:
class A {
m(int[] ints) { }
}
class B extends A {
#Override
m(int... ints) { }
}
As the other answers point out, varargs are purely a compile-time feature and there's no difference at runtime. I tried searching for the specific reasoning behind the warning but couldn't turn anything up. Likely it's considered bad practice to alternate method overrides between varargs and non-varargs because it's confusing and arbitrary. But this is in general - your use case seems more reasonable as long as callers are always going to be using a statically-typed MyCodec instead of coding to interface with a Codec<byte[], char[]>.
Unfortunately there is no way to suppress this warning - even #SuppressWarnings("all") won't make it yield. Which is unfortunate considering how obscure of a warning it is. Here's an ancient conversation about this same issue: http://echelog.com/logs/browse/eclipse/1196982000 (scroll to 20:45:02) - proving it's bit people long before you. Seems like an Eclipse bug that it can't be suppressed.
I wrote two test files. Here's the first:
public class Test {
public static void main(String... args) {
System.out.println(java.util.Arrays.toString(args));
}
}
And here's the second:
public class Test {
public static void main(String[] args) {
System.out.println(java.util.Arrays.toString(args));
}
}
(The only difference between these two files is the String[] args vs String... args.)
Then, I ran javap -c on each file to see the disassembly. The contents of the main method were identical:
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: invokestatic #3 // Method java/util/Arrays.toString:([Ljava/lang/Object;)Ljava/lang/String;
7: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
10: return
The only difference was the method header, which was simply the method signature of each method:
public static void main(java.lang.String[]);
public static void main(java.lang.String...);
With this in mind, I would say that it's a safe assumption that nothing bad will happen.
According to the bytecode, no problem.
public byte[] encode(char...);
flags: ACC_PUBLIC, ACC_VARARGS
LineNumberTable:
line 4: 0
LocalVariableTable:
Start Length Slot Name Signature
0 2 0 this LMyEncoder;
0 2 1 args [C // char[]
Code:
stack=1, locals=2, args_size=2
0: aconst_null
1: areturn // return null;
LineNumberTable:
line 4: 0
LocalVariableTable:
Start Length Slot Name Signature
0 2 0 this LMyEncoder;
0 2 1 args [C
public java.lang.Object encode(java.lang.Object);
flags: ACC_PUBLIC, ACC_BRIDGE, ACC_VARARGS, ACC_SYNTHETIC
LineNumberTable:
line 1: 0
LocalVariableTable:
Start Length Slot Name Signature
Code:
stack=2, locals=2, args_size=2
0: aload_0
1: aload_1
2: checkcast #27 // class "[C" -> char[]
5: invokevirtual #28 // Method encode:([C)[B -> return byte[]
8: areturn
LineNumberTable:
line 1: 0
LocalVariableTable:
Start Length Slot Name Signature
If you make a call using one reference of the interface, the method with the flag ACC_BRIDGE check (checkcast) if the type of the argument is the same as is defined in the type parameter (java.lang.ClassCastException otherwise, but will never happen if you allways provides the type parameter), then run the method implementation.
In another hand, if you compile this with javac, none warning is show.
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