什么是Java的反射
反射是java的动态机制,允许我们在程序[运行期间]再确定对象的实例化,属性的操作,方法的调用等。
获取一个类的类对象的3种方式
方式一:调用运行时类的属性:类名.class
Class cls = String.class;
Class cls = Person.class;
方式二:调用Class的静态方法:Class.forName(String classPath)
//参数要求传入的是对应类的完全限定名:包名.类名
Class clazz = Class.forName("com.atguigu.java.Person");
Class clazz = Class.forName("java.lang.String");
方式三:通过运行时类的对象,调用getClass()
Person p1 = new Person();
Class clazz = p1.getClass();
方式四:使用类的加载器:ClassLoader (了解)
ClassLoader classLoader = ReflectionTest.class.getClassLoader();//获取当前类的类加载器
Class clazz = classLoader.loadClass("com.atguigu.java.Person");
Class类中常用方法
- newInstance():调用此方法,创建对应的运行时类的对象。内部调用了运行时类的空参的构造器。返回值类型:Object
要想此方法正常的创建运行时类的对象,要求:
1.运行时类必须提供空参的构造器
2.空参的构造器的访问权限得够。通常,设置为public。
在javabean中要求提供一个public的空参构造器。原因:
1.便于通过反射,创建运行时类的对象
2.便于子类继承此运行时类时,默认调用super()时,保证父类此构造器
可以通过反射,获取对应的运行时类中所有的属性、方法、构造器、父类、接口、父类的泛型、包、注解、异常等
获取包名
- getPackage() 返回值类型 Package
//获取包名
String packageName = cls.getPackage().getName();
获取类名
- getName();获取完全限定类名(包名.类名) 返回值类型 String
- getSimpleName();获取类名 返回值类型 String
获取属性:
实际使用过程中我们几乎不会直接对属性进行操作,而是通过调用属性的get(),set()方法.
- getField(String fieldName):获取指定属性 返回值类型:Field
- getFields():获取当前运行时类及其父类中声明为public访问权限的属性 返回值类型:Field[ ]
- getDeclaredFields():获取当前运行时类中声明的所有属性。(不包含父类中声明的属性) 返回值类型:Field[ ]
获取方法:
- getMethod(String method,Class… argsType):获取指定被publice修饰的方法 返回值类型:Method
(参数1:方法名,参数2:参数类型的Class对象(无参方法可不写))
- getDeclaredMethod(String method,Class… argsType):获取指定方法((包含私有方法) 返回值类型:Method
- getMethods();获取类中所有publice权限的方法对象(包含父类中的方法) 返回值类型 Method[ ]
Method称为方法对象,该类的每一个实例用于表示一个方法,该对象上记录这其表示的方法里的所有信息
如:方法的访问修饰符,返回值类型,方法名,参数列表信息等。
- getDeclaredMethods():获取类中所有方法的对象(包含私有方法) 返回值类型: Method[ ]
当我们获取到指定方法对象后,想要执行此方法时,可以调用Method类中invoke()方法
Method对象提供了调用该方法的操作:
Object invoke(Object obj,Object[] args)
参数1:当前Method对象表示的方法的所属对象
参数2:该方法的实参列表(无参可不写)
返回值:实际调用该方法后该方法的返回值。
对于私有方法可以调用setAccessible(true)方法,打开私有方法的访问权限,然后使用invoke()调用.
通过反射调用方法示例:
public class ReflectDemo6 {
public static void main(String[] args) throws Exception {
Person p = new Person();
// p.heihei();//编译不通过,heihei是Person的私有方法,不能在类的外部被调用!
Class cls = Class.forName("reflect.Person");
Object obj = cls.newInstance();
Method method = cls.getDeclaredMethod("heihei" ,String.class);//参数类型,顺序,个数必须与方法一致
method.setAccessible(true);//强行打开访问权限,此时下面就不会出现非法访问异常,并可调用私有方法!!!
method.invoke(obj,"11");//实参的个数,类型,顺序也必须与方法定义一致
}
}
获取构造器
- getConstructors():获取当前运行时类中声明为public的构造器 返回值类型:Constructor[ ]
- getDeclaredConstructors():获取当前运行时类中声明的所的构造器 返回值类型:Constructor[ ]
/*使用有参构造器实例化对象*/
public class ReflectDemo3 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
Person p = new Person("李四",22);
System.out.println(p);
//1获取类对象
Class cls = Class.forName("reflect.Person");
/* 2通过类对象获取特定的构造器
Constructor:构造器
Class提供的获取特定构造器的方法:
Constructor getConstructor(Class... argsType)
要将特定构造器的参数类型对应的类对象有序传入即可
java.lang.reflect.Constructor构造器对象,该类的每一个实例表示一个构造器
*/
//获取 Person(String,int)
Constructor c = cls.getConstructor(String.class,int.class);//这里传入每个参数类型对应的类对象
//3利用构造器实例化对象
Object obj = c.newInstance("王五",33);//new Person("王五",33);
System.out.println(obj);
}
}
获取注解
- getAnnotations():获取修饰当前类的注解 返回值类型:Annotation[ ]
判断是否被指定注解修饰
- isAnnotationPresent(Class annotationClass):判断是否被指定注解修饰 返回值类型:boolean
/* 在反射机制中访问注解 */
public class ReflectDemo8 {
public static void main(String[] args) throws Exception {
//如何知道一个类是否被某个注解标注?
Class cls = Class.forName("reflect.Person");
// Class cls = Class.forName("reflect.Student");
/* 实际上除了Class之外,其他几个反射对象:
Method,Constructor,Field等都有这个方法:boolean isAnnotationPresent(Class cls)
用于判断:方法,构造器,属性等是否又被某个特定的注解标注 */
//当前Person类是否被注解@AutoRunClass标注了?
boolean mark = cls.isAnnotationPresent(AutoRunClass.class);
if(mark){
System.out.println("被标注了!");
}else{
System.out.println("没有被标注!");
}
}
}
获取注解中的参数
通过反射获取注解中的参数,这就是springboot框架部分的工作原理,比如@Controller @RequestMapping()等
/**在反射机制中获取注解的参数*/
public class ReflectDemo10 {
public static void main(String[] args) throws Exception {
//根据@AutoRunMethod上的参数值作为我们自动调用方法的次数
Class cls = Class.forName("reflect.Person");
Object obj = cls.newInstance();
Method[] methods = cls.getDeclaredMethods();
for(Method method : methods){
if(method.isAnnotationPresent(AutoRunMethod.class)){//判断该方法是否被@AutoRunMethod注解修饰
//当我们确定一个方法被特定的注解标注后,就可以获取该注解从而得到注解中传递的参数
AutoRunMethod arm = method.getAnnotation(AutoRunMethod.class);
int value = arm.value();//获取该方法上@AutoRunMethod注解上传递的参数值
System.out.println(method.getName()+":注解@AutoRunMethod中指定的参数value的值为:"+value);
for(int i=0;i<value;i++){
method.invoke(obj);
}
}
}
}
}
/*自定义的AutoRunMethod注解*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoRunMethod {
int value() default 1;
}
/*测试用的Person类*/
@AutoRunClass
public class Person {
@AutoRunMethod(5)
public void sayHello(){
System.out.println(name+":Hello!");
}
}
利用反射和注解自动调用当前类同一个包中被@AutoRunClass标注的类中被@AutoRunMethod标注的方法
/**
* 自动调用那些与当前类Test4在同一个包中被@AutoRunClass标注的类中那些被@AutoRunMethod标注的方法
*/
public class Test4 {
public static void main(String[] args) throws Exception {
File dir = new File(Test4.class.getResource(".").toURI());//获取当前类所在的路径
File[] files = dir.listFiles(f->f.getName().endsWith(".class"));//获取当前路径下的所有.class文件
for(File file : files){//遍历文件
String fileName = file.getName();//获取文件名(类名.class)
String className = fileName.substring(0,fileName.indexOf("."));//将文件名按照 . 拆分 得到类名
Class cls = Class.forName(Test4.class.getPackage().getName()+"."+className);//创建类的Class对象
if(cls.isAnnotationPresent(AutoRunClass.class)) {//判断该类是否被@AutoRunClass注解修饰
Object o = cls.newInstance();//创建该类的实例
Method[] methods = cls.getDeclaredMethods();//获取该类中的所有方法
for (Method method : methods) {//遍历所有方法
if (method.isAnnotationPresent(AutoRunMethod.class)) {//判断该方法是否被@AutoRunMethod修饰
method.invoke(o);//执行方法
}
}
}
}
}
}
/*两个自定义的注解*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoRunClass {}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoRunMethod {
int value() default 1;
}