Having a class like this:
public class Sample1 {
public class Inner {
private int f;
}
void go() {
Inner in = new Inner();
int value = in.f;
}
}
The byte-code for the go method (before java-11) calls that known synthetic method:
static int access$000(nestmates.Sample1$Inner);
descriptor: (Lnestmates/Sample1$Inner;)I
flags: ACC_STATIC, ACC_SYNTHETIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: getfield #1 // Field f:I
4: ireturn
from:
0: new #2 // class nestmates/Sample1$Inner
3: dup
4: aload_0
5: invokespecial #3 // Method nestmates/Sample1$Inner."<init>":(Lnestmates/Sample1;)V
8: astore_1
9: aload_1
10: invokestatic #4 // Method nestmates/Sample1$Inner.access$000:(Lnestmates/Sample1$Inner;)I
I've known about this for quite some time, but I've never questioned myself why this is like this? Why javac has to do this in the first place, and why not directly getField and this indirection via the static method. Thank you.
Because JVMS doesn't allow access to private members of a different class, whether it is an inner class or not. JDK 11 added the concept of nestmates to overcome this limitation.
The reason is that the outer and inner classes compile to different class files, which means they can't access each others' private members.
The synthetic method is generated to effectively widen the access from private to package-private, but it achieves that via unnecessary indirection.
In Java 11, they introduced a new concept for this purpose which does allow classes in different files to access each others' private members in certain cases (i.e. this one).
See the JEP: https://openjdk.org/jeps/181
It's a simple conclusion based on a number of factors:
At the JVM (class file / java.exe) level, inner classes simply do not exist. At all. Javac 'fakes' it, by naming your Inner class Sample1$Inner (that dollar isn't a rendering, that's its actual JVM-level name, a $ is just a symbol, just as valid as I or n), adding a parameter of type Sample1 as first param to all constructors of Inner, replacing new Sample1() with new Sample1(this), replacing instanceOfOuter.new Sample1() with new Sample1(instanceOfOuter), having a final field of type Sample1 (which those constructors set by using that first param), translating all calls to outer methods as being invoked on that field (given that there's no "outer this" at the JVM level, as there is no outer class), and so on. You can see all this using javap -c.
private members cannot be accessed by any type other than itself, therefore, given that an inner class ceases to be that once compilation has concluded (because the JVM doesn't have the inner class concept in the first place), it needs a package-private (or protected or public, as you want) way to get at it.
The JVM also has no idea what synthetic means. It's a flag, yes, and the JVM completely ignores. It doesn't know what it implies, it doesn't affect how code is to be run or interpreted at all. It's javac that knows what synthetic means. Namely: flag anything you made up to glue together the carefully managed fakery to make inner classes seem like a thing when they are purely a java-the-language (i.e. compiler) feature and don't exist at the java-the-runtime level - and when reading class files, be blind to them. Act like these synthetic methods do not exist. As in, if javac is asked to compile code that attempts to call a synthetic method, act the same way as if compiling code that attempts to call a non-existant method.
As a comment has already noted, more recent versions of java do introduce this notion of inner classes at the JVM level, but not quite by enshrining the idea of 'inner class' in the JVM Specification, but by having the 'nestmates' concept, which lets class files list other class names that get to 'see' and call/interact with private elements. Modern javacs, if targeting modern JVMs ('modern' defined here as: "Has the nestmates feature available"), will use nestmates and forego all the synthetics.
Related
In Java, how many constructors can we create within a single class.
Strictly speaking, the JVM classfile format limits the number of methods (including all constructors) for a class to less than 65536. And according to Tom Hawtin, the effective limit is 65527. Each method signature occupies a slot in the constant pool. Since some of the 65535 pool entries are (unavoidably) consumed by other things, it is not possible for a well-formed class file to use all of the possible method / constructor ids.
Reference - JVMS 4.1 The ClassFile Structure
However, if you are writing sensible Java code the normal way, you won't encounter that limit.
How many should you have? It depends on the classes use-cases. It is often nice to have multiple "convenience" constructor overloads, and implement them using this(...) to chain to a "master" constructor. (However, you can go over the top. There are N! possible combinations (overloads) of N distinct parameters.)
If you find that you are writing an excessive (subjective!) number of constructors, you should maybe look at alternatives such as the Builder Pattern.
Like with the maximum number of lambdas or the maximum of nested method invocations, we have to make a distinction between the formal Java language specification and technical limits, either due to the formally specified class file format or due to compiler limitations or bugs.
As often, the language specification does not define any limits on the number of constructors. So there’s only the practical limitation that the class declaration must be representable in the byte code format.
Constructors get compiled to special methods (named <init>), so in the class file, they share a table with ordinary methods, which is limited to 65535 entries. We can max this out by not declaring any ordinary methods. Further, since every constructor must have a distinct signature, each constructor needs its own type signature string in the constant pool, which is limited to 65534 entries on its own.
The constant pool also serves other purposes, like holding the declaration of this class, super class and the name of the Code attribute, which is needed when having constructors, as well as the linkage information of the super class’ constructor, we have to invoke, so this is the limiting factor on the class file side.
So the minimum constant pool entries needed, are
super class name (modified UTF8 entry)
super class (type Class, referring to 1.)
this class name (modified UTF8 entry)
this class (type Class, referring to 3.)
the constructor’s “method” name <init> (modified UTF8 entry)
a name&type entry referring to 5. and a super constructor signature (may be shared with one of our constructor’s signature)
a method entry referring to 2. and 6. (for the super constructor invocation)
the attribute name Code (modified UTF8 entry)
Given these required entries and the limit of 65534 entries (the size plus one is stored as unsigned two byte quantity), we get a class file limit of 65526 constructors and indeed, I could generate a valid class file using the ASM library with that number of constructors and not more.
Actually, you could get more if you name your class java.lang.Object, as in that special case, there is no super class to declare and no super constructor to invoke. Decide yourself, which actual limit you want to call the maximum number…
As said, there is a 3rd limitation, the compiler implementation. When using a Java compiler, you have to make sure that it doesn’t generate debug information (in case of javac, use -g:none) and no other optional attributes which could occupy constant pool entries. But with javac of JDK11, the performance will drop significantly when you start defining lots of constructors. I got the following compilation times:
1000 constructors: 1 second
2000 constructors: 2 seconds
5000 constructors: 10 seconds
10000 constructors: 1 minute
15000 constructors: 2 minutes
20000 constructors: 4 minutes
30000 constructors: 10 minutes
40000 constructors: 20 minutes
50000 constructors: between 25 minutes and ½ hour
65526 constructors: between 45 minutes and 1 hour
So javac eventually managed to max out the class file limit, but we may consider a practical limit even before that.
The Eclipse compiler seems to deal better with such source files, but still, maxing out the number of constructors made the IDE almost unusable. With debug symbols turned off and a bit of patience, I managed to compile a class with 65526 constructors with Eclipse. Declaring 65528 constructors produced an error message regarding too many constant pool entries and declaring 65527 constructors revealed a bug in Eclipse, producing a corrupt class file declaring zero constant pool entries (as said earlier, the number is stored as count plus one, so compiler vendors have to keep in mind that the limit is not 65535 but 65534).
Java Support Constructor Overloading(When java class contain multiple constructors, it is called as constructor is overloaded).A class can have multiple constructors, as long as their signature(parameter) are not the same.So you can define many constructors as you need.There is no limit.
Here is a Example:-
class Demo {
private String name;
private String city;
private Double salary;
public Demo() {
}
public Demo(String name) {
this.name = name;
}
public Demo(Double salary) {
this.city = city;
}
public Demo(String name,String city) {
this.name = name;
this.city = city;
}
}
You can have 65535 constructors in a class(According to Oracle docs). But IMPORTANTLY keep this in your mind. We achieve this only by CONSTRUCTOR OVERLOADING ( https://beginnersbook.com/2013/05/constructor-overloading/ ). You can create many constructors but with different signatures.
tl;dr
For a class with reasonable functionality you will first run into other problems, both technical and non-technical. Technical limits imposed by the constant pool of the class file format for a useless class are:
65526 for a class with a name of your choosing.
65527 for a class named Code (one per class loader).
65530 for a class named java.lang.Object (one per VM).
The details
The question was closed before I had posted my answer. So here's bits additional to that which #Holger has already covered.
The relevant section of the JVM spec is 4.1. The ClassFile Structure in the Java SE 11 edition.
u2 constant_pool_count;
cp_info constant_pool[constant_pool_count-1];
Note the -1. The entries are numbered from 1. 0 is used to indicate:
a class with no superclass (interesting!)
a formal parameter with no name
a module with no version information
a module dependence with no version information
If the class is called Code then this string constant will be deduped with the required Code attribute name (see #Holger's answer). You will need a very old version of javac if you want to access it from outside of the default package.
It may be possible to remove a couple of the entries, if writing in byte code is not cheating, by throwing null (not a constant in bytecode) instead of calling super() or similar. I can't remember the exact details of bytecode verification for constructors - they certain cannot terminate normally without calling a this() or a super().
javac running increasingly slowly (O(n^2)ish?) is a good example of graceful degradation. :)
Playtime
You can see this for yourself quite easily by compiling small classes with varying numbers of constructors.
public class Min1 {
public Min1() {
}
/* Followed by (int a), (byte a), (int a, byte b), etc. */
}
Compile without debug info (do people still accidentally distribute class files with debug info?).
javac -g:none Min1.java
List contents with good old javap.
javap -verbose Min1
Should give you something like
Classfile /Users/tackline/code/scratch/minimal_class/Min1.class
Last modified Dec 5, 2018; size 119 bytes
MD5 checksum c1a6b7c31c286165e01cc4ff240e7718
public class Min1
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
#1 = Methodref #3.#7 // java/lang/Object."<init>":()V
#2 = Class #8 // Min1
#3 = Class #9 // java/lang/Object
#4 = Utf8 <init>
#5 = Utf8 ()V
#6 = Utf8 Code
#7 = NameAndType #4:#5 // "<init>":()V
#8 = Utf8 Min1
#9 = Utf8 java/lang/Object
{
public Min1();
descriptor: ()V
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
}
You can have as many constructors in a class as you wish.. JAVA doesn't impose any restrictions on the number of constructors a class can have.. Just that constructors can be either parameterized or default..
default constructor: Default constructor does not have a parameters and is used to initialize every object with same data.
parametrized constructor: Parameterized constructor is having one or more parameters and is used to initialized each object with different data.
You can create 65535 constructors in one class
More information: Oracle Docs
There's no limit on the number of constructors a class can have.
I've googled this extensively to no avail. I cannot seem to wrap my head around this concept. Why are static final fields accepted in local classes? Such as the following example below:
public void sayGoodbyeInEnglish() {
class EnglishGoodbye {
public static final String farewell = "Bye bye";
public void sayGoodbye() {
System.out.println(farewell);
}
}
System.out.println(EnglishGoodbye.farewell);
EnglishGoodbye myEnglishGoodbye = new EnglishGoodbye();
myEnglishGoodbye.sayGoodbye();
}
In class EnglishGoodbye the variable farewell is allowed? Why? I'm confused. Why is that allowed but no static variable? I understand why it cannot access the members of the enclosing scope unless they are a compiler time constant as those variables cease to exist when the function ends but the class may not. Right? Im just confused on this.
Thanks!
It's not, in general.
But farewell is a special kind of static final: one whose value is a constant, as defined by JLS 15.28. That means that it's not being initialized at that location, which is what's actually disallowed in non-static classes (including local classes), as per JLS 8.1.3.
The JLS states this explicitly (and in bold font) in 8.1.3 (note the "unless" part):
It is a compile-time error if an inner class declares a member that is explicitly or implicitly static, unless the member is a constant variable (§4.12.4).
If you change that line to either remove the final modifier or make the expression non-constant (for example, new String("Bye bye")), then you'll get the compilation error you expect:
Test.java:5: error: Illegal static declaration in inner class EnglishGoodbye
public static final String farewell = new String("Bye bye");
^
modifier 'static' is only allowed in constant variable declarations
1 error
A bit more:
The reason this is allowed is that constant variables are treated specially by the compiler. In particular, it's allowed to inline them -- the resulting bytecode doesn't have the farewell field at all! If you decompile the class (javap -c YourClassName), you'll see something like this:
public void sayGoodbyeInEnglish();
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String Bye bye
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
...
The above corresponds to this line:
System.out.println(EnglishGoodbye.farewell);
It's a bit daunting, but notice that "3:" line. The program isn't loading the value of the field farewell, it's loading constant #3 (which it note in a comment is the String "Bye bye") (you can see a list of the bytecodes on wikipedia).
Because farewell is a constant variable (and not a "real" static member), and thus can be inlined in the code, it doesn't matter where you define it -- the variable's lifecycle is essentially that of the whole JVM, not any one class or instance, and thus it can be declared anywhere.
Disassembling some Java 8 code I found out that some invokestatic calls on static methods in interface (particularly this was java.util.function.Function.identity()) uses InterfaceMethodRef in const pool; this is what javap -s -c -v p show me:
15: invokestatic #66 // InterfaceMethod java/util/function/Function.identity:()Ljava/util/function/Function;
According to JVM 8 spec this is not possible, and when I've used this instruction in classfile with version Java 7 (major version=51), it has thrown VerifyError on this instruction.
However, when I've changed the major version to 52, it started working like a charm. Note that I am running on Oracle JDK 1.8.0_60. I wonder why this change was needed (the invoked method is linked statically, isn't it?) and whether this is documented anywhere.
Well, before Java 8, static methods in interfaces were not allowed, so obviously, any attempt to use them in a previous version or within a class file having an older version is doomed to fail, regardless of how it is implemented in Java 8.
Before Java 8, we had the following two rules:
The class_index item of a CONSTANT_Methodref_info structure must be a class type, not an interface type.
The class_index item of a CONSTANT_InterfaceMethodref_info structure must be an interface type.
(See JVMSpec 7 §4.4.2)
The method descriptor of an invokestatic must refer a CONSTANT_Methodref_info entry
(See JVMSpec 7 §6.5)
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.
It might not look that clear that “symbolic reference to a method” precludes interface methods, but without that assumption we had no difference to the behavior of Java 8 at all. It also will become clearer by comparing with the JVM specification for Java 8 or considering that interface methods were always implied to be non-static.
It should be obvious that for adding support for static methods in interfaces, to be invoked via invokestatic, at least one rule has to change.
If we look at the rules and their wording, we see that the first one is quite clear whereas in the second one, it’s not entirely obvious that “symbolic reference to a method” is referring to CONSTANT_Methodref_info entries and precluding a “symbolic reference to an interface method”. The decision was to change that rule and make the wording clearer at the same time:
(JVMSpec 8 §6.5):
The run-time constant pool item at that index must be a symbolic reference to a method or an interface method (§5.1), which gives the name and descriptor (§4.3.3) of the method as well as a symbolic reference to the class or interface in which the method is to be found.
Now it’s clear that invokestatic may refer to interface methods and thus the first rule doesn’t need to be touched, and it hasn’t been touched. But note that the first rule never mandated the interface methods to be non-static. It’s only about whether the declaring type is an interface or not.
This obviously reduces the value of the distinction between CONSTANT_Methodref_info and CONSTANT_InterfaceMethodref_info but this was unavoidable. If the first rule was relaxed instead, it would also soften this distinction.
But there was a strong reason to change the invocation side instead: due to the introduction of default methods, there is now the possibility to invoke an overridden default method via invokespecial just like other overridden non-abstract methods before. Thus, invokespecial may now refer to interface methods as well.
Insisting on a match between the type of the invocation instruction and the constant pool entry type, like with the old rules, would imply that in the case of default methods, we would sometimes need two pool entries to describe the same target method, one for invokeinterface and a different one for invokespecial.
I've googled this extensively to no avail. I cannot seem to wrap my head around this concept. Why are static final fields accepted in local classes? Such as the following example below:
public void sayGoodbyeInEnglish() {
class EnglishGoodbye {
public static final String farewell = "Bye bye";
public void sayGoodbye() {
System.out.println(farewell);
}
}
System.out.println(EnglishGoodbye.farewell);
EnglishGoodbye myEnglishGoodbye = new EnglishGoodbye();
myEnglishGoodbye.sayGoodbye();
}
In class EnglishGoodbye the variable farewell is allowed? Why? I'm confused. Why is that allowed but no static variable? I understand why it cannot access the members of the enclosing scope unless they are a compiler time constant as those variables cease to exist when the function ends but the class may not. Right? Im just confused on this.
Thanks!
It's not, in general.
But farewell is a special kind of static final: one whose value is a constant, as defined by JLS 15.28. That means that it's not being initialized at that location, which is what's actually disallowed in non-static classes (including local classes), as per JLS 8.1.3.
The JLS states this explicitly (and in bold font) in 8.1.3 (note the "unless" part):
It is a compile-time error if an inner class declares a member that is explicitly or implicitly static, unless the member is a constant variable (§4.12.4).
If you change that line to either remove the final modifier or make the expression non-constant (for example, new String("Bye bye")), then you'll get the compilation error you expect:
Test.java:5: error: Illegal static declaration in inner class EnglishGoodbye
public static final String farewell = new String("Bye bye");
^
modifier 'static' is only allowed in constant variable declarations
1 error
A bit more:
The reason this is allowed is that constant variables are treated specially by the compiler. In particular, it's allowed to inline them -- the resulting bytecode doesn't have the farewell field at all! If you decompile the class (javap -c YourClassName), you'll see something like this:
public void sayGoodbyeInEnglish();
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String Bye bye
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
...
The above corresponds to this line:
System.out.println(EnglishGoodbye.farewell);
It's a bit daunting, but notice that "3:" line. The program isn't loading the value of the field farewell, it's loading constant #3 (which it note in a comment is the String "Bye bye") (you can see a list of the bytecodes on wikipedia).
Because farewell is a constant variable (and not a "real" static member), and thus can be inlined in the code, it doesn't matter where you define it -- the variable's lifecycle is essentially that of the whole JVM, not any one class or instance, and thus it can be declared anywhere.
Suppose you have legacy java code which can not be compiled by an up-to-date version of java. e.g.
public class ProviderUnavailableException extends Exception {
private int cause;
public int getCause(){
return cause;
}
// rest of implementation
}
Back at the time of Java 1.3 this code was valid.
In Java 1.4 the class Throwable 'redefined' the method getCause(). It looks like this:
public Throwable getCause()
Now the legacy code is invalid (because 'int' is not a subtype of 'Throwable') but does not lead to runtime problems. Or can this happen under some circumstances?
Is it correct that back a the compile time, the compiler had generated the byte code to handle the execution of the method getCause solely in that class and therefore 'knows' that no super class has to be invoked?
EDIT
i checked the byte code of the legacy code with "javap -c".
public int getCause();
Code:
0: aload_0
1: getfield #2; //Field _cause:I
4: ireturn
So it's returning the local field. Seems ok for me.
In bytecode the method is referred to by its name, parameter types and return type. So the two methods are quite separate.
In the bytecode notation, your 1.3 method would be (I think):
getCause()J
While the new 1.4 method is:
getCause()Ljava/lang/Throwable;
You can see the signatures using javap -s.
Covariant return types are actually implemented at compile time by javac using synthetic bridge methods. They do not have runtime support. So if you override Object run() with String run() javac will create both methods in the derived class with Object run() calling String run().
Firstly the signature change is not a language change. Updating libraries is not a syntactical or feature change for java the language. Your question relates to how the runtime late binds methods. The easiest way to verify is to simulate the same problem USO g your classes substituting your own.