Different basic details of Java

Reading needs a certain foundation, which is not suitable for people who first know Java. There are some concise languages, some knowledge points or a brush, and you don't know that you need to consult the materials in detail. The purpose of this article is to supplement some details of Java foundation, some slightly in-depth places and some places that are easy to ignore. I believe you will have a different feeling from reading stereotyped articles in the past. More knowledge will be added later.

Method overloading, overriding, and private

  • Static methods can be inherited, but cannot be overridden and bound to classes. Private methods can be inherited, but they cannot be accessed because of permission problems, so they cannot be overridden. (however, you can expand the access rights of other subclasses and "rewrite", but it is not actually rewritten.)
    Essential reason: non virtual methods (invokestatic or invokespecial) will convert the symbolic references in the constant pool into direct references in the parsing stage. Virtual methods will replace the symbolic references by dynamically linking to real classes and methods during the runtime.

  • Rewriting essence:
    1. Find the actual type of the object executed by the first element at the top of the operand stack and record it as C.

    2. If a method matching the description and simple name in the constant is found in type C, the access permission is verified. If it passes, the direct reference of this method is returned, and the search process ends; If it does not pass, it returns Java Lang. illegalaccesserror exception.

    3. Otherwise, search and verify each parent class of C from bottom to top according to the inheritance relationship in step 2.

    4. If the appropriate method is never found, throw Java lang . Abstractmethoderror exception.

  • Virtual method table:

    Dynamic dispatch is frequently used in object-oriented programming. If you have to search the appropriate target in the method metadata of the class again in the process of each dynamic dispatch, it may affect the execution efficiency. Therefore, in order to improve performance, the JVM establishes a virtual method table in the method area of the class (non virtual methods will not appear in the table). Use an index table instead of a lookup.

    There is a virtual method table in each class, which stores the actual entry of each method.

Differences between i + + and + + i at the JVM level

int i = 8; i = i++;

bipush 8 is to push 8 into the operand stack.
istore_ i = the number of local variables stored in the stack from i to 8, that is, the number of local variables stored in the stack from 1 to 8;
iload_1 is to press the i at the first position in the variable table to the top of the operand stack. At this time, the top of the stack is 8
iinc 1 by 1 is i plus 1 at the first position in the variable table, then i=9 in the variable table
istore_1. Save 8 at the top of the stack back to i in the variable table, then i=8; Because i is assigned in the java code.

int i= 8; i = ++i;

0 bipush 8 pushes 8 into the operand stack
2 istore_1. When the operand stack is out of the stack, the i stored in the first position of the local variable table is i = 8 in the code;
3 iinc 1 by 1: add 1 to i at the first position in the variable table, then i=9 in the variable table
6 iload_1. The i at the first position in the variable table is pressed to the top of the operand stack. At this time, the top of the stack is 9
7 istore_1 saves 9 at the top of the stack back to i in the variable table, then i=9;

The embodiment of generic type in inheritance:

Although class A is the parent of class B, G "a" and G "B" do not have child parent relationship, and they are juxtaposed.

Supplement: Class A is the parent of class B, and a "G" is the parent of B "G"

< > csdn is a special character, so it is replaced by Chinese.

Array support covariance:

For example: Fruit[] fruit = new Apple[10];

However, you can still add both apple class and orange class, which will report an error when running

Generic containers can only be used by introducing wildcards? Perform covariance and inversion:

List<? extends Fruit> flist = new ArrayList<Apple>();

But it can't be added. It can only be read

Compiler doesn't know list <? What is the specific type held by extensions fruit >, so once you perform this type of upward transformation, you will lose the ability to pass any object to it. But you must know that it is the type of fruit, which can be used to receive.

List<? super Fruit> fruits = new ArrayList<Object>();

Can only be added, not read

The compiler will not report an error because fruits accepts the base class type of Fruit, which can reference its subtype (polymorphism)

