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中,动态代理是一种利用反射机制在运行时动态创建代理对象的技术。动态代理可以在不知道具体接口或类的情况下,动态地创建代理对象来代替原始对象进行方法调用,从而实现一些额外的功能或逻辑。
在动态代理中,通常会使用 类和 接口。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,我们可以在方法调用前后添加额外的逻辑,实现一些横切关注点的功能,如日志记录、性能监控等。