Thinking in Java - Study Notes - (14) Type Information

Java Programming Ideas - Chapter 14 - Type Information

Runtime Type Information (RTTI) allows you to discover and use type information while your program is running.

It frees you from the constraints of only doing type-oriented operations at compile time, and allows you to use some very powerful programs.
The way Java lets us identify information about objects and classes at runtime:

  • "traditional" RTTI , assuming we know all types at compile time;

  • The "reflection" mechanism allows us to discover and use class information at runtime.

Why RTTI is needed

Consider again the base class Shape , and the derived classes Circle , Square , Triangle .

When you put a Shape object into an array of List<Shape> , it will upcast. But the concrete type of the object is also lost when upcasting to Shape . For arrays, they are just objects of class Shape .

When an element is fetched from an array, this kind of window - which actually holds everything as an Object - automatically casts the result back to Shape.

This is the most basic form of RTTI, because in Java, all type conversions are checked for correctness at runtime. This is what RTTI's name means: at runtime, identify the type of an object.

Here, the RTTI type conversion is not thorough: Object is converted to Shape , not to Circle , etc. This is because at present we only know that the List<Shape> holds the Shape . At compile time, this is enforced by the container and Java's generics system; at run time, it is enforced by type conversion operations.

Next is the polymorphism mechanism. What kind of code the Shape object actually executes is determined by the specific objects Circle , Square , and Triangle pointed to by the reference .

Using RTTI , you can query the exact type of object that a Shape reference points to, and then select or cull exceptions.

Class object

To understand how RTTI works in Java, you must first know how type information is represented at runtime. This work is done by special objects called Class objects , which contain class-related information.

A class is a part of a program, and each class has a Class object. In other words, whenever a new class is written and compiled, a Class object is produced (more appropriately, stored in a .class file of the same name). To run objects of this class, the Java Virtual Machine ( JVM ) running this program will use a subsystem called a "class loader".

The classloader subsystem can actually contain a chain of classloaders, but there is only one native classloader , which is part of the JVM implementation. Native class loaders load so-called trusted classes, including Java API classes, which are usually loaded from the local disk. In this chain, there is usually no need to add additional class loaders, but if you have special needs, there is a way to hook additional class loaders.

All classes are dynamically loaded into the JVM when they are first used .

The class is loaded when the program creates its first reference to a static member of the class. This proves that the constructor is also a static method of the class, even if the static keyword is not used before the constructor .

Therefore, a Java program is not fully loaded before it starts running, its parts are loaded only when necessary.

The class loader first checks whether the Class object of this class has been loaded. If not already loaded, the default class loader will look for the .class file based on the class name (for example, some additional class loader might look for bytecode in a database). When the bytecode for this class is loaded, they are verified to ensure that it has not been corrupted and does not contain bad Java code (one of the measures used in Java for security purposes).

Once a Class object of a class is loaded into memory, it is used to create all objects of that class.

Methods of Class Objects

  • forName(String) Enter a fully qualified class name and return a reference to the Class object

  • isInterface() is an interface

  • getInterfaces() get interfaces

  • getSuperclass() gets the direct base class

  • getSimpleName() Gets the class name without the package name.

  • newInstance() must have a default constructor.


package thinkinginjava;

import java.util.Random;

class Initable {
    static final int staticFinal = 47;
    static final int staticFinal2 = ClassInitialization.rand.nextInt(1000);
    static {
        System.out.println("Initializing Initable");
    }
}

class Initable2 {
    static int staticNonFinal = 147;
    static {
        System.out.println("Initializing Initable2");
    }
}

class Initable3 {
    static int staticNonFinal = 74;
    static {
        System.out.println("Initiablizing Initable3");
    }
}

public class ClassInitialization {
    public static Random rand = new Random(47);

    public static void main(String[] args) {
        Class initable = Initable.class;
        System.out.println("After creating Initable ref");
        // 这行代码(staticFinal)不会触发初始化
        System.out.println(Initable.staticFinal);
        // 这行代码(staticFinal2)会触发初始化,因为它不是编译期常量
        System.out.println(Initable.staticFinal2);
        // 会触发初始化,因为不同时是final的
        System.out.println(Initable2.staticNonFinal);
        // 会触发初始化,因为要产生Class引用
        try {
//            System.out.println(Initable3.class.getName());
            Class initable3 = Class.forName("thinkinginjava.Initable3");
            System.out.println("After creating Initable3 ref");
            System.out.println(Initable3.staticNonFinal);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }
} /* Output
After creating Initable ref
47
Initializing Initable
258
Initializing Initable2
147
Initiablizing Initable3
After creating Initable3 ref
74
*/

Initialization is effectively as "lazy" as possible. As you can see from the creation of the reference to the initable , just using the .class syntax to get a reference to the class does not cause initialization. However, in order to generate a Class reference, Class.forName() is initialized immediately. As seen in the creation of the reference to initable3 .

If a static field is not final , then when accessing it, the problem requires linking (allocating storage for the field) and initialization (initializing the storage) before it can be read, as in Initable2 .staticNonFinal as seen in the access.

RTTI has a third form in Java, the keyword instanceof .

Equivalence of instanceof and Class

instanceof and isInstance() maintain the concept of type, which means "are you this class, or are you a derived class of this class?" And if you compare actual Class objects with ==, inheritance is not considered -- It's either this exact type, or it's not.

Reflection: class information at runtime

The only real difference between RTTI and reflection is that, for RTTI, the compiler opens and inspects the .class file at compile time. For the reflection mechanism, the .class file is not available at compile time, so the .class file is opened and checked at runtime .

Dynamic proxy

Delegates are one of the fundamental design patterns.
If you don't understand it very well, you have to read a book on design patterns.

empty object

There is no need to perform additional checks for null .

Interface and Type Information

Reflection makes it easy to access private methods and even anonymous classes.

Guess you like

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