java-反射

Posted 甘劭

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java-反射相关的知识,希望对你有一定的参考价值。

1. Class实例

其实就是一些类型(类 接口 数组 基本数据类型  void)的字节码对象

Class 类的实例表示正在运行的 Java 应用程序中的类和接口(字节码对象);

枚举是一种类,注释(指的是注解Annotation)是一种接口;

每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象;

基本的 Java 类型(booleanbytecharshortintlongfloatdouble)和关键字 void 也表示为 Class 对象;

  注意

  1 Class和它的实例的产生: Class的实例是已经存在的类型,所以不能够直接new一个Class对象出来,而通过已知的类型和Class来获得

  2、同一种类型不管通过什么方式得到Class的实例都是相等的;一个类型的字节码对象只有一份

    线程同步:同步监听对象字节码对象来充当同步监听  始终保证都共享的是同一个同步监听对象

  3Class的实例就看成是Java中我们学过的所有的数据类型在JVM中存在的一种状态(字节码对象)

    String.class   int.class  List.class  int[].class  int[][].class

在 Java API 中,获取 Class 类对象有三种方法:

第一种,使用 Class.forName 静态方法。当你知道该类的全路径名时,你可以使用该方法获取 Class 类对象。

Class clz = Class.forName("java.lang.String");

第二种,使用 .class 方法。

这种方法只适合在编译前就知道操作的 Class。

Class clz = String.class;

第三种,使用类对象的 getClass() 方法。

String str = new String("Hello");
Class clz = str.getClass();

 Class实例 的其他获得方式的一些注意点

public static void main(String[] args) throws Exception {
	//1. 获取接口的字节码对象的两种方式
		Class list1 = Class.forName("java.util.List");
		Class list2 = List.class;
		System.out.println(list1 == list2);  //true
		
		
	//2. 获取数组的字节码对象的两种方式
		int[] a = {1,5,8};
		Class a1 = int[].class;
		Class a2 = a.getClass();
		System.out.println(a1 == a2);        //true
		
		int[] b = {};
		Class b1 = b.getClass();
		System.out.println(a1 == b1);		//true
		
		String[] s = {};
		Class s1 = s.getClass();
		String[][] ss = {};
		Class s2 = ss.getClass();
		System.out.println(s1 == s2);		//false
//		综上:具有相同元素类型  和  维数的数组都共享该 Class 对象;
		
		
	//3. 获取基本数据类型、包装类型 的字节码对象的几种方式
		Class int1 = int.class;
		Class int2 = Integer.TYPE;
		//获取 包装类型  的字节码对象的三种方式
		Class intc1 = Class.forName("java.lang.Integer");
		Class intc2 = Integer.class;
		Class intc3 = new Integer(10).getClass();
		
/*	注意点:	Integer 是 Object Type 对象类型, int 是 Primitive Type 原始类型
			Integer 有成员变量,有成员方法,int 无成员变量,无成员方法
			Integer.class 与 int.class 不同
			Integer.TYPE 与 int.class 相同
*/
		System.out.println(int1 == int2);   //true
		System.out.println(intc1 == int2); 	//false
		System.out.println(intc1 == intc3);  //true
		
	 
/*	4. void 和基本数据类型只能通过类型名称获取该字节码对象
		 还可以通过其对应的包装类的TYPE属性获取其字节码对象。
		 * eg 获取int的字节码对象
		 * 		int.class或者 Integer.TYPE
		 * 	  获取void的字节码对象
		 * 		void .class 或者 Void.TYPE
		 * 
	其中: Void类是一个不可实例化的占位符类,它持有对标识Java关键字void的Class对象的引用。
		 并且本身的构造函数为private,即该类是不可以实例化的
	底层代码:	
		public final class Void {
		    public static final Class<Void> TYPE = (Class<Void>) Class.getPrimitiveClass("void");
		    private Void() {}
		}
*/		
		 Class v1 = void.class;
		 Class v2 = Void.TYPE;
		 
		 Class vc1 = Void.class;
		 Class vc2 = Class.forName("java.lang.Void");
		 
		 System.out.println(v1 == v2);    //true
		 System.out.println(v1 == vc2);   //false
	}

  

 

2. Constructor类是什么?
  Constructor是一个类,位于java.lang.reflect包下。
  在Java反射中 Constructor类描述的是 类的构造方法信息

如何获取Constructor类对象?
    - getConstructors(): 获取一个类中所有非私有化的构造方法

     - getConstructor(): 获取非私有 无参的构造方法
    - getConstructor(Class[] params): 获取类的特定构造方法,params参数指定构造方法的参数类型


    - getDeclaredConstructors(): 获取类中所有的构造方法(public、protected、default、private)

    - getDeclaredConstructor():   获取私有化的无参构造方法
    - getDeclaredConstructor(Class[] params): 获取类的特定构造方法,params参数指定构造方法的参数类型

 

package com.ganshao.Test2;
import java.lang.reflect.Constructor;

