[Java] basic knowledge for a java programmer

Article catalog

Java Basics

data type

Basic types

  • byte/8
  • char/16
  • short/16
  • int/32
  • float/32
  • long/64
  • double/64
  • boolean/~

Boolean only has two values: true and false. It can be stored in 1 bit, but the specific size is not specified. The JVM converts the boolean type data to int at compile time, using 1 for true and 0 for false. The JVM supports Boolean arrays, but it is implemented by reading and writing byte arrays.

Packing type

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

Integer x = 2;     // Boxing called Integer.valueOf(2)
int y = x;         // X.intValue() called for unpacking

Cache pool

new Integer(123) and Integer.valueOf(123) differs in that:

  • new Integer(123) creates a new object every time;
  • Integer.valueOf(123) objects in the cache pool will be used, and multiple calls will get references to the same object.
Integer x = new Integer(123);
Integer y = new Integer(123);
System.out.println(x == y);    // false
Integer z = Integer.valueOf(123);
Integer k = Integer.valueOf(123);
System.out.println(z == k);   // true

The valueOf() method is relatively simple to implement, which is to determine whether the value is in the cache pool first, and if so, directly return the contents of the cache pool.

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

When using the wrapper type corresponding to these basic types, if the value range is within the buffer pool range, you can directly use the objects in the buffer pool.

String (string)

overview

String is declared final, so it cannot be inherited. (wrapper classes such as Integer cannot be inherited)

In Java 8, char array is used inside String to store data.

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

After Java 9, the implementation of String class uses byte array to store String, and uses coder to identify which encoding is used.

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

    /** The identifier of the encoding used to encode the bytes in {@code value}. */
    private final byte coder;
}

Immutability of String:

The value array is declared final, which means that the value array cannot reference other arrays after initialization. And there is no method to change the value array inside the String, so you can ensure that the String is immutable.

Immutable benefits:

  1. Can cache hash values

Because the hash value of String is often used, for example, String is used as the key of HashMap. The immutable feature can make the hash value immutable, so only one calculation is needed.

  1. Need for String Pool

If a String object has been created, a reference is obtained from the String Pool. Only when String is immutable can String Pool be used.

  1. Security

String is often used as a parameter. The immutability of string can ensure that the parameter is immutable. For example, if the string is variable as a network connection parameter, the string is changed during the network connection. The party who changes the string thinks that it is connected to other hosts, but the actual situation is not necessarily.

  1. Thread safety

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

String, StringBuffer and StringBuilder

  1. variability
  • String is immutable
  • StringBuffer and StringBuilder are variable
  1. Thread safety
  • String is immutable, so it is thread safe
  • StringBuffer is thread safe and internally synchronized
  • StringBuilder is not thread safe

String Pool

String Pool holds all literal strings, which are determined at compile time. Not only that, you can also use string's inter() method to add strings to String Pool during the run.

When a string calls the inter() method, if there is already a string in the String Pool that is equal to the string value (determined by using the equals() method), then a reference to the string in the String Pool will be returned; otherwise, a new string will be added to the String Pool and a reference to the new string will be returned.

In the following example, s1 and s2 create two different strings in the way of new String(), while s3 and s4 obtain the same string reference through s1. Inter(). intern() first places the string referred to in s1 into String Pool, and then returns the string reference. So s3 and s4 refer to the same string.

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

If you create a string in the literal form of "bbb", the string will be automatically placed in the String Pool.

String s5 = "bbb";
String s6 = "bbb";
System.out.println(s5 == s6);  // true

Prior to Java 7, String Pool was placed in the runtime constant pool, which was a permanent generation. In Java 7, String Pool is moved to the heap. This is because the space of permanent generation is limited, which will lead to OutOfMemoryError error in the case of a large number of strings.

new String("abc")

In this way, a total of two string objects will be created (provided there is no "abc" string object in the String Pool).

  • "abc" belongs to string literal, so a string object will be created in String Pool during compilation to point to this "abc" string literal;
  • Using new creates a string object in the heap.

