Java基础之RTTI和反射

在Java中运行时识别对象和类的信息有两种方式:第一种是传统的RTTI,假设我们在编译时就已经知道了所有的类型;另一种是反射,我们可以在运行时获取类和对象的所有信息。

在介绍反射之前先介绍一下Class对象,反射的实现离不开此对象。

Class对象

每个类都有其对应的Class对象,此处的Class即java.lang.Class类,包含在.class文件中。class文件的加载在以后的JVM文章中详细介绍,现在只讨论Class对象。

Class对象的获取方式有三种:

  1. 调用Class类的静态方法Class.forName(类的全限定名):如果该类未被加载,则JVM会调用类加载器加载此类。如果找不到此类会抛出ClassNotFoundException;
  2. 通过该类的对象调用getClass()方法:此方法是Object中的方法,因此任何对象都可以调用此方法,并且在调用该方法时此类一定已经被加载了;
  3. 类字面常量:类似类名.class,但此方式也可以用在接口,数组和基本类型中,且在编译期就受到检查,避免运行时找不到类。该方式不会触发类的初始化。

Class对象由类本身和类加载器决定其唯一性,如果类已经被加载了,以上三种方式获得的Class对象是同一个。

Class类的部分方法如下:

//将传入的Class对象强制转换为其子类表示的Class对象
public <U> Class<? extends U> asSubclass(Class<U> clazz);
//强制类型转换,不能转换则抛出ClassCastException异常
public T cast(Object obj);
//查找并加载指定路径的Class
public static Class<?> forName(String className)throws ClassNotFoundException;
//获取标注该对象的注解
public <A extends Annotation> A getAnnotation(Class<A> annotationClass);
//获取此类上的全部注解
public Annotation[] getAnnotations();
//获取类定义的公共内部类以及从父类或父接口继承的内部类
public Class<?>[] getClasses();
//获取此类的类加载器
public ClassLoader getClassLoader();
//获取数组中的Class对象
public native Class<?> getComponentType();
//获取public构造函数对象
public Constructor<?>[] getConstructors() throws SecurityException;
//返回直接标注在该元素上的全部注解
public Annotation[] getDeclaredAnnotations();
//获取类定义的全部内部类,不包括父类定义
public Class<?>[] getDeclaredClasses() throws SecurityException;
//返回类中的全部构造函数对象,包括private
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes);
//获取类中的指定的字段,不包括父类继承的
public Field getDeclaredField(String name);
//获取类中的指定的public字段,包括父类继承的
public Field getField(String name);
//获取类中的全部字段,不包括父类继承的
public Field[] getDeclaredFields() throws SecurityException;
//获取类中的public字段,包括父类继承的
public Field[] getFields() throws SecurityException;
//获取类中的全部方法,不包括父类继承的
public Method[] getDeclaredMethods() throws SecurityException;
//获取类中的public方法,包括父类继承的
public Method[] getMethods() throws SecurityException;
//根据方法名称和方法参数的Class数组获取方法对象
public Method getMethod(String name, Class<?>... parameterTypes);
//获取此类的全限定类名
public String getName();
//获取此类的包名
public Package getPackage();
//获取此类的类名
public String getSimpleName();
//获取父类Class
public native Class<? super T> getSuperclass();
//返回是否注释类型
public boolean isAnnotation();
//返回是否数组类型
public native boolean isArray();
//返回是否接口
public native boolean isInterface();
//创建此类的实例,只有该类有默认构造器时才可以调用,否则抛异常
public T newInstance();

以下是Class类的一些方法举例。

