I won't get off work until I finish this article on Java basics

Posted by lisa71283 on Thu, 17 Feb 2022 13:41:46 +0100

TIP

This article is included in< Introduction · YuQue >For more in-depth articles, please go to< Introduction · YuQue> .

data type

There are eight basic data types in the Java language:

  1. boolean/1
  2. byte/8
  3. char/16
  4. short/16
  5. int/32
  6. float/32
  7. long/64
  8. double/64

The eight basic data types are divided into four categories:

  1. Integer: byte, short, int, long
  2. Floating point type: float, double
  3. Character type: char
  4. boolean: boolean

Basic types have corresponding packaging types. The assignment between basic types and their corresponding packaging types is completed by automatic packing and unpacking.

Here is an example:

Integer x = 2;     //  Packing
 int y = x;         //  Unpacking

Cache pool

First, let's look at a piece of code. After reading the code, we can predict what the result is.

Integer i1 = 40;
Integer i2 = 40;
Integer i3 = 0;
Integer i4 = new Integer(40);
Integer i5 = new Integer(40);
Integer i6 = new Integer(0);

System.out.println("i1=i2 \t" + (i1 == i2));
System.out.println("i1=i2+i3 \t" + (i1 == i2 + i3));
System.out.println("i4=i5 \t" + (i4 == i5));
System.out.println("i4=i5+i6 \t" + (i4 == i5 + i6));

// Guess the result first

Correct answer:

i1=i2   true
i1=i2+i3    true
i4=i5   false
i4=i5+i6    true

It seems to be an Easy problem, but the Result output by Console is exactly the opposite to what we think. Why is this Result?

The answer is cache pool. Let's analyze the source code together.

The compiler calls the valueOf() method in the buffer pool wide basic type auto boxing procedure

Therefore, Integer i1=40; It is equivalent to: integer I1 = integer valueOf(40);

public final class Integer extends Number implements Comparable<Integer> {

    private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];

        static {
            // High can be configured by attributes: Java lang.Integer. IntegerCache. high
            // Value range of high: [127,Integer.MAX_VALUE]
            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);
                    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++); // Cache pool initialization

            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }

        private IntegerCache() {}
    }
    
    /**
     * Returns an {@code Integer} instance representing the specified
     * {@code int} value.  If a new {@code Integer} instance is not
     * required, this method should generally be used in preference to
     * the constructor {@link #Integer(int)}, as this method is likely
     * to yield significantly better space and time performance by
     * caching frequently requested values.
     *
     * This method will always cache values in the range -128 to 127,
     * inclusive, and may cache other values outside of this range.
     *
     * @param  i an {@code int} value.
     * @return an {@code Integer} instance representing {@code i}.
     * @since 1.5
     */
    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }
}

In the integer source code, there is an internal class integercache, which is a cache pool. The internal implementation is an integer array, which caches - 128~X (the value range of X: 127 ≤ x ≤ Integer.MAX). The value of X can be obtained through Java lang.Integer. IntegerCache. high to configure the upper limit of the cache pool. If the upper limit is not configured, it defaults to 127.

Therefore, whether configured or not, use Integer i1 = XXX; First judge whether there is a cache pool in XXX. If so, find the reference of the object in the cache pool and use it directly. If not, create a new object.

After listening to the above explanation, I believe you can't wait to have a try, so let's review the above code.

Integer i1 = 40;                    // Using cache pool objects
Integer i2 = 40;                    // Using cache pool objects
Integer i3 = 0;                     // Using cache pool objects
Integer i4 = new Integer(40);       // Create a new object without using the cache pool object
Integer i5 = new Integer(40);       // Create a new object without using the cache pool object
Integer i6 = new Integer(0);        // Create a new object without using the cache pool object

i1 == i2;                           // Both objects are objects of the cache pool. The answer is true
i1 == i2 + i3;                      // i2 + i3 is operated in the memory stack, so i2 and i3 will be unpacked. In fact, the comparison is the basic type (40 = = 40 + 0). Their values are the same, so the result is True.
i4 == i5;                           // Both objects are created separately, and the answer is false
i4 == i5 + i6;                      // Reference i1 == i2 + i3;

// If you change 40 to 400, what will be the result?

Integer i1 = 400;                   
Integer i2 = 400;                   
Integer i3 = 0;                     
Integer i4 = new Integer(400);      
Integer i5 = new Integer(400);      
Integer i6 = new Integer(0);        