Because fruits defines that the lower bound is fruit type, the compiler does not know exactly what the type is, and cannot find a suitable type to accept the return value, but Object can certainly.

We call those objects that can only be read from the Producer, and we can read them safely from the Producer; Objects that can only be written to are called consumers.

Hash algorithm:

Requirements for an excellent hash algorithm:

  • The original data cannot be deduced from the hash value. The mapped data has no corresponding relationship with the original data
  • A small change in the input data will get a completely different hash value, and the same data will get the same value
  • The execution efficiency of hash algorithm should be efficient, and the hash value can be calculated quickly for long text
  • The collision probability of hash algorithm is small

hash can be realized in the following ways:

Direct addressing method (linear function of keywords), digital analysis method (for example, the last few digits of month, year and day change greatly, and the latter digits are used to form hash address), square middle method (the middle few digits after the square of keywords), folding method (dividing keywords into parts with the same digits and adding them), random number method (using a random function to take the random value of key value), Division and remainder method.

How to resolve hash conflicts:

Open address (plus increment), then hash, chain address method, and establish a public overflow area.

HashMap:

  • hash=(h = key.hashCode()) ^(h >>> 16)

Reason: this can be applied to each digit to make it more evenly dispersed.

  • tab[i = (n-1)&hash]

Compared with% is faster, which is also the reason why the capacity is 2 to the nth power (this formula can be applied only with the nth power of 2).

  • Understanding the concurrency problem of HashMap indirectly proves that you have seen the source code:

1, It goes without saying that when put at the same time, the data of one thread will be overwritten.

2, When two threads expand their capacity at the same time, in the resize method

if ((e = oldTab[j]) != null) 
                oldTab[j] = null

He will set the original node to be empty (convenient for gc), so only one thread can get the data, and other threads will lose it.

  • resize optimization

After the capacity expansion in 1.7, it will still be recalculated and key% the new length value. (there will be a lot of double counting)

Compared with oldCap, newCap in 1.8 only moves 1 left by one bit. n-1 same principle

If the new bit is 0, it will still be the original position. If it is 1, it will be + oldCap.

 do {
                        next = e.next;
     					//Important: if = = 0, it means that the new bit is 0
                        if ((e.hash & oldCap) == 0) {
                            if (loTail == null)
                                loHead = e;
                            else
                                loTail.next = e;
                            loTail = e;
                        }
                        else {//Otherwise, it is 1
                            if (hiTail == null)
                                hiHead = e;
                            else
                                hiTail.next = e;
                            hiTail = e;
                        }
                    } while ((e = next) != null);
                    if (loTail != null) {
                        loTail.next = null;
                        //Pay attention here
                        newTab[j] = loHead;
                    }
                    if (hiTail != null) {
                        hiTail.next = null;
                        //Pay attention here
                        newTab[j + oldCap] = hiHead;
                    }

Another optimization point is that compared with 1.7, 1.8 is changed from ab initio interpolation to tail interpolation, and there will be no concurrent ring formation. Head insertion method (the original linked list will be inverted during capacity expansion. If two threads invert it at the same time, it will become a ring.)

  • Comparison of HashMap with red black tree and AVL. (log N)

The red black tree does not pursue complete balance, but only requires partial balance, which reduces the requirements of rotation, so as to improve the performance. For inserting and deleting nodes, the balance can be restored by rotating up to 3 times. The complexity is O(1), and its performance is higher than that of AVL

In practical application, if the number of searches is much greater than that of insertion and deletion, choose AVL. If it is similar, choose red black tree

Characteristics of red black tree: 1. The nodes are either black or red. 2. The root node is black. 3. Leaf nodes are black. 4. The left and right children of each red node are black.

  • ArrayList

When initializing the assignment, it is an empty array. When adding elements to the array, it will be expanded to 10

During capacity expansion

newCapacity = oldCapacity + (oldCapacity >> 1) The even number is the original 1.5 Times, odd 1.5 About times
elementData = Arrays.copyOf(elementData, newCapacity);
  • The Value of HashSet is an Object of static (the same as HashMap)

