Java RTTI和反射机制

一、Java的RTTI 

       RTTIRun-Time Type Identification,通过运行时类型判定)的含义就是在运行时识别一个对象的类型,其对应的类是Class对象,每个java里面的类都对应一个Class对象(在编写并且编译后),这个对象被保存在这个类的同名class文件里。

      RTTI有两种形式:(1)传统的RTTI;(2)反射reflection机制。

1. 类型检查

作用: 避免类型向下转型是发生 ClassCastException 类型转换异常。

两种方法
   1. 通过 instanceof 运算符
           对象引用    instanceof  类名  
   2. Class对象调用 isInstance (对象引用)

		//1. 获取类的 Class 对象
		Class clazz = Class.forName("com.jq.reflection.Student");
		
		//2. 反射创建类对象
		Object obj = clazz.newInstance();
		
		//第一种方法
		if(obj instanceof Student) {
			Student stu = (Student)obj;
			System.out.println("instanceof 检查 向下转型成功 " + stu);
		}
		
		//第二种方式
		if(clazz.isInstance(obj)) {
			Student stu = (Student)obj;
			System.out.println("clazz 检查 向下转型成功: " + stu);
		}

----结果----
instanceof 检查 向下转型成功 com.jq.reflection.Student@7852e922
clazz 检查 向下转型成功: com.jq.reflection.Student@7852e922

 

二、反射机制概述

    JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。

     1. Java反射机制的作用:

   在运行时判断任意一个对象所属的类;在运行时构造任意一个类的对象;在运行时判断任意一个类所具有的成员变量方法;在运行时调用任意一个对象的方法;生成动态代理。

    1).反射创建该类的字节码对象

    2)分析类的结构(类中的属性、方法)

    反射机制的源头Class对象,通过Class对象来获取构造器,方法,字段并进行操作。

、获取Class对象引用的三种方式

    在Java 中可以通过三种方法获取类的字节码(Class)对象

   Class 对象:是对一个普通类运行时状态的描述。  任何一个类都有一个对应的 Class 对象,在类编译通过时生成最初保存在与其同名的 .class 字节码文件中。这个类第一次使用时, JVM 会检查该类的 Class 对象有没有被加载, 只加载一次

1. 通过对象调用 getClass() 方法
        需要提前 new 一个对象, 一般用于类型检查。    
2. 通过 Class 类中的静态方法 forName(全限定名) 即包名.类名
        一般用于加载驱动。
3.  通过 类名 .class 字面量(基本类型int,double等也可以使用.class)
        一般用于参数传递。

//1.通过对象调用 getClass() 方法
Student stu = new Student();
Class c1 = stu.getClass();
System.out.println(c1);

//2. 通过 Class 类中的静态方法 forName(全限定名) 即包名.类名
Class c2 = null;
try {
	 c2  = Class.forName("com.jq.reflection.Student");
	System.out.println(c2);
} catch (ClassNotFoundException e) {
	// TODO Auto-generated catch block
	e.printStackTrace();
}

//3. 通过 类名.class 字面量(基本类型int,double等也可以使用.class)
Class<Student> c3 = Student.class;
System.out.println(c3);

//注意3种方式获得的Class对象是同一个
System.out.println(c1==c2);
System.out.println(c1==c3);
System.out.println(c2==c3);


----结果----
class com.jq.reflection.Student
class com.jq.reflection.Student
class com.jq.reflection.Student
true
true
true

注意:在运行期间,一个类,只有一个Class对象产生。即3种方式获得的Class对象是同一个 Class 对象引用。

 

四、通过反射机制获取类的结构

     查看Class类在java中的api详解, 取消对非公共属性/方法/构造器的访问权限检查

1、通过反射创建类的对象

       1)调用 Class 对象的 newInstance( )方法,实例化对象,注意:类必须要有无参数的构造器

		/**
		 * 1. 类必须要有无参构造器,否则报 java.lang.InstantiationException 实例化异常
		 */
		//1. 获取类的 Class 对象
		Class c = Student.class;
		try {
			Student stu = (Student) c.newInstance();	//注意:返回类型是Object,向下转型
			System.out.println(stu);
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		}

----结果----
com.jq.reflection.Student@7852e922

     2)通过 Class 对象获取 xxxConstructor 构造器,由构造器调用 newInstance( )方法 ,实例化对象

    • Constructor<T> getConstructor(<?>... parameterTypes)

      返回一个 Constructor对象,该对象反映 Constructor对象表示的类的指定的公共 函数。

      Constructor<?>[] getConstructors()

      返回包含一个数组 Constructor对象反射由此表示的类的所有公共构造 对象。

      • public void setAccessible(boolean flag)
                           throws SecurityException
        将此对象的accessible标志设置为指示的布尔值。 true的值表示反射对象应该在使用时抑制Java语言访问检查。 false的值表示反映的对象应该强制执行Java语言访问检查。

        首先,如果有一个安全管理器,它的checkPermission方法被调用一个ReflectPermission("suppressAccessChecks")权限。

        SecurityException如果升高flagtrue但此对象的可访问性可以不改变(例如,如果该元素对象是Constructor对象为类 )。

        一个SecurityException如果这个对象是提高Constructor对象类java.lang.Class ,以及flag是真的。

