Java hidden problems

Keywords: Java less JDK

1. Comparison between basic type and reference type

1.1. Which of the following four variables is false

Integer i01 = 59;
int i02 = 59;
Integer i03 =Integer.valueOf(59);
Integer i04 = new Integer(59);

(1) Integer in order to save space and memory, the number between - 128 and 127 will be cached in memory;
(2) valueOf(): when calling this method, the internal implementation makes a judgment to determine whether the current incoming value is between - 128 and 127 and whether the object already exists in IntergCache. If it exists, it will directly return the reference. If not, it will create a new object
(3) The basic type exists in the memory stack. When comparing with the reference type, the reference type will be automatically boxed to compare the value without comparing the memory address;

Integer a=123;
Integer b=123;
System.out.println(a==b);        // Output true
System.out.println(a.equals(b));  // Output true
a=1230;
b=1230;
System.out.println(a==b);        // Output false
System.out.println(a.equals(b));  // Output true

1.2 is the automatic boxing and unboxing mechanism a compilation feature or a virtual machine runtime feature? How are the differences realized?
The auto boxing mechanism automatically completes the replacement at compile time. In the packing stage, the method of valueOf is automatically replaced, and in the unpacking stage, the method of xxxValue is automatically replaced;
If the valueOf method parameter of Integer type is between - 128 and 127, it will directly return the reference of the existing object in the internal cache pool. If the parameter is other range value, it will return the new object;
The Double type is similar to the Integer type. It also calls the valueOf method of Double, but the difference between Double is that no matter how many parameter values are passed in, a new object will be used to express the value (because the number of floating-point data in the specified range is uncertain, the number of Integer and so on is certain, so it can be cached)
Note: the valueOf methods of Integer, Short, Byte, Character and Long are similar, while Double and Float are special. Each time a new wrapper object is returned, the wrapper type is on both sides: = = reference is compared, equals is compared; expression (including arithmetic operation) is on both sides:== Compared with the numerical value (automatically triggering the unpacking process), the packing type equals method will not undergo type conversion;
1.3.Integer i = 1; i += 1; what has been done

  • Integer i = 1; Auto boxing: use valueOf() method to box int as integer type
  • i += 1; first unpack the Integer type i to int (use intValue() method to unpack the Integer to int
    int). After the addition operation, i is boxed into Integer type

2. On the comparison between String + and StringBuffer

When String + is written as an expression (more precisely, as an assignment statement), the efficiency is actually faster than String buffer

public class Main{  
	public static void main(String[] args){ 
		String string = "a" + "b" + "c";
		StringBuffer stringBuffer = new StringBuffer();
		stringBuffer.append("a").append("b").append("c");
		string = stringBuffer.toString();
	}
}

2.1. String + is faster than Stringbuffer because the compiler optimizes constants when compiling this program.
It will directly synthesize a, b and c into a constant abc and save it in the corresponding class file {}. See the following decompiled Code:

public class Main{}
	public static void main(String[] args){
	      String string = "abc";
	      StringBuffer stringBuffer = new StringBuffer();
	      stringBuffer.append("a").append("b").append("c");
	      string = stringBuffer.toString();
	}
}

The reason is that string + is actually completed by Stringbuilder. Generally speaking, Stringbuilder is faster than Stringbuffer. This is because the thread of Stringbuilder is not safe and there is less time cost of thread lock, so the writing speed of string + is still faster;

/*   1   */
String a = "a";
String b = "b";
String c = "c";
String string = a + b + c;
/*   2   */
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append(a);
stringBuffer.append(b);
stringBuffer.append(c);
string = stringBuffer.toString();

Here is an example:

 public static void main(String[] args)
  {
    String a = "a";
    String b = "b";
    String c = "c";
    long start = System.currentTimeMillis();
    for (int i = 0; i < 100000000; i++) {
         String string = a + b + c;
         if (string.equals("abc")) {}
    }
    System.out.println("string+ cost time:" + (System.currentTimeMillis() - start) + "ms");
    start = System.currentTimeMillis();
    for (int i = 0; i < 100000000; i++) {
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append(a);
        stringBuffer.append(b);
        stringBuffer.append(c);
        String string = stringBuffer.toString();
        if (string.equals("abc")) {}
    }
    System.out.println("stringbuffer cost time:" + (System.currentTimeMillis() - start) + "ms");
  }

