zoukankan      html  css  js  c++  java
  • Java反射机制详解

    作用

      反射的定义:在运行状态中,能获取任意一个类的所有方法和属性;能调用一个对象的所有方法和属性。这种动态获取类信息和动态调用对象方法和属性的功能就是Java的反射机制。

      注意定义中的措辞,是所有的方法和属性,即使是私有的也能调用。所以功能是非常强大的。但在我们日常开发中很少会用到反射机制,因为正是这种强大的机制反而会破坏我们应用代码的封装性。日常中不用不代表就没用,很多框架的设计其实都用到了反射机制。如果熟悉Spring的话不难发现,Spring中有大量关于反射的应用。比如我们在配置XML时就会被要求写对应类的全限定名,在Spring的BeanFactor中就会读取这些配置文件,并通过反射去创建Class对象。连接数据库时,会要求指定驱动程序,比如com.mysql.jdbc.Driver,其实也通过反射机制加载的。

    原理

      在此之前先要明白,在Java中万物皆对象,某个类本身可以是个对象,类里面的结构,比如属性、方法等也可以是个对象。在JVM进行类加载的过程中,在第一步也就是加载步骤,会在内存中生成一个代表该类的Class对象。需要注意这个Class对象和该类的实例对象不是同一个东西。Class对象的类型就是Class,大家可以看看Class的API或者直接看它的源码。我们使用Class.forName("类的全限定名")时其实就是获取这个类的Class对象。具体的API可以看下面。通过这个Class对象我们就可以获取这个类的属性、方法、注解等信息。那么是如何获取这些信息的?首先Class这个类提供了获取这些信息的方法,既然提供了获取的方法,那说明这些信息肯定存在于某些地方。还是得从JVM层面来说,一个类的静态成员,即静态方法和属性都是存在方法区的,这些成员在类加载时就生成了,所以它们与该类的实例对象没有什么关系,只与这个类本身也就是Class对象相关。而那些非静态成员会随着实例对象的创建一起存进Java堆当中,所以我们获取这些非静态成员时,一定是根据某个实例对象来获取的。这两种情况会在下面的示例代码中展现出来。

    API

      下面的API摘自:https://www.jianshu.com/p/9be58ee20dee,感兴趣的可以去看看。

      与Java反射相关的类如下:

    类名

    用途

    Class类

    代表类的实体,在运行的Java应用程序中表示类和接口

    Field类

    代表类的成员变量(成员变量也称为类的属性)

    Method类

    代表类的方法

    Constructor类

    代表类的构造方法

      Class类

      Class代表类的实体,在运行的Java应用程序中表示类和接口。在这个类中提供了很多有用的方法,这里对他们简单的分类介绍。

    • 获得类相关的方法

    方法

    用途

    asSubclass(Class<U> clazz)

    把传递的类的对象转换成代表其子类的对象

    Cast

    把对象转换成代表类或是接口的对象

    getClassLoader()

    获得类的加载器

    getClasses()

    返回一个数组,数组中包含该类中所有公共类和接口类的对象

    getDeclaredClasses()

    返回一个数组,数组中包含该类中所有类和接口类的对象

    forName(String className)

    根据类名返回类的对象

    getName()

    获得类的完整路径名字

    newInstance()

    创建类的实例

    getPackage()

    获得类的包

    getSimpleName()

    获得类的名字

    getSuperclass()

    获得当前类继承的父类的名字

    getInterfaces()

    获得当前类实现的类或是接口

    • 获得类中属性相关的方法

    方法

    用途

    getField(String name)

    获得某个公有的属性对象

    getFields()

    获得所有公有的属性对象

    getDeclaredField(String name)

    获得某个属性对象

    getDeclaredFields()

    获得所有属性对象

    • 获得类中注解相关的方法

    方法

    用途

    getAnnotation(Class<A> annotationClass)

    返回该类中与参数类型匹配的公有注解对象

    getAnnotations()

    返回该类所有的公有注解对象

    getDeclaredAnnotation(Class<A> annotationClass)

    返回该类中与参数类型匹配的所有注解对象

    getDeclaredAnnotations()

    返回该类所有的注解对象

    • 获得类中构造器相关的方法

    方法

    用途

    getConstructor(Class...<?> parameterTypes)

    获得该类中与参数类型匹配的公有构造方法

    getConstructors()

    获得该类的所有公有构造方法

    getDeclaredConstructor(Class...<?> parameterTypes)

    获得该类中与参数类型匹配的构造方法

    getDeclaredConstructors()

    获得该类所有构造方法

    • 获得类中方法相关的方法

    方法

    用途

    getMethod(String name, Class...<?> parameterTypes)

    获得该类某个公有的方法

    getMethods()

    获得该类所有公有的方法

    getDeclaredMethod(String name, Class...<?> parameterTypes)

    获得该类某个方法

    getDeclaredMethods()

    获得该类所有方法

    • 类中其他重要的方法

    方法

    用途

    isAnnotation()

    如果是注解类型则返回true

    isAnnotationPresent(Class<? extends Annotation> annotationClass)

    如果是指定类型注解类型则返回true

    isAnonymousClass()

    如果是匿名类则返回true

    isArray()

    如果是一个数组类则返回true

    isEnum()

    如果是枚举类则返回true

    isInstance(Object obj)

    如果obj是该类的实例则返回true

    isInterface()

    如果是接口类则返回true

    isLocalClass()

    如果是局部类则返回true

    isMemberClass()

    如果是内部类则返回true

      Field类

      Field代表类的成员变量(成员变量也称为类的属性)。

    方法

    用途

    equals(Object obj)

    属性与obj相等则返回true

    get(Object obj)

    获得obj中对应的属性值

    set(Object obj, Object value)

    设置obj中对应属性值

      Method类

      Method代表类的方法。

    方法

    用途

    invoke(Object obj, Object... args)

    传递object对象及参数调用该对象对应的方法

      Constructor类

      Constructor代表类的构造方法。

    方法

    用途

    newInstance(Object... initargs)

    根据传递的参数创建类的对象

    示例

      首先创建一个被反射的类:

    public class People {
        //静态属性
        public static String Version="1.1.1";
        private String Name;
        private Integer Age;
        private String Habit;
        //公共构造函数
        public People(String name, Integer age, String habit) {
            Name = name;
            Age = age;
            Habit = habit;
        }
        //私有构造函数
        private People(String name){
            Name=name;
        }
    
        public People(){
    
        }
        //私有方法
        private void isPrivate(String data){
            System.out.println("这是个私有方法,传入的参数为:"+data);
        }
        //公有方法
        public String getName() {
            return Name;
        }
        //静态方法
        public static void StaticVoid(){
            System.out.println("这是一个静态方法");
        }
    }

      接下来通过Java提供的反射方法获取这个类的构造函数、方法、属性:

    public class reflectionTest {
        public static void main(String[] args) {
            try {
                System.out.println("----------------------获取Class对象-----------------------");
                //因为我这里People和reflectionTest在同一包下,如果不在同一包下需要传入完整的类路径
                Class clazz = Class.forName("People");
                System.out.println(clazz);
                /**
                 * 还有两种获取Class对象的方法,这几种方法获取的Class对象都是同一个对象:
                 * 一般都会用foeName的方式,因为下面第一种方法需要导包
                 * 第二种方法,对象都创建出来了,除非是要调用私有的东西,那我还反射个毛。
                 * 1.Class clazz=People.class;
                 * 2.People p=new People();
                 *   Class clazz=p.getClass();
                 * */
    
                System.out.println("----------------------获取构造函数对象-----------------------");
                //获取所有构造函数,不包括私有的,如果想包括私有就用getDeclaredConstructors
                Constructor[] constructors = clazz.getConstructors();
                for (Constructor c : constructors) {
                    System.out.println(c);
                }
                //获取私有构造函数并调用(String.class为构造函数参数类型)
                Constructor constructor = clazz.getDeclaredConstructor(String.class);
                //忽略访问修饰符,如果是共有的就不需要这一步
                constructor.setAccessible(true);
                //根据这个构造函数创建一个People实例对象
                People people = (People) constructor.newInstance("张三");
                //最后通过对象输出刚刚设置的姓名
                System.out.println(people.getName());
    
                System.out.println("----------------------获取方法对象-----------------------");
                //获取所有公共方法,包括父类的方法,比如Object的equals,如果想包含私有方法就用getDeclaredMethods,但只有本类的方法
                Method[] methods = clazz.getMethods();
                for (Method method : methods) {
                    System.out.println(method);
                }
                //获取一个私有方法并调用
                Method method = clazz.getDeclaredMethod("isPrivate", String.class);
                //忽略访问修饰符
                method.setAccessible(true);
                //访问私有方法,如果这个私有方法有返回值用一个变量接收就行了。
                //通过invoke调用该私有方法,除了要传入这个方法本来的参数,比如这里的耶low,还需要一个该方法对应类的对象,我们就用上面反射获取的people对象
                method.invoke(people, "耶low");
                //获取静态方法并调用,发现调用静态方法不需要实例对象,这也符合原理中所说的。
                Method method2=clazz.getMethod("StaticVoid");
                method2.invoke(null);
    
                System.out.println("----------------------获取属性对象-----------------------");
                //获取本类的所有属性,包括私有的
                Field[] fields=clazz.getDeclaredFields();
                for(Field field:fields){
                    System.out.println(field);
                }
                //获取私有属性并调用
                Field field=clazz.getDeclaredField("Name");
                field.setAccessible(true);
                //设置这个私有属性
                field.set(people,"李四");
                //然后获取一下Name,看看设置成功没
                System.out.println(people.getName());
                //获取静态属性
                Field field2=clazz.getField("Version");
                //输出静态属性的值
                System.out.println(field2.get(clazz));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
  • 相关阅读:
    amuse ui(web插件,js插件,css样式)?
    解决ajax重复提交问题?
    AJAX防重复提交的办法总结?
    iOS中发送HTTP请求的方案
    巧用Xode中的代码块(转)
    SVProgressHUD进度条蒙版
    NSTimer scheduledTimerWithTimeInterval与timerWithTimeInterval、initWithFireDate的区别
    小结RunLoop
    初学RunLoop
    CocoaPods安装及使用详情
  • 原文地址:https://www.cnblogs.com/lbhym/p/13036799.html
Copyright © 2011-2022 走看看