类的构造方法:

//---------------构造方法-------------------
		
		//1. 无参构造方法
		public Student(){
		}
		
		//2. 有一个参数的构造方法
		public Student(String username) {
			this.username = username;
		}
		
		//3. 有多个参数的构造方法
		public Student(Integer id, String username, String sex) {
			this.id = id;
			this.username = username;
			this.sex = sex;
			System.out.println("学号:"+id+ "姓名:"+username+ " 性别:"+sex);
		}

		//4.受保护的构造方法
		protected Student(boolean n){
			System.out.println("受保护的构造方法 n = " + n);
		}
		
		//5. 私有构造方法
		private Student(int id){
			this.id = id;
			System.out.println("私有的构造方法   学号:"+ id);
		}
		//5. 缺省构造方法
		Student(int id, String sex){
			this.id = id;
		}

获取构造器及类对象: 私有构造器创建类对象时,con3.setAccessible(true);  //取消对私有构造器的访问权限检查

//1. 获取类的 Class 对象
Class clazz = Class.forName("com.jq.reflection.Student");

//2.1 获取所有的公共的构造器
System.out.println("----所有公共的构造方法----"); 
Constructor[] constructors = clazz.getConstructors();
for (Constructor constructor : constructors) {
	System.out.println(constructor);
}
 
//2.2 获取所有的构造器 
 System.out.println("----所有构造方法(包括 公共,私有,受爱护和缺省 构造器)----"); 
 Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
 for (Constructor constructor : declaredConstructors) {
	 System.out.println(constructor);
}

//2.3 获取公有、无参的构造器 
System.out.println("----获取公有、无参的构造方法----");
Constructor con = clazz.getConstructor(null);	//无参的构造方法de类型是一个null,不写也可以
System.out.println(con);

//2.3 获取公有、带参的构造器 
System.out.println("----获取公有、无参的构造方法----");
Constructor con2 = clazz.getConstructor(Integer.class, String.class, String.class);	//切记是Class类型
System.out.println(con2);

System.out.println("----获取私有构造方法,并调用----");
Constructor con3 = clazz.getDeclaredConstructor(int.class); 
System.out.println(con3);

//3.1 公有构造方法获取类的对象
System.out.println("\n----## 获取类的对象----");
Student stu2 = (Student) con2.newInstance(001,"李四","男");
System.out.println(stu2);

//3.2 私用有构造方法获取类的对象
con3.setAccessible(true);  //取消对私有构造器的访问权限检查
Student stu3 = (Student) con3.newInstance(002);
System.out.println(stu3);

结果:

----所有公共的构造方法----
public com.jq.reflection.Student()
public com.jq.reflection.Student(java.lang.String)
public com.jq.reflection.Student(java.lang.Integer,java.lang.String,java.lang.String)
----所有构造方法(包括 公共,私有,受爱护和缺省 构造器)----
com.jq.reflection.Student(int,java.lang.String)
private com.jq.reflection.Student(int)
protected com.jq.reflection.Student(boolean)
public com.jq.reflection.Student()
public com.jq.reflection.Student(java.lang.String)
public com.jq.reflection.Student(java.lang.Integer,java.lang.String,java.lang.String)
----获取公有、无参的构造方法----
public com.jq.reflection.Student()
----获取公有、无参的构造方法----
public com.jq.reflection.Student(java.lang.Integer,java.lang.String,java.lang.String)
----获取私有构造方法,并调用----
private com.jq.reflection.Student(int)

----## 获取类的对象----
学号:1姓名:李四 性别:男
com.jq.reflection.Student@7852e922
私有的构造方法   学号:2
com.jq.reflection.Student@4e25154f

 

2、通过反射分析类的属性

    • Field getField(String name)

      返回一个 Field对象,它反映此表示的类或接口的指定公共成员字段 对象。

      Field[] getFields()

      返回包含一个数组 Field对象反射由此表示的类或接口的所有可访问的公共字段 对象。

    • Field getDeclaredField(String name)

      返回一个 Field对象,它反映此表示的类或接口的指定已声明字段 对象。

      Field[] getDeclaredFields()

      返回的数组 Field对象反映此表示的类或接口声明的所有字段 对象。

字段:

    public Integer id;
    String username;
    private String sex;

		//1. 获取类的 Class 对象
		Class clazz = Class.forName("com.jq.reflection.Student");
		
		//2. 反射创建类对象
		Student stu = (Student) clazz.newInstance();
		
		//3.1 获取所有的公共的属性,包括继承来的属性
		System.out.println("----所有的公共的属性,包括继承来的属性----");
		Field[] fields = clazz.getFields();
		for (Field field : fields) {
			System.out.println(field);
		}
		//3.2 获取所有的所有权限的属性,包括private修饰的,不包括继承来的
		System.out.println("----所有权限的属性,包括private修饰的,不包括继承来的属性----");
		Field[] fields1 = clazz.getDeclaredFields();
		for (Field field : fields1) {
			System.out.println(field);
		}
		 
		System.out.println("----属性值得get set----");
		//3.3 获取指定的公共的属性
		Field fid = clazz.getField("id"); //注意参数是属性名称,String类型的
		fid.set(stu, 101);
		System.out.println("id="+fid.get(stu)); 
		
		//3.4  获取指定的私有的属性
		Field fsex = clazz.getDeclaredField("sex");
		fsex.setAccessible(true); //取消对私有属性的访问权限检查
		System.out.println("sex=" + fsex.get(stu));
                fsex.set(stu, "女");
		System.out.println("sex=" + fsex.get(stu)); 


