Java之反射

1. Java Reflection

Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法

2. Java反射机制提供的功能

  1. 在运行时判断任意一个对象所属的类
  2. 在运行时构造任意一个类的对象
  3. 在运行时判断任意一个类所具有的成员变量和方法
  4. 在运行时调用任意一个对象的成员变量和方法
  5. 生成动态代理

3. Java反射机制研究及应用

3.1 反射相关的主要API

java.lang.Class:代表一个类
java.lang.reflect.Method:代表类的方法
java.lang.reflect.Field:代表类的成员变量
java.lang.reflect.Constructor:代表类的构造方法

4. Class类

4.1 Class类的常用方法

4.2 实例化Class类对象(四种方法)

前提:若已知具体的类,通过类的class属性获取,该方法 最为安全可靠,程序性能最高
实例:Class clazz = String.class;
前提:已知某个类的实例,调用该实例的getClass()方法获取Class对象
实例:Class clazz = “www.xyd.com”.getClass();
前提:已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException
实例:Class clazz = Class.forName(“java.lang.String”);
其他方式(不做要求)
ClassLoader cl = this.getClass().getClassLoader();
Class clazz4 = cl.loadClass(“类的全类名”);

4.3 代码

public class Test1 {
	public static void main(String[] args) {
		Person p = new Person();
		
		//若已知具体的类,通过类的class属性获取
		Class s0 = Person.class;
		
		//已知某个类的实例,调用该实例的getClass()方法获取Class对象
		Class s1 = p.getClass();
		System.out.println(s1.getCanonicalName());
		
		//通过Class的静态方法forName(String className)来获取一个类的Class实例
		//forName(String className)方法中的参数是你要获取的Class实例的类的全路径(包名.类名)
		try {
			Class s2 = Class.forName("reflectionBag.Person");//这个是获取Class实例的常用方式
		
		}catch(ClassNotFoundException e){
			e.printStackTrace();
		}
	}

}

5. 通过反射调用类的完整结构

使用反射可以取得:

5.1 实现的全部接口

public Class<?>[] getInterfaces()
确定此对象所表示的类或接口实现的接口。

5.2 所继承的父类

public Class<? Super T> getSuperclass()
返回表示此 Class 所表示的实体(类、接口、基本类型)的父类的 Class。

5.3 全部的构造器

public Constructor<T>[] getConstructors()
返回此 Class 对象所表示的类的所有public构造方法。
public Constructor<T>[] getDeclaredConstructors()
返回此 Class 对象表示的类声明的所有构造方法。

5.4 Constructor类中

取得修饰符: public int getModifiers();
取得方法名称: public String getName();
取得参数的类型:public Class<?>[] getParameterTypes();

5.5 代码

public class Test2 {
	public static void main(String[] args) {
//		Student s = new Student();
		try {
			Class clazz = Class.forName("reflectionBag.Student");
			Class superClazz = clazz.getSuperclass();//获取父类
			System.out.println("父类:"+superClazz.getName());
			
			//获取所有实现接口名称
			Class[] interClazz = clazz.getInterfaces();
			for(Class c: interClazz) {
				System.out.println("接口:"+c.getName());
			}
			System.out.println("==========");
			//获取共有的构造方法
			Constructor[] cons = clazz.getConstructors();
			for(Constructor c:cons) {
				System.out.println("公有的构造方法名:"+c.getName());
				//getModifiers取得方法的修饰符,返回数字1代表public
				System.out.println("公有的构造方法名:"+c.getName()+"方法修饰符:"+c.getModifiers());
				//getParameterTypes()取得参数类型,需要一个数组接收
				Class[] partypes = c.getParameterTypes();
				for(Class p:partypes) {
					System.out.println("公有的构造方法名:"+c.getName()+"方法修饰符:"+c.getModifiers()+"参数类型"+p.getName());
				}
				
		
			}
			System.out.println("============================");
			//获取所有的构造方法,包括共有、私有,返回数字2代表private
			Constructor[] cons1 = clazz.getDeclaredConstructors();
			for(Constructor c:cons1) {
				System.out.println("所有的构造方法名:"+c.getName());
				//getModifiers取得方法的修饰符,返回数组1代表public
				System.out.println("所有的构造方法名:"+c.getName()+"方法修饰符:"+c.getModifiers());
				//getParameterTypes()取得参数类型,需要一个数组接收
				Class[] partypes = c.getParameterTypes();
				for(Class p:partypes) {
					System.out.println("所有的构造方法名:"+c.getName()+"方法修饰符:"+c.getModifiers()+"参数类型"+p.getName());
				}
			}
			
		}catch(ClassNotFoundException e) {
			e.printStackTrace();
		}
	}

	
}