i1 == i2;                           
i1 == i2 + i3;                      
i4 == i5;                           
i4 == i5 + i6;         
// answer:
// First of all, before answering the question, you should first ask whether Java is configured lang.Integer. IntegerCache. high,
// If it is configured, how much should be provided.
// If it is configured and the value is less than 400, it indicates that high is less than 400, which indicates that 400 is not the value of the cache pool.
//      Integer i1 = 400; New object created
//      Integer i4 = new Integer(400); New object created
//      The answer should be: false, true, false, true
// If it is configured and the value is greater than 400, it indicates that high is greater than 400, which indicates that 400 is the value of the cache pool.
//      Integer i1 = 400; Use cache pool
//      Integer i4 = new Integer(400); New object created
//      The answer should be: true, true, false, true
// If not configured, please refer to "if configured, the value is less than 400".

The buffer pool corresponding to the basic type is as follows:

  • boolean values true and false
  • all byte values
  • short values between -128 and 127
  • int values between -128 and 127
  • char in the range \u0000 to \u007F

I believe you have seen through the essence of cache pool. If you don't understand it, see you in the comment area~

String

There are 8 basic types and a special type String in JAVA language.

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];
}

characteristic:

  • final decoration, non inheritable
  • The internal storage is of char [] type. Once the value is initialized, it cannot be changed, and even the internal method modification is not provided.

Benefits:

  1. String Pool needs

If a string object has been created, the reference will be obtained from the String Pool. String Pool can only be used if string is immutable.

  1. You can cache hash values

The hash code of string is often used in Java. For example, in a HashMap or HashSet. Immutability ensures that the hashcode is always the same, so that it can be redeemed without worrying about changes. This means that hashcode does not need to be calculated every time it is used. This is more efficient.

  1. security

String is widely used as a parameter of many java classes, such as network connection, open file, etc. If the string is not immutable, the connection or file will be changed, which may lead to serious security threats. This method thinks it is connected to a machine, but this is not the case. Variable strings can also cause security problems in reflection because the parameter is a string.

  1. Security

String immutability is inherently thread safe and can be used safely in multiple threads.

Talk about String, StringBuffer and StringBuilder

  1. variability
    1. String immutable
    2. StringBuffer and StringBuilder variable
  2. Thread safety
    1. String is immutable, so it is thread safe
    2. StringBuilder is not thread safe
    3. StringBuffer is thread safe and uses synchronized internally for synchronization

Learn more about StringBuffer and StringBuilder:

java - String, StringBuffer, and StringBuilder - Stack Overflow


Talk about string intern()

First look at the intern() source code in the String class in the Java language.

public final class String
        implements java.io.Serializable, Comparable<String>, CharSequence {
   /**
    * Returns a canonical representation for the string object.
    * <p>
    * A pool of strings, initially empty, is maintained privately by the
    * class {@code String}.
    * <p>
    * When the intern method is invoked, if the pool already contains a
    * string equal to this {@code String} object as determined by
    * the {@link #equals(Object)} method, then the string from the pool is
    * returned. Otherwise, this {@code String} object is added to the
    * pool and a reference to this {@code String} object is returned.
    * <p>
    * It follows that for any two strings {@code s} and {@code t},
    * {@code s.intern() == t.intern()} is {@code true}
    * if and only if {@code s.equals(t)} is {@code true}.
    * <p>
    * All literal strings and string-valued constant expressions are
    * interned. String literals are defined in section 3.10.5 of the
    * <cite>The Java&trade; Language Specification</cite>.
    *
    * @return a string that has the same contents as this string, but is
    *          guaranteed to be from a pool of unique strings.
    */
    public native String intern();
}

Introduction: the intern method is a native method of the String class

Function: it first checks whether the string constant pool exists. If it exists, it will directly return the objects in the pool. If it does not exist, it will be put into the pool and return the objects in the pool.

String s1 = new String("aaa");
String s2 = new String("aaa");
System.out.println(s1 == s2);           // false
String s3 = s1.intern();
System.out.println(s2.intern() == s3);  // true

String s4 = "hellojava.";
String s5 = "hellojava.";
System.out.println(s4 == s5);  // true

s1 and s2: two different objects are created in the way of new String(), so s1 == s2, and the answer is false.

s2.intern() == s1.intern(): because the values of S1 and S2 are the same, the objects in the same pool are called after intern(), and the answer is true.