Composition and inheritance

  • Inheritance (Is-a)

Advantages: it is easy to form a new implementation; Reduce code redundancy; It is easy to modify the reused implementation.

Disadvantages:

The internal details of the parent class are visible to the child class, which destroys the encapsulation.

The method inherited from the parent class by the child class is determined at the compilation time. The method inherited from the parent class cannot be changed during operation.

If the method of the parent class is modified, the method of the child class must also be modified accordingly. Subclasses and superclasses are highly coupled.

  • Combination (has-a)

advantage:

The current object can only call its method through the contained object, so the internal details of the contained object are not visible to the current object.

There is a low coupling relationship between the current object and the contained object. If you modify the code in the class containing the object, you do not need to modify the code of the current object.

The current object can dynamically bind the contained objects at runtime. You can assign values to the objects it contains through the set method

Disadvantages:

It is easy to produce too many objects.

In order to combine multiple objects, the interface must be carefully defined.

  • Selection of combination and inheritance

Composition is more flexible and stable than inheritance. Preferred combination

Select inheritance:

A special class (String, Object, wrapper class) is more than a role of the parent class.

Instances of subclasses do not need to become objects of another class. (dynamic binding is not required)

A subclass is an extension of a parent class

Unpacking and packing

It will be unpacked in case of operator and boxed in case of equals.

The packing is converted to the corresponding packing class according to whether the value is int or long.

-128 to 127 have cache.

if(i >= IntegerCache.low && i <= IntegerCache.high)
	return IntegerCache.cache[i+(-IntegerCache.low)];
return new Integer(i);

The reason why String class is final modification

1. To implement string constant pool. (in the design, string reuse brings higher efficiency and saves more space)

2. For thread safety.

3. To implement String, you can create the immutability of HashCode. After the HashCode is cached, it does not need to be changed.

  • Remaining issues
String str1 = "abc";  // In constant pool

String str2 = new String("abc"); // On the pile

When assigning directly, the string "abc" will be stored in the constant pool with only one copy. At this time, the assignment operation is equal to creating 0 or 1 objects. If "abc" already exists in the constant pool, the object will not be created, and the reference will be directly assigned to str1; If there is no "abc" in the constant pool, create an object and assign the reference to str1.

new String("abc");

When the JVM encounters the above code, it will first retrieve whether there is "abc" in the constant pool. If there is no "abc" String, it will first create a String in the constant pool. Then execute the new operation. A String object storing "abc" will be created in the heap memory, and the reference of the object will be assigned to str2. This procedure creates 2 objects.

If the corresponding String already exists when retrieving the constant pool, only one new String object will be created in the heap, and only one object will be created in this process.

  • The intern method adds the string to the constant pool. (some strings are not created in the string constant pool)

reflex

  • Dynamic agent

It refers to the method that the client calls other objects through the proxy class, and it is the proxy object that dynamically creates the target class when the program runs.

interface Human{
    String getBelief();
    void eat(String food);

}
//Proxy class
class SuperMan implements Human{
    @Override
    public String getBelief() {
        return "I believe I can fly!";
    }
    @Override
    public void eat(String food) {
        System.out.println("I love eating" + food);
    }
}
class HumanUtil{
    public void method1(){
        System.out.println("====================General method I====================");
    }
    public void method2(){
        System.out.println("====================General method II====================");
    }
}
class ProxyFactory{
    //Call this method to return an object of a proxy class. Solve problem 1
    public static Object getProxyInstance(Object obj){//obj: object of the proxied class
        MyInvocationHandler handler = new MyInvocationHandler();
		handler.bind(obj);
		return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler);
    }
}
class MyInvocationHandler implements InvocationHandler{
    private Object obj;//You need to use the object of the proxy class for assignment
    public void bind(Object obj){
        this.obj = obj;
    }
    //When we call method a through the object of proxy class, we will automatically call the following method: invoke()
    //The function of method a to be executed by the proxy class is declared in invoke()
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        HumanUtil util = new HumanUtil();
        util.method1();
        //Method: that is, the method called for the proxy class object, which is also the method to be called by the proxy class object
        //obj: object of the proxied class
        Object returnValue = method.invoke(obj,args);
        util.method2();
        //The return value of the above method is the return value of invoke() in the current class.
        return returnValue;
    }
}