6. 通过反射调用类中的指定方法

格式:
Object invoke(Object obj, Object … args)
说明:

  1. Object 对应原方法的返回值,若原方法无返回值,此时返回null
  2. 若原方法若为静态方法,此时形参Object obj可为null
  3. 若原方法形参列表为空,则Object[] args为null
  4. 若原方法声明为private,则需要在调用此invoke()方法前,显式调用方法对象的setAccessible(true)方法,将可访问private的方法。

6.1 调用共有的无参构造

Object obj = clazz.newInstance();//clazz.newInstance()实例化对象,相当于调用类的无参构造
Student s = (Student)obj;//Object对象强转成Student对象

6.2 调用共有的有参构造

Constructor c = clazz.getConstructor(String.class);//指定获取一个参数为string的构造函数
Student stu = (Student)c.newInstance("第一中学");//c.newInstance("")实例化对象,并强转为Student对象

6.3 调用私有的构造方法

Constructor c1 = clazz.getDeclaredConstructor(String.class,int.class);//指定获取参数为string和int的私有构造
c1.setAccessible(true);//解除私有的封装,下面就可以对这个私有方法强制调用
Student stu1 = (Student)c1.newInstance("第一中学",20);

6.4 获取类的所有方法

Method[] ms = clazz.getMethods();//获取共有的方法
//Method[] ms = clazz.getDeclaredMethods();//获取所有方法,包括共有和私有
			for(Method m:ms) {
				System.out.println("方法名:"+m.getName());//获取方法的方法名
				System.out.println("修饰符::"+m.getModifiers());//获取方法的修饰符
				System.out.println("返回值类型:"+m.getReturnType());//获取方法的返回值类型
				Class [] ps = m.getParameterTypes();//获取方法的参数类型,为数组,通过for each循环获取
				if(ps!=null && ps.length>0) {
					for(Class p:ps) {
						System.out.println(p.getName());
					}
				}

6.5 调用类的指定的共有方法

Constructor c3 = clazz.getConstructor();
Object obj = c3.newInstance();
Method m1 = clazz.getMethod("work", int.class);//调用共有方法
m1.invoke(obj,11);

6.6 调用类的指定的私有方法

Method m2 = clazz.getDeclaredMethod("test", String.class);//调用私有方法
m2.setAccessible(true);//解除私有的封装,下面就可以对这个私有方法强制调用
m2.invoke(obj, "李四");

6.7 调用类的重载方法

Method m3 = clazz.getMethod("work", int.class, String.class);
m3.invoke(obj, 20,"李四");

6.8 调用有返回值无参数的方法

Method m4 = clazz.getMethod("getSchool");
String school = (String)m4.invoke(obj);
System.out.println(school);

7. 通过反射调用类中的指定属性

7.1 调用指定共有属性

//通过反射创建一个实例
Constructor con = clazz.getConstructor();//创建一个的构造方法
Student stu = (Student)con.newInstance();//创建一个实例

Field f = clazz.getField("school");//获取名称为school的属性
f.set(stu, "第一中国");//设置school属性值
String school1 = (String)f.get(stu);//获取stu对象的属性值
System.out.println(school1);

7.2 调用指定私有属性

Field f1 = clazz.getDeclaredField("age");
f1.setAccessible(true);//解除私有的封装,下面就可以强制的调用这个属性
f1.set(stu, 20);//set()传入参数值
int age = (int) f1.get(stu);//get()获取参数值
System.out.println(age);

8. 获取类所在的包

Package p = clazz.getPackage();
System.out.println(p.getName());
posted @ 2020-01-15 14:10  吓吾一跳  阅读(184)  评论(0编辑  收藏  举报