The following is the source code of the String constructor. You can see that when one String object is used as the constructor parameter of another String object, the contents of the value array will not be completely copied, but will all point to the same value array.

public String(String original) {
    this.value = original.value;
    this.hash = original.hash;
}

operation

Parameter passing

Java's parameters are passed in as values, not as references.

In the following code, the dog of Dog dog is a pointer, which stores the address of the object. When a parameter is passed into a method, it essentially passes the address of the object to the parameter as a value.

public class Dog {

    String name;

    Dog(String name) {
        this.name = name;
    }

    String getName() {
        return this.name;
    }

    void setName(String name) {
        this.name = name;
    }

    String getObjectAddress() {
        return super.toString();
    }
}

Changing the field value of an object in a method changes the field value of the original object because it references the same object.

class PassByValueExample {
    public static void main(String[] args) {
        Dog dog = new Dog("A");
        func(dog);
        System.out.println(dog.getName());          // B
    }

    private static void func(Dog dog) {
        dog.setName("B");
    }
}

But when the pointer refers to other objects in the method, the two pointers in the method and out of the method point to different objects. When one pointer changes the content of the object it points to, it has no effect on the object pointed to by the other pointer.

public class PassByValueExample {
    public static void main(String[] args) {
        Dog dog = new Dog("A");
        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) {
        System.out.println(dog.getObjectAddress()); // Dog@4554617c
        dog = new Dog("B");
        System.out.println(dog.getObjectAddress()); // Dog@74a14482
        System.out.println(dog.getName());          // B
    }
}

keyword

final

1. Decorate data

The declared data is a constant. It can be a compile time constant or a constant that cannot be changed after the runtime is initialized.

  • For the basic type, final keeps the value unchanged;
  • For the reference type, final makes the reference unchanged and cannot reference other objects, but the referenced object itself can be modified.
final int x = 1;
// x = 2;  // cannot assign value to final variable 'x'

2. Modification method

A declaration method cannot be overridden by a subclass.

The private method is implicitly specified as final. If the signature of the method defined in the subclass is the same as that of a private method in the base class, then the method of the subclass does not override the method of the base class, but defines a new method in the subclass.

3. Decoration

The declaration class is not allowed to be inherited.

static

1. Static variable

  • Static variable: also known as class variable, that is to say, this variable belongs to class. All instances of class share static variable, which can be accessed directly through class name. Static variables exist only one copy in memory.
  • Instance variable: each instance created will generate an instance variable, which will live and die with the instance.
public class A {

    private int x;         // Instance variable
    private static int y;  // Static variable

    public static void main(String[] args) {
        // int x = A.x;  // Non-static field 'x' cannot be referenced from a static context
        A a = new A();
        int x = a.x;
        int y = A.y;
    }
}

2. Static method

Static methods exist when the class is loaded and do not depend on any instance. So a static method must have an implementation, that is, it cannot be an abstract method.

public abstract class A {
    public static void func1(){
    }
    // public abstract static void func2();  
    // Illegal combination of modifiers: 'abstract' and 'static'
}

You can only access the static fields and static methods of your class. You cannot have this and super keywords in the methods. Therefore, these two keywords are associated with specific objects.

public class A {

    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
    }
}

3. Static statement block

A static statement block runs once during class initialization.

public class A {
    static {
        System.out.println("123");
    }

    public static void main(String[] args) {
        A a1 = new A();
        A a2 = new A();
    }
}
123

4. Static internal class

The non static inner class depends on the instance of the outer class, that is to say, you need to create the outer class instance before you can use this instance to create the non static inner class. Static inner classes are not required.

public class OuterClass {
	//Non static inner class
    class InnerClass {
    }
	//Static inner class
    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 inner classes cannot access non static variables and methods of outer classes.

5. Static guide package

When using static variables and methods, ClassName is no longer specified, which simplifies the code, but greatly reduces the readability.

import static com.xxx.ClassName.*

6. 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");
}

