Java 类型信息

104 阅读6分钟

Java RTTI(Run-Time Type Information)是指在运行时获取和操作对象的类型信息的能力。在Java中,RTTI主要通过反射机制来实现,可以在运行时动态地获取类的信息、调用方法、访问字段等。

Class 对象

.class 产生的Class引用不会引发初始化,而如果是通过 Class.forName() 立即就进行了初始化。

static final 编译时常量,不需要对该类进行初始化就可以读取。 这种情况下就能确保这种行为吗?当然不是,参考下面示例:

// 编译时常量,不需要初始化。
static final int staticFinal = 47; 
// 非编译时常量,访问前会强制对类进行初始化。
static final int staticFinal2 = new Random(47).rand.nextInt(1000); 

Class 对象提供了泛化(<?>)对类型进行限定,通过泛型语法可以让编译器强制执行额外的类型检查。

Class intClass = int.class;
Class<Integer> genericIntClass = int.class;
genericIntClass = Integer.class; // Same thing.
intClass = double.class;
// genericIntClass = double.class; // Illegal

?? 是Java 泛型通配符的一种,表示“任何事物“。Class<?>优于普通的Class,即便它们是等价的,并且普通的Class不会产生编译器警告⚠️信息。Class<?>的好处是你本身就使用了一个非具体的引用类,而不是碰巧或者疏忽。

Class<?> intClass = int.class;
intClass = double.class;

限定某种类型或子类型,结合 extends 一起使用。

Class<? extends Number> bounded = int.class;
bounded = double.class;
bounded = Number.class;
....

通过Class引用获取对象的实例

class CountedInteger {
    private static long counter;
    private final long id = ++counter;
    // private CountedInteger(){}
    @Override
    public String toString() {
        return Long.toString(id);
    }
}
public class Main {
    public static void main(String[] args) throws InstantiationException, 
    IllegalAccessException {
        Class<CountedInteger> type = CountedInteger.class;
        CountedInteger countedInteger = type.newInstance();
        System.out.println(countedInteger);
    }
} /* Output: 1 */

当 CountedInteger 类的构造器为私有的时候会产生访问异常IllegalAccessException

Exception in thread "main" java.lang.IllegalAccessException: 
Class Main can not access a member of class CountedInteger with modifiers "private"

instanceof 关键字

instanceof 关键字会返回一个布尔值,告诉我们对象是不是某个特定类型的实例。比如:

if(x instanceof Dog)
    ((Dog)x).bark();

使用 instanceof 可以帮助我们有效的避免ClassCastException异常。如果你的代码里面出现了很多的 instanceof 语句,这可能是一个很糟糕的设计。

Class.isInstance 方法提供了一种动态地检测对象的途径。我们可以把instanceof 替换为isInstance.

语法格式:Type.class.isInstance(type)

class Animal {
    public void eat() { System.out.println("Animal is eating"); }
}
class Dog extends Animal {
    public void bark() { System.out.println("Dog is barking"); }
}
public class Main {
    public static void main(String[] args) {
        Animal animal = new Dog();
        // animal instanceof Animal
        //if (animal instanceof Animal) {
        //   System.out.println("animal is an instance of Animal class");
        //}
        if (Animal.class.isInstance(animal)) {
            System.out.println("animal is an instance of Animal class");
        }
        if (Dog.class.isInstance(animal)) {
            System.out.println("animal is an instance of Dog class");
        }
    }
}
/**
 * Output: 
 * animal is an instance of Animal class
 * animal is an instance of Dog class
 */

java.lang.reflect 类库

Class 类与 java.lang.reflect 类库一起对反射的概念进行了支持,该类库包含了Field、Method、Constructor,每个类都实现了Member接口。这些类型都是JVM在运行时创建的,用以表示未知类里的成员。

Class类

Class类代表一个类或接口,在运行时可以获取类的信息,如类名、父类、实现的接口、成员变量、方法等。

Field类

代表类的成员变量,通过Field类可以获取和设置类的字段值。

Method类

代表类的方法,通过Method类可以调用类的方法。

Constructor类

代表类的构造函数,通过Constructor类可以创建类的实例。

Modifier类

提供了一组静态方法,用于操作修饰符,如public、private、static等。

For example