public class Main {
    public static void main(String[] args) {
        try {
            //1.Class.forName()方式
            Class apple1 = Class.forName("Apple");
            //2.类名.class方式
            Class apple2 = Apple.class;
            //3.对象.getClass()
            Apple apple = new Apple();
            Class apple3 = apple.getClass();
            System.out.println(apple1 == apple2);
            System.out.println(apple2 == apple3);
            //类名.class不会触发初始化
            Class bananaClass = Banana.class;
            //调用静态变量触发初始化
            System.out.println("查看Banana类的flag:" + Banana.flag);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        Class orangeClass = Orange.class;
        //得到类的全限定包名
        System.out.println("Orange类的全限定类名:" + orangeClass.getName());
        //得到类的名字
        System.out.println("Orange类的类名:" + orangeClass.getSimpleName());
        //判断Class是否是接口
        System.out.println("Orange类是否为接口:" + orangeClass.isInterface());
        //判断Class是否是数组
        System.out.println("Orange类是否是数组:" + orangeClass.isArray());
        //获得实现的所有接口
        Class[] interfaces = orangeClass.getInterfaces();
        System.out.println("Orange类实现的接口:" + interfaces[0]);
        //获取其直接父类
        System.out.println("Orange类的直接父类:" + orangeClass.getSuperclass());
        try {
            //创建一个Object对象,使用此方法的类必须含有无参构造器
            Orange orange = (Orange) orangeClass.newInstance();
            System.out.println("使用newInstance()创建的对象的name:" + orange.getName());
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        //获取该类的全部公共字段,包括其父类的
        Field[] fields = orangeClass.getFields();
        System.out.println("Orange类的全部公共字段:");
        for(Field field:fields){
            System.out.println(field);
        }
        System.out.println();
        //获取该类的全部字段,不包括父类继承的
        Field[] declaredFields = orangeClass.getDeclaredFields();
        System.out.println("Orange类的全部字段:");
        for(Field field:declaredFields){
            System.out.println(field);
        }
        System.out.println();
        //获取public修饰的成员方法,包括父类继承的
        Method[] methods = orangeClass.getMethods();
        System.out.println("Orange类的全部公共方法:");
        for(Method method:methods){
            System.out.println(method);
        }
        System.out.println();
        //获取该类的全部方法,不包括父类继承的
        Method[] declaredMethods = orangeClass.getDeclaredMethods();
        System.out.println("Orange类的全部方法:");
        for (Method method:declaredMethods){
            System.out.println(method);
        }
    }
}

class Fruit{
    public String shape;
    static {
        System.out.println("Fruit Class is loading...");
    }
    protected String name;
    protected String color;
    public String getShape() {
        return shape;
    }
    public void setShape(String shape) {
        this.shape = shape;
    }
    public String getName(){
        return this.name;
    }
    public void setName(String name){
        this.name = name;
    }
    public String getColor(){
        return color;
    }
    public void setColor(){
        this.color = color;
    }
}
class Apple extends Fruit{
    static {
        System.out.println("Apple Class is loading...");
    }
    public Apple(){
        name = "apple";
        color = "green";
    }
}
class Banana extends Fruit{
    static boolean flag = true;
    static {
        System.out.println("Banana Class is loading...");
    }
    public Banana(){
        name = "banana";
        color = "yellow";
    }
}
class Orange extends Fruit implements Serializable {
    public String publicField;
    private String privateField;
    protected String protectedField;
    static {
        System.out.println("Orange Class is loading...");
    }
    public Orange(String name,String color){
        this.name = name;
        this.color = color;
    }
    public String getPublicField() {
        return publicField;
    }
    public void setPublicField(String publicField){
        this.publicField = publicField;
    }
    public String getPrivateField() {
        return privateField;
    }
    public void setPrivateField(String privateField){
        this.privateField = privateField;
    }
    public String getProtectedField() {
        return publicField;
    }
    public void setProtectedField(String protectedField){
    this.protectedField = protectedField;
    }
}

输出结果

Fruit Class is loading...
Apple Class is loading...
true
true
Banana Class is loading...
查看Banana类的flag:true
Orange类的全限定类名:Orange
Orange类的类名:Orange
Orange类是否为接口:false
Orange类是否是数组:false
Orange类实现的接口:interface java.io.Serializable
Orange类的直接父类:class Fruit
Orange Class is loading...
使用newInstance()创建的对象的name:orange
Orange类的全部公共字段:
public java.lang.String Orange.publicField
public java.lang.String Fruit.shape

Orange类的全部字段:
public java.lang.String Orange.publicField
private java.lang.String Orange.privateField
protected java.lang.String Orange.protectedField

Orange类的全部公共方法:
public void Orange.setPrivateField(java.lang.String)
public java.lang.String Orange.getPublicField()
public java.lang.String Orange.getPrivateField()
public void Orange.setPublicField(java.lang.String)
public void Orange.setProtectedField(java.lang.String)
public java.lang.String Orange.getProtectedField()
public java.lang.String Fruit.getName()
public void Fruit.setName(java.lang.String)
public void Fruit.setColor()
public java.lang.String Fruit.getColor()
public void Fruit.setShape(java.lang.String)
public java.lang.String Fruit.getShape()
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()

Orange类的全部方法:
public void Orange.setPrivateField(java.lang.String)
public java.lang.String Orange.getPublicField()
public java.lang.String Orange.getPrivateField()
public void Orange.setPublicField(java.lang.String)
public void Orange.setProtectedField(java.lang.String)
public java.lang.String Orange.getProtectedField()

RTTI

RTTI(Run-Time Type Identification),即运行时类型检查。在运行时可以判断一个对象的类型。类型信息在运行时通过Class对象表示,.java文件经编译后会生成一个包含了类型信息.class文件,因此在编译期就需要知道其类型。

反射

JAVA反射机制是在运行状态中,对于任意一个实体类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。在运行时我们获取可以某个类的Class对象,再获取其构造函数Constructor对象,成员变量Field对象和成员方法Method对象并且可以动态的使用他们。

除了Class类是我们使用反射必须的类外,还有几个类例如Constructor(构造函数类),Field(字段类)和Method(方法类)是我们需要了解的。说白了,反射就是在运行时使用这几个类获取指定类的信息以及操纵构造函数构造实例、设置指定字段值或设置方法参数执行指定方法等等的操作。

Constructor对象

构造器类,主要使用方法如下:

//获取无参构造函数
Constructor[] noArgumentsConstructor = orangeClass.getConstructors();
//获取有参构造函数getConstructor(类<?>... parameterTypes)
Constructor constructor = orangeClass.getConstructor(String.class,String.class);
Orange orange = (Orange) constructor.newInstance("orange", "orange");
System.out.println("通过有参构造函数对象构造的Orange对象:"+orange);

输出结果:通过有参构造函数对象构造的Orange对象:Orange{name='orange', color='orange'}

Field对象

获取某个类的全部属性在Class中已经聊过了,现在谈谈通过Field对象设置属性值。

//获取Orange类的全部成员变量
Orange orange = new Orange("orange","orange");
Field[] orangeFields = orange.getClass().getFields();
for(int i=0;i<orangeFields.length;i++){
System.out.println("Orange类的属性"+orangeFields[i].getName()+"的修饰符为"+orangeFields[i].getModifiers());
//当属性为"name"时,修改此对象的name属性
if("name".equals(orangeFields[i].getName())){
    try {
        orangeFields[i].set(orange,"橘子");
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    }
}
System.out.println("修改后的Orange对象为:"+orange.toString());

输出结果为:

Orange类的属性publicField的修饰符为1
Orange类的属性shape的修饰符为1
Orange类的属性name的修饰符为1
Orange类的属性color的修饰符为1
修改后的Orange对象为:Orange{name='橘子', color='orange'}

其中getModifiers()方法返回的为int类型,1==public,2==private,4==protected,8==static,16==final,32==synchronized,64==volatile,128==transient,256==native,512==interface,1024==abstract,2048==strict。

Methods对象

方法类,其核心方法是invoke()方法,JDK动态代理最终即是调用的此方法。

//指定类中的指定方法,第一个参数为该类的实例,后面参数为该方法的参数
public Object invoke(Object obj, Object... args);

下面为获取某个特定方法和调用此方法的方式。

//反射调用成员方法
Method method = orangeClass.getMethod("setPublicField",String.class);
//调用orange对象的setPublicField()方法,入参为publicField
method.invoke(orange,"publicField");
System.out.println("通过反射调用setPublicField()方法后publicField的值为:"+orange.getPublicField());
//反射调用静态方法,静态方法的invoke()第一个参数为null
Method staticMethod = orangeClass.getMethod("print",String.class);
staticMethod.invoke(null,"我是通过反射调用的静态方法哟~");

需要注意的是调用静态方法时invoke()的第一个参数为null,而方法中没有参数时可以只给第一个参数。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值