Each time we do it 100 million times, we will see that string + is really faster than string buffer. Is it destroyed in an instant? Let's see the results.

2.2. String splicing method: +, concat() and append() methods. Append() is the fastest, followed by concat(), and + is the slowest

  • The compiler optimizes + by using the append() method of StringBuilder. The compiler uses the append() method to append and then converts it to String string with toString(). The key reason for the slowdown lies in the new stringbuilder() and toString(). However, 10 W StringBuilder objects are created here, and they need to be converted into strings every time Stringconcat:
  • The source code of concat(), which looks like a digital copy form, we know that the processing speed of array is very fast, but because the method is finally like this: return new String(0, count + otherLen, buf); this also creates 10 W string pairs, which is the root cause of its slow down
  • The append() method splices strings: no new string object is generated;

3. Static code block, static variable

Its action level is class; it constructs code block, constructor and construction, and its action level is object
(1) Static code block, which is executed as the class is loaded, will be executed as long as the class is loaded, and will only be loaded once, mainly used to initialize the class.
(2) The code block is constructed, which is executed once every time an object is created, and takes precedence over the constructor. It is mainly used to initialize the common initialization contents of different objects and initialize the instance environment.
(3) Constructor, which is executed once every time an object is created; at the same time, the constructor initializes a specific object, while the construction code initializes all objects, with different scopes;
==>Through the above analysis, the execution order of the three should be: static code block > construction code block > constructor.
3.1. Java class initialization process

  • First, initialize the static member variables and static code blocks in the parent class according to the order in which they appear in the program;
  • Then, the static member variables and static code blocks in the subclass are initialized according to the order in which they appear in the program;
  • Secondly, initialize the common member variables and code blocks of the parent class, and execute the construction method of the parent class;
  • Finally, initialize the common member variables and code blocks of the subclass, and execute the construction method of the subclass;

3.2. Do not call virtual methods that may be overloaded in the constructor
When the parent constructor executes, it calls the overloaded method of the child class. However, the class field of the child class is still in the initial stage, and the memory layout has just been completed:

public class Base {
      private String baseName = "base";
      public Base(){
         callName();
      }
      public void callName(){
         System. out. println(baseName);
      }
      static class Sub extends Base{
         private String baseName = "sub";
         @Override
         public void callName(){
            System. out. println (baseName) ;
         }
      }
      public static void main(String[] args){
         Base b = new Sub();
      }

}

3.3. Java assignment order
(1) Static variable assignment of parent class
(2) Static variable assignment of itself
(3) Assignment of parent class member variable
(4) Parent block assignment
(5) Parent constructor assignment
(6) Member variable assignment
(7) Self block assignment
(8) Self constructor assignment
3.4 Java code execution sequence

public class TestExecuteCode {
   public static void main(String[] args) {
      System.out.println(new B().getValue());
   }
   static class A {
      protected int value;
      public A(int v){
         setValue(v);
      }
      public void setValue(int value) {
         this.value = value;
      }
      public int getValue() {
         try {
            value++;
            return value;
         } finally {
            this.setValue(value);
            System.out.println(value);
         }
      }
   }
   static class B extends A {
      public B(){
         super(5);
         setValue(getValue() - 3);
      }
      @Override
      public void setValue(int value){
         super.setValue(2 * value);
      }
   }
}
  • Results of implementation: 22, 34, 17
    (1) In subclass B, the setValue method in parent class A is overridden: super(5) / / the constructor of parent class is called, in which setValue(value) in the constructor is: this.setValue(value) / / in the final block of the setValue method of subclass is also called, while in the setValue method of subclass: super.setValue(2*value); //The setValue method of parent class A is called.
    (2)try… catch... Return return value in the finally block: Although the value in the finally block has changed, the value returned in the try block should be the value stored before return
  • When a parent class executes, if a method of a subclass overrides the method of the parent class, the overriding method of the called subclass
    4. Give an expression to calculate how many bases it can be calculated
    If equation 7 * 15 = 133 is true, what base is used? It can be solved by solving the equation, which can be converted into the equation:
7 * (1 * x + 5) = 1 * x^2 + 3 * x + 3
x^2 -4x - 32 = 0
x = -4 or x = 8

If the following formula holds: 78 + 78 = 123, it is expressed in decimal system:

7 * x + 8 + 7 * x + 8 = 1 * x^2 + 2 * x + 3
x^2 - 12 * x - 13 = 0
x = -1, x = 13

5. Data type of expression

5.1 type conversion in basic types

  • All values of byte, short, char will be promoted to int;
  • If one operand is long, the result is long;
  • If there is an operand of float type, the calculation result is float type;
  • If there is an operand of double type, the calculation result is double type;
  • The final decorated variable is a constant. If the constant value is calculated directly during the operation, the variables without final decoration will be automatically promoted to int type after being added
 byte b1=1,b2=2,b3,b6;
final byte b4=4,b5=6;
b6=b4+b5;// If b4 and b5 are constants, they will be calculated directly according to the original value and will not be promoted to int type
b3=(b1+b2);// Compile error
System.out.println(b3+b6);

Remember that the operation of any integer type in JDK is based on int

private static final long mil_seconds = 24 * 60 * 60 * 1000;
private static final long micro_seconds = 24 * 60 * 60 * 1000 * 1000;
public static void main(String[] args) {
 System.out.println(micro_seconds / mil_seconds);
}

In the above code, micro_seconds has exceeded the maximum value of int type during operation, overflowing. In addition, if the basic type is compared or calculated with the corresponding packing type, the packing type will be unpacked automatically, such as the following code:
5.2. Type conversion in trinomial operation
When using the binomial operator, try to ensure that the types of the two return values are the same, or type conversion will be triggered. The conversion rules are as follows:
(1) If the return value X and the return value Y are of the same type, then the return type is undoubtedly this type;
(2) If the two return values X and Y are of different types, the return value type is their closest parent class. Give an example:

// String and Boolean both implement the Serializable interface
Serializable serializable = a == b ? "true" : Boolean.FALSE;
// All classes inherit the Object class
Object o = a == b ? new ArrayList<>() : new TernaryOperatorDemo();

(3) For a basic data type, if one of the return values X is of type byte, short or char and the other is of type int:

  • If it can be determined that the value range of Y is within the value range of X during compilation, the return type is x, otherwise it is y.
  • If the return value X is not of the above types, the hidden type conversion will be triggered;
    (4) When the basic data type and the object data type meet, the result of the three item operation is the basic data type by default;
    Example:
private static void test1(int a, int b) {
    // Trigger hidden type conversion, int type 9 to 9.0D
    System.out.println(a == b ? 9.9 : 9);
    // Compile time judgment, 98 in char, converted to b
    System.out.println(a == b ? 'a' : 98);
    // Compile time judgment, out of char range, transfer to int uniformly
    System.out.println(a == b ? 'a' : Integer.MAX_VALUE);
    // The value of b cannot be determined at compile time, which triggers hidden type conversion and unified conversion to int
    System.out.println(a == b ? 'a' : b);
    System.out.println(a != b ? 'a' : b);
    Map<String, Long> map = new HashMap<>();
    map.put("b", 1L);
    // When the basic data type meets the object data type, it will be converted to the basic data class by default,
    // map.get("a") returns null. When it is converted to basic data type, a null pointer exception is reported
    System.out.println(map == null ? -1L : map.get("a"));
  }

6. Print the current directory and subdirectories according to the directory structure