The last is the initialization of the constructor.

public InitialOrderTest() {
    System.out.println("Constructor");
}

In the case of inheritance, the order of initialization is:

  1. Parent class (static variable, static statement block)
  2. Subclass (static variable, static statement block)
  3. Parent class (instance variable, common statement block)
  4. Parent class (constructor)
  5. Subclass (instance variable, common statement block)
  6. Subclass (constructor)

Methods in Object

overview

All methods in Object:

public native int hashCode()

public boolean equals(Object obj)

protected native Object clone() throws CloneNotSupportedException

public String toString()

public final native Class<?> getClass()

protected void finalize() throws Throwable {}

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

equals()

1. Equivalence relationship

Two objects have equivalence relations, which need to meet the following five conditions:

Ⅰ Reflexivity

x.equals(x); // true

Ⅱ symmetry

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

III. transitivity

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

IV consistency

Multiple calls to the equals() method leave the result unchanged

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

V comparison with null

Call x.equals(null) on any non null object x and the result is false

x.equals(null); // false

2. Equivalence and equality

  • For the basic type, = = determines whether the two values are equal. There is no equals() method for the basic type. b
  • For reference types, = = determines whether two variables reference the same object, while equals() determines whether the referenced object is equivalent.
Integer x = new Integer(1);
Integer y = new Integer(1);
System.out.println(x.equals(y)); // true
System.out.println(x == y);      // false

3. Implementation steps

  1. Check whether it is a reference of the same object. If it is a direct return of true;
  2. Check whether it is the same type, if not, return false directly;
  3. Transform the Object object;
  4. Determines whether the specified key fields are equal.
public class EqualExample {

    private int x;
    private int y;
    private int z;

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        EqualExample that = (EqualExample) o;

        if (x != that.x) return false;
        if (y != that.y) return false;
        return z == that.z;
    }
}

hashCode()

hashCode() returns a hash value, while equals() is used to determine whether two objects are equivalent. The hash value of two equivalent objects must be the same, but the two objects with the same hash value are not necessarily equivalent. This is because the calculation of hash value is random, and the two objects with different values may calculate the same hash value.

When overriding the equals() method, the hashCode() method should always be overridden to ensure that the hash values of the two equivalent objects are equal.

Collection classes such as HashSet and HashMap use the hashCode() method to calculate the location where an object should be stored. Therefore, to add an object to these collection classes, you need to let the corresponding class implement the hashCode() method.

In the following code, two equivalent objects are created and added to the HashSet. We want to treat these two objects as the same, and only add one object to the collection. However, EqualExample does not implement the hashCode() method, so the hash values of the two objects are different, resulting in the addition of two equivalent objects to the collection.

EqualExample e1 = new EqualExample(1, 1, 1);
EqualExample e2 = new EqualExample(1, 1, 1);
System.out.println(e1.equals(e2)); // true
HashSet<EqualExample> set = new HashSet<>();
set.add(e1);
set.add(e2);
System.out.println(set.size());   // 2

The ideal hash function should be uniform, that is, unequal objects should be evenly distributed to all possible hash values. This requires the hash function to take into account the values of all fields. You can treat each field as a bit of R-base, and then form an R-base integer.

R generally takes 31, because it is an odd prime number. If it is an even number, when there is a multiplication overflow, the information will be lost, because multiplying with 2 is equivalent to moving one bit to the left, and the leftmost bit is lost. And the multiplication of a number and 31 can be converted into shift and subtraction: 31 * x = = (x < < 5) - X. the compiler will automatically perform this optimization.

@Override
public int hashCode() {
    int result = 17;
    result = 31 * result + x;
    result = 31 * result + y;
    result = 31 * result + z;
    return result;
}

toString()

Default return ToStringExample@4554617c In this form, the value after @ is the unsigned hexadecimal representation of the hash code.

public class ToStringExample {

    private int number;

