Java basic technical details summary

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.

  1. All values ​​of type byte, short, charwill be promoted to inttype
  2. If one of the operands is of longtype, the result of the evaluation is of longtype
  3. If one of the operands is of floattype, the result of the evaluation is of floattype
  4. If one of the operands is of doubletype, the result of the evaluation is of doubletype

The variable declared as finalwill be optimized by the JVM, so the third sentence will be optimized at compile time, and there will b6 = 10be 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| < errused 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, returnbut there are finallystatements to be executed, so the returnfollowing statements are executed first, such as ( x++), and the value to be returned is saved to a local variable.
2. Execute finallythe content of the statement, including the returnstatement, then ignore the try and returnreturn directly.

return value problem. tryThe return value of the statement in (or catch) can be considered returnto 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 returncan 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 finallythe 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 Objectresult of the returned object is that the test fails. Here, the conversion rules of the ternary operator are involved:

  1. If the two operands are of normal types, type conversion is performed as normal, such asint => long => float => double
  2. 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 countis equivalent to no change. After reading the bytecode, you may not feel it. Write the code processed by the compiler:

To summarize countthe processing flow here:

  1. The JVM copies the count value (whose value is 0) to the temporary variable area.
  2. The count value is incremented by 1, and the count value is 1 at this time.
  3. Return the value of the temporary variable area, note that this value is 0 and has not been modified.
  4. 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

instanceofThe operands on the left and right sides of the operator must have an inheritance or derivation relationship, otherwise the compilation will not succeed. Therefore, instanceofoperators 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 , NaNthere 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.NaNreturns 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 valueOfthe method is a parseIntprocess 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 parseIntthe 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 valueOfobtaining 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 == 128that the result is naturally true

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325300640&siteId=291194637
Recommended