The Java language documentation says:
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.
My understanding is if we have a piece of code:
private final int x = 10;
Then, the compiler will replace every occurrence of x in the code with literal 10.
But suppose the constant is initialized at run-time:
private final int x = getX(); // here getX() returns an integer value at run-time.
Will there be any performance drop (howsoever negligible it may be) compared to the compile-time constant?
Another question is whether the below line of code:
private int y = 10; // here y is not final
is treated in same way as compile-time constant by the compiler?
Finally, what I understand from the answers are:
final static means compile-time constant
just final means it's a constant but is initialized at run-time
just static means initialized at run-time
without final is a variable and wouldn't be treated as constant.
Is my understanding correct?
Compile time constant must be:
declared final
primitive or String
initialized within declaration
initialized with constant expression
So private final int x = getX(); is not constant.
To the second question private int y = 10; is not constant (non-final in this case), so optimizer cannot be sure that the value would not change in the future. So it cannot optimize it as good as constant value. The answer is: No, it is not treated the same way as compile time constant.
The JLS makes the following distinctions between final variables and constants:
final variables
A variable can be declared final. A final variable may only be
assigned to once. It is a compile-time error if a final variable is
assigned to unless it is definitely unassigned immediately prior to
the assignment (§16 (Definite Assignment)).
Once a final variable has been assigned, it always contains the same
value. If a final variable holds a reference to an object, then the
state of the object may be changed by operations on the object, but
the variable will always refer to the same object. This applies also
to arrays, because arrays are objects; if a final variable holds a
reference to an array, then the components of the array may be changed
by operations on the array, but the variable will always refer to the
same array.
A blank final is a final variable whose declaration lacks an
initializer.
constants
A constant variable is a final variable of primitive type or type
String that is initialized with a constant expression (§15.28).
From this definition, we can discern that a constant must be:
declared final
of primitive type or type String
initialized within its declaration (not a blank final)
initialized with a constant expression
What about compile-time constants?
The JLS does not contain the phrase compile-time constant. However, programmers often use the terms compile-time constant and constant interchangeably.
If a final variable does not meet the criteria outlined above to be considered a constant, it should technically be referred to as a final variable.
According to JLS, there is no requirement that "constant variable" should be static.
So "constant variable" maybe static or non-static (instance variable).
But JLS imposes some other requirements for a variable to be a "constant variable" (besides being just final):
being only String or primitive
initialized inline only, because it is final, and blank final is not allowed
initialized with "constant expression" = "compile-time constant expression" (see JLS quote below)
4.12.4. final Variables (JLS)
A constant variable is a final variable of primitive type or type String that is initialized with a constant expression (§15.28).
15.28. Constant Expressions
A compile-time constant expression is an expression denoting a value
of primitive type or a String that does not complete abruptly and is
composed using only the following:
Literals of primitive type and literals of type String (§3.10.1,
§3.10.2, §3.10.3, §3.10.4, §3.10.5)
Casts to primitive types and casts to type String (§15.16)
The unary operators +, -, ~, and ! (but not ++ or --) (§15.15.3,
§15.15.4, §15.15.5, §15.15.6)
The multiplicative operators *, /, and % (§15.17)
The additive operators + and - (§15.18)
The shift operators <<, >>, and >>> (§15.19)
The relational operators <, <=, >, and >= (but not instanceof)
(§15.20)
The equality operators == and != (§15.21)
The bitwise and logical operators &, ^, and | (§15.22)
The conditional-and operator && and the conditional-or operator ||
(§15.23, §15.24)
The ternary conditional operator ? : (§15.25)
Parenthesized expressions (§15.8.5) whose contained expression is a
constant expression.
Simple names (§6.5.6.1) that refer to constant variables (§4.12.4).
Qualified names (§6.5.6.2) of the form TypeName . Identifier that
refer to constant variables (§4.12.4).
There might be a really small performance drop on some machines for private final int x = getX(); since that would involve at least one method call (besides the fact that this isn't a compile-time constant) but as you said, it would be negligible so why bother?
As for the second question: y isn't final and thus is not a compile time constant, since it might change at runtime.
The final keyword means that a variable will be initialized once and only once. A real constant need to be declared static as well.
So, none of your examples are treated as constants by the compiler. Nevertheless, the final keyword tells you (and to the compiler) that your variables will be initialized once only (in the constructor or literally).
If you need their values assigned at compile time your fields must be static.
Performance is not really that affected, but have in mind that primitive types are immutable, once you have created one it will hold that value in memory until the garbage collector removes it.
So, if you have a variable y = 1; and then you change it to y = 2; in memory the JVM will have both values, but your variable will "point" to the latter.
private int y = 10; // here y is not final
is treated in same way as compile time constant by the compiler ?
No. This is an instance variable, created, initialized an used at runtime.
Just keep in mind that in the following code, x is not compile time constant:
public static void main(String[] args) {
final int x;
x= 5;
}
private final int x = getX();
Will be called the first time your object is declared. The performance "drop" will depend on getX() but that's not the kind of things to create some bottleneck.
Simply speaking while compilation the compiler replaces the reference with the actual value specified, instead of using the reference parameter.
public static void main(String[] args) {
final int x = 5;
}
ie. while compilation the complier take the initialised value of 5 directly for compliation than using the reference variable 'x';
Please check this explanation
Related
I have a doubt regarding type conversion between numeric values. Below is a snapshot from the book java complete reference. It says integer literals are successfully converted to byte or short provided the range of the value defined fits into the range of byte/short. However, when I try to run the following program it throws an error. Kindly clarify. Thanks.
book
import java.util.Scanner;
class Test{
public static void main(String args[])
{ int a=10;
byte b=16;
b=a;
System.out.println(b);
}}
/tmp/IQOgFrykLL/Test.java:12: error: incompatible types: possible lossy conversion from int to byte
b=a;
^1 error
It says integer literals are successfully converted to byte or short
b=a doesn't involve an integer literal.
An integer literal is something like 10 or 16: an actual number that appears in the source code.
b=10 would work, because 10 is an int literal.
Actually, it's a bit more general than only working with int literals: you can make this assignment with any compile-time constant value.
For example, if you were to make a final, it would work, because then a would be a compile-time constant value, and essentially could then be replaced by the int literal 10 wherever it is used.
Note that the definition of "compile time constant" is quite strict: it has to be a literal, or a final variable initialized with a compile-time constant value.
Declaring a non-final variable and never changing its value doesn't make it compile-time constant: you have to tell the compiler that its value can't change via final.
Declaring a variable as final doesn't make it a compile-time constant either: the value you assign it has to be a compile-time constant too.
So far I thought that effectively final and final are more or less equivalent and that the JLS would treat them similar if not identical in the actual behavior. Then I found this contrived scenario:
final int a = 97;
System.out.println(true ? a : 'c'); // outputs a
// versus
int a = 97;
System.out.println(true ? a : 'c'); // outputs 97
Apparently, the JLS makes an important difference between the two here and I am not sure why.
I read other threads like
Difference between final and effectively final
Effectively final variable vs final variable
What does a variable being “effectively final” mean?
but they do not go into such detail. After all, on a broader level they appear to be pretty much equivalent. But digging deeper, they apparently differ.
What is causing this behavior, can anyone provide some JLS definitions that explain this?
Edit: I found another related scenario:
final String a = "a";
System.out.println(a + "b" == "ab"); // outputs true
// versus
String a = "a";
System.out.println(a + "b" == "ab"); // outputs false
So the string interning also behaves differently here (I dont want to use this snippet in real code, just curious about the different behavior).
First of all, we are talking about local variables only. Effectively final does not apply to fields. This is important, since the semantics for final fields are very distinct and are subject to heavy compiler optimizations and memory model promises, see $17.5.1 on the semantics of final fields.
On a surface level final and effectively final for local variables are indeed identical. However, the JLS makes a clear distinction between the two which actually has a wide range of effects in special situations like this.
Premise
From JLS§4.12.4 about final variables:
A constant variable is a final variable of primitive type or type String that is initialized with a constant expression (§15.29). Whether a variable is a constant variable or not may have implications with respect to class initialization (§12.4.1), binary compatibility (§13.1), reachability (§14.22), and definite assignment (§16.1.1).
Since int is primitive, the variable a is such a constant variable.
Further, from the same chapter about effectively final:
Certain variables that are not declared final are instead considered effectively final: ...
So from the way this is worded, it is clear that in the other example, a is not considered a constant variable, as it is not final, but only effectively final.
Behavior
Now that we have the distinction, lets lookup what is going on and why the output is different.
You are using the conditional operator ? : here, so we have to check its definition. From JLS§15.25:
There are three kinds of conditional expressions, classified according to the second and third operand expressions: boolean conditional expressions, numeric conditional expressions, and reference conditional expressions.
In this case, we are talking about a numeric conditional expressions, from JLS§15.25.2:
The type of a numeric conditional expression is determined as follows:
And that is the part where the two cases get classified differently.
effectively final
The version that is effectively final is matched by this rule:
Otherwise, general numeric promotion (§5.6) is applied to the second and third operands, and the type of the conditional expression is the promoted type of the second and third operands.
Which is the same behavior as if you would do 5 + 'd', i.e. int + char, which results in int. See JLS§5.6
Numeric promotion determines the promoted type of all the expressions in a numeric context. The promoted type is chosen such that each expression can be converted to the promoted type, and, in the case of an arithmetic operation, the operation is defined for values of the promoted type. The order of expressions in a numeric context is not significant for numeric promotion. The rules are as follows:
[...]
Next, widening primitive conversion (§5.1.2) and narrowing primitive conversion (§5.1.3) are applied to some expressions, according to the following rules:
In a numeric choice context, the following rules apply:
If any expression is of type int and is not a constant expression (§15.29), then the promoted type is int, and other expressions that are not of type int undergo widening primitive conversion to int.
So everything is promoted to int as a is an int already. That explains the output of 97.
final
The version with the final variable is matched by this rule:
If one of the operands is of type T where T is byte, short, or char, and the other operand is a constant expression (§15.29) of type int whose value is representable in type T, then the type of the conditional expression is T.
The final variable a is of type int and a constant expression (because it is final). It is representable as char, hence the outcome is of type char. That concludes the output a.
String example
The example with the string equality is based on the same core difference, final variables are treated as constant expression/variable, and effectively final is not.
In Java, string interning is based on constant expressions, hence
"a" + "b" + "c" == "abc"
is true as well (dont use this construct in real code).
See JLS§3.10.5:
Moreover, a string literal always refers to the same instance of class String. This is because string literals - or, more generally, strings that are the values of constant expressions (§15.29) - are "interned" so as to share unique instances, using the method String.intern (§12.5).
Easy to overlook as it is primarily talking about literals, but it actually applies to constant expressions as well.
Another aspect is that if the variable is declared final in the body of the method it has a different behaviour from a final variable passed as parameter.
public void testFinalParameters(final String a, final String b) {
System.out.println(a + b == "ab");
}
...
testFinalParameters("a", "b"); // Prints false
while
public void testFinalVariable() {
final String a = "a";
final String b = "b";
System.out.println(a + b == "ab"); // Prints true
}
...
testFinalVariable();
it happens because the compiler knows that using final String a = "a" the a variable will always have the "a" value so that a and "a" can be interchanged without problems.
Differently, if a is not defined final or it is defined final but its value is assigned at runtime (as in the example above where final is the a parameter) the compiler doesn't know anything before its use. So the concatenation happens at runtime and a new string is generated, not using the intern pool.
Basically the behaviour is: if the compiler knows that a variable is a constant can use it the same as using the constant.
If the variable is not defined final (or it is final but its value is defined at runtime) there is no reason for the compiler to handle it as a constant also if its value is equal to a constant and its value is never changed.
This question already has answers here:
Static vs Instance Variables: Difference?
(6 answers)
Closed 5 years ago.
static _____ defaultValue;
public static void main(String[] args){
System.out.println(defaultValue);
}
Options:
A. One
B. Two
C. Three
D. Four
The answer in my book:
C. Since defaultValue is an instance variable, it is automatically initialized to the corresponding value for that type. For double the default value is 0.0. By contrast, it is 0 for int, long, and short. Therefore Option C is correct.
Now my question is defaultValue is a static variable how can it be an instance variable? It should not have any initial value. Please correct me as I know I am wrong somewhere. I am studying for OCA java certification exam and I found this question in a book by Scott Selikoff and Jeanne Boyarsky.
Variables that are declared as a class member variable, be static or instance, and not assigned a value will be assigned their default value. Note defaultValue is a static class member variable.
It's not always necessary to assign a value when a field is declared. 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. Primitive Data Types
The default values that will be assigned are as follows.
Of the four primitive types given (double, int, long and short), each will be assigned with 0, however, the output depends on how the primitive type is converted to a String. The way in which a String is generated from a primitive is almost identical, just with distinct methods for each primitive type. Let's examine the int primitive type:
By calling System.out.println(int i) you effectively call System.out.print(int i) with a line.separator used to terminate the line, as described in the PrintStream documentation. The System.out.print(int i) method takes the string produced by String.valueOf(int i) which is translated into bytes according to the platform's default character encoding. Finally, the String documentation shows that String.valueOf(int i) acts as a wrapper for Integer.toString(int i)
Hence, the String value for a primitive type is generated found by invoking the toString() method with the primitive value as a parameter on the wrapper Class corresponding to the primitive type (Integer for int, Short for short, etc).
How do these methods convert their default value to a String? All of the primitive values in the question (double, int, long and short) are assigned a default value of 0 (0.0d for double). The toString(primitive_type value) for each of the wrapper classes state that:
Double :
if m is zero, it is represented by the characters "0.0"; thus, negative zero produces the result "-0.0" and positive zero produces the result "0.0". toString(double d)
Integer :
Returns a String object representing the specified integer. The argument is converted to signed decimal representation and returned as a string, exactly as if the argument and radix 10 were given as arguments to the toString(int, int) method. toString(int i)
If the magnitude is zero, it is represented by a single zero character '0' ('\u0030') toString(int i, int radix)
Long :
Returns a String object representing the specified long. The argument is converted to signed decimal representation and returned as a string, exactly as if the argument and the radix 10 were given as arguments to the toString(long, int) method. toString(long i)
If the magnitude is zero, it is represented by a single zero character '0' ('\u0030') toString(long i, int radix)
Short :
Returns a new String object representing the specified short. The radix is assumed to be 10. toString(short s) Note that inspecting the code shows that this method is simply a wrapper for return Integer.toString((int)s, 10);
Hence, int, long and short will all output the character '0' when the value is 0 whereas the double value will print the characters '0.0'.
The answer in your book is incorrectly motivated, as you suspected.
Since defaultValue is an instance variable
It is a static variable. If it was an instance variable, this code wouldn't even compile.
it is automatically initialized to the corresponding value for that type.
Correct, but this applies to both instance and static variables.
For double the default value is 0.0. By contrast, it is 0 for int, long, and short.
There is no 'by contrast' about it. The default values are really zero in all these cases. System.out.println() will print 0.0 in the case of double and float, but what's in the variable is indistuinguishable from zero.
Therefore Option C is correct.
There is no 'therefore' about it. There are several non sequiturs here, as well as several errors of fact.
I found this question in a book by Scott Selikoff and Jeanne Boyarsky.
Good to know, so we can avoid it.
What is written in the book is correct except the line where it is says "Since defaultValue is an instance variable". As defaultValue is a static variable it cannot be a instance variable but it is off course a class variable. As it is a class variable it is automatically initialized to the corresponding value for that type.
C. Since defaultValue is an instance variable - this statement is wrong, because defaultValue is not an instance variable - it's a class variable as its declaration contains static keyword. Static variables are associated with class enclosing them. Another worth mentioning fact is that the static variables are initialized before instance variables (instance variables doesn't contain static as their modifier - every instance has its own variable and any changes in that variable doesn't affect other instances' variables).
Now my question is defaultValue is a static variable how can it be an instance variable? It should not have any initial value. - this statement is wrong. It is not an instance variable, it's a class variable. Anyway it will have initial value, because only local variables are not initialized to their default value (local variables are those, whose scope is method they're declared in. For example, if you declared defaultValue in the scope of main() method or any other method, it wouldn't be initialized to its default value.
When program starts execution and you haven't assigned any specific value to your variables, variables containing primitive types are initialized to their default values (you can see the list of these default values at the link below) and those containing objects are initialized to null.
Here, at "Default Values" paragraph, is a list of values that are assigned to primitive types:
https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html
In Java,
a variable of a reference type modified by final cannot be changed to refer to any other object
a variable of a primitive type modified by final cannot be changed to have a different value
I asked that at Does `final` means a field is not mutable?
Does readonly in C# also make the same distinction between variables of value types and reference types?
Thanks.
When used as a modifier to a variable the readonly in C# is the equivalent of Java’s final therefore they behave exactly the same way for both value types and reference types in their respective languages.
Java's "Final" Behaviour
You can only initialize a final variable once and only once. This has nothing to do with whether an objects state can be modified or not.
C# "readonly" Behaviour
The readonly keyword is a modifier that you can only use on fields. Given a variable that is declared with a readonly modifier, any assignments to the fields introduced by the declaration can only occur as part of the declaration or in a constructor in the same class.
The Readonly modifier prevents fields from being changed. Therefore any attempt to change them later are disallowed.
In Java,
a variable of a reference type modified by final cannot be changed to refer to any other object
a variable of a primitive type modified by final cannot be changed to have a different value
Does readonly in C# also make the same distinction between variables of value types and reference types?
That is mostly the case, yes. Neither final nor readonly fields may be modified after you leave a constructor: a reference type field may not be assigned to point to a different object, and a value type field may not be assigned a different value. But that's not the whole story.
While Java's final and C#'s readonly modifiers largely serve the same purpose, there are some differences you should be aware of.
Assignment Rules
A final field in Java must be assigned exactly once, either with an inline field initializer, or by assignment in each constructor[1]. A readonly field in C# can only be initialized inline or in a constructor, but there are no restrictions on how many times it may be assigned; it may be assigned once, more than once, or never.
Value Types and Mutability
There is a subtle behavioral difference between readonly fields of value types versus reference types. When a value type field is marked readonly, any access to that field results in a copy being made. This prevents you from performing any potentially mutating operations on the field itself, effectively rendering it internally immutable. For example:
struct TestStruct {
public int Count;
public int Increment() { return ++Count; }
}
class MutableTest {
TestStruct s;
public void Test() {
Console.WriteLine(s.Increment());
}
}
class ImmutableTest {
readonly TestStruct s;
public void Test() {
Console.WriteLine(s.Increment());
}
}
If you declare a MutableTest and invoke Test() twice, you will see it print 1 and then 2. Do the same with an ImmutableTest instance, and you will see 1 and then 1 again.
Note, however, that Java has no notion of user-defined value types: all value types in Java are immutable primitives, so declaring a primitive field as final does not and could not affect "internal" mutability in this way.
Neither modifier affects the internal mutability of reference types, e.g., adding a final or readonly modifier will not, on its own, prevent you from modifying the target object's fields or properties, nor will it prevent you from calling methods that may mutate the object.
Compile-Time Constants
Unlike readonly, the final modifier may be combined with static to define a compile-time constant. If a static final field has a primitive or String value, and its inline initializer is a compile-time constant expression, then the compiler may replace any references to it with the underlying constant value. In such cases, the field will not actually be read. The same can be achieved in C# with the const keyword; it cannot be done with readonly.
Modification by Reflection
It is worth noting that a readonly field may be modified through reflection. This effectively bypasses the requirement that readonly fields be assigned only during object construction. The same is not true in Java: any attempt to reflectively modify a final field will trigger an exception.
Local Variables and Formal Parameters
Lastly, in Java, formal method parameters and local variables may be marked as final. This is strictly a language-level feature, and it has no impact on the generated bytecode[2]. A final variable, like a field, must be assigned exactly once. C# has no equivalent to readonly or final for local variables or formal parameters.
[1] Alternatively, a final field could be assigned once in an instance initializer block. However, these are seldom used, and the use of an instance initializer is equivalent to prepending the block's contents to the beginning of each instance constructor.
[2] Technically, in some cases a formal parameter's final designation may be recorded in a metadata table, but the bytecode within the method body is unaffected.
readonly makes no distinction between reference types or value types. readonly simply means that the value stored in a variable can not be changed outside a field initializer or a constructor.
What makes you think it’s necessary to differentiate between value and reference types? Variables are placeholder of values, regardless their type. What that value is or represents is another story.
So if I have a static final Object CONSTANT = null, for some reason if I reference that in another piece of code like doSomething(CONSTANT), it won't be in-lined onto the code during compilation. So instead of being doSomething(null) after being compiled, it would be doSomething(CONSTANT).
Your CONSTANT is not a compile time constant because the JLS says it is not. The only types that can be used in constant expressions are the primitive types and String.
The sense of it is that an Object instance (in general) has a semantically significant object identity that distinguishes it from other Object instances. This Object identity cannot be encoded in a class file ... or at least, it can't be encoded with the current classfile formats. (And if it could, there would be all sorts of other problems ...)
The value null could (in theory) be handled as a special case, except that there is not a great deal of point. Specifically, you can't use null in any of the contexts where a "compile time constant" is required (or advantageous) from the linguistic perspective. For instance:
You can't have null as a case expression.
Since == for reference types is not a constant expression, you can't use it for the Java "conditional compilation" idiom involving an if with a constant expression as a condition. (And besides null == null is not a useful condition ...)
As far as inlining is concerned, while the "constant" cannot be inlined in the bytecodes (because of the JLS rules about what a "constant expression" is), the JIT compiler's optimizer would be permitted to do this, and may actually do it ... if there are tangible performance benefits.
Reference:
JLS 15.28 - Constant Expressions
In your case the CONSTANT is not compile time constant.
Compile-time constant is a constant and its value is known at compile time and
it won’t change further, then the compiler replaces the constant name everywhere
in the code with its value .
Generally a primitive type or a string literal that is declared with final is treated as compile time constant by the compiler.
Example:
final int a=10;
final String constant =”this is compile time const”;
These both are compile time constants we can use Compile-time constant expressions in case labels of switch statements
Example for non compile time constants
final String xyz = new String(”this is not a compile time const");
here xyz string object is not a compile time constant.Because this 'xyz' string object is going to create at run time and here compiler knows about the reference only not the string object.
The same is applicable to your static final Object CONSTANT = null
Accroding to JLS
Type of the null literal is the null type;its value is the null reference.