    public ToStringExample(int number) {
        this.number = number;
    }
}

ToStringExample example = new ToStringExample(123);

System.out.println(example.toString());	//ToStringExample@4554617c

clone()

1. cloneable

clone() is the protected method of Object. It is not public. If a class does not explicitly override clone(), other classes cannot directly call the clone() method of the class instance.

public class CloneExample {
    private int a;
    private int b;
}

CloneExample e1 = new CloneExample();
CloneExample e2 = e1.clone(); 	// 'clone()' has protected access in 'java.lang.Object'

Overriding clone() yields the following implementation:

public class CloneExample {
    private int a;
    private int b;

    @Override
    public CloneExample clone() throws CloneNotSupportedException {
        return (CloneExample)super.clone();
    }
}

CloneExample e1 = new CloneExample();

try {
    CloneExample e2 = e1.clone();
} catch (CloneNotSupportedException e) {
    e.printStackTrace();
}

//java.lang.CloneNotSupportedException: CloneExample

CloneNotSupportedException was thrown above because CloneExample does not implement the clonable interface.

It should be noted that the clone () method is not a method of the clonable interface, but a protected method of the Object. The clonable interface only specifies that if a class does not implement the clonable interface and calls the clone() method, it will throw a CloneNotSupportedException exception.

public class CloneExample implements Cloneable {
    private int a;
    private int b;

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

2. Light 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();
    }
}

ShallowCloneExample e1 = new ShallowCloneExample();
ShallowCloneExample e2 = null;

try {
    e2 = e1.clone();
} catch (CloneNotSupportedException e) {
    e.printStackTrace();
}
e1.set(2, 222);
System.out.println(e2.get(2)); // 222

3. Deep copy

The reference types of the copy 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;
    }
}

DeepCloneExample e1 = new DeepCloneExample();
DeepCloneExample e2 = null;

try {
    e2 = e1.clone();
} catch (CloneNotSupportedException e) {
    e.printStackTrace();
}
e1.set(2, 222);
System.out.println(e2.get(2)); // 2

4. Alternative to clone()

Using the clone() method to copy an object is complex and risky, it throws an exception and requires a type conversion. According to the Effective Java book, it's better not to use clone(), you can use copy constructor or copy factory to copy an object.

//copy constructor 
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];
    }
}

CloneConstructorExample e1 = new CloneConstructorExample();
CloneConstructorExample e2 = new CloneConstructorExample(e1);

e1.set(2, 222);
System.out.println(e2.get(2)); // 2

Three characteristics

encapsulation

Abstract data type is used to encapsulate data and data-based operations, so as to form an indivisible independent entity. The data is protected inside the abstract data type, and the internal details are hidden as much as possible. Only some external interfaces are reserved to make it contact with the external. Users do not need to care about the details inside the object, but they can access the object through the external interface provided by the object.

advantage:

  • Reduce coupling: develop, test, optimize, use, understand, and modify independently
  • Reduce the burden of maintenance: it can be easier to understand, and it can not affect other modules when debugging
  • Effective performance tuning: you can analyze to determine which modules affect the performance of the system
  • Improve software reusability
  • Reduces the risk of building large systems: even though the entire system is not available, these independent modules may be available

inherit

Inheritance implements IS-A relationship. For example, Cat and Animal are a kind of IS-A relationship. Therefore, Cat can inherit from Animal to obtain the non private attributes and methods of Animal.

Inheritance should follow the principle of leech substitution. Subclass objects must be able to replace all the parent objects.

polymorphic

Polymorphism can be divided into compile time polymorphism and run time polymorphism

  • Compile time polymorphism mainly refers to overloading of methods
  • Runtime polymorphism refers to the specific type of object references defined in a program that are determined during runtime

There are three conditions for runtime polymorphism:

  • inherit
  • Override (override)
  • Upward transformation

inherit

Access rights

There are three access modifiers in Java: private, protected, and public. Without the access modifier, the package level is visible.