public class ProxyTest {
    public static void main(String[] args) {
        SuperMan superMan = new SuperMan();
        //proxyInstance: object of proxy class
        Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
        //When a method is called through a proxy class object, it will automatically call the method with the same name in the proxy class
        String belief = proxyInstance.getBelief();
        System.out.println(belief);
        proxyInstance.eat("Sichuan Mala");
        System.out.println("*****************************");
        NikeClothFactory nikeClothFactory = new NikeClothFactory();
        ClothFactory proxyClothFactory = (ClothFactory) ProxyFactory.getProxyInstance(nikeClothFactory);
        proxyClothFactory.produceCloth();

    }
}

  //Method 1
  Class clazz = Class.forName("com.java.Person");
  //Method 2
  ClassLoader classLoader = ReflectionTest.class.getClassLoader();
  Class clazz = classLoader.loadClass("com.java.Person");

forName interprets the class, initializes the class, initializes the static variables, and executes the contents of the static code block, while loadClass only initializes the The class file is loaded into the JVM and will be initialized only the first time newInstance is created.

Singleton mode

  • Lazy style
class Bank{

    private Bank(){}

    private volatile static Bank instance = null;

    public static Bank getInstance(){
        //Mode 1: slightly less efficient
//        synchronized (Bank.class) {
//            if(instance == null){
//
//                instance = new Bank();
//            }
//            return instance;
//        }
        //Mode 2: higher efficiency
        if(instance == null){

            synchronized (Bank.class) {
                if(instance == null){

                    instance = new Bank();
                }

            }
        }
        return instance;
    }

}

Role of using volatile (Prohibition of instruction rearrangement)

instance = new Bank();

This code is executed in three steps:

1. Allocate memory space for instance. 2. Initialize instance. 3. Point instance to the allocated memory address.

However, due to instruction rearrangement (compiler optimized reordering, without changing the semantics of a single thread), it will become 1 - > 3 - > 2, which makes it possible to return an uninitialized instance object in the case of multithreading.

mixed

  • The purpose of exception handling is to continue running when the program has an error without crashing

  • Identifier: 26 English letters in upper and lower case, 0-9, $or_ Composition, number cannot start.

  • Second mechanism: 0b octal: 0 hex: 0x

  • Method overloading is only related to the number and type of parameters

  • Process oriented: it emphasizes the functional behavior, taking the function as the minimum unit and considering how to do it.
    Object oriented: emphasize the objects with functions, take the class / object as the minimum unit, and consider who will do it.

  • When to call finalize method is uncertain. The Java language specification does not guarantee that the finalize methods will be executed in time, and there is no guarantee that they will be executed at all.

  • Abstract class is a template design, and interface is a radial design. (whoever needs this function will implement this interface)

  • java bytecode is a pseudo machine code, which is naturally higher than analytical language. JVM is not an analytical language, but a semi compiled and semi analytical language. Analytical language has no compilation process and directly parses the source code text, which is equivalent to a compilation during execution. Although java bytecode cannot fully correspond to the local machine code, it can be simply mapped to the local machine code, There is no need to do complex syntax analysis and other compilation processing. Of course, it is faster than pure parsing language.
    Interpretive language is compiled into machine language at runtime, which is naturally inefficient. java is more efficient from bytecode to machine language. In addition, the jvm will cache hot code.

Keywords: Java Back-end

Added by B34ST on Thu, 17 Feb 2022 06:13:07 +0200