I'm trying to understand exactly how lambdas and higher order functions work at the JVM level in modern Java. I wrote this simple test class:
public final class Main {
public static void main(String[] args) {
var s = new Object[] { 1.0, 2.0, 3.0 };
System.out.println(sum(s, x -> 1000000.0));
}
public static double sum(Object[] s, Function<Object, Double> f) {
var r = 0.0;
for (var a : s) {
r += f.apply(a);
}
return r;
}
}
that compiles to this:
Compiled from "Main.java"
public final class prover.Main {
public prover.Main();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static double sum(java.lang.Object[], java.util.function.Function<java.lang.Object, java.lang.Double>);
Code:
0: dconst_0
1: dstore_2
2: aload_0
3: astore 4
5: aload 4
7: arraylength
8: istore 5
10: iconst_0
11: istore 6
13: iload 6
15: iload 5
17: if_icmpge 50
20: aload 4
22: iload 6
24: aaload
25: astore 7
27: dload_2
28: aload_1
29: aload 7
31: invokeinterface #7, 2 // InterfaceMethod java/util/function/Function.apply:(Ljava/lang/Object;)Ljava/lang/Object;
36: checkcast #13 // class java/lang/Double
39: invokevirtual #15 // Method java/lang/Double.doubleValue:()D
42: dadd
43: dstore_2
44: iinc 6, 1
47: goto 13
50: dload_2
51: dreturn
public static void main(java.lang.String[]);
Code:
0: iconst_3
1: anewarray #2 // class java/lang/Object
4: dup
5: iconst_0
6: dconst_1
7: invokestatic #19 // Method java/lang/Double.valueOf:(D)Ljava/lang/Double;
10: aastore
11: dup
12: iconst_1
13: ldc2_w #23 // double 2.0d
16: invokestatic #19 // Method java/lang/Double.valueOf:(D)Ljava/lang/Double;
19: aastore
20: dup
21: iconst_2
22: ldc2_w #25 // double 3.0d
25: invokestatic #19 // Method java/lang/Double.valueOf:(D)Ljava/lang/Double;
28: aastore
29: astore_1
30: getstatic #27 // Field java/lang/System.out:Ljava/io/PrintStream;
33: aload_1
34: invokedynamic #33, 0 // InvokeDynamic #0:apply:()Ljava/util/function/Function;
39: invokestatic #36 // Method sum:([Ljava/lang/Object;Ljava/util/function/Function;)D
42: invokevirtual #42 // Method java/io/PrintStream.println:(D)V
45: return
private static java.lang.Double lambda$main$0(java.lang.Object);
Code:
0: ldc2_w #48 // double 1000000.0d
3: invokestatic #19 // Method java/lang/Double.valueOf:(D)Ljava/lang/Double;
6: areturn
}
Now, the lambda function itself gets compiled to the private static method at the end, that much is clear enough. But where is it referred to? The code to call sum seems to be:
33: aload_1
34: invokedynamic #33, 0 // InvokeDynamic #0:apply:()Ljava/util/function/Function;
39: invokestatic #36 // Method sum:([Ljava/lang/Object;Ljava/util/function/Function;)D
Is that somehow referring to the lambda function? If so, how? What's the reference?
Using javap -p -v will produce a section labeled BootstrapMethods that lists all bootstrap methods used to initialize lambdas:
BootstrapMethods:
0: #41 REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:(Ljava/lang/invoke/MethodHandles$Lookup;Ljava/lang/String;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodType;Ljava/lang/invoke/MethodHandle;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/CallSite;
Method arguments:
#42 (Ljava/lang/Object;)Ljava/lang/Object;
#43 REF_invokeStatic Scratch.lambda$main$0:(Ljava/lang/Object;)Ljava/lang/Double;
#44 (Ljava/lang/Object;)Ljava/lang/Double;
This one contains references to the methods implementing the actual code (Scratch.lambda$main$0 in my case, the exact name will vary depending on compiler-vendor/-version/-flags).
Note that the representation in the Class files has intentionally been kept at a fairly high level (there are bootstrap methods that return the actual code to be executed at run time). This means that the JVM doesn't have a lot of restrictions as to how to implement and optimize this. That also means that studying the bytecode will only ever tell you so much, because the JVM can pretty freely interpret what it sees there.
Related
In Java, if I'm performing multiple methods on an object, I can chain them, or I can make a temporary variable, like so
Chaining
System.out.println( str.substring(0,4).substring(0,2));
Temp variable
String tmp = str.substring(0,4);
tmp = tmp.substring(0,2);
System.out.println(tmp);
Obviously, the difference is negligible in this example, but could make an impact when you're doing this over thousands of strings/some other object.
My question is, is one of these more "efficient" in terms of not making extra object allocations or filling the heap (and thus making GC get called sooner)?
I tried to compare the bytecodes of the two in a loop over a couple strings, but it looks similar, sans for the last few lines. I don't understand all the bytecode calls, so I'm not sure if any of these have to do with allocating new objects.
Compiled from "TestNoTmp.java"
public class TestNoTmp {
public TestNoTmp();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_4
1: anewarray #2 // class java/lang/String
4: dup
5: iconst_0
6: ldc #3 // String These
8: aastore
9: dup
10: iconst_1
11: ldc #4 // String Are__
13: aastore
14: dup
15: iconst_2
16: ldc #5 // String Some_
18: aastore
19: dup
20: iconst_3
21: ldc #6 // String Strings
23: aastore
24: astore_1
25: aload_1
26: astore_2
27: aload_2
28: arraylength
29: istore_3
30: iconst_0
31: istore 4
33: iload 4
35: iload_3
36: if_icmpge 69
39: aload_2
40: iload 4
42: aaload
43: astore 5
45: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
48: aload 5
50: iconst_0
51: iconst_4
52: invokevirtual #8 // Method java/lang/String.substring:(II)Ljava/lang/String;
55: iconst_0
56: iconst_2
57: invokevirtual #8 // Method java/lang/String.substring:(II)Ljava/lang/String;
60: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
63: iinc 4, 1
66: goto 33
69: return
}
public class TestTmp {
public TestTmp();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_4
1: anewarray #2 // class java/lang/String
4: dup
5: iconst_0
6: ldc #3 // String These
8: aastore
9: dup
10: iconst_1
11: ldc #4 // String Are__
13: aastore
14: dup
15: iconst_2
16: ldc #5 // String Some_
18: aastore
19: dup
20: iconst_3
21: ldc #6 // String Strings
23: aastore
24: astore_1
25: aload_1
26: astore_2
27: aload_2
28: arraylength
29: istore_3
30: iconst_0
31: istore 4
33: iload 4
35: iload_3
36: if_icmpge 77
39: aload_2
40: iload 4
42: aaload
43: astore 5
45: aload 5
47: iconst_0
48: iconst_4
49: invokevirtual #7 // Method java/lang/String.substring:(II)Ljava/lang/String;
52: astore 6
54: aload 6
56: iconst_0
57: iconst_2
58: invokevirtual #7 // Method java/lang/String.substring:(II)Ljava/lang/String;
61: astore 6
63: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream;
66: aload 6
68: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
71: iinc 4, 1
74: goto 33
77: return
}
In your example, you're working with Strings, which are immutable. In your code:
str.substring(0,4).substring(0,2)
the first call to substring must generate a new String object because str cannot be modified. Similarly, the second call to substring on that new String object will create another new String object.
The difference in bytecodes is simply a result of the order in which the compiler calls methods. In the TestTmp case, all the string manipulation occurs before the call to PrintStream. For TestNoTmp, the String calls happen within the PrintStream call, which is very logical when you look at the code.
To answer your question, this will make no difference in terms of object allocation and therefore GC impact.
I have the following code:
StringBuilder str = new StringBuilder("foo");
for(Field f : fields){
str.append("|" + f);
}
str.append("|" + bar);
String result = str.toString();
I know compiler will optimize string concatenation "|" + f and replace it with StringBuilder. However will a new StringBuilder be created or the existing str will be used in Java 8? How about Java 9?
By default in java-9 there will be no StringBuilder for string concatenation; it is a runtime decision how it's made via the invokedynamic. And the default policy is not a StringBuilder::append one.
You can also read more here.
Under java-8 a new one will be created (really easy to spot two occurrences of invokespecial // Method java/lang/StringBuilder."<init>":()V in the de-compiled bytecode.
Also, you have a suggestion about append.append...; just notice that this is much better than sb.append ... sb.append, and here is why.
As String concatenation optimization is performed by the Java compiler, you can see what it does by decompiling the byte code:
$ cat Test.java
interface Field {}
public class Test {
static String toString(Field[] fields, Object bar) {
StringBuilder str = new StringBuilder("foo");
for(Field f : fields){
str.append("|" + f);
}
str.append("|" + bar);
return str.toString();
}
}
$ javac Test.java
$ javap -c Test.class
Compiled from "Test.java"
public class stackoverflow.Test {
public stackoverflow.Test();
Code:
0: aload_0
1: invokespecial #8 // Method java/lang/Object."<init>":()V
4: return
static java.lang.String toString(stackoverflow.Field[], java.lang.Object);
Code:
0: new #16 // class java/lang/StringBuilder
3: dup
4: ldc #18 // String foo
6: invokespecial #20 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
9: astore_2
10: aload_0
11: dup
12: astore 6
14: arraylength
15: istore 5
17: iconst_0
18: istore 4
20: goto 53
23: aload 6
25: iload 4
27: aaload
28: astore_3
29: aload_2
30: new #16 // class java/lang/StringBuilder
33: dup
34: ldc #23 // String |
36: invokespecial #20 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
39: aload_3
40: invokevirtual #25 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
43: invokevirtual #29 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
46: invokevirtual #32 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
49: pop
50: iinc 4, 1
53: iload 4
55: iload 5
57: if_icmplt 23
60: aload_2
61: new #16 // class java/lang/StringBuilder
64: dup
65: ldc #23 // String |
67: invokespecial #20 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
70: aload_1
71: invokevirtual #25 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
74: invokevirtual #29 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
77: invokevirtual #32 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
80: pop
81: aload_2
82: invokevirtual #29 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
85: areturn
}
As you can see, the code invokes StringBuilder constructors (Method java/lang/StringBuilder."<init>":) in 3 places, so new StringBuilders would be created in each iteration (unless the just-in-time compiler performs fancy optimization).
This is very unlikely to be a significant performance problem, but in the unlikely case it is you can easily fix this by rewriting to
str.append("|").append(f);
As per the Java 9 API documentation
Implementation Note:
The implementation of the string concatenation operator is left to the discretion of a Java compiler, as long as the compiler ultimately conforms to The Java™ Language Specification. For example, the javac compiler may implement the operator with StringBuffer, StringBuilder, or java.lang.invoke.StringConcatFactory depending on the JDK version. The implementation of string conversion is typically through the method toString, defined by Object and inherited by all classes in Java.
According to this it will create a new String builder each iteration in your case. So, as mentioned by several people here, using the below code is more optimized
append("|").append(f)
You can find API Documentation here
I read Jon Skeet's answer about concatenating strings with +. I wonder whether the compiler also recognizes appending constand strings with a StringBuffer/StringBuilder.
This code for constructing a URL has a good intention:
StringBuffer sb = new StringBuffer(constant1);
sb.append(nonconstant);
sb.append("?");
sb.append(constant2);
sb.append("=");
sb.append(constant3);
sb.append("&");
sb.append(constant4);
sb.append("=");
sb.append(constant5);
However, if Stringbuffer.append() is not optimized by the compiler for constants, I'd say the following code would be more efficient:
StringBuffer sb = new StringBuffer(constant1);
sb.append(non-constant);
sb.append("?" + constant2 + "=" + constant3 + "&" + constant4 + "=" + constant5);
because the compiler would optimize the + string concatenation at compile time.
Why not try it out? In java 1.7, the main method of the following class:
public class Concat1
{
private static final String constant2 = "c2";
private static final String constant3 = "c3";
public void main(String[] args)
{
StringBuilder sb = new StringBuilder();
sb.append(args[0]);
sb.append("?");
sb.append(constant2);
sb.append("=");
sb.append(constant3);
System.out.println(sb.toString());
}
}
(I changed the number of constant for clarity) yields the following byte code:
public class Concat1 {
public Concat1();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."":()V
4: return
public void main(java.lang.String[]);
Code:
0: new #2 // class java/lang/StringBuilder
3: dup
4: invokespecial #3 // Method java/lang/StringBuilder."":()V
7: astore_2
8: aload_2
9: aload_1
10: iconst_0
11: aaload
12: invokevirtual #4 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
15: pop
16: aload_2
17: ldc #5 // String ?
19: invokevirtual #4 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
22: pop
23: aload_2
24: ldc #6 // String c2
26: invokevirtual #4 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
29: pop
30: aload_2
31: ldc #7 // String =
33: invokevirtual #4 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
36: pop
37: aload_2
38: ldc #8 // String c3
40: invokevirtual #4 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
43: pop
44: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream;
47: aload_2
48: invokevirtual #10 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
51: invokevirtual #11 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
54: return
}
while the following class:
public class Concat2
{
private static final String constant2 = "c2";
private static final String constant3 = "c3";
public void main(String[] args)
{
StringBuilder sb = new StringBuilder();
sb.append(args[0]);
sb.append("?" + constant2 + "=" + constant3);
System.out.println(sb.toString());
}
}
is compiled to:
public class Concat2 {
public Concat2();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."":()V
4: return
public void main(java.lang.String[]);
Code:
0: new #2 // class java/lang/StringBuilder
3: dup
4: invokespecial #3 // Method java/lang/StringBuilder."":()V
7: astore_2
8: aload_2
9: aload_1
10: iconst_0
11: aaload
12: invokevirtual #4 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
15: pop
16: aload_2
17: ldc #5 // String ?c2=c3
19: invokevirtual #4 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
22: pop
23: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
26: aload_2
27: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
30: invokevirtual #8 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
33: return
}
So apparently, you are right. In the second class the append method of the StringBuilder is only called twice, while in the first case it is called for each constant string.
Does the compiler optimize Stringbuffer/StringBuilder.append() for constant strings?
No.
However, I think that the premise of your proposed optimization is incorrect. I suggest that you take the two versions of the code and compile them. Then use javap to see what the compiled code looks like in each case.
(FWIW, I expect that your "optimization" will not improve the generated code. It will most likely create a second StringBuilder to concatenate the intermediate string, and convert that into a String. You end up with roughly the same number of append operations, plus the creation of an extra temporary StringBuilder and an extra temporary String.)
I was wondering would there be a performance differences while i use logical operators instead of several if statements. I saw a nice link, does this apply to java also?
I just created class like
class Test{
static java.util.Random r=new java.util.Random();
boolean test(){
return r.nextBoolean();
}
void test1(){
if (test() && test() && test())
System.out.println("3x yes");
}
void test2(){
if (test())
if (test())
if (test())
System.out.println("3x yes");
}
}
compiled it then decompiled by javap -c Test and got these result
class Test {
static java.util.Random r;
Test();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":
()V
4: return
boolean test();
Code:
0: getstatic #2 // Field r:Ljava/util/Random;
3: invokevirtual #3 // Method java/util/Random.nextBoole
an:()Z
6: ireturn
void test1();
Code:
0: aload_0
1: invokevirtual #4 // Method test:()Z
4: ifeq 29
7: aload_0
8: invokevirtual #4 // Method test:()Z
11: ifeq 29
14: aload_0
15: invokevirtual #4 // Method test:()Z
18: ifeq 29
21: getstatic #5 // Field java/lang/System.out:Ljava/
io/PrintStream;
24: ldc #6 // String 3x yes
26: invokevirtual #7 // Method java/io/PrintStream.printl
n:(Ljava/lang/String;)V
29: return
void test2();
Code:
0: aload_0
1: invokevirtual #4 // Method test:()Z
4: ifeq 29
7: aload_0
8: invokevirtual #4 // Method test:()Z
11: ifeq 29
14: aload_0
15: invokevirtual #4 // Method test:()Z
18: ifeq 29
21: getstatic #5 // Field java/lang/System.out:Ljava/
io/PrintStream;
24: ldc #6 // String 3x yes
26: invokevirtual #7 // Method java/io/PrintStream.printl
n:(Ljava/lang/String;)V
29: return
static {};
Code:
0: new #8 // class java/util/Random
3: dup
4: invokespecial #9 // Method java/util/Random."<init>":
()V
7: putstatic #2 // Field r:Ljava/util/Random;
10: return
}
As you can see bytecodes of test1 and test2 are same, so there is no difference in using
if (test() && test() && test())
or
if (test())
if (test())
if (test())
It's implementation dependent - so you'll need to benchmark to be sure.
Having said that, most JIT compilers are smart enough to optimise boolean comparisons very effectively so you are unlikely to see any difference.
The only area where logical operators are likely to offer a substantial advantage are in cases where you are using them to perform bitwise computations. This can be very efficient since it can result in branchless code that exploits hardware instructions.
Both forms compile to the same code. Contrary to the suggestions in other answers, this is not an 'optimization', and it would be astonishing if different compilers did different things. There is only one sensible way to compile && and that is by treating it the same as another 'if'. I can't even think of a non-sensible way.
I was curious about how the JVM works. Does the JVM acknowledge method accesibility rules like 'private' protected or is that only done at compile time?
For example, is it possible at around line37 to do some bytecode manipulation and call a protected method, say test3? Normally the compiler would not let me call that method because it is declared protected. But I was curious if that protected rule is enforced at runtime?
u.test1();
// Is it possible at runtime, to call 'test3' through bytecode manipulation
// #line37
package org.berlin.algo.basic.test;
public class RunX {
private String zzz = "rrrrr";
public void test1() {
// Note: I am intentionally use 'new' here as part of my test, not a
// good practice I know but allowed by the language.
Object x = new String("Test1 -----[1.1] " + zzz);
x = new String("Test1 --- [1.2]" + x.toString());
System.out.println(x);
this.test2();
this.test3();
}
/**
* Here, I noticed that the compiler removed my 'test2' code block.
* Does that always happen?
*/
private void test2() {
Object x = new String("Test2#line21--->>> [2.1]");
System.out.println(x);
}
protected void test3() {
Object x = new String("Test3#line27 {Will the JVM enforce the 'protected' method rule for test3? --->>> [3.1]");
x = new String("Test3#line28--->>> [3.2]");
System.out.println(x);
}
public static void main(final String [] args) {
System.out.println("Running");
RunX u = new RunX();
u.test1();
// Is it possible at runtime, to call 'test3' through bytecode manipulation
// #line37
System.out.println("Done");
}
} // End of the Class //
/*
JVM bytecode: javap -v RunX
Compiled from "RunX.java"
public class org.berlin.algo.basic.test.RunX extends java.lang.Object
SourceFile: "RunX.java"
minor version: 0
major version: 50
Constant pool:
const #1 = class #2; // org/berlin/algo/basic/test/RunX
const #2 = Asciz org/berlin/algo/basic/test/RunX;
...
...
const #84 = Asciz SourceFile;
const #85 = Asciz RunX.java;
{
public org.berlin.algo.basic.test.RunX();
Code:
Stack=2, Locals=1, Args_size=1
0: aload_0
1: invokespecial #10; //Method java/lang/Object."<init>":()V
4: aload_0
5: ldc #12; //String rrrrr
7: putfield #14; //Field zzz:Ljava/lang/String;
10: return
LineNumberTable:
line 3: 0
line 5: 4
line 3: 10
LocalVariableTable:
Start Length Slot Name Signature
0 11 0 this Lorg/berlin/algo/basic/test/RunX;
public void test1();
Code:
Stack=5, Locals=2, Args_size=1
0: new #21; //class java/lang/String
3: dup
4: new #23; //class java/lang/StringBuilder
7: dup
8: ldc #25; //String Test1 -----[1.1]
10: invokespecial #27; //Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
13: aload_0
14: getfield #14; //Field zzz:Ljava/lang/String;
17: invokevirtual #30; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
20: invokevirtual #34; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
23: invokespecial #38; //Method java/lang/String."<init>":(Ljava/lang/String;)V
26: astore_1
27: new #21; //class java/lang/String
30: dup
31: new #23; //class java/lang/StringBuilder
34: dup
35: ldc #39; //String Test1 --- [1.2]
37: invokespecial #27; //Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
40: aload_1
41: invokevirtual #41; //Method java/lang/Object.toString:()Ljava/lang/String;
44: invokevirtual #30; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
47: invokevirtual #34; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
50: invokespecial #38; //Method java/lang/String."<init>":(Ljava/lang/String;)V
53: astore_1
54: getstatic #42; //Field java/lang/System.out:Ljava/io/PrintStream;
57: aload_1
58: invokevirtual #48; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V
61: aload_0
62: invokespecial #54; //Method test2:()V
65: aload_0
66: invokevirtual #57; //Method test3:()V
69: return
LocalVariableTable:
Start Length Slot Name Signature
0 70 0 this Lorg/berlin/algo/basic/test/RunX;
27 43 1 x Ljava/lang/Object;
protected void test3();
Code:
Stack=3, Locals=2, Args_size=1
0: new #21; //class java/lang/String
3: dup
4: ldc #66; //String Test3#line27 {Will the JVM enforce the 'protected' method rule for test3? --->>> [3.1]
6: invokespecial #38; //Method java/lang/String."<init>":(Ljava/lang/String;)V
9: astore_1
10: new #21; //class java/lang/String
13: dup
14: ldc #68; //String Test3#line28--->>> [3.2]
16: invokespecial #38; //Method java/lang/String."<init>":(Ljava/lang/String;)V
19: astore_1
20: getstatic #42; //Field java/lang/System.out:Ljava/io/PrintStream;
23: aload_1
24: invokevirtual #48; //Method java/io/PrintStream.println:(Ljava/lang/Object;)V
27: return
LocalVariableTable:
Start Length Slot Name Signature
0 28 0 this Lorg/berlin/algo/basic/test/RunX;
10 18 1 x Ljava/lang/Object;
public static void main(java.lang.String[]);
Code:
Stack=2, Locals=2, Args_size=1
0: getstatic #42; //Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #72; //String Running
5: invokevirtual #74; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: new #1; //class org/berlin/algo/basic/test/RunX
11: dup
12: invokespecial #76; //Method "<init>":()V
15: astore_1
16: aload_1
17: invokevirtual #77; //Method test1:()V
20: getstatic #42; //Field java/lang/System.out:Ljava/io/PrintStream;
23: ldc #79; //String Done
25: invokevirtual #74; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
28: return
LocalVariableTable:
Start Length Slot Name Signature
0 29 0 args [Ljava/lang/String;
16 13 1 u Lorg/berlin/algo/basic/test/RunX;
}
*/
To the JLS!
15.12.4 Runtime Evaluation of Method Invocation
At run time, method invocation requires five steps. First, a target reference may be computed. Second, the argument expressions are evaluated. Third, the accessibility of the method to be invoked is checked. Fourth, the actual code for the method to be executed is located. Fifth, a new activation frame is created, synchronization is performed if necessary, and control is transferred to the method code.
The wording of the JLS indicates that the accessibility would be checked at runtime.
The JVM does acknowledge these. They can be overridden, by calling setAccessible(true) as Prashant Bhate does, but by default they are enforced. (See http://download.oracle.com/javase/6/docs/api/java/lang/reflect/AccessibleObject.html#setAccessible%28boolean%29.)
By the way, you write that "the compiler doesn't encode type method visibility rules into the Java bytecde file"; but it does. In addition to the above, it has to encode these, for a number of reasons. For example:
you can compile a class A that references class B even if you only have the compiled version of class B.
you can inspect a method's visibility via reflection (the getModifiers() method).
private methods aren't virtual -slash- can't be overridden by subclasses.
If you want to call this method from outside current class you could call private & protected methods using reflection.
Method m = RunX.class.getDeclaredMethod("test3");
m.setAccesible(true);
m.invoke(u);
however you can call this protected (and also private) method directly from main() without any issues.
Oli mentioned it rightly that ultimately you can do anything if you come to extent of byte code manipulation (if done correctly !!!).
Although I will like answer your question of accessibility honor at runtime in Java. If you have any doubts then please go ahead and use reflection to call the private method of one class from other class and you will get your answer. Java creates the function table of class at runtime when loading it and allow the refererence to the functions in limit of accessibility rule. However Java provides facility where you can call the private methods via reflection using setAccessible(true) on the method reference before invoking it.