You can add access modifiers to a class or its members (fields and methods).

  • Class visible indicates that other classes can use this class to create instance objects.
  • Member visible means that other classes can access the member with the instance object of this class;

protected is used to decorate members, indicating that members are visible to subclasses in the inheritance system, but this access modifier has no meaning to classes.

A well-designed module hides all implementation details and clearly isolates its API from its implementation. Modules communicate with each other only through their APIs, and one module does not need to know the internal work of other modules. This concept is called information hiding or encapsulation. Therefore, the access rights should make every class or member inaccessible to the outside world as much as possible.

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 the access level of the parent class. This is to ensure that where the parent instance can be used, the child instance can be used instead, that is, to ensure that the Richter replacement principle is met.

The field must not be public, because if you do this, you will lose control of the field modification behavior, and the client can modify it at will. For example, in the following example, AccessExample has the id public field. If we want to use int to store the id field at a certain time, we need to modify all the client code.

public class AccessExample {
    public String id;
}

You can use public getter and setter methods to replace public fields, so that you can control the modification behavior of the fields.

public class AccessExample {

    private int id;

    public String getId() {
        return id + "";
    }

    public void setId(String id) {
        this.id = Integer.valueOf(id);
    }
}

However, there are exceptions. If it is a package level private class or a private nested class, the direct exposure of members will not have a significant impact.

public class AccessWithInnerClassExample {
	
    //Private nested class
    private class InnerClass {
        int x;
    }

    private InnerClass innerClass;

    public AccessWithInnerClassExample() {
        innerClass = new InnerClass();
    }

    public int getValue() {
        return innerClass.x;  // Direct access
    }
}

Abstract classes and interfaces

1. Abstract class

Abstract classes and methods are declared using the abstract keyword. If a class contains abstract methods, the class must be declared as an abstract class.

The biggest difference between an abstract class and a normal class is that an abstract class cannot be instantiated but can only be inherited.

public abstract class AbstractClassExample {

    protected int x;
    private int y;

    public abstract void func1();	//Abstract method

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

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

AbstractClassExample ac1 = new AbstractClassExample(); 
// 'AbstractClassExample' is abstract; cannot be instantiated
AbstractClassExample ac2 = new AbstractExtendClassExample();
ac2.func1();

2. Interface

Interface is an extension of abstract class. Before Java 8, it can be regarded as a completely abstract class, that is to say, it cannot have any method implementation.

Since Java 8, interfaces can also have default method implementations, because the maintenance cost of interfaces that do not support default methods is too high. Before Java 8, if an interface wanted to add new methods, it was necessary to modify all classes that implemented the interface so that they all implemented the new methods.

  • The members (fields + methods) of the interface are all public by default, and are not allowed to be defined as private or protected
  • The fields of the interface are static and final by default
public interface InterfaceExample {

    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 InterfaceImplementExample implements InterfaceExample {
    @Override
    public void func1() {
        System.out.println("func1");
    }
}

// InterfaceExample ie1 = new InterfaceExample(); 
// 'InterfaceExample' is abstract; cannot be instantiated
InterfaceExample ie2 = new InterfaceImplementExample();
ie2.func1();
System.out.println(InterfaceExample.x);

3. Comparison

  • From the design level, abstract classes provide an IS-A relationship, which needs to meet the Li type replacement 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 IS-A relationship between the interface and the class implementing the interface.
  • From the perspective 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 limitation.
  • Members of an interface can only be public, while members of an abstract class can have multiple access rights.

4. Use selection

Use interface:

  • You need to make all unrelated classes implement a method, for example, all unrelated 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, not all of them are public.
  • Non static and non constant fields need to be inherited.

In many cases, interfaces take precedence over abstract classes. Because the interface does not have the strict class hierarchy requirements of abstract classes, it can flexibly add behavior for a class. And from Java 8, the interface can also have a default method implementation, which makes the cost of modifying the interface very low.

super