s4 == s5: creating a string instance in double quotation marks will automatically put the new object into the String Pool.

reference resources:
What is Java String interning? - Stack Overflow

Meituan technical team: Deep parsing of String#intern

operation

Parameter transfer

Java parameters are passed into methods in the form of value passing, not by reference.

Dog dog = new Dog(); // dog is a pointer that stores the address of the object.

When a parameter is passed into a method, it is essentially to pass the address of the object into the formal parameter as a value. Therefore, if you change the object referenced by the pointer in the method, the two pointers point to completely different objects. If one party changes the content of the object it points to, it will have no impact on the other party.

Here is a piece of code to take you in-depth understanding.

public class Dog {
    private String name;
    public Dog(String name) {
        this.name = name;
    }
    public String getName() {
        return this.name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getObjectAddress() {
        return super.toString();
    }
}

public class Example {
    public static void main(String[] args) {
        Dog dog = new Dog("A");                     // dog is a pointer. Let's call it pointer 1 for the time being
        System.out.println(dog.getObjectAddress()); // Dog@4554617c
        func(dog);
        System.out.println(dog.getObjectAddress()); // Dog@4554617c
        System.out.println(dog.getName());          // A
    }
    private static void func(Dog dog) {             // dog is the pointer, here is pointer 2 (Note: pointer 1 and pointer 2 are not completely equal, pointer 1 is an argument, pointer 2 is a formal parameter, pointer 1 and pointer 2 point to the same object, and the address of the object is: Dog@4554617c )
        System.out.println(dog.getObjectAddress()); // Dog@4554617c
        /*
         * That's it
         * Contents of pointer 1: Dog@4554617c
         * Contents of pointer 2: Dog@4554617c
         * Description: pointer 1 and pointer 2 point to the same object
         */
        dog = new Dog("B");
        /*
         * That's it
         * Contents of pointer 1: Dog@4554617c
         * Contents of pointer 2: Dog@74a14482
         * Note: pointer 1 and pointer 2 do not point to the same object
         */
        System.out.println(dog.getObjectAddress()); // Dog@74a14482
        System.out.println(dog.getName());          // B
    }
}

float and double

Let's review the basic data types. float occupies 32 bits and double occupies 64 bits.

float f1 = 1.1;     // 1.1 literal quantity is of double type. It is received with float and belongs to downward transformation. Therefore, precision will be lost. (if there is an error in compilation, convert the displayed one. float f1 = (float) 1.1;)
float f2 = 1.1F;    // 1.1F is of float type. It is normal to receive with float.
double d1 = 1.1;    // 1.1 literal quantity is of double type. It is normal to receive with double.
double d2 = 1.1D;   // 1.1D make it clear that the literal quantity is of double type. It is normal to receive with double.

short s1 = 1;       // Define a short variable s1 with a value of 1
short s2 = 32767;   // Define a short variable s2 with a value of 32767
short s3 = 32768;   // Define a variable s2 of short type with a value of 32768 (compilation error, short has only 16 bits, and the value range is [- 32768, 32767], while 32768 belongs to the category of int type. Converting int type to short type belongs to downward transformation, which may cause loss of precision, so it needs to be forcibly converted: short s3 = (short) 32768;)

Implicit type conversion

Automatic type conversion (implicit type conversion): from small type to large type can be completed automatically.

byte -> short -> int -> long -> float -> double

char -> int

byte b1 = 1;
short s1 = b1;
int i1 = s1;
long l1 = i1;
float f1 = l1;
double d1 = f1;

char c1 = 1;
int i2 = c1;

switch

switch (x) { // Incompatible types. Found: 'long', required: 'char, byte, short, int, Character, Byte, Short, Integer, String, or an enum'
    case ...:
        break;
    case ...:
        break;
    default:
        break;
}

Starting from Java 7, switch statements support String types.

inherit

Access rights

  • There are three access modifiers in Java: private, protected and public. If the access modifier is not added, it means that it is visible at the package level.
  • Single inheritance is used in Java, and extensions is used for keywords.
  • If the method of the subclass overrides the method of the parent class, the access level of the method in the subclass is not allowed to be lower than that of the parent class. This is to ensure that subclass instances can be used wherever parent instances can be used, that is, to ensure that the Richter substitution principle is met.
  • The field must not be public, because if you do so, you will lose control over the modification behavior of this field, and the client can modify it at will.

Abstract classes and interfaces

