反射
java能够反射的前提:已经加载过这个类,就可以通过类名来寻找到这个类的所有相关信息
就跟人类的记忆反射一样,事先在记忆中有了这个事物的具体信息,人就可以通过名字来记忆中寻找相关事务的具体信息
Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
Java反射机制提供的功能
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时调用任意一个对象的成员变量和方法
- 生成动态代理
一个类极限情况都有什么?
- 一个父类,多个接口
- 多个属性
- 多个构造方法,包括构造的参数及其类型
- 多个普通方法,包括参数及其类型,返回值类型
Class类
在Object类中定义了以下的方法,此方法将被所有子类继承:
public final Class getClass()
通过Class可以完整地得到一个类中的完整结构
实例化Class类对象的四种方法
public class Test {
public static void main(String[] args) {
Person p = new Person();
Class c0 = Person.class; //通过类名.Class可以创建指定类的Class实例
Class c1 = p.getClass(); //通过一个类的实例对象的getClass()方法,获取对应实例对象的类的Class实例
try {
//通过Class的静态方法forName(String classname)来获取一个类的Class实例
//forName(String classname)方法中的参数是你要获取的Class实例的类的全路径(包名.类名)
Class c2 = Class.forName("day15.Person");
} catch (Exception e) {
e.printStackTrace();
}
}
}
通过反射调用类的完整结构
- 实现的全部接口
- 所继承的父类
- 全部的构造器
- 全部的方法
- 全部的Field
public class Test1 {
public static void main(String[] args) {
try {
Class c1 = Class.forName("com.company.day15.Student");
Class superclass = c1.getSuperclass();//获取父类
System.out.println(superclass.getName());
Class[] interfaces = c1.getInterfaces(); //获取所有接口
for (Class c : interfaces) {
System.out.println(c.getName());
}
//获取到类的public构造方法
Constructor[] constructors = c1.getConstructors();
for (Constructor c : constructors) {
System.out.println("构造方法名称:" + c.getName()); //获取方法名称
//getModifiers取得方法修饰符,返回数组1代表public
System.out.println("构造方法:" + c.getName() + "的修饰符是:" + c.getModifiers());
//getParameterTypes获取构造方法的参数类型
Class[] parameterTypes = c.getParameterTypes();
for (Class cl : parameterTypes) {
System.out.println("构造方法:" + c.getName() + "的参数类型是:" + cl.getName());
}
}
//获取到类的所有构造方法,包括public和private
Constructor[] declaredConstructors = c1.getDeclaredConstructors();
for (Constructor c : declaredConstructors) {
System.out.println("--------------------------");
System.out.println("构造方法名称:" + c.getName()); //获取方法名称
//getModifiers取得方法修饰符,返回数组1代表public,返回数组2代表private
System.out.println("构造方法:" + c.getName() + "的修饰符是:" + c.getModifiers());
Class[] parameterTypes = c.getParameterTypes();
for (Class cl : parameterTypes) {
System.out.println("构造方法:" + c.getName() + "的参数类型是:" + cl.getName());
}
System.out.println("--------------------------");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
如何用反射的构造方法来创建对象?
//相当于调用Student类的无参public的构造方法
Object o = c1.newInstance();
Student stu = (Student) o;
//指定获取有一个参数并且指定为String类型的public构造方法
Constructor constructor = c1.getConstructor(String.class);
//newInstance实例化对象
Student stu1 = (Student) constructor.newInstance("第一中学");
System.out.println(stu1.school);
通过反射机制,可以强制的调用私有的构造方法
//指定获取有两个参数(String, int)的private构造方法
Constructor declaredConstructor = c1.getDeclaredConstructor(String.class, int.class);
//解除私有的封装
declaredConstructor.setAccessible(true);
Student stu2 = (Student) declaredConstructor.newInstance("张三", 12);
System.out.println(stu2.name + "---" + stu2.age);
通过反射,获取类的所有方法
// Method[] methods = c1.getMethods(); //获取到类的所有的public方法
Method[] methods = c1.getDeclaredMethods(); //获取类的所有方法,包括public和private
for (Method m : methods) {
System.out.println("方法名:" + m.getName());
System.out.println("返回值:" + m.getReturnType());
System.out.println("修饰符:" + m.getModifiers());
Class[] pcs = m.getParameterTypes();//获取方法的参数类型
if (pcs != null && pcs.length > 0) {
for (Class c : pcs) {
System.out.println("参数类型:" + c.getName());
}
}
System.out.println("--------------------");
}
通过反射,获取类的所有属性
//Field[] fields = c1.getFields(); //获取类的public属性,包含父类的public属性
Field[] fields = c1.getDeclaredFields(); //获取本类(不包括父类的属性)的所有属性,包括public和private
for (Field fd : fields) {
System.out.println("修饰符:" + fd.getModifiers());
System.out.println("属性类型:" + fd.getType());
System.out.println("属性名称:" + fd.getName());
}
通过反射,获取类所在的包
Package aPackage = c1.getPackage(); //获取类所在的包
System.out.println(aPackage.getName());
通过反射机制,调用指定方法
/**
* 注意:下面不论是反射调用setInfo还是test方法
* 都调用的是obj对象方法,obj对象实际上就是Student对象
*/
Constructor con = c1.getConstructor(); //获取无参构造
Object obj = con.newInstance(); //使用无参构造创建对象
Method setInfo = c1.getMethod("setInfo", String.class, String.class); //得到名称为setInfo,参数为String,String的方法
setInfo.invoke(obj, "张三", "第一中学"); //参数1是需要实例化的对象,后面的参数是调用当前方法的实参
//如果想调用一个私有方法
Method setInfo1 = c1.getDeclaredMethod("test", String.class);
setInfo1.setAccessible(true); //解除私有封装
setInfo1.invoke(obj, "李四");
//调用一个重载方法
Method setInfo2 = c1.getMethod("setInfo", int.class);
setInfo2.invoke(obj, 23);
//调用有返回值的方法
Method getSchool = c1.getMethod("getSchool");
String school = (String) getSchool.invoke(obj);
System.out.println(school);
通过反射机制,调用指定属性
//反射创建一个对象
Constructor con = c1.getConstructor();
Student stu = (Student) con.newInstance();
Field field = c1.getField("school");//获取名称为school的属性
field.set(stu, "第三中学"); //对stu对象的school属性设置值"第三中学"
String school = (String) field.get(stu);//获取school属性的值
System.out.println(school);
//如果是私有属性
Field field1 = c1.getDeclaredField("classRoom");
field1.setAccessible(true);
field1.set(stu, "高三四班");
String classRoom = (String) field1.get(stu);
System.out.println(classRoom);
Java动态代理
假如:一个java项目,其中有100个java类,每个类中有10个方法,总共1000个方法。
需求:需要在每个java方法上加2句话,在方法执行前输出“这个方法开始执行”,在方法执行后输出“这个方法已经完成”
如果用传统方式改动太麻烦,不现实,所以引进一个特殊的方式:动态代理
public interface ITestDemo {
public void test1();
public void test2();
}
public class TestDemoImpl implements ITestDemo {
@Override
public void test1() {
System.out.println("执行test1()方法");
}
@Override
public void test2() {
System.out.println("执行test2()方法");
}
}
/**
* 动态代理类
*/
public class ProxyDemo implements InvocationHandler {
Object obj; //被代理的对象
public ProxyDemo(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method.getName() + "方法开始执行");
Object result = method.invoke(obj, args); //执行的是指定代理对象的指定方法
System.out.println(method.getName() + "方法执行完毕");
return result;
}
}
public class Test2 {
public static void main(String[] args) {
ITestDemo iTestDemo = new TestDemoImpl();
iTestDemo.test1();
iTestDemo.test2();
System.out.println("===================");
/**
* 需求:
* 在执行方法前打印test1或test2开始执行
* 在执行方法后打印test1或test2执行完毕
* 打印的方法名要和当时调用方法保持一致
*/
InvocationHandler handler = new ProxyDemo(iTestDemo);
/**
* Proxy.newProxyInstance(ClassLoader, interfaces, h)
* 参数1:代理对象的类加载器
* 参数2:被代理对象的接口
* 参数3:代理对象
* 返回值:成功被代理后的对象,返回的是Object类型,需要根据实际情况强转
*/
ITestDemo it = (ITestDemo) Proxy.newProxyInstance(handler.getClass().getClassLoader(), iTestDemo.getClass().getInterfaces(), handler);
it.test1();
System.out.println("===================");
it.test2();
}
}
注意:如果一个对象想要通过Proxy.newProxyInstance方法被代理,那么这个对象的类一定要有相应的接口。