//Person 类
class Person {
	private String name;
	public Person(String name) {
		this.name = name;
	}
	private Person() {}
}
//Student 类
class Student {
	public Student(String name) {}
}
public class Test1 {
	public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException {
		Class c1 = Class.forName("com.ganshao.Test2.Person");//获取Person类的字节码对象
		/*
		 * public Constructor<T> getConstructor(Class<?>... parameterTypes)
		 * 返回一个 Constructor 对象,它反映此 Class 对象所表示的类的指定公共构造方法。
		 * */
		Constructor pc1= c1.getConstructor(String.class);		//不可以去获取私有化的构造方法
		Constructor con1 = c1.getDeclaredConstructor();			//可以去获取私有化的无参构造方法
		System.out.println(pc1);    //public com.ganshao.Test2.Person(java.lang.String)
		System.out.println(con1);   //private com.ganshao.Test2.Person()
		
		System.out.println("---");
	//获取一个类中所有非私有化的构造方法
		Constructor[] cons = c1.getConstructors();
		for (Constructor constructor : cons) {
			System.out.println(constructor);
		}
		System.out.println("---");
		
	//获取一个类中所有的构造方法(跟权限无关)
		Constructor[] cons1 = c1.getDeclaredConstructors();   //注意末尾加了   "s"
		for (Constructor constructor : cons1) {
			System.out.println(constructor);
		}
		System.out.println("---");
		
		Constructor s1 = Student.class.getDeclaredConstructor();
		System.out.println(s1);     //报错:NoSuchMethodException,因为没有私有的构造方法
	}
}

  

3. 创建对象三种的方式
  * 1.  new 构造器 ,可以创建有参数对象、无参数对象,不能调用priavte的构造方法
  * 2.  字节码对象.newInstance()   只能够调用 无参数的 非私有的 构造方法, 故当类的 无参的构造方法 被私有化就会报错。
  * 3.  通过Constructor的对象.newInstance(Object...init)获得    //可以创建有参数对象、无参数对象,通过setAccessible(true)的方式去调用priavte的构造方法

 注意:方法2.3是通过反射创建类对象的

 

AccessibleObject 类   是Method、Field、Constructor类的基类,

它提供了标记反射对象的能力,以抑制在使用时使用默认Java语言访问控制检查,从而能够任意调用被私有化保护的方法、域和构造函数;

  提供了:void setAccessible(boolean flag) 方法

    值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。值为 false 则指示反射*的对象应该实施 Java 语言访问检查。

//Student 类
class Student {
	private String name;
	
	public Student(String name) {this.name = name;}
	private Student() {}
	
	@Override
	public String toString() {
		return "Student [name=" + name + "]";
	}
}
public class Test1 {
	public static void main(String[] args) throws Exception {	
	//2. 字节码对象.newInstance()
//		Class c = Student.class;
//		Student s = (Student)(c.newInstance());
//		System.out.println(s);   // 此时 如果 Student()构造方法 访问权限设置为 private 会报错。
		
	//3. 通过Constructor方法获得
		Class c = Student.class;
		Constructor constructor = c.getDeclaredConstructor();
		constructor.setAccessible(true);     //让方法的private修饰符在这一次创建对象时失效
		Student s2 =(Student)(constructor.newInstance());
		System.out.println(s2);            //Student [name=null]
		
		Constructor constructor1 = c.getConstructor(String.class);
		Student s3 =(Student)(constructor1.newInstance("张三"));
		System.out.println(s3);          //Student [name=张三]
	}
}

  

4. 通过反射获取类属性、方法、构造器

我们通过 Class 对象的 getFields() 方法可以获取 Class 类的属性,但无法获取私有属性。

Class clz = Apple.class;
Field[] fields = clz.getFields();
for (Field field : fields) {
    System.out.println(field.getName());
}

输出结果是:

price

而如果使用 Class 对象的 getDeclaredFields() 方法则可以获取包括私有属性在内的所有属性:

Class clz = Apple.class;
Field[] fields = clz.getDeclaredFields();
for (Field field : fields) {
    System.out.println(field.getName());
}

输出结果是:

name
price

与获取类属性一样,当我们去获取类方法、类构造器时,如果要获取私有方法或私有构造器,则必须使用有 declared 关键字的方法。

其中 :注意末尾加不加  “s” ,加s 获取指定的,    不加 s 获取所有的。     getMethod(可以有参数)         getMethods()   。

 

5. 通过反射执行方法

获得字节码对象-->获取方法对象  -->通过方法的对象.invoke() 去执行方法

Object invoke(Object obj, Object... args)
返回值类型Object 参数:该方法所属的对象, [...]多个实际参数

package 通过反射执行方法;
import java.lang.reflect.Method;

class Pig{
	private void t1(){
		System.out.println("t1方法执行了");
	}
	public int t2(int i){
		return i;
	}
	public static void t3(){
		System.out.println("t3 方法执行了 ");
	}
}
public class Test1 {
	public static void main(String[] args) throws Exception {
		/*通过反射执行方法
		Object invoke(Object obj, Object... args)   
		返回值类型Object   参数:该方法所属的对象,  [...]多个实际参数*/
		
		Class pigClass = Pig.class;                      //获得字节码对象
		Method m = pigClass.getMethod("t2", int.class);  //获取方法对象
		System.out.println(m);           //public int 通过反射执行方法.Pig.t2(int)
		Object obj = m.invoke(new Pig(),100);            //通过方法的对象.invoke() 去执行方法
		System.out.println(obj);          //100
	}
}

  











以上是关于java-反射的主要内容,如果未能解决你的问题,请参考以下文章

反射机制入门

反射机制入门

反射机制入门

使用反射在外部JAR / CLASS上调用包含Hibernate事务的方法(Java EE)

为啥我的 Ray March 片段着色器反射纹理查找会减慢我的帧速率?

OpenGL片段着色器不照亮场景