Java反射详解

什么是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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值