  • Access to the constructor of the parent class: you can use the super() function to access the constructor of the parent class, which delegates the parent class to complete some initialization work. It should be noted that the subclass must call the constructor of the parent class to complete the initialization work, generally calling the default constructor of the parent class. If the subclass needs to call other constructors of the parent class, then it can use the super() function.
  • Access to the members of the parent class: if a child class overrides a method of the parent class, it can be implemented by using the super keyword to reference the method of the parent class.
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 SuperExtendExample extends SuperExample {

    private int z;

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

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

SuperExample e = new SuperExtendExample(1, 2, 3);
e.func();

//SuperExample.func()
//SuperExtendExample.func()

Rewriting and overloading

1. Override

In the inheritance system, a subclass implements a method exactly the same as the parent in method declaration.

In order to satisfy the Li substitution principle, rewriting has the following three limitations:

  • The access permission of the subclass method must be greater than or equal to that of the parent method;
  • The return type of a subclass method must be the return type of the parent method or its subtype.
  • The type of exception thrown by a subclass method must be the type of exception thrown by the parent class or its subtype.

Using the @ Override annotation, you can ask the compiler to help you check that the above three constraints are met.

In the following example, SubClass is a SubClass of SuperClass, which overrides the func() method of SuperClass. Among them:

  • The access permission of the subclass method is public, which is greater than the protected of the parent class.
  • The return type of the subclass is ArrayList, which is a subclass of the return type List of the parent class.
  • The type of Exception thrown by the subclass is Exception, which is a subclass of Throwable thrown by the parent.
  • The subclass Override method uses the @ Override annotation, which allows the compiler to automatically check if the constraints are met.
class SuperClass {
    protected List<Integer> func() throws Throwable {
        return new ArrayList<>();
    }
}

class SubClass extends SuperClass {
    @Override
    public ArrayList<Integer> func() throws Exception {
        return new ArrayList<>();
    }
}

When calling a method, first check whether there is a corresponding method in this class. If not, check whether it is inherited from the parent class. Otherwise, we need to transform the parameters to see if there is a corresponding method after they are converted to the parent class. In general, the priority of method calls is:

  1. this.func(this)
  2. super.func(this)
  3. this.func(super)
  4. super.func(super)
/*
    A
    |
    B
    |
    C
    |
    D
 */

class A {

    public void show(A obj) {
        System.out.println("A.show(A)");
    }

    public void show(C obj) {
        System.out.println("A.show(C)");
    }
}

class B extends A {

    @Override
    public void show(A obj) {
        System.out.println("B.show(A)");
    }
}

class C extends B {
}

class D extends C {
}

public static void main(String[] args) {

    A a = new A();
    B b = new B();
    C c = new C();
    D d = new D();

    // show(A obj) exists in A, which is called directly
    a.show(a); // A.show(A)
    // show(B obj) does not exist in a, transforming B into its parent class A
    a.show(b); // A.show(A)
    // There is show(C obj) inherited from A in B, which is called directly
    b.show(c); // A.show(C)
    // show(D obj) does not exist in B, but there is show(C obj) inherited from A, which transforms D into its parent class C
    b.show(d); // A.show(C)

    // It refers to B object, so the call result of ba and B is the same
    A ba = new B();
    ba.show(c); // A.show(C)
    ba.show(d); // A.show(C)
}

2. Overload

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

It should be noted that different return values and the same others are not overload.

reflex

summary

Each class has a class object that contains information about the class. When compiling a new class, a. Class file with the same name will be generated. The content of the file holds the class object.

Class loading is equivalent to the loading of class objects. Classes are dynamically loaded into the JVM only when they are first used. It can also be used Class.forName("com.mysql.jdbc.Driver ") this way to control the loading of classes. This method will return a class object.

Reflection can provide class information at run time, and this class can be loaded at run time, even when the. Class of this class does not exist at compile time.

In short, with reflection, we can get information about members and members of each type in the program or assembly at run time. The types of general objects in a program are determined at compile time, while the Java reflection mechanism can dynamically create objects and call their properties, and the types of such objects are unknown at compile time. So we can directly create an object through reflection mechanism, even if the type of the object is unknown at compile time.

The core of reflection is that the JVM dynamically loads classes or calls methods / access properties at run time. It does not need to know who the running object is in advance (when writing code or during compilation).

Java reflection mainly provides the following functions:

  • Judge the class of any object at runtime;
  • Construct the object of any class at runtime;
  • Judge the member variables and methods of any class at runtime (even private methods can be called through reflection);
  • Calling a method of any object at run time

Important: run time, not compile time

Class and java.lang.reflect Together, it supports reflection, java.lang.reflect The class library mainly includes the following three classes:

  • Field: you can use the get() and set() methods to read and modify the fields associated with the field object;
  • Method: you can use the invoke() method to call the method associated with the method object;
  • Constructor: you can use constructor's newInstance() to create a new object.

Basic application

Get Class object

There are three methods:

(1) Use the Class's forName static method:

 public static Class<?> forName(String className)
 Class.forName(driver);	//For example, in JDBC development, this method is often used to load database drivers

(2) Get the class of an object directly, for example:

Class<?> klass = int.class;
Class<?> classInt = Integer.TYPE;

(3) Call the getClass() method of an object, for example:

StringBuilder str = new StringBuilder("123");
Class<?> klass = str.getClass();
Determine whether it is an instance of a class

Generally, we use the instanceof keyword to determine whether it is an instance of a Class. At the same time, we can also use the isInstance() method of the Class object in the reflection to determine whether it is an instance of a Class. It is a native method:

public native boolean isInstance(Object obj);
Create instance

There are two main ways to generate objects by reflection.

  1. Use the newInstance() method of the Class object to create an instance of the Class corresponding to the Class object.
Class<?> c = String.class;
Object str = c.newInstance();
  1. First obtain the specified Constructor object through the Class object, and then call the newInstance() method of the Constructor object to create an instance. This method constructs an instance of the Class with the specified Constructor.
//Get the Class object Class corresponding to String <? > 
c = String.class;
//Get the constructor of String class with a String parameter
Constructor constructor = c.getConstructor(String.class);
//Create instance from constructor
Object obj = constructor.newInstance("23333");
System.out.println(obj);
Get member variable (field) of class

The Class class provides the following methods to get fields:

  • Field getField(name): get the field of a public (including the parent class) according to the field name
  • Field getDeclaredField(name): get a field of the current class according to the field name (excluding the parent class)
  • Field[] getFields(): get the field of all public (including the parent class)
  • Field[] getDeclaredFields(): get all fields of the current class (excluding the parent class)

Get field value:

A Field object contains all the information of a Field:

  • getName(): returns the field name, for example, "name";
  • getType(): returns the field type, which is also a Class instance. For example, String.class ;
  • getModifiers(): returns the modifier of a field. It is an int. different bit s represent different meanings.

Normally, the Main class cannot access the private field of the class. To fix the error, change private to public, or write a sentence before calling getXxx():

Field.setAccessible(true);

call Field.setAccessible(true) means that regardless of whether this field is public, it is allowed to access.

Access method

The Class class provides the following methods to obtain the Method:

  • Method getMethod(name, Class...): get the method of a public (including the parent class)
  • Method getDeclaredMethod(name, Class...): get a method of the current class (excluding the parent class)
  • Method[] getMethods(): get methods of all public (including parent class)
  • Method[] getDeclaredMethods(): get all methods of the current class (excluding the parent class)

A Method object contains all the information of a Method:

  • getName(): returns the method name, for example: "getScore";
  • getReturnType(): return method return value type, also a Class instance, for example: String.class ;
  • getParameterTypes(): returns the parameter type of the method, which is a Class array, for example:{ String.class, int.class };
  • getModifiers(): returns the modifier of a method. It is an int. different bit s represent different meanings.

Call method:

When we get a method from the class, we can call it with the invoke() method. The prototype of invoke method is:

public Object invoke(Object obj, Object... args)
        throws IllegalAccessException, IllegalArgumentException,
           InvocationTargetException

Here is an example:

public class test1 {
    public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Class<?> klass = methodClass.class;
        //Create an instance of methodClass
        Object obj = klass.newInstance();
        //Get the add method of methodClass class
        Method method = klass.getMethod("add",int.class,int.class);
        //Call the method corresponding to method = > Add (1,4)
        Object result = method.invoke(obj,1,4);
        System.out.println(result);
    }
}
class methodClass {
    public final int fun = 3;
    public int add(int a,int b) {
        return a+b;
    }
    public int sub(int a,int b) {
        return a-b;
    }
}

To call non public methods, we use the Method.setAccessible(true) allow it to call

Get constructor information

The method to obtain the Constructor through the Class instance is as follows:

  • getConstructor(Class...): get a public Constructor;
  • getDeclaredConstructor(Class...): get a certain Constructor;
  • getConstructors(): get the constructors of all public;
  • getDeclaredConstructors(): get all constructors.

Note that Constructor is always the construction method defined by the current class, which has nothing to do with the parent class, so there is no polymorphism problem.

When a non public Constructor is called, access must be allowed first through the setAccessible(true) setting. setAccessible(true) may fail.

Get object instance

The main method is to get an instance of the Constructor Class through the getConstructor method of the class class. The Constructor class has a newInstance method to create an object instance

public T newInstance(Object ... initargs)

This method can call the corresponding Constructor to create an object instance based on the parameters passed in.

Advantages and disadvantages

Advantages of reflection:

  • Extensibility: applications can use fully qualified names to create instances of extensible objects to use user-defined classes from outside.
  • Class browser and visual development environment: a class browser needs to be able to enumerate the members of a class. Visual development environments, such as ides, can benefit from leveraging the type information available in reflection to help programmers write the right code.
  • Debugger and testing tools: the debugger needs to be able to check private members in a class. Test tools can use reflection to automatically call discoverable API definitions defined in classes to ensure high code coverage in a set of tests.

Disadvantages of reflection:

Although the reflection is very powerful, it cannot be abused. If a function can be done without reflection, it's best not to. The following points should be kept in mind when we use reflection technology.