  • Abstract classes and abstract methods are declared using the abstract keyword.
  • Abstract classes generally contain abstract methods, which must be located in abstract classes.
  • The biggest difference between abstract classes and ordinary classes is that abstract classes cannot be instantiated. You need to inherit abstract classes to instantiate their subclasses.

public abstract class AbstractExample {
    
    protected Integer x;
    private Integer y;

    public abstract void func1();

    public void func2() {
        System.out.println("func2");
    }
}

public class Example extends AbstractExample {
    @Override
    public void func1() {
        System.out.println("func1");
    }
}

  • An interface is an extension of an abstract class.
  • Before Java 8, it can be regarded as a completely abstract class, that is, it cannot have any method implementation.
  • Starting from Java 8, the interface can also have the default method implementation, because the maintenance cost of the interface that does not support the default method is too high.
  • Interface members (fields + methods) are public by default, and cannot be defined as private or protected.
  • The fields of the interface are static and final by default.

public interface MyInterfaceExample {
    void func1();

    default void func2(){
        System.out.println("func2");
    }

    int x = 123;
    // int y;               // Variable 'y' might not have been initialized
    public int z = 0;       // Modifier 'public' is redundant for interface fields
    // private int k = 0;   // Modifier 'private' not allowed here
    // protected int l = 0; // Modifier 'protected' not allowed here
    // private void fun3(); // Modifier 'private' not allowed here
}

public class MyInterfaceImplExample implements MyInterfaceExample {
    @Override
    public void func1() {
        System.out.println("func1");
    }
}

Comparison:

  • From the design level, the abstract class provides an IS-A relationship, so it must meet the internal substitution principle, that is, the subclass object must be able to replace all the parent objects. The interface is more like a LIKE-A relationship. It only provides a method to implement the contract, and does not require the interface and the class implementing the interface to have an IS-A relationship.
  • In terms of use, a class can implement multiple interfaces, but cannot inherit multiple abstract classes.
  • The fields of the interface can only be static and final, while the fields of the abstract class have no such restrictions.
  • Interface members can only be public, while members of abstract classes can have multiple access rights.

Interface used:

  • Irrelevant classes need to implement a method. For example, irrelevant classes can implement the compareTo() method in the comparable interface;
  • Multiple inheritance is required.

Use abstract classes:

  • You need to share code in several related classes.
  • You need to be able to control the access rights of inherited members, instead of all being public.
  • You need to inherit non static and non constant fields.

In many cases, the interface takes precedence over the abstract class, because the interface has no strict class hierarchy requirements of the abstract class, and can flexibly add behavior to a class. Starting from Java 8, the interface can also be implemented by default, which makes the cost of modifying the interface very low.

super

  • Accessing the constructor of the parent class: you can use the super() function to access the constructor of the parent class, so as to delegate the parent class to complete some initialization work. Note: super() must be placed in the first line of the subclass construction method.
  • Accessing members of the parent class: if a subclass overrides the implementation of a method in the parent class, you can use the super keyword to reference the implementation of the parent class's method.

public class SuperExample {
    protected int x;
    protected int y;

    public SuperExample(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public void func() {
        System.out.println("SuperExample.func()");
    }
}


public class SubExample extends SuperExample {
    private int z;

    public SubExample(int x, int y, int z) {
        super(x, y);
        this.z = z;
    }

    @Override
    public void func() {
        super.func();
        System.out.println("SubExample.func()");
    }
}

rewrite

Concept: it exists in the inheritance system. It means that the subclass implements a method exactly the same as the parent class in method declaration.

In order to meet the principle of Li formula substitution, rewriting has the following two limitations:

  1. The access permission of the subclass method must be greater than or equal to that of the parent method;
  2. The return type of a subclass method must be the return type of a parent method or its subtype.

Using the @ Override annotation, you can ask the compiler to help check whether the above two restrictions are met.

heavy load

Concept: existing in the same class means that a method has the same name as an existing method, but at least one parameter type, number and order is different.

Note: overloading has nothing to do with the return value. The reason is related to bytecode. Please refer to this article for details: Talk about byte code enhancement technology · language bird

Object

public final native Class<?> getClass()

public native int hashCode()

public boolean equals(Object obj)

protected native Object clone() throws CloneNotSupportedException

public String toString()

public final native void notify()

public final native void notifyAll()

public final native void wait(long timeout) throws InterruptedException

public final void wait(long timeout, int nanos) throws InterruptedException

public final void wait() throws InterruptedException

protected void finalize() throws Throwable {}

getClass()

It is a native method. When an object calls this method, it can get the class type of the object.

hashCode()