public class PrintDirectory {
   public static void main(String[] args) {
      File file = new File("E: \\download");
      PrintDirectory pd = new PrintDirectory();
      pd.listDirectory(file,0);
   }
   //List subdirectories of this directory
   private void listDirectory(File dir,int level){
      System.out.println(getSpace(level) + dir.getName());
      level++;
      File[] files = dir.listFiles();
      for(int i=0;i<files.length;i++){
         if(files[i].isDirectory()){
            listDirectory(files[i],level);
         }else{
            System.out.println(getSpace(level)+files[i].getName());
         }
      }
   }
   //Print directory according to directory structure
   private String getSpace(int level){
      StringBuilder sb = new StringBuilder();
      for(int i=0;i<level;i++){
         sb.append("|--");
      }
      return sb.toString();
   }
}

7. Number of bytes occupied by boolean

  • In Java virtual machine, there is no byte code instruction for boolean value. The boolean value operated by java language expression is replaced by int data type in Java virtual machine after compilation.
  • Java virtual machine directly supports array of boolean type. For navarra instruction of virtual machine, please refer to section newarray to create such array. To access and modify the Boolean array, use the balload and bastore instructions of the byte array;
  • Because in the virtual machine specification, the boolean value is replaced by the int data type in the Java virtual machine after compilation, and int is 4 bytes, so the boolean value is 4 bytes.
  • To access and modify the baload and bastore instructions of the common byte type array, because they are common, only two bytes can be common, so a byte in the byte array is a byte, so the boolean in the boolean array is a byte.
    Summary: in the case of an array, a boolean is 1 byte, and a single boolean is 4 bytes
    In the Java specification, there is no clear indication of the size of the boolean. In the Java virtual machine specification, the definition of single boolean taking up 4 bytes and one byte of boolean array is given. It depends on whether the virtual machine implementation is in accordance with the specification or not, so one byte and four bytes are possible.
class LotsOfBooleans{
    boolean a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, aa, ab, ac, ad, ae, af;
    boolean b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, ba, bb, bc, bd, be, bf;
    boolean c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, ca, cb, cc, cd, ce, cf;
    boolean d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, da, db, dc, dd, de, df;
    boolean e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, ea, eb, ec, ed, ee, ef;
}
class LotsOfInts{
    int a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, aa, ab, ac, ad, ae, af;
    int b0, b1, b2, b3, b4, b5, b6, b7, b8, b9, ba, bb, bc, bd, be, bf;
    int c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, ca, cb, cc, cd, ce, cf;
    int d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, da, db, dc, dd, de, df;
    int e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, ea, eb, ec, ed, ee, ef;
}
public class Test{
    private static final int SIZE = 100000;
    public static void main(String[] args) throws Exception{
        LotsOfBooleans[] first = new LotsOfBooleans[SIZE];
        LotsOfInts[] second = new LotsOfInts[SIZE];
        System.gc();
        long startMem = getMemory();
        for (int i=0; i < SIZE; i++) {
            first[i] = new LotsOfBooleans();
        }
         System.gc();
        long endMem = getMemory();

	System.out.println ("Size for LotsOfBooleans: " + (endMem-startMem));
        System.out.println ("Average size: " + ((endMem-startMem) / ((double)SIZE)));
        System.gc();
        startMem = getMemory();
        for (int i=0; i < SIZE; i++) {
            second[i] = new LotsOfInts();
        }
        System.gc();
        endMem = getMemory();
        System.out.println ("Size for LotsOfInts: " + (endMem-startMem));
        System.out.println ("Average size: " + ((endMem-startMem) / ((double)SIZE)));
        // Make sure nothing gets collected
        long total = 0;
        for (int i=0; i < SIZE; i++) {
            total += (first[i].a0 ? 1 : 0) + second[i].a0;
        }
        System.out.println(total);
    }
    private static long getMemory(){
        Runtime runtime = Runtime.getRuntime();
        return runtime.totalMemory() - runtime.freeMemory();
    }
}

In addition, most instructions do not support the integer types byte, char, short. The compiler extends byte and short type data with symbols to corresponding int type data at compile time or run time, and extends boolean and char type data zero bit to corresponding int type data;

Published 29 original articles, won praise 4, visited 630
Private letter follow

Posted by MtPHP2 on Sun, 26 Jan 2020 00:06:15 -0800