JavaSE 反射机制(下)
四、获取类的方法
- 获取方法函数:
public Method[] getDeclaredMethods()
返回此Class对象所表示的类或接口的全部方法。public Method[] getMethods()
返回此Class对象所表示的类或接口的public的方法。
- 常用方法:(在获取方法过程中)
方法名 | 功能 |
Method[] getMethods() | 获取类的所有公有方法 |
Method[] getDeclaredMethods() | 获取类的所有方法(公有+私有) |
getName() | 获取方法名称 |
getReturnType() | 获取返回值类型 |
getModifiers() | 获取修饰符(public–>1; private–>2) |
getParameterTypes() | 获取方法的参数类型,返回Class[], 数组元素个数为参数个数 |
- 上述方法案例:
package com.reflection;
//父类
public class Person {
public String name;
int age;
}
package com.reflection;
//子类
public class Student extends Person{
String school;
private String privateField;
//无参构造
public Student(){
System.out.println("调用的是public Student()");
}
//有参构造
public Student(String school){
System.out.println("调用的是public Student(String school)");
this.school = school;
}
//私有有参构造
private Student(String name, int age){
System.out.println("调用的是private Student(String name, int age)");
this.name = name;
this.age = age;
}
public void moveType() {
System.out.println("骑自行车上学");
}
public void studyInfo() { System.out.println("学习中学知识"); }
public String getSchool(){
return this.school;
}
private void test(String name){
}
}
package com.reflection;
import java.lang.reflect.Method;
public class Test3 {
public static void main(String[] args) {
try {
Class clazz3 = Class.forName("com.reflection.Student");
//获取到类的所有公有方法
Method[] ms = clazz3.getMethods();
for (Method m : ms) {
System.out.println("方法名: "+ m.getName());
System.out.println("返回值类型: "+ m.getReturnType());
System.out.println("修饰符: "+ m.getModifiers());
Class[] pcs = m.getParameterTypes();//获取方法的参数类型,返回Class数组,方法参数个数等于数组元素个数
if (pcs.length>0){
for (Class pc : pcs) {
System.out.println("参数类型: " + pc.getName());
}
}
System.out.println("-----------------------------------------------");
}
System.out.println("\n========分==================割===================线===========\n");
//获取类的所有方法,包含公有私有
Method[] ams = clazz3.getDeclaredMethods();
for (Method m : ams) {
System.out.println("方法名: "+ m.getName());
System.out.println("返回值类型: "+ m.getReturnType());
System.out.println("修饰符: "+ m.getModifiers());
Class[] pcs = m.getParameterTypes();//获取方法的参数类型,返回Class数组,方法参数个数等于数组元素个数
if (pcs.length>0){
for (Class pc : pcs) {
System.out.println("参数类型: " + pc.getName());
}
}
System.out.println("-----------------------------------------------");
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
/*运行结果:
方法名: moveType
返回值类型: void
修饰符: 1
-----------------------------------------------
方法名: studyInfo
返回值类型: void
修饰符: 1
-----------------------------------------------
方法名: getSchool
返回值类型: class java.lang.String
修饰符: 1
-----------------------------------------------
========分==================割===================线===========
方法名: test
返回值类型: void
修饰符: 2
参数类型: java.lang.String
-----------------------------------------------
方法名: moveType
返回值类型: void
修饰符: 1
-----------------------------------------------
方法名: studyInfo
返回值类型: void
修饰符: 1
-----------------------------------------------
方法名: getSchool
返回值类型: class java.lang.String
修饰符: 1
-----------------------------------------------
*/
五、获取类的属性和包
- 获取属性函数:
public Field[] getDeclaredFields()
返回此Class对象所表示的类或接口的全部属性。public Field[] getFields()
返回此Class对象所表示的类或接口的public的属性。
注意:1. getDeclaredFields()仅获取本类中所有属性,不包含父类。
2. getFields()获取本类及其父类中的public修饰属性。
- 获取包函数:
Package getPackage()
返回此Class对象所在的包名称。
- 常用方法:(在获取属性和包中)
方法名 | 功能 |
getFields() | 获取类(本类及父类)的公有属性 |
getDeclaredFields() | 获取本类中所有的属性(公有+私有) |
getModifiers() | 获取属性修饰符 |
getType() | 获取属性的类型 |
getName() | 获取属性名称 |
Package getPackage() | 获取该类所属包名 |
- 以上方法案例展示:
注:父类Person()、子类Student()同(四)中案例。
package com.reflection;
import java.lang.reflect.Field;
public class Test5 {
public static void main(String[] args) {
try {
Class clazz4 = Class.forName("com.reflection.Student");
Field[] fs = clazz4.getFields(); //获取类中所有公有属性,同时包含父类的属性
for (Field f : fs) {
System.out.println("修饰符: " + f.getModifiers());
System.out.println("属性类型: " + f.getType());
System.out.println("属性名称: " + f.getName());
System.out.println("-----------------------------------");
}
System.out.println("\n==========分===========割===========线===========\n");
Field[] afs = clazz4.getDeclaredFields(); //获取类中所有属性(私有+公有),不包含父类的属性。
for (Field af : afs) {
System.out.println("修饰符: " + af.getModifiers());
System.out.println("属性类型: "+af.getType());
System.out.println("属性名称: " + af.getName());
System.out.println("-----------------------------------");
}
System.out.println("\n==========分===========割===========线===========\n");
Package p = clazz4.getPackage();
System.out.println(p.getName());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
/*运行结果:
修饰符: 1
属性类型: class java.lang.String
属性名称: school
-----------------------------------
修饰符: 1
属性类型: class java.lang.String
属性名称: name
-----------------------------------
==========分===========割===========线===========
修饰符: 1
属性类型: class java.lang.String
属性名称: school
-----------------------------------
修饰符: 2
属性类型: class java.lang.String
属性名称: privateField
-----------------------------------
==========分===========割===========线===========
com.reflection
*/
六、调用类中指定方法、指定属性
– 调用指定方法
注:方法调用前需要通过构造器创建obj对象,为之后的方法调用做准备!
实际调用分以下四种情况讨论:
- 调用公有方法
Method m = clazz.getMethod("调用方法名","参数.class"...);
m.invoke(obj,"args"...);
//此处的invoke英文意思有“唤起”的意思,此处表示向对象obj中注入参数。 - 调用私有方法
需要额外注明m.setAccessible(true)
,该语句表示解除私有封装,强行调用。
其他语句同上。 - 调用重载方法
只需注意填入参数的不同即可。 - 调用有返回值的方法
需要注意对返回值进行接收,接受过程中可能需要对对象进行强转。
以上情况案例展示:
注:该案例引入Teacher类,其父类Person同上。
package com.reflection;
//Teacher类
public class Teacher extends Person{
public String school;
private String privateField;
//私有方法
private void test(String name){
this.name = name;
System.out.println("这里调用private void test(String name)方法");
}
//公有方法
public void setInfo(String name, String school){
this.name = name;
this.school = school;
System.out.println("这里调用public void setInfo(String name, String school)方法");
}
//重载方法
public void SetInfo(String school){
this.school = school;
System.out.println("这里调用public void setInfo(String school)方法");
}
//有返回值方法
public String getSchool(){
System.out.println("这里调用public String getSchool()方法");
return school;
}
}
package com.reflection;
//调用指定方法
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class Test6 {
public static void main(String[] args) {
try {
/*
以下不论是反射调用setInfo还是test方法
都调用的obj对象的方法,obj对象实际上就是Teacher对象
*/
Class clazz5 = Class.forName("com.reflection.Teacher");
Constructor con = clazz5.getConstructor(); //获取无参构造
Object obj = con.newInstance(); //使用无参构造创建对象
//调用公有方法
Method m = clazz5.getMethod("setInfo", String.class, String.class); //得到名称为setInfo,参数是(String,String)的方法
m.invoke(obj,"卡莉","第一中学");//参数1为需要实例化的对象,参数2为调用当前方法的实际参数
//调用私有方法(需要解封)
Method m1 = clazz5.getDeclaredMethod("test", String.class);
m1.setAccessible(true); //解除私有封装,则可调用私有方法
m1.invoke(obj,"苏瞻");
//调用重载方法,只需注意填入参数不同即可
Method m2 = clazz5.getMethod("SetInfo", String.class);
m2.invoke(obj,"第二中学");
//调用有返回值的方法,需要对返回值进行接收
Method m3 = clazz5.getMethod("getSchool");
String school = (String)m3.invoke(obj);//调用有返回值但是没有参数的方法
System.out.println(school);
} catch (Exception e) {
e.printStackTrace();
}
}
}
– 调用指定属性
注:方法调用前需要通过构造器创建obj对象,此处案例强转为Teacher对象,为之后调用属性准备!
实际调用分两种情况讨论:
- 调用公有属性
Field f = clazz.getField("属性名称");
f.set(对象名,“传入参数”);
//设置对应属性值f.get(对象名);
//获取对象的对应属性值 - 调用私有属性
Field f = clazz.getDeclaredField("私有属性名称");
f.setAccessible(true)
//非常重要!!!进行解除封装f.set(对象名,“传入参数”)
//设置对应属性值f.get(对象名);
//获取对象的对应属性值
以上情况案例展示:
注:Teacher类同上
package com.reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
public class Test7 {
public static void main(String[] args) {
try {
Class clazz6 = Class.forName("com.reflection.Teacher");
//反射创建一个对象
Constructor con = clazz6.getConstructor();
Teacher tea = (Teacher)con.newInstance();
//调用公有属性
Field f = clazz6.getField("school");
f.set(tea,"第三中学");//对tea对象的school属性设置值 "第三中学"
String school = (String)f.get(tea); //获取tea对象的school属性值
System.out.println(school);
System.out.println("----------------");
//调用私有属性:使用getDeclareField()方法,并解除封装
Field f1 = clazz6.getDeclaredField("privateField");
f1.setAccessible(true);
f1.set(tea,"测试私有属性");
String field = (String)f1.get(tea);
System.out.println(field);
} catch (Exception e) {
e.printStackTrace();
}
}
}
/*运行结果:
第三中学
----------------
测试私有属性
*/
七、动态代理
什么是动态代理?
例:一个Java项目,其中有100个java类,每个java有10个方法,总共1000个方法。现在有一个需求,需要在每个java方法上加上2句话,在方法执行前输出“这个方法开始执行”,在方法执行后输出“这个方法已经完成”。此时就需要使用到动态代理!
Proxy :专门完成代理的操作类,是所有动态代理类的父类。通过此类为一个或多个接口动态地生成实现类。
动态代理步骤:
- 创建需要被代理的类以及接口。
- 创建一个实现接口
InvocationHandler
的类,它必须实现invoke方法,以完成代理的具体操作。(ProxyDemo为实现接口的类,test为被代理对象),则使用如下语句生成代理对象handler。InvocationHandler handler = new ProxyDemo(test);
- 通过Proxy的静态方法,返回一个Subject接口代理。
newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)
- 通过Subject代理调用RealSubject实现类的方法。
注意:如果一个对象想要通过Proxy.newProxyInstance方法被代理,那么这个对象的类一定要有相应的接口。
案例展示:
package com.proxy;
//被代理的接口
public interface ITestDemo {
void test1();
void test2();
}
package com.proxy;
//实现被代理接口的类
public class TestDemoImpl implements ITestDemo{
@Override
public void test1() {
System.out.println("执行test1()方法");
}
@Override
public void test2() {
System.out.println("执行test2()方法");
}
}
package com.proxy;
//实现了InvocationHandler接口的动态代理类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* 动态代理类
*/
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(this.obj,args); //执行的是指定代理对象的指定的方法
System.out.println(method.getName()+"方法执行完成");
return result; //返回Object对象
}
}
注意:其中invoke()方法有三个参数:
- 动态代理类的引用,通常情况下不需要它。但可以使用getClass()方法,得到proxy的Class类从而取得实例的类信息,如方法列表,annotation等。
- 方法对象的引用,代表被动态代理类调用的方法。从中可得到方法名,参数类型,返回类型等等
- args对象数组,代表被调用方法的参数。注意基本类型(int,long)会被装箱成对象类型(Interger, Long)
package com.proxy;
//测试类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class Test1 {
public static void main(String[] args) {
//如果一个对象想要通过Proxy.newProxyInstance方法被代理
//那么这个对象的类一定要有相应的接口
//就像本类中ITestDemo接口和实现类TestDemoImpl
ITestDemo test = new TestDemoImpl();
test.test1();
test.test2();
System.out.println("====================================");
/**
* 需求:
* 在执行test1和test2方法时,需要加入一些东西
* 在执行方法前打印test1/test2开始执行
* 在执行方法后打印test1/test2执行完成
* 打印的方法名要和当时调用的方法保存一致
*/
InvocationHandler handler = new ProxyDemo(test); //handler为代理对象,代理test
//参数1为代理对象的类加载器
//参数2为被代理的对象的接口
//参数3为代理对象
//返回的值是成功被代理后的对象,返回Object类型,需要根据当时的情况去转换类型
ITestDemo t = (ITestDemo) Proxy.newProxyInstance(handler.getClass().getClassLoader(),test.getClass().getInterfaces(),handler);
t.test1();
System.out.println("====================================");
t.test2();
}
}
/*测试结果:
执行test1()方法
执行test2()方法
====================================
test1方法开始执行
执行test1()方法
test1方法执行完成
====================================
test2方法开始执行
执行test2()方法
test2方法执行完成
*/
注意:Proxy.newProxyInstance()方法有三个参数:
- 代理对象类加载器(Class Loader)
- 被代理对象的接口
- InvocationHandler接口实例对象(也称代理对象)。所有动态代理类的方法调用,都会交由InvocationHandler接口实现类里的invoke()方法去处理。这是动态代理的关键所在。
写在最后
我以后会经常抱你。
To Dottie!