• Java 中的 反射机制


    概念明确

    什么是类的对象?

      类的对象就是:基于某个类 new 出来的对象,也称为实例对象。这个很容易理解,就不过多阐述了。

    什么是类对象?

      类对象就是:类加载的产物,封装了一个类的所有信息(类名、父类、接口、属性、方法、构造方法)。

      包含类信息的.class文件被JVM加载到内存后,一个个的类就变成了一个个的 java.long.Class 对象,每个类有且只有一个java.long.Class对象,这些对象包含了类的全部信息,这些对象存储在方法区中。

      每一个对象实例和创建它的类对象之间都有一条类型引用,代表着此实例是这个类型的。


    反射机制

    什么是反射机制?

      在 Java 中要使用一个类首先要将该类加载到内存中,系统会为该类生成一个java.lang.Class的实例。这个 Class 对象的作用很大,通过它系统可以访问到JVM中该类的信息,同时 Class 对象也是实现 Java 反射机制的核心要素。

    反射(Reflection)是指:

      程序可以访问、检测和修改它本身状态或行为的一种能力,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。

      上面的解释并不太通俗易懂,我们看看下面的阐述:

      在高级语言中,允许改变程序结构或变量类型的语言称为动态语言,例如Perl、Python、Ruby等就是动态语言,而像C、C++、Java这类语言在程序编译时就确定了程序的结构和变量的类型,因此不是动态语言。尽管如此,Java还是为开发者提供了一个非常有用的与动态相关的机制-反射(Reflection)。

      运用反射机制可以在运行时加载和使用编译期间未知的类型。也就是说,Java程序可以加载在运行时才得知类名的class,并生成其对象实体,或访问其属性,或唤起其成员方法。通俗点讲,所谓Java的反射机制,就是在Java程序运行时动态地加载并使用在编译期并不知道的类。


    反射机制的作用

      ① 获取一个类的信息。

      ② 通过类型的名称动态生成并操作对象。


    获取一个类的Class对象

      通过一个类的Class对象可以获取该类的信息,包括类的构造函数、属性、方法等,那么如何来获取一个类的Class对象呢?

    在Java中获取一个类的Class对象的方法有3种:

      ➷ Class.forName(className)。『推荐使用』

      ➷ 调用某个类的class属性获取该类对应的Class对象。

      ➷ 调用对象的getClass()方法获取该对象所属类对应的Class对象。

    代码演示:

    // Person 类
    public class Person implements Serializable, Cloneable{
        private String name;
        private int age;
        
        public Person(String name, int age) {
            super();
            this.name = name;
            this.age = age;
        }
    
        public Person() {
            super();
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "Person [name=" + name + ", age=" + age + "]";
        }
        
        public void eat() {
            System.out.println(name + "正在吃东西。。。");
        }
        
        public void eat(String food) {
            System.out.println(name + "正在吃" + food + "。。。");
        }
        
        private void privateMethod() {
            System.out.println("这是一个私有方法");
        }
        
        public static void staticMethod() {
            System.out.println("这是一个静态方法");
        }
        
    }
    public class Test {
    
        public static void main(String[] args) throws Exception {
            // 1.创建类的对象
            Person zhangsan = new Person("张三", 18);
            System.out.println(zhangsan.toString());
            
            // 2.获取类对象
            
            // 方式一:通过类的对象,获取类对象
            Person s = new Person();
            Class<?> c = s.getClass();
            System.out.println(c.toString());
            
            // 方式二:通过类名获取类对象
            Class<?> c1 = Person.class;
            System.out.println(c1.toString());
            
            // 方式三:通过静态方法获取类对象 [推荐使用]
            Class<?> c2 = Class.forName("com.ruoli.reflect.Person");
            System.out.println(c2.toString());
        }
    }

    运行结果:

      一般情况下生成一个类的对象是不需要使用反射的。但是有些特殊的情况必须用到反射才能实现类的实例化。比如要对一个类进行操作,但是这个类的类名需要从配置文件中读取,事先并不知道该类的类名等信息。这样在编译时就无法确定该类的类名,也就无法按照传统的方法构建类的对象了。所以可以先读取配置文件并拿到这个类的全类名,然后利用反射生成该类的对象,并进行相应的操作。一个很好的例子就是设计模式中的简单工厂模式。

      综上所述,所以大多数情况下都是使用Class.forName(className)这种方法来获取类对象的,也推荐大家在日常编程中多实用这种方法。


    获取类的名字,包名,父类,接口

    代码演示:

    public class Test {
    
        public static void main(String[] args) throws Exception {
            // 1.获取类对象
            Class<?> c2 = Class.forName("com.ruoli.reflect.Person");
            
            // 2.获取类的名字
            System.out.println("类名:" + c2.getName());
            
            // 3.获取类的包名
            System.out.println("包名:" + c2.getPackage().getName());
            
            // 4.获取类的父类
            System.out.println("父类:" + c2.getSuperclass().getName());
            
            // 5.获取类的接口
            Class<?>[] classes = c2.getInterfaces();
            System.out.println("接口:" + Arrays.toString(classes));
            
            // 6.获取类的简称
            System.out.println("简称:" + c2.getSimpleName());
        }
    }

    运行结果:


    获取类的构造方法,并创建对象

    代码演示:

    public class Test {
    
        public static void main(String[] args) throws Exception {
            // 1.获取类对象
            Class<?> c2 = Class.forName("com.ruoli.reflect.Person");
            
            // 2.获取类的构造方法        
            // 2.1获取全部的构造方法
    //        Constructor<?>[] cons = c2.getConstructors();
    //        for (Constructor<?> constructor : cons) {
    //            System.out.println(constructor.toString());
    //        }
            
            // 2.2获取无参的构造方法,创建对象
    //        Constructor<?> con = c2.getConstructor();
    //        System.out.println(con.toString());
    //
    //        Person zhangsan = (Person)con.newInstance();
    //        System.out.println(zhangsan.toString());
            
            // 简单方法:实际上就是调用上面的无参构造
    //        Person xiaoming = (Person)c2.newInstance();
    //        System.out.println(xiaoming.toString());
            
            // 2.3获取带参的构造方法,创建对象
            Constructor<?> con = c2.getConstructor(String.class, int.class);
            System.out.println(con.toString());
            
            Person xiaoming = (Person)con.newInstance("小明", 19);
            System.out.println(xiaoming.toString());
        }
    }

    运行结果:


    获取类中的方法,并调用方法

    代码演示:

    public class Test {
    
        public static void main(String[] args) throws Exception {
            // 1.获取类对象
            Class<?> c2 = Class.forName("com.ruoli.reflect.Person");
            
            // 2.获取类的方法
            
            // 2.1 getMethods() 只能获取公开的方法,包括从父类继承的方法
    //        Method[] methods = c2.getMethods();
    //        for (Method method : methods) {
    //            System.out.println(method.toString());
    //        }
            
            // 2.2 getDeclaredMethods() 获取类中的所有方法,包括私有、默认、保护的方法,不包含继承的
    //        Method[] methods = c2.getDeclaredMethods();
    //        for (Method method : methods) {
    //            System.out.println(method.toString());
    //        }
            
            // 2.3获取单个方法   
            
            // 获取无参、无返回值的方法eat(): 
            Method eatMethod = c2.getMethod("eat");
            System.out.println(eatMethod.toString());
            Person zhangsan = (Person)c2.newInstance();
            eatMethod.invoke(zhangsan);
            
            // 获取无参、有返回值的方法toString(): 
            Method toStringMethod = c2.getMethod("toString");
            System.out.println(toStringMethod.toString());
            Object result = toStringMethod.invoke(zhangsan);
            System.out.println(result);
            
            // 获取带参、无返回值的方法eat(String food): 
            Method eatMethod2 = c2.getMethod("eat", String.class);
            System.out.println(eatMethod2.toString());
            eatMethod2.invoke(zhangsan, "鸡腿");
    
            // 获取私有方法privateMethod()
            Method privateMethod = c2.getDeclaredMethod("privateMethod");
            System.out.println(privateMethod.toString());
            // 此时调用方法(私有方法),没有访问权限
            // 设置访问权限无效
            privateMethod.setAccessible(true);
            privateMethod.invoke(zhangsan);
            
            // 获取静态方法staticMethod()
            Method staticMethod = c2.getMethod("staticMethod");
            System.out.println(staticMethod.toString());
            staticMethod.invoke(null);
        }
    }

    运行结果:

      这里需要注意的是,当我们通过反射获取一个类的私有方法时,我们是没有权限来使用的,因此需要设置访问权限,然后才能使用这个私有方法。


    获取类中的属性

    代码演示:

    public class Test {
    
        public static void main(String[] args) throws Exception {
            // 1.获取类对象
            Class<?> c2 = Class.forName("com.ruoli.reflect.Person");
            
            // 2.获取属性    公开的字段,父类继承的字段
    //        Field[] fields = c2.getFields();
    //        System.out.println(fields.length);
            
            // 2.获取属性    获取所有的属性,但不包含继承的
    //        Field[] fields = c2.getDeclaredFields();
    //        System.out.println(fields.length);
    //        for (Field field : fields) {
    //            System.out.println(field);
    //        }
            
            // 2.获取单个属性
            Field nameField = c2.getDeclaredField("name");
            nameField.setAccessible(true);
            // 赋值
            Person zhangsan = (Person)c2.newInstance();
            nameField.set(zhangsan, "张三");
            // 获取值
            System.out.println(nameField.get(zhangsan));
        }
    }

    运行结果:


    反射机制总结

    反射机制

      所谓Java的反射机制,就是在Java程序运行时动态地加载并使用在编译期并不知道的类。

    反射机制的作用

      ➷ 反射机制可以动态获取一个类的信息,包括该类的属性和方法,这个功能可应用于对class文件进行反编译。

      ➷ 反射机制也可以通过类型的名称动态生成对象,并调用对象中的方法。

    反射机制的优缺点

      ✔ 优点:可以在运行时获取一个类的实例,大大提高了系统的灵活性和可扩展性。

      ✘ 缺点:性能较差,安全性不高,破坏了类的封装性。

     

  • 相关阅读:
    Lc617_合并二叉树
    Lc257_二叉树的所有路径
    Lc222_完全二叉树的节点个数
    记github下载上传遇到的各种问题
    Lc101_对称二叉树
    Lc222_翻转二叉树
    二叉树的dfs 与 bfs (递归遍历)
    全球最火的程序员学习路线!没有之一!3天就在Github收获了接近1w点赞
    大二逃课总结的1.2w字的计算机网络知识!扫盲!
    「IDEA插件精选」安利一个IDEA骚操作:一键生成方法的序列图
  • 原文地址:https://www.cnblogs.com/ruoli-0/p/13920774.html
Copyright © 2020-2023  润新知