Don't forget the basics of development, and write a lot of basic content that is easy to forget. Here we will go back to the source and summarize some details in the Java language specification and basic classes. For all details about the Java Language Specification, refer to The Java® Language Specification, Java SE 8 Edition (JLS8) .
This article will continue to be added. .
Decimal to integer
Math.floor(x)
Returns the nearest integer less than or equal to x, the return type is double;Math.round(x)
Equivalent to rounding, the return value is long or int;Math.ceil(x)
Returns the nearest integer greater than or equal to x, the return type is double.
Static Blocks and Building Blocks
Static block : declared with static, executed when the JVM loads the class, executed only once and takes precedence over the main function.
Structural block : The class is defined directly with {}, which is executed every time an object is created, which is equivalent to adding the content of the construction block to the front of the constructor (much like inserting an inline function into each constructor, the construction block is equivalent to for inline functions).
Execution order priority: Static block > Constructor block > Constructor method
When there is an inheritance relationship, the execution order is usually: parent class static block => child class static block => parent class construction block => parent class construction method => child class construction block => child class construction method
Test:
public class test {
public static void main(String[] args) {
new Derived();
}
}
class Base {
static {
System.out.println("fucking => Base::static");
}
{
System.out.println("fucking => Base::before");
}
public Base() {
System.out.println("Base::Base<init>");
}
}
class Derived extends Base {
static {
System.out.println("fucking => Derived::static");
}
{
System.out.println("fucking => Derived::before");
}
public Derived() {
super();
System.out.println("Derived::Derived<init>");
}
}
output:
fucking => Base::static
fucking => Derived::static
fucking => Base::before
Base::Base<init>
fucking => Derived::before
Derived::Derived<init>
Code Snippet: Operator Rules - Addition Rules
byte b1 = 1, b2 = 2, b3, b6;
final byte b4 = 4, b5 = 6;
b6 = b4 + b5;
b3 = (b1 + b2);
System.out.println(b3 + b6);
The data type of the expression is automatically promoted. Regarding the automatic promotion of the type, pay attention to the following rules. Result: Compilation error on the fourth line.
- All values of type
byte
,short
,char
will be promoted toint
type - If one of the operands is of
long
type, the result of the evaluation is oflong
type - If one of the operands is of
float
type, the result of the evaluation is offloat
type - If one of the operands is of
double
type, the result of the evaluation is ofdouble
type
The variable declared as final
will be optimized by the JVM, so the third sentence will be optimized at compile time, and there will b6 = 10
be no problems.
if statement comparing float x with "zero value"
if (fabs(x) < 0.00001f)
Regarding try and finally float types, there are also double types. These decimal types will not be directly equal to zero when they approach 0, and generally approach 0 infinitely. Therefore, == cannot be used to judge. It should be |x-0| < err
used to judge, where |x-0| represents the absolute value, and err represents the limit error, which is represented by the program fabs(x) < 0.00001f
.
1. The first one is executed try
, return
but there are finally
statements to be executed, so the return
following statements are executed first, such as ( x++
), and the value to be returned is saved to a local variable.
2. Execute finally
the content of the statement, including the return
statement, then ignore the try and return
return directly.
return value problem. try
The return value of the statement in (or catch
) can be considered return
to be placed at the top of the thread stack: if the return value is a primitive type, the top is the value, and if the return value is a reference type, the top is a reference. The statement in finally return
can modify the object corresponding to the reference, but cannot modify the basic type. But whether it is a primitive type or a reference type, it can be overridden by finally
the returned "concrete value" concrete value.
Type conversion problem of ternary operator
The types in the ternary operator must be the same, such as the following code:
int i = 40;
String as_e1 = String.valueOf(i < 50 ? 233 : 666);
String as_e2 = String.valueOf(i < 50 ? 233 : 666.0);
assertEquals(true, as_e1.equals(as_e2));
If the two operands cannot be converted, no conversion is performed, and the Object
result of the returned object is that the test fails. Here, the conversion rules of the ternary operator are involved:
- If the two operands are of normal types, type conversion is performed as normal, such as
int => long => float => double
- If both operands are literal numbers, return the type with the larger range
Some pitfalls of the auto-increment operator in Java
Observe the following piece of code:
public class AutoIncTraps {
public static void main(String[] args) {
int count = 0;
for(int i = 0; i < 10; i++) {
count = count++;
}
System.out.println(count);
}
The print result of this code is 0
, that is to say, auto-increment is useless here, which is different from C++. Decompile and take a look at the bytecode (main function part):
public static main([Ljava/lang/String;)V
L0
LINENUMBER 6 L0
ICONST_0
ISTORE 1
L1
LINENUMBER 7 L1
ICONST_0
ISTORE 2
L2
FRAME APPEND [I I]
ILOAD 2
BIPUSH 10
IF_ICMPGE L3
L4
LINENUMBER 8 L4
ILOAD 1
IINC 1 1
ISTORE 1
L5
LINENUMBER 7 L5
IINC 2 1
GOTO L2
L3
LINENUMBER 10 L3
FRAME CHOP 1
GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
ILOAD 1
INVOKEVIRTUAL java/io/PrintStream.println (I)V
L6
LINENUMBER 11 L6
RETURN
public class AutoIncTraps {
public AutoIncTraps() {
}
public static void main(String[] args) {
byte count = 0;
for(int i = 0; i < 10; ++i) {
int var3 = count + 1;
count = count;
}
System.out.println(count);
}
}
This is equivalent to creating a local variable to store count++
, but not returning, so it count
is equivalent to no change. After reading the bytecode, you may not feel it. Write the code processed by the compiler:
To summarize count
the processing flow here:
- The JVM copies the count value (whose value is 0) to the temporary variable area.
- The count value is incremented by 1, and the count value is 1 at this time.
- Return the value of the temporary variable area, note that this value is 0 and has not been modified.
- The return value is assigned to count, and the count value is reset to 0.
Just looking at the bytecode of this one is relatively abstract. Let's take a look at the bytecode of these three sentences, which is easier to understand by comparison:
count = ++count;
count = count++;
count++;
Bytecode:
L4
LINENUMBER 9 L4
IINC 1 1
ILOAD 1
ISTORE 1
L5
LINENUMBER 10 L5
ILOAD 1
IINC 1 1
ISTORE 1
L6
LINENUMBER 11 L6
IINC 1 1
In addition, the auto-increment operation is not an atomic operation , which will be involved when summarizing concurrent programming later.
Notes on the instanceof operator
instanceof
The operands on the left and right sides of the operator must have an inheritance or derivation relationship, otherwise the compilation will not succeed. Therefore, instanceof
operators can only be used on objects, not on primitive types (which are not automatically unpacked).
Here are some typical examples:
public class FuckingIOF {
@Test
public void test() {
List<Object> list = new ArrayList<>();
list.add("String" instanceof Object);
list.add(new String() instanceof Object);
list.add(new Object() instanceof String);
//list.add('a' instanceof Character); //此句会编译错误
list.add(null instanceof String);
list.add((String)null instanceof String);
list.add(null instanceof Object);
list.add(new Generic<String>().isDataInstance(""));
list.forEach(System.out::println);
}
}
class Generic<T> {
public boolean isDataInstance(T t) {
return t instanceof Date;
}
}
Run results and analysis:
true => String是Object的子类
true => 同上
false => 同上
false => Java语言规范规定null instanceof ? 都是false
false => 同上,无论怎么转换还是null
false => 同上
false => 由于Java泛型在编译时会进行类型擦除,因此这里相当于Object instanceof Date了
Weird NaN type
According to JLS8 4.2.3 , NaN
there are the following provisions:
- The numerical comparison operators < , <= , > , and >= return false if either or both operands are NaN (§15.20.1).
- The equality operator == returns false if either operand is NaN.
- In particular, (x=y) will be false if x or y is NaN.
- The inequality operator != returns true if either operand is NaN (§15.21.1).
- In particular, x!=x is true if and only if x is NaN.
Note that it Double.NaN == Double.NaN
returns false , which is actually following the IEEE 754 standard. NaN represents an abnormal number (such as a number obtained by dividing by 0), which is defined as:
/**
* A constant holding a Not-a-Number (NaN) value of type
* {@code double}. It is equivalent to the value returned by
* {@code Double.longBitsToDouble(0x7ff8000000000000L)}.
*/
public static final double NaN = 0.0d / 0.0;
This question was seen on StackOverflow. The following three expressions: Integer class static cache && valueOf and parseInt comparison
System.out.println(Integer.valueOf("127") == Integer.valueOf("127"));
System.out.println(Integer.valueOf("128") == Integer.valueOf("128"));
System.out.println(Integer.parseInt("128") == Integer.valueOf("128"));
The results are:
true
false
true
Why is this result? Let's take a look at the source code of the valueOf method:
public static Integer valueOf(String s) throws NumberFormatException {
return Integer.valueOf(parseInt(s, 10));
}
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
It can be seen that valueOf
the method is a parseInt
process of reading the cache on the basis of the method. Let's look at the source code of the IntegerCache class again:
/**
* Cache to support the object identity semantics of autoboxing for values between
* -128 and 127 (inclusive) as required by JLS.
*
* The cache is initialized on first usage. The size of the cache
* may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
* During VM initialization, java.lang.Integer.IntegerCache.high property
* may be set and saved in the private system properties in the
* sun.misc.VM class.
*/
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
Let's take a look at parseInt
the prototype of the method, which returns a native int value: the original JVM will cache a part of the Integer object (the default range is -128 - 127
), when valueOf
obtaining the Integer object, if it is within the cache range, it will directly return the cached Integer object, Otherwise, an Integer object will be new. The returned upper limit can be set by JVM parameters -XX:AutoBoxCacheMax=<size>
and cannot be less than 127 (refer to JLS 5.1.7). This way we can explain Integer.valueOf("127") == Integer.valueOf("127")
why it is true, because they all get the same cache object, which is Integer.valueOf("128") == Integer.valueOf("128")
equivalent to default by default new Integer(128) == new Integer(128)
, and the result is naturally false.
public static int parseInt(String s) throws NumberFormatException
Since the wrapper type is automatically unpacked when a native value is compared with a wrapper value, it Integer.parseInt("128") == Integer.valueOf("128")
is equivalent to 128 == 128
that the result is naturally true