class Person2 {
    private int age;
    private String name;
    public Person2(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public void displayInfo() {
        System.out.println("Name: " + name + " , Age: " + age);
    }
}
public class Main {
    public static void main(String[] args) throws Exception {
        //获取Person类的Class对象
        Class<?> personClass = Class.forName("org.spi.provider.reflect.Person2");
        // 获取Person类的构造函数
        Constructor<?> constructor = personClass.getConstructor(String.class, int.class);
        // 创建Person对象
        Object person = constructor.newInstance("Alice", 30);
        // 获取Person类的displayInfo方法
        Method displayInfoMethod = personClass.getMethod("displayInfo");
        // 调用displayInfo方法
        displayInfoMethod.invoke(person);
        // 获取Person类的name字段
        Field nameField = personClass.getDeclaredField("name");
        nameField.setAccessible(true);
        // 设置可访问私有字段
        String name = (String) nameField.get(person);
        System.out.println("Name: " + name);
    }
}
/**
 * Output: 
 * Name: Alice , Age: 30
 * Name: Alice
 *///~

通过Java.lang.reflect类库,我们可以实现一些高级的功能,如动态代理、反射调用方法、动态生成类等。但是需要注意的是,使用反射会带来性能上的损失,并且可能会使代码更加复杂和难以理解,因此应该谨慎使用。

动态代理

在Java中,动态代理是一种利用反射机制在运行时动态创建代理对象的技术。动态代理可以在不知道具体接口或类的情况下,动态地创建代理对象来代替原始对象进行方法调用,从而实现一些额外的功能或逻辑。

在动态代理中,通常会使用 ProxyProxy 类和 InvocationHandlerInvocationHandler 接口。Proxy类用于创建代理对象,而InvocationHandler接口则定义了代理对象调用时的处理逻辑。

newProxyInstance 方法


Returns an instance of a proxy class for the specified interfaces 
that dispatches method invocations to the specified invocation handler.
Proxy.newProxyInstance throws IllegalArgumentException for the 
same reasons that Proxy.getProxyClass does.
Params:
* loader – the class loader to define the proxy class 
* interfaces – the list of interfaces for the proxy class to implement 
* h – the invocation handler to dispatch method invocations to
Returns:
a proxy instance with the specified invocation handler of a proxy class that is  
defined by the specified class loader and that implements the specified interfaces

@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h) {
   ... ...                                     
}

InvocationHandler 接口

package java.lang.reflect;

InvocationHandler is the interface implemented by the invocation 
handler of a proxy instance.
Each proxy instance has an associated invocation handler. 
When a method is invoked on a proxy instance, 
the method invocation is encoded and dispatched 
to the invoke method of its invocation handler.

public interface InvocationHandler {

//Processes a method invocation on a proxy instance and returns the result. 
//This method will be invoked on an invocation handler when a method is invoked 
//on a proxy instance that it is associated with.
 public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}
interface Animal { void eat(); }
class Dog implements Animal {
    @Override
    public void eat() {
        System.out.println("Dog is eating");
    }
}
class MyInvocationHandler implements InvocationHandler {
    private Object target;
    public MyInvocationHandler(Object target) {
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 调用eat方法前后分别执行了额外的处理逻辑
        System.out.println("Before method invocation");
        Object result = method.invoke(target, args);
        System.out.println("After method invocation");
        return result;
    }
}
public class Main {
    public static void main(String[] args) {
        Animal dog = new Dog();
        Animal proxy = (Animal) Proxy.newProxyInstance(
                Animal.class.getClassLoader(),
                new Class[]{Animal.class},
                new MyInvocationHandler(dog));
        proxy.eat();
    }
}
/**
 * Output:
 * Before method invocation
 * Dog is eating
 * After method invocation
 *///~

我们定义了一个Animal接口和一个Dog类实现了该接口。然后定义了一个MyInvocationHandler类实现了InvocationHandler接口,在invoke方法中添加了对方法调用前后的处理逻辑。在main方法中,我们首先创建了一个Dog对象,然后使用Proxy.newProxyInstance方法动态创建了一个代理对象,代理对象会在调用eat方法前后添加额外的处理逻辑。

这表明代理对象在调用eat方法前后分别执行了额外的处理逻辑。通过动态代理和InvocationHandler,我们可以在方法调用前后添加额外的逻辑,实现一些横切关注点的功能,如日志记录、性能监控等。