  • It is a native method.
  • hashCode() returns a hash value, and equals() is used to determine whether two objects are equivalent.
  • Two equivalent objects must have the same hash value, but two objects with the same hash value are not necessarily equivalent.
  • When overriding the equals() method, you should always override the hashCode() method to ensure that the hash values of the two equivalent objects are also equal.

equals()

equivalence relation

(1) Reflexivity

x.equals(x); // true

(2) Symmetry

x.equals(y) == y.equals(x); // true

(3) Transitivity

if (x.equals(y) && y.equals(z)){
    x.equals(z); // true;
}

(4) Consistency (multiple calls to the equals() method result in the same.)

x.equals(y) == x.equals(y); // true

(5) Compare with null (calling x.equals(null) on any object x that is not null will result in false.)

x.equals(null); // false;

equals() and==

  • For basic types, = = determines whether two values are equal. Basic types have no equals() method.
  • For reference types, = = determines whether two variables refer to the same object (that is, compare addresses), while equals() determines whether the referenced objects are equivalent (that is, compare values).

clone()

  • clone() is a protected modified method of the Object class, not public.
  • If the Object wants to call the clone() method, it also needs to implement the clonable interface. Note that the clone() method is not a method of the clonable interface, but a protected method of the Object. The clonable interface only stipulates that if a class does not implement the clonable interface and calls the clone() method, it will throw clonnotsupportedexception.

Shallow copy

The reference type of the copy object and the original object refer to the same object.

public class ShallowCloneExample implements Cloneable {
    private int[] arr;

    public ShallowCloneExample() {
        arr = new int[10];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = i;
        }
    }

    public void set(int index, int value) {
        arr[index] = value;
    }

    public int get(int index) {
        return arr[index];
    }

    @Override
    protected ShallowCloneExample clone() throws CloneNotSupportedException {
        return (ShallowCloneExample) super.clone();
    }
}
public class Main {
   public static void main(String[] args) {
      ShallowCloneExample e1 = new ShallowCloneExample();
      ShallowCloneExample e2 = null;
      try {
         e2 = e1.clone();
      } catch (CloneNotSupportedException e) {
         e.printStackTrace();
         return ;
      }
      e1.set(2, 222);
      System.out.println(e2.get(2)); // 222
   }
}

Deep copy

The reference types of the copied object and the original object refer to different objects.

public class DeepCloneExample implements Cloneable {
    private int[] arr;

    public DeepCloneExample() {
        arr = new int[10];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = i;
        }
    }

    public void set(int index, int value) {
        arr[index] = value;
    }

    public int get(int index) {
        return arr[index];
    }

    @Override
    protected DeepCloneExample clone() throws CloneNotSupportedException {
        DeepCloneExample result = (DeepCloneExample) super.clone();
        result.arr = new int[arr.length];
        for (int i = 0; i < arr.length; i++) {
            result.arr[i] = arr[i];
        }
        return result;
    }
}
public class Main{
   public static void main(String[] args) {
      DeepCloneExample e1 = new DeepCloneExample();
      DeepCloneExample e2 = null;
      try {
         e2 = e1.clone();
      } catch (CloneNotSupportedException e) {
         e.printStackTrace();
         return ;
      }
      e1.set(2, 222);
      System.out.println(e2.get(2)); // 2
   }
}

clone() alternative

Using the clone() method to copy an object is complex and risky. It will throw exceptions and require type conversion. As mentioned in the Effective Java book, it's better not to use clone(). You can use copy constructor or copy factory to copy an object.

public class CloneConstructorExample {
    private int[] arr;

    public CloneConstructorExample() {
        arr = new int[10];
        for (int i = 0; i < arr.length; i++) {
            arr[i] = i;
        }
    }

    public CloneConstructorExample(CloneConstructorExample original) {
        arr = new int[original.arr.length];
        for (int i = 0; i < original.arr.length; i++) {
            arr[i] = original.arr[i];
        }
    }

    public void set(int index, int value) {
        arr[index] = value;
    }

    public int get(int index) {
        return arr[index];
    }
}
  
