Day14
反射
Java Reflection
Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
Java反射机制提供的功能
- 在运行时判断任意一个对象所属的类。
- 在运行时构造任意一个类的对象。
- 在运行时判断任意一个类所具有的成员变量和方法。
- 在运行时调用任意一个对象的成员变量和方法。
- 生成动态代理。
Java反射机制研究及应用
反射相关的主要API:
- java.lang.Class:代表一个类
- java.lang.reflect.Constructor:代表类的构造方法
- java.lang.reflect.Field:代表类的成员变量
- java.lang.reflect.Method:代表类的方法
一、Class类
概念
- 在Object类中定义了以下的方法,此方法将被所有子类继承:
public final Class getClass()
该方法返回值的类型是一个Class类,此类是Java反射的源头, 实际上所谓反射从程序的运行结果来看也很好理解,即:可以通过对象获得类的名称。
package reflection;
public class TestClass {
public static void main(String[] args) {
Person p = new Person();
Class cla = p.getClass();
// Class<? extends Person> cla = p.getClass();
}
}
反射可以得到的信息包括:某个类的构造器、属性、方法、实现的接口。 对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。一个 Class 对象包含了特定某个类的有关信息。
- Class本身也是一个类。
- Class对象只能由系统建立。
- 一个类在 JVM 中只会有一个Class实例。
- 一个Class对象对应的是一个加载到JVM中的一个 .class文件
- 每个类的实例都会记得自己是由哪个 Class 实例所生成的。
- 通过Class可以完整地得到一个类的完整结构。
Class类的常用方法
方法 | 意义 |
---|---|
static Class forName(String name) | 根据类的全名(包名+类名)获取Class对象 |
Object newInstance() | 创建目标类的对象 |
getName() | 获取类的全名(包名+类名) |
Class getSuperclass() | 获取所有父类的Class对象 |
Class[ ] getInterfaces() | 获取所有实现的接口 |
ClassLoader getClassLoader() | 获取类的类加载器 |
Constructor[ ] getConstructors() | 获取类的所有构造器 |
Field[ ] getDeclaredFields() | 获取类的所有属性 |
Method getMethod(String name, Class… paramTypes) | 获取类的指定方法 |
- 注意:newInstance()方法,jdk9之后建议通过构造器调用,格式如:(clazz是一个Class对象)
clazz.getConstructor().newInstance();
实例化Class类对象的四种方法
- 若已知具体的类,通过类的class属性获取。
例:Class clazz = String.class; - 已知某个类的实例,调用该实例的getClass()方法获取Class对象。
例:Class clazz = “123”.getClass(); - 已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName() 获取,可能抛出ClassNotFoundException
例:Class clazz = Class.forName(“java.lang.String”); - 其他方式(不做要求)。
ClassLoader cl = this.getClass().getClassLoader();
Class clazz4 = cl.loadClass(“类的全类名”);
一般常用forName()获取Class对象。
package reflection;
public class TestClass {
public static void main(String[] args) {
Person p = new Person();
//第一种:已知具体的类
Class c1 = Person.class;
//第二种:已知类的实例
Class c2 = p.getClass();
//第三种:已知类的全名(包名+类名)
try {
Class c3 = Class.forName("reflection.Person");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
二、通过反射调用类的完整结构
1. 继承的父类
- public Class<? Super T> getSuperclass()
返回此 Class 对象所表示的类(接口、基本类型)的父类的 Class对象。
2. 实现的全部接口
- public Class<?>[ ] getInterfaces()
返回此 Class 对象所表示的类或接口实现的全部接口。
3. 全部的构造器
- public Constructor[ ] getConstructors()
返回此 Class 对象所表示的类的所有public构造方法。 - public Constructor[ ] getDeclaredConstructors()
返回此 Class 对象表示的类声明的所有(包括私有)构造方法。 - public Constructor get(Declared)Constructor(Class… paramType)
返回指定参数的构造器,若参数列表为空,则返回无参构造器。
注意:
1.调用私有构造器、属性、方法后,要用 setAccessible(true); 解除private限制。
2.缺省、私有、受保护修饰的都要使用Declared。
Constructor类中:
- 获取构造器修饰符:public int getModifiers()
- 获取构造器方法名称:public String getName()
- 获取构造器参数类型:public Class<?>[ ] getParameterTypes()
代码
1.上述功能代码实现
package reflection;
public class Person {
String name;
int age;
public Person() {}
public Person(String name) {
this.name = name;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
package reflection;
public interface Move {
void move();
}
package reflection;
public interface Work {
void work();
}
package reflection;
public class Student extends Person implements Move, Work{
String school;
public Student() {}
@SuppressWarnings("unused")
private Student(String name, String school) {
super(name);
this.school = school;
}
public Student(String name, int age, String school) {
super(name, age);
this.school = school;
}
@Override
public void work() {
System.out.println("Study");
}
@Override
public void move() {
System.out.println("By bus");
}
@SuppressWarnings("unused")
private void printInfo(int i, int s) {
System.out.println(i + s);
}
public void showInfo(String str, int i) {
System.out.println(str + i);
}
public String returnInfo(String str, int i, Student stu) {
return str + i + stu.name;
}
}
package reflection;
import java.lang.reflect.Constructor;
public class TestClass {
public static void main(String[] args) {
try {
Class cla = Class.forName("reflection.Student");//获取Class对象
Class superCla = cla.getSuperclass();//获取全部的父类Class对象
System.out.println("父类:" + superCla.getName());//父类:reflection.Person
Class[] impleInter = cla.getInterfaces();
for(Class c : impleInter) {
System.out.println("实现的接口:" + c.getName());//实现的接口:reflection.Move
} //实现的接口:reflection.Work
Constructor[] con1 = cla.getConstructors();//获取public构造器
for(Constructor c : con1) {
System.out.println("构造器名称:" + c.getName() + "\t修饰符:" + c.getModifiers());
}//构造器名称:reflection.Student 修饰符:1
Constructor[] con2 = cla.getDeclaredConstructors();//获取所有构造器
for(Constructor c : con2) {
System.out.println("构造器名称:" + c.getName() + "\t修饰符:" + c.getModifiers());
//构造器名称:reflection.Student 修饰符:1 //构造器名称:reflection.Student 修饰符:2
Class[] clazz = c.getParameterTypes();//获取构造器参数类型
for(Class c1 : clazz) {
System.out.println("参数类型:" + c1.getName());
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.使用反射机制创建对象
package reflection;
import java.lang.reflect.Constructor;
/**
* 使用反射机制创建对象
* 无参构造器
* @author MCC
*
*/
public class TestClass {
public static void main(String[] args) {
try {
Class<?> clazz = Class.forName("reflection.Student");
Constructor<?> con = clazz.getConstructor();//调用无参构造器
Object obj = con.newInstance();//无参构造器新建对象 ,不能传入参数
Student stu = (Student)obj;
stu.name = "XiaoMing";
stu.age = 21;
stu.school = "阳光中学";
System.out.println(stu.name + " " + stu.age + " " + stu.school);
} catch (Exception e) {
e.printStackTrace();
}
}
}
package reflection;
import java.lang.reflect.Constructor;
/**
* 使用反射机制创建对象
* 有参构造器
* @author MCC
*
*/
public class TestClass {
public static void main(String[] args) {
try {
Class<?> clazz = Class.forName("reflection.Student");
Constructor<?> con = clazz.getConstructor(String.class, int.class, String.class);//调用三个参数构造器
Object obj = con.newInstance("XiaoMing", 21, "阳光中学");//使用上述构造器新建对象并赋值,若不赋值则初始化为默认值
Student stu = (Student)obj;
System.out.println(stu.name + " " + stu.age + " " + stu.school);
} catch (Exception e) {
e.printStackTrace();
}
}
}
package reflection;
import java.lang.reflect.Constructor;
/**
* 使用反射机制创建对象
* 私有构造器
* @author MCC
*
*/
public class TestClass {
public static void main(String[] args) {
try {
Class<?> clazz = Class.forName("reflection.Student");
Constructor<?> con = clazz.getDeclaredConstructor(String.class, String.class);//调用两个参数的私有构造器
con.setAccessible(true);//解除private限制
Object obj = con.newInstance("XiaoMing", "阳光中学");
Student stu = (Student)obj;
System.out.println(stu.name + " " + stu.age + " " + stu.school);
} catch (Exception e) {
e.printStackTrace();
}
}
}
4. 全部的属性
- public Field[ ] getFields()
返回此Class对象所表示的类或接口的public的Field。 - public Field[ ] getDeclaredFields()
返回此Class对象所表示的类或接口的全部Field。 - public Field get(Declared)Field(String name)
返回类或接口的指定Field。
Field类中:
- 获取Field修饰符:public int getModifiers()
- 获取Field名称:public String getName()
- 获取Filed类型:public Class<?> getType()
5. 全部的方法
- public Method[ ] getMethods()
返回此Class对象所表示的类或接口的public的方法。 - public Method[ ] getDeclaredMethods()
返回此Class对象所表示的类或接口的全部方法。 - public Method get(Declared)Method(String name, Class… paramType)
返回类或接口的指定方法。
Method类中:
- 获取方法修饰符:public int getModifiers(),返回值为1,代表public;返回值为2,代表private
- 获取方法名称:public String getName()
- 获取方法参数类型:public Class<?>[ ] getParameterTypes()
- 获取方法返回值类型:public Class<?> getReturnType()
6. 类所在的包
- Package getPackage()
返回此Class对象所表示的类或接口所在的包。
代码
package reflection;
import java.lang.reflect.*;
public class TestClass {
public static void main(String[] args) {
try {
Class<?> clazz = Class.forName("reflection.Student");
//获取全部属性
// Field[] fd = clazz.getFields();
Field[] fd = clazz.getDeclaredFields();
for(Field f : fd) {
System.out.println("名称:" + f.getName() + "修饰符:" + f.getModifiers() + "类型:" + f.getType());
}
//获取全部方法
Method[] me = clazz.getMethods();
for(Method m : me) {
System.out.println("名称:" + m.getName() + "修饰符:" + m.getModifiers() + "返回值类型:" + m.getReturnType());
Class<?>[] ms = m.getParameterTypes();
// if(ms != null && ms.length > 0) {//不输出空参数
for(Class<?> mc : ms) {
System.out.println("参数类型:" + mc.getName());
}
// }
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
package reflection;
import java.lang.reflect.*;
try {
Class<?> clazz = Class.forName("reflection.Student");
// System.out.println(clazz.getPackageName());
Package pac = clazz.getPackage();
System.out.println(pac.getName());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
三、通过反射调用类中的指定方法、指定属性
调用指定方法
通过反射,调用类中的方法,通过Method类完成。步骤:
- 1.通过Class类的 getMethod(String name, Class… parameterTypes) 方法获取一个Method对象,参数1是方法名,参数2是该方法参数类型的Class对象(区分方法重载)。
- 2.使用 Object invoke(Object obj, Object[ ] args) 进行调用,参数1是调用该方法的对象,参数2是传入该方法的实参。
注意:
Object invoke(Object obj, Object … args)
- Object 对应原方法的返回值,若原方法无返回值,此时返回null。
- 若原方法若为静态方法,此时形参Object obj可为null。
- 若原方法形参列表为空,则Object[ ] args为null。
- 若原方法声明为private,则需要在调用 invoke() 方法前,调用方法对象的setAccessible(true)方法,才可访问private方法。
代码
package reflection;
import java.lang.reflect.*;
public class AppointMethod {
public static void main(String[] args) {
try {
//调用指定的普通方法
Class<?> clazz = Class.forName("reflection.Student");
Constructor<?> con = clazz.getConstructor(String.class, int.class, String.class);
Object obj = con.newInstance("小明", 21, "阳光中学");//有参构造器实例化对象
//获取指定方法
Method m = clazz.getMethod("showInfo", String.class, int.class);
m.invoke(obj, "lalala", 123);//lalala123
//调用指定的私有方法
Constructor<?> con1 = clazz.getConstructor();
Object obj1 = con1.newInstance();//无参构造器实例化对象
//获取指定方法
Method m1 = clazz.getDeclaredMethod("printInfo", int.class, int.class);
//私有方法使用前一定要设置可访问
m1.setAccessible(true);
m1.invoke(obj1, 2, 1);//3
//调用无参方法
Method m2 = clazz.getMethod("move");//无参时,第2个参数不写
m2.invoke(obj);//无参,因此不用初始化,参数2不写 //By bus
//调用重载方法
//重载方法参数名相同,但参数列表不同,只需将参数列表改动为想要调用的方法即可
//调用有返回值方法
Method m3 = clazz.getMethod("returnInfo", String.class, int.class, Student.class);
// String str = (String) m3.invoke(obj, "xyz", 2, (Student)obj);
String str = (String) m3.invoke(obj, "xyz", 2, obj);
System.out.println(str);//xyz2小明
} catch (Exception e) {
e.printStackTrace();
}
}
}
完整代码
package reflection;
import java.lang.reflect.*;
/**
* 反射机制调用指定方法、属性
* @author MCC
*先获取所有方法,再调用其中的一个指定方法,若已知指定方法,可以直接调用
*/
public class AppointMethod {
public static void main(String[] args) {
try {
System.out.println("=============方法=============");
Class<?> clazz = Class.forName("reflection.Student");
//获取所有方法
Method[] me = clazz.getDeclaredMethods();
for(Method m : me) {
System.out.println("方法名:" + m.getName() + "\t修饰符:" + m.getModifiers());
Class<?>[] cla = m.getParameterTypes();
for(Class<?> c : cla) {
System.out.println("参数类型" + c.getName());
}
}
System.out.println("=============构造器=============");
/**
* 运行结果:
* 方法名:move 修饰符:1
* 方法名:work 修饰符:1
* 方法名:printInfo 修饰符:2
* 参数类型int
* 参数类型reflection.Student
* 方法名:showInfo 修饰符:1
* 参数类型java.lang.String
* 参数类型int
*/
//获取所有构造器,目的:挑选合适的构造器实例化对象
Constructor<?>[] co = clazz.getDeclaredConstructors();
for(Constructor<?> c : co) {
System.out.println("构造器名:" + c.getName() + "\t修饰符:" + c.getModifiers());
Class<?>[] cla = c.getParameterTypes();
for(Class<?> ca : cla) {
System.out.println("参数类型:" + ca.getName());
}
}
/**
* 运行结果:
* 构造器名:reflection.Student 修饰符:1
* 参数类型:java.lang.String
* 参数类型:int
* 参数类型:java.lang.String
* 构造器名:reflection.Student 修饰符:2
* 参数类型:java.lang.String
* 参数类型:java.lang.String
* 构造器名:reflection.Student 修饰符:1
*/
//调用指定构造器实例化对象
// Object obj = clazz.getConstructor(String.class, int.class, String.class).newInstance("小明", 21, "阳光中学");
// Student stu = (Student)obj;
//调用指定方法
//接下来可以使用stu进行操作,或者进行如下操作
Constructor<?> con = clazz.getConstructor(String.class, int.class, String.class);
Object obj = con.newInstance("小明", 21, "阳光中学");//有参构造器实例化对象
//获取指定方法
Method m = clazz.getMethod("showInfo", String.class, int.class);
m.invoke(obj, "lalala", 123);
//调用指定的私有方法
Constructor<?> con1 = clazz.getConstructor();
Object obj1 = con1.newInstance();//无参构造器实例化对象
//获取指定方法
Method m1 = clazz.getDeclaredMethod("printInfo", int.class, int.class);
//私有方法使用前一定要设置可访问
m1.setAccessible(true);
m1.invoke(obj1, 2, 1);
} catch (Exception e) {
e.printStackTrace();
}
}
}
调用指定属性
在反射机制中,可以直接通过Field类操作类中的属性,通过Field类提供的 set() 和 get() 方法就可以完成设置和取得属性内容的操作。
- public Field getField(String name):返回此Class对象表示的类或接口的指定的public的Field。
- public Field getDeclaredField(String name):返回此Class对象表示的类或接口的指定的Field。
在Field中:
- public Object get(Object obj):获取指定对象obj上此Field的属性内容。
- public void set(Object obj, Object value):设置指定对象obj上此Field的属性内容。
注:在类中属性都设置为private的前提下,在使用 set() 和 get() 方法时,首先要使用Field类中的 setAccessible(true) 方法将需要操作的属性设置为可以被外部访问。public void setAccessible(true) 访问私有属性时,让这个属性可见。
package reflection;
import java.lang.reflect.*;
public class AppointField {
public static void main(String[] args) {
try {
//反射机制创建一个对象
Class<?> clazz = Class.forName("reflection.Student");
Object obj = clazz.getConstructor().newInstance();
// Student stu = (Student)obj;
//访问指定属性
Field f = clazz.getDeclaredField("school");
//如果是私有属性,要设置权限
// f.setAccessible(true);
f.set(obj, "阳光中学");
String str = (String)f.get(obj);
System.out.println(str);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Java动态代理
需求:一个Java项目包括100个类,每个类有10个方法,一共有1000个方法,现在要在每个方法执行的开始和结束增加一个输出语句,即,在方法执行前输出:开始执行,在方法执行结束后输出:执行完毕。我们要手动更改这1000个方法吗?因为每个方法要增加的内容相同,有没有一个统一的处理办法呢?
- Proxy :专门完成代理的操作类,是所有动态代理类的父类。通过此类为一个或多个接口动态地生成实现类。
- 创建一个动态代理类所对应的Class对象:
static Object newProxyInstance(ClassLoader loader, Class<?>[ ] interfaces, InvocationHandler h)
参数意义:
ClassLoader loader:代理类的类加载器。
Class<?>[ ] interfaces:实现类实现的全部接口。
InvocationHandler h:代理类的对象。
返回值:
接口的代理对象,要用接口类型接收。
动态代理步骤:
- 1.创建一个实现 InvocationHandler接口的类,并实现 invoke()方法,以完成代理的具体操作。
- 2.创建被代理的类以及实现其他接口。
- 3.通过 Proxy 的静态方法:
newProxyInstance(ClassLoader loader, Class<?>[ ] interfaces, InvocationHandler h) 创建一个接口代理对象,注意,该对象若要进行强制类型转换,只能用接口类型转换和接收。 - 4.通过步骤3创建的接口代理对象调用实现类的方法。
注意:如果一个类想要通过 Proxy.newProxyInstance()的方法被代理,那么该类一定要实现了某些接口,没有实现接口的类,不能被动态代理。
package reflection.proxy;
/**
* 接口
* @author MCC
*
*/
public interface Inter {
void showInfo();
double count();
}
package reflection.proxy;
/**
* 实现类
* @author MCC
*
*/
public class ImpInter implements Inter{
double fnum;
double lnum;
public ImpInter() {}
public ImpInter(double fnum, double lnum) {
this.fnum = fnum;
this.lnum = lnum;
}
@Override
public void showInfo() {
System.out.println("fnum:" + this.fnum + "\tlnum:" + this.lnum);
}
@Override
public double count() {
double res = this.fnum + this.lnum;
return res;
}
}
package reflection.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 动态代理类:代理的实际是接口
* @author MCC
*
*/
public class DynamicProxy implements InvocationHandler{
Object obj;//被代理对象
public DynamicProxy(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method.getName() + " 开始执行!");
Object res = method.invoke(this.obj, args);
System.out.println(method.getName() + " 执行完毕!");
return res;
}
}
package reflection.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) {
// Inter test = new ImpInter();
// InvocationHandler handler = new DynamicProxy(new ImpInter());
InvocationHandler handler = new DynamicProxy(new ImpInter(1.21, 3.62));
// Inter it = (Inter)Proxy.newProxyInstance(handler.getClass().getClassLoader(), new ImpInter().getClass().getInterfaces(), handler);
Inter it = (Inter)Proxy.newProxyInstance(DynamicProxy.class.getClassLoader(), new ImpInter().getClass().getInterfaces(), handler);
it.showInfo();
it.count();
}
}