首先,看一下反射最重要的四句代码:
package com.wu.test01;
/**
* @author wu
* @version 2022/1/18 10:35 PM
*/
public class AliPay implements Mtwm {
@Override
public void payOnline() {
//具体的支付宝支付:
System.out.println("我已经点了外卖,我正在使用支付宝进行支付" );
}
}
public static void main(String... args) throws Exception {
//定义一个字符串,用来模拟前台的支付方式:
String str = "com.wu.test01.AliPay";
//下面的代码就是利用反射
Class cls = Class.forName(str);
Object o = cls.newInstance();
Method method = cls.getMethod("payOnline");
method.invoke(o);
}
什么是反射?
然后,反射的定义是:在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能成为java语言的反射机制。
反射可以用来做什么
反射所体现的就是一种动态性,就像一个镜子,通过反射的api获取一个类内部的所有内部信息。这也是我认为它叫反射的一个含义。
1、获取构造器和创建对象
public static void main(String... args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//获取字节码信息
Class cls = Student.class;
//通过字节码信息可以获取构造器
//getConstructors只能获取当前运行时类的被public修饰的构造器
Constructor[] c1 = cls.getConstructors();
for (Constructor c : c1) {
System.out.println(c);
}
System.out.println("------------------");
//getDeclaredConstructors:获取运行时类的全部修饰符的构造器
Constructor[] c2 = cls.getDeclaredConstructors();
for(Constructor c:c2){
System.out.println(c );
}
System.out.println("------------------");
//获取指定的构造器
//得到空构造器
Constructor con1 = cls.getConstructor();
System.out.println( con1);
//得到两个参数的有参构造器
Constructor con2 = cls.getConstructor(double.class, double.class);
System.out.println(con2);
//得到一个参数的有参构造器:并且是private修饰的
Constructor con3 = cls.getDeclaredConstructor(int.class);
System.out.println(con3);
//有了构造器以后我就可以创建对象
Object o1 = con1.newInstance();
System.out.println(o1);
Object o2 = con2.newInstance(180.5, 170.6);
System.out.println(o2);
}
2、获取属性和对属性赋值
public static void main(String... args) throws NoSuchFieldException, IllegalAccessException, InstantiationException {
//获取运行时类的字节码信息
Class cls = Student.class;
//获取属性
//getFields:获取运行时类和父类中被public修饰的属性
Field[] fields = cls.getFields();
for (Field field : fields) {
System.out.println(field);
}
System.out.println("--------------");
Field[] declaredFields = cls.getDeclaredFields();
for (Field field : declaredFields) {
System.out.println(field);
}
System.out.println("--------------");
//获取指定的属性
Field score = cls.getField("score");
System.out.println(score);
Field sno = cls.getDeclaredField("sno");
System.out.println(sno);
System.out.println("--------------");
//属性的具体结构:
//获取修饰符
int modifiers = sno.getModifiers();
System.out.println(modifiers);
System.out.println(Modifier.toString(modifiers));
System.out.println(Modifier.toString(sno.getModifiers()));
//获取属性的数据类型
Class clazz = sno.getType();
System.out.println(clazz.getName());
//获取属性的名字
String name = sno.getName();
System.out.println(name);
System.out.println("--------------");
//给属性赋值:(给属性设置值,必须要有对象)
Field sco = cls.getField("score");
Object obj = cls.newInstance();
sco.set(obj, 98);
System.out.println(obj);
}
3、获取方法和调用方法
public static void main(String... args) throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
//获取字节码信息
Class cls = Student.class;
//获取方法:
//getMethods:获取运行时类的方法还有所有父类中的方法(被public修饰)
Method[] methods = cls.getMethods();
for (Method m : methods) {
System.out.println(m);
}
System.out.println("----------------" );
//:获取运行时类中的所有方法
Method[] declaredMethods = cls.getDeclaredMethods();
for (Method m : declaredMethods) {
System.out.println(m);
}
System.out.println("----------------");
//获取指定的方法:
Method showInfo1 = cls.getMethod("showInfo");
System.out.println(showInfo1);
Method showInfo2 = cls.getMethod("showInfo", int.class, int.class);
System.out.println(showInfo2);
Method work = cls.getDeclaredMethod("work",int.class);
System.out.println(work);
System.out.println("----------------");
//获取方法的具体结构:
/*
@注解
修饰符 返回值类型 方法名(参数列表) throws XXXX{}
*/
//名字
System.out.println(work.getName());
//修饰符
int modifiers = work.getModifiers();
System.out.println(Modifier.toString(modifiers));
//返回值
System.out.println(work.getReturnType());
//参数列表
Class[] parameterTypes = work.getParameterTypes();
for (Class c:parameterTypes){
System.out.println(c);
}
//获取注解
Method myMethod = cls.getMethod("myMethod");
Annotation[] annotations=myMethod.getAnnotations();
for (Annotation a:annotations){
System.out.println(a);
}
//获取异常
Class[] exceptionTypes = myMethod.getExceptionTypes();
for (Class c:exceptionTypes){
System.out.println(c);
}
//调用方法
Object o=cls.newInstance();
myMethod.invoke(o);
System.out.println(showInfo2.invoke(o,12,45));
}
4、获取类的接口,所在包,注解
public static void main(String... args) {
//获取字节码信息
Class cls = Student.class;
//获取运行时类的接口
Class[] interfaces = cls.getInterfaces();
for (Class c : interfaces) {
System.out.println(c);
}
//得到父类的接口
//先得到父类的字节码信息
Class superclass = cls.getSuperclass();
//得到接口
Class[] interfaces1 = superclass.getInterfaces();
for (Class c : interfaces1) {
System.out.println(c);
}
//获取运行时类所在的包
Package aPackage = cls.getPackage();
System.out.println(aPackage);
System.out.println(aPackage.getName());
//获取运行类的注解
Annotation[] annotations = cls.getAnnotations();
for (Annotation a : annotations) {
System.out.println(a);
}
}
反射相关类
Class类
方法 | 说明 |
getClassLoader() | 获得类的加载器 |
forName(String className) | 根据类名返回类的对象 |
getName() | 获得类的完整路径名字 |
newInstance() | 创建类的实例 |
getPackage() | 获得类的包 |
getSuperclass() | 获得当前类继承的父类的名字 |
getInterfaces() | 获得当前类实现的类或是接口 |
Field类
方法 | 说明 |
getField(String name) | 获得公有的属性对象 |
getFields() | 获得所有公有的属性对象 |
getDeclaredFields() | 获得所有属性对象 |
getDeclaredField(String name) | 获得某个属性对象 |
Method类
方法 | 说明 |
getAnnotation(Class<A> annotationClass) | 返回该类中与参数类型匹配的公有注解对象 |
getAnnotations() | 返回该类所有的公有注解对象 |
getDeclaredAnnotation(Class<A> annotationClass) | 返回该类中与参数类型匹配的所有注解对象 |
getDeclaredAnnotations() | 返回该类所有的注解对象 |
Constructor类
方法 | 说明 |
getConstructor(Class...<?> parameterTypes) | 获得该类中与参数类型匹配的公有构造方法 |
getConstructors() | 获得该类的所有公有构造方法 |
getDeclaredConstructor(Class...<?> parameterTypes) | 获得该类中与参数类型匹配的构造方法 |
getDeclaredConstructors() | 获得该类所有构造方法 |
反射通过字节码信息获取类,获取公共方法属性的,还可以获取私有、保护等信息。那么就引出两个问题:
两个问题
1、创建一个对象,是用new Person() 还是利用反射?
建议使用new的方式,因为这样在编译的时候就确定了要初始化的对象,而不是等到运行时。那我们什么时候用反射呢?就是不确定要new哪个对象的时候,在运行时来决定,这就体现了反射的动态性。
2、反射是否破坏了封装性?
通过上面的反射访问一个类的内部私有属性,方法等,貌似与封装性想违背,封装性有私有和共有来保证代码的安全性,在外部调用共有方法。反射最大的特点是动态性,解决的是能不能调用的问题,可以调用私有方法,但是平时不建议调用。所以两者并不冲突。