public class Main{
   public static void main(String[] args) {
      CloneConstructorExample e1 = new CloneConstructorExample();
      CloneConstructorExample e2 = new CloneConstructorExample(e1);
      e1.set(2, 222);
      System.out.println(e2.get(2)); // 2
   }
}

toString()

Print out the information of the object.

notify() and notifyAll()

  • wait and notify are one of the ways for threads to communicate.
  • notify() wakes up a thread blocking the current object wait.
  • notifyAll() wakes up all threads blocked in the current object wait.

wait()

  • wait and notify are one of the ways for threads to communicate.
  • wait() stands for endless waiting.
  • wait(0) stands for endless waiting.
  • wait(x) stands for waiting for X, and the specific unit will be specified in the second parameter.

For more communication methods between threads, please refer to: Thread · language bird

finalize()

  • finalize() is similar to the destructor of C language. It will be called when the object is about to be recycled.
  • finalize() marks @ Deprecated(since = "9") in JDK9, which means that this method is outdated after JDK9 and is not recommended.
  • finalize() can only be called once at most in the life cycle of an object. It is generally used to prolong the life cycle of the object.
  • Finalize () process: when an object becomes unreachable to (GC Roots), GC will judge whether the object overrides the finalize () method. If not, it will be recycled directly. Otherwise, if the object has not executed the finalize () method, put it into the F-Queue queue, and a low priority thread will execute the finalize () method of the object in the queue. After executing the finalize() method, GC will judge whether the object is reachable again. If it is not reachable, it will be recycled. Otherwise, the object will be "resurrected".

For more garbage collection and finalize(), please refer to: JVM garbage collection mechanism · language bird

static keyword

Static variable

  • Instance variable: also known as class variable, that is, this variable belongs to a class. All instances of the class share static variables and can be accessed directly through the class name; Static variables exist only once in memory.
  • Static variable: every time an instance is created, an instance variable will be generated, which will live and die with the instance.

public class Demo {
    private int x;         // Instance variable
    private static int y;  // Static variable
}

Static method

  • Static methods exist when the class is loaded and do not depend on any instances. Therefore, a static method must be implemented, that is, it cannot be an abstract method.
  • You can only access the static fields and static methods of the class to which you belong. The method cannot have this and super keywords.

public abstract class Demo {
   private static int x;
   private int y;

   public static void func1() {
      int a = x;
      // int b = y;  // Non-static field 'y' cannot be referenced from a static context
      // int b = this.y;     // 'A.this' cannot be referenced from a static context
   }
   // public abstract static void func2();  // Illegal combination of modifiers: 'abstract' and 'static'
}

Static code block

  • Static statement blocks are run once during class initialization.

public class Demo {
   private int x;         // Instance variable
   private static int y;  // Static variable

   static {
      System.out.println("123,Execute only once.");
   }
}

Static inner class

  • Non static inner classes depend on instances of outer classes, while static inner classes do not.
  • Static internal classes cannot access non static variables and methods of external classes.

public class OuterClass {
    class InnerClass {
    }

    static class StaticInnerClass {
    }

    public static void main(String[] args) {
        // InnerClass innerClass = new InnerClass(); // 'OuterClass.this' cannot be referenced from a static context
        OuterClass outerClass = new OuterClass();
        InnerClass innerClass = outerClass.new InnerClass();
        StaticInnerClass staticInnerClass = new StaticInnerClass();
    }
}

Static guide bag

  • When using static variables and methods, there is no need to specify ClassName, which simplifies the code, but greatly reduces the readability.

Initialization sequence

  • Static variables and static statement blocks take precedence over instance variables and ordinary statement blocks. The initialization order of static variables and static statement blocks depends on their order in the code.

public static String staticField = "Static variable";

static {
    System.out.println("Static statement block");
}

public String field = "Instance variable";

{
    System.out.println("Common statement block");
}

public MyClassName(){
    System.out.println("Construction method");
}

In case of inheritance, the initialization order is:

  • Parent class (static variable, static statement block)
  • Subclasses (static variables, static statement blocks)
  • Parent class (instance variable, common statement block)
  • Parent class (constructor)
  • Subclasses (instance variables, common statement blocks)
  • Subclass (constructor)

For more class loading principles and processes, please refer to: JVM class loading mechanism · language bird

final keyword

final modifier class

final modifies the class, which means that the class is not allowed to be inherited, such as the String class we often use. The meaning of doing so is generally for design, and sometimes for safety.