  • Performance overhead: Reflection involves dynamic type resolution, so the JVM cannot optimize the code. Therefore, the efficiency of reflection operations is much lower than that of non reflection operations. We should avoid using reflection in code that is often executed or in programs that require high performance.
  • Security restrictions: using reflection technology requires the program to run in an environment without security restrictions. If a program has to run in a secure environment, such as an Applet, that's the problem.
  • Internal exposure: because reflection allows code to perform operations that are not normally allowed (such as accessing private properties and methods), using reflection can lead to unexpected side effects, which can lead to code dysfunctionality and compromise portability. Reflection code breaks the abstraction, so when the platform changes, the behavior of the code may change.

abnormal

Throwable can be used to represent any class that can be thrown as an Exception. It can be divided into two types: error and Exception. Error is used to indicate the error that cannot be handled by the JVM. Exceptions are divided into two types:

  • Exception detected: try is required catch… Statement capture and processing, and can be recovered from the exception;
  • Unchecked exception: it is a program runtime error. For example, dividing by 0 will cause an Arithmetic Exception. At this time, the program crashes and cannot be recovered.

generic paradigm

public class Box<T> {
    // T stands for "Type"
    private T t;
    public void set(T t) { this.t = t; }
    public T get() { return t; }
}

annotation

Java annotation is some meta information added in the code, which is used to analyze and use some tools in compiling and running, and plays the function of explanation and configuration. Annotations do not and cannot affect the actual logic of the code, but only play an auxiliary role.

Tags: Java jvm encoding network

Posted on Fri, 26 Jun 2020 01:10:57 -0400 by steveonly20