----结果----
----所有的公共的属性,包括继承来的属性----
public java.lang.Integer com.jq.reflection.Student.id
----所有权限的属性,包括private修饰的,不包括继承来的属性----
public java.lang.Integer com.jq.reflection.Student.id
java.lang.String com.jq.reflection.Student.username
private java.lang.String com.jq.reflection.Student.sex
----属性值得get set----
id=101
sex=null
sex=女

3、通过反射获取类的方法并 invoke() 调用

在运行时调用任意一个对象的方法;生成动态代理

    • 方法 getMethod(String name, <?>... parameterTypes)

      返回一个 方法对象,它反映此表示的类或接口的指定公共成员方法 对象。

      方法[] getMethods()

      返回包含一个数组 方法对象反射由此表示的类或接口的所有公共方法 对象,包括那些由类或接口和那些从超类和超接口继承的声明。

    • 方法 getDeclaredMethod(String name, <?>... parameterTypes)

      返回一个 方法对象,它反映此表示的类或接口的指定声明的方法 对象。

      方法[] getDeclaredMethods()

      返回包含一个数组 方法对象反射的类或接口的所有声明的方法,通过此表示 对象,包括公共,保护,默认(包)访问和私有方法,但不包括继承的方法。

方法:

	//1. 无参公共方法
	public void sleep(){
		System.out.println("睡觉--"); 
	}
	//2. 带参公共方法
	public void game(String game){
		System.out.println("打游戏:" + game);
	}
	//2. 带参私有方法
	private int study(String course){
		System.out.println("学习"+course+"--"); 
		return 100;
	}

        //getter setter 方法

测试

		//1. 获取类的 Class 对象
		Class clazz = Class.forName("com.jq.reflection.Student");
		
		//2. 反射创建类对象
		Student stu = (Student) clazz.newInstance();
		
		//3.1 获取类中的所有的公共的方法,包括继承的方法
		System.out.println("----所有的公共的方法,包括继承的方法----");
		Method[] methods = clazz.getMethods();
		for (Method method : methods) {
			System.out.println(method);
		}
		//3.2 获取所有权限的方法,包括private修饰的,但不包括继承来的方法
		System.out.println("----所有权限的方法,包括private修饰的,但不包括继承来的方法----");
		Method[] declaredMethods = clazz.getDeclaredMethods();
		for (Method method : declaredMethods) {
			System.out.println(method);
		}
		 
		System.out.println("----invoke方法调用----");
		//3.3 获取指定的公共无参方法
		Method mSleep =clazz.getMethod("sleep", null);	
		mSleep.invoke(stu, null);	//类对象,方法参数(无可不写)
		
		//3.3 获取指定的公共有参方法
		Method mGame =clazz.getMethod("game", String.class);//方法名(String类型) 参数Class类型
		mGame.invoke(stu, "王者荣耀");
		
		//3.4  获取指定的私有带参方法
		Method mStudy = clazz.getDeclaredMethod("study", String.class);
		mStudy.setAccessible(true); //取消对私有方法的访问权限检查
		mStudy.invoke(stu, "java课程");

----结果----
所有的公共的方法,包括继承的方法----
public java.lang.Integer com.jq.reflection.Student.getId()
public void com.jq.reflection.Student.sleep()
public void com.jq.reflection.Student.setId(java.lang.Integer)
public void com.jq.reflection.Student.setSex(java.lang.String)
public java.lang.String com.jq.reflection.Student.getUsername()
public java.lang.String com.jq.reflection.Student.getSex()
public void com.jq.reflection.Student.game(java.lang.String)
public void com.jq.reflection.Student.setUsername(java.lang.String)
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()
所有权限的方法,包括private修饰的,但不包括继承来的方法----
public java.lang.Integer com.jq.reflection.Student.getId()
public void com.jq.reflection.Student.sleep()
public void com.jq.reflection.Student.setId(java.lang.Integer)
public void com.jq.reflection.Student.setSex(java.lang.String)
public java.lang.String com.jq.reflection.Student.getUsername()
public java.lang.String com.jq.reflection.Student.getSex()
public void com.jq.reflection.Student.game(java.lang.String)
public void com.jq.reflection.Student.setUsername(java.lang.String)
private int com.jq.reflection.Student.study(java.lang.String)
invoke方法调用----
睡觉--
打游戏:王者荣耀
学习java课程--

猜你喜欢

转载自blog.csdn.net/qq_42402854/article/details/81266201
今日推荐