final modifier attribute

final modifier class attribute, if yes:

  • Basic type, then keep the value unchanged.
  • Reference type, then ensure that the referenced object remains unchanged.

final modification method

  • The final modifier method represents a method that does not support overriding by subclasses.
  • If the method is a private modifier, it is actually an implicit declaration of the final modifier.
  • If the signature of the method defined in the subclass is the same as that of a private method in the base class, the method of the subclass does not override the method of the base class, but defines a new method in the subclass.

final modifier method variable

In many source codes, we can see the final keyword on the method parameters. Let's analyze it in detail below.

final keyword modifies method parameters, which means that modification is not allowed. There are two modified variables:

  • Basic type
  • reference type

final modifies method parameters, which are basic types:

Let's imagine that if the basic type is passed in and the final keyword is not used for modification, can the change be perceived at the call after the internal modification of the method? The answer is no, because in Java, it is value passing. If the final keyword modification is used, can the outside perceive it? The answer is No.

Summary: if final modifies a method parameter, and the type of the parameter is the basic type. This parameter cannot be perceived externally whether final modification is used or not, then using final modification just wants to ensure that the value of the parameter variable does not change internally. This is an idea and a design.

final modifies method parameters, which are reference types:

You may understand the reference type better. It is forbidden to change the reference, but is it true? Let's pass the above parameters again. If you still have questions, please refer to the following code.

Summary: if final modifies a method parameter and the type of the parameter is reference type, the parameter will not be re referenced externally no matter whether final modification is used or not. Then using final modification is just to ensure that parameter variables will not be re referenced internally. This is an idea and a design.

public class Demo {
    private Integer x;

    public Integer getX() {
        return x;
    }

    public void setX(Integer x) {
        this.x = x;
    }

    public static void main(String[] args) {
        Demo d = new Demo();
        d.setX(1);
        fun1(d);// The internal of the method is not decorated with final. It is re referenced, and the external value is still 1
        fun2(d);// No final modification is used inside the method. The attribute of the object is changed, and the external value is also changed. The value is 2
        fun3(d);// Method is internally decorated with final, and re referencing will compile and report an error
        fun4(d);// The final modifier is used inside the method to change the attribute of the object, and the external value is also changed. The value is 2
        System.out.println(d.getX());
    }

    private static void fun1(Demo d) {
        d = new Demo();
        d.setX(2);
    }

    private static void fun2(Demo d) {
        d.setX(2);
    }

    private static void fun3(final Demo d) {
//        d = new Demo();//  Compilation reports an error. The method parameters modified by final are not allowed to be re referenced
        d.setX(2);
    }

    private static void fun4(final Demo d) {
        d.setX(2);
    }
}

synchronized keyword

  • synchronized is a keyword in Java. It is a lock.
  • JDK1. Before 6, many people would call it heavyweight lock. JDK1. After 6, synchronized has carried out various optimizations, including biased locks and lightweight locks introduced in Java SE 1.6 to reduce the performance consumption caused by obtaining and releasing locks, as well as the storage structure and upgrade process of locks.

synchronized modified static method

Lock the current class object. Before entering the synchronization code, you need to obtain the lock of the current class object.

synchronized modifier instance method

It is used to lock the current instance. Before entering the synchronization code, you need to obtain the lock of the current instance.

synchronized modifier block

Specify the lock object, lock the given object, and obtain the lock of the given object before entering the synchronization code base.

What is the principle of synchronized locks? Why can any object become a lock? Where is the lock?

For a deeper understanding of Synchronized, please refer to: In depth understanding of synchronized · YuQue

reflex

Please refer to: Reflection explanation · language bird

abnormal

Please refer to: Detailed explanation of anomalies - YuQue

generic paradigm

The feature of Java generics has been added since JDK 1.5. Therefore, in order to be compatible with previous versions, the implementation of Java generics adopts the strategy of "pseudo generics", that is, Java supports generics in syntax, but the so-called "type erase" will be carried out in the compilation stage, Replace all generic representations (in angle brackets) with specific types (their corresponding primitive types), as if there were no generics at all.

For details, please refer to: Generic explanation · language bird

annotation

Please refer to: Notes · YuQue

Meta annotation

Please refer to: Detailed explanation of Yuan annotation · YuQue

reference material

For more systematic learning, please go to:

https://www.yuque.com/javahome

Topics: Java JavaEE Back-end OOP