1. Java Reflection
Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法
2. Java反射机制提供的功能
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时调用任意一个对象的成员变量和方法
- 生成动态代理
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)
说明:
- Object 对应原方法的返回值,若原方法无返回值,此时返回null
- 若原方法若为静态方法,此时形参Object obj可为null
- 若原方法形参列表为空,则Object[] args为null
- 若原方法声明为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());