天天看点

在运行时识别对象和类的信息——传统的RTTI和Java反射机制1. 引言2. 运行时识别对象和类的信息(RTTI)3. 反射

  • 1. 引言
  • 2. 运行时识别对象和类的信息(RTTI)
    • 2.1. 传统的RTTI
  • 3. 反射
    • 3.1. Class
      • 3.1.1. 如何获取Class类的实例
    • 3.2. Constructor
      • 3.2.1. 在获取Class对象之后,如何获取反射类的对象
    • 3.3. Method
    • 3.4. Field

1. 引言

此处先回顾以下反射的概念:反射就是类在运行的过程中,通过Java API 提供的反射技术,动态的获取类的属性和方法,进而通过类的对象来调用他的方法。

对于反射技术自己只是一知半解,网上的博客大多千篇一律,此处根据Class类和JVM的类加载机制的加载过程中,从逻辑上分析反射技术。

类加载机制的加载过程:

第一步:通过一个类的权限定名获取此类,并将其转换为字节码文件

第二步:将字节码文件转换为方法区的运行时数据结构

第三步:在内存中生成一个代表该类的java.lang.Class对象,作为方法区这个类的各种数据结构的入口。

2. 运行时识别对象和类的信息(RTTI)

在阅读《java编程思想》这本书-类型信息-章节时对RTTI和反射有了更深层次的认识

java中的“向上转型,向下转型”时要检查对象的类型信息,在向上转型时不需要显示转换,它是安全的;但是在向下转型时需要进行显示转换。

2.1. 传统的RTTI

RTTI指的是在运行时识别类和对象的信息,识别类和对象信息的有两种方式,一种是传统的"RTTI",假设我们在编译时已经知道了所有的类型信息;一种是"反射"机制

,它允许我们在运行时发现和使用类的信息

RTTI和反射之间真正的区别在于,对RTTI来说,编译器在编译期间打开和检查.class文件,而对于反射机制来说,.class文件在编译时是不可获取的,所以在运行时打开和检查.class文件

  • 传统的RTTI形式包括三种:

第一种:传统的类型转换,在需要被转换的对象前面添加圆括号,括号中为转换后的对象

第二种:通过关键字instanceof来判断类型是否合法,即对象是否属于某个特定类的实例

第三种:通过Class.forName(String)来获取Class对象

对于第三种方法一般认为是在运行时获取类型信息的,但是name参时是classpath路径下.class文件的文件名,因此在编译时编译器已经知道所要处理的类

3. 反射

3.1. Class

Class类表示正在运行的java应用程序中的类和接口,(通俗的讲:Class类代表的是你要反射生成的那个类),它保存着运行时的类的所有信息

Class类没有构造方法,Class对象是在加载类时由JVM和类加载器生成的,也就是加载过程的第三步。

  • 创建Person类
public class Person {

	//创建私有属性
	private String name;
	private int age;
	private String sex;

	//构造方法
	Person() {

	}

	Person(String name, int age, String sex) {
		this.name = name;
		this.sex = sex;
		this.age = age;
	}

	public String getName() {
		return name;
	}

	//get和set方法
	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public String getSex() {
		return sex;
	}

	public void setSex(String sex) {
		this.sex = sex;
	}

}
           

3.1.1. 如何获取Class类的实例

1.Object类的getClass()方法,

2.Class的forName()方法

3.要被反射的类的class属性

public static void main(String[] args) throws ClassNotFoundException {
		Person person = new Person();
		/*
		 * 获取代表运行时类的Class对象
		 */

		//1.通过Object.getClass()
		Class class1 = person.getClass();

		//2.通过运行时类的class属性
		Class class2 = Person.class;

		//3.通过Class类的forName()方法
		Class class3 = Class.forName("reflect.Person");
	}

           

3.2. Constructor

提供了对类的构造函数的信息和访问信息

3.2.1. 在获取Class对象之后,如何获取反射类的对象

第一种:先获取class对象,然后通过class对象的newInstance()方法创建类对象

newInstance()方法在创建对象的时候只能调用类的无参构造函数,如果需要调用类的有参构造函数还需要使用Constructor类中的newInstance()方法

Class class3 = Class.forName("reflect.Person");
		
Object object = class1.newInstance();
           

第二种:先获取类的构造方法对象Constructor,然后通过Constructor的newInstance()方法创建类对象

//获取类的所有公共构造方法,只能调用public构造方法
    Constructor[] constructors1 = class1.getConstructors();

    //获取类的所有构造方法,包括私有的和非私有的
    Constructor[] constructors2 = class1.getDeclaredConstructors();

    //获取类的无参私有构造方法
    Constructor constructor3 = class1.getDeclaredConstructor();

    //获取类的带参数的私有构造方法
    Constructor constructor4 = class1.getDeclaredConstructor(new Class[] {});

    //创建对象实例
    Person porson = constructor3.newInstance();

           

可以根据构造方法的参数,在获取类的构造方法时指定参数.

3.3. Method

获取某个类的全部构造方法或着自定义的构造方法或着指定的构造方法

//获取某个类下的一个指定的非私有方法
    Method method = class1.getMethod("method",new Class[] { });
        
    //获取类下的所有方法,包括父类(Object)的方法
    Method[] method1 = class1.getMethods();
    
    //获取类的自定义的方法
    Method[] methods = class1.getDeclaredMethods();
		 
           

3.4. Field

获取类中的成员变量

//获取指定的成员变量
    Field field = class1.getField("name");
    System.out.println("获取指定的name成员变量:" + field);

    //获取类的所有成员变量
    Field[] fields = class1.getDeclaredFields();
    for (Field item : fields) {
        System.out.println(item + "   ");
    }
    //获取类的public成员变量
    Field[] fields1 = class1.getFields();
    for (Field item : fields1) {
        System.out.println(item + "   ");
    }