• Java系列 - 反射


    • 是什么:什么是反射?
    • 为什么:为什么会有反射?
    • 怎么办:如何使用反射
    • 在哪里使用?

    什么是反射?

    • 正常情况下:先有类,然后有对象
    import java.util.Date;//先有类
    
    public class ReflectTest1 {
        public static void main(String[] args) {
            Date date = new Date();//后有对象
            System.out.println(date);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 反射中的意思是: 通过对象来找类
    import java.util.Date;
    
    public class ReflectTest2 {
        public static void main(String[] args) {
            // 对象
            Date date = new Date();
    
            // 找类
            System.out.println(date.getClass());
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    为什么会有反射?

    反射其实是一种语言特性,设计反射的目的是为了溯源:找到源头类,然后使用源头类。

    大部分情况都可以通过 new一个对象 来使用类以及类上方法,但是有些情况下无法使用 new 来创建对象,因为类是再运行时动态创建的,压根就不存在,此时就是反射上场了。
    当然我们先用简单的例子来讲解。

    正常使用一个类的方式是: 首先先有这个类,然后通过new ClassName()来创建这个类的对象,然后就能够借助这个对象调用这个类上面的方法。

    代码演示:实现一个唱歌的功能类

    package com.ifdom.reflection;
    
    /**
     * 1. 先有 Song 这个歌曲类
     **/
    class Song {
        public void sing() {
            System.out.println("我正在唱歌");
        }
    }
    
    /**
     * 2.然后通过 `new Song()` 创建出对象 song,借助 song 对象来调用类上面的方法 sing()
     **/
    public class Reflection1 {
        public static void main(String[] args) {
            Song song = new Song();
            song.sing();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    而反射的使用方式有所不同,需要理解:正常使用一个类,等于我们已经有了工具箱,并且知道工具箱上有哪些方法(工具)了,可以直接使用。

    反射是需要先找到,先找到,先找到:先把工具箱(类)找到,把工具箱中的工具(构造方法,属性,方法)找到,找到之后才能使用。

    如何使用反射?- 01 溯源

    反射首先要溯源(寻找对象是由哪个类产生),你只有找到了源头,歌曲类,锤子类,镰刀类,工具箱类…找到了源头,才能调用源头类的属性和方法,否则调用空气呢?

    反射溯源有3种方法:

    • 1.通过对象上的 anyObject.getClass() 方法
    package com.ifdom.reflection;
    
    /**
     * Reflection
     **/
    public class Reflection1 {
        public static void main(String[] args) {
            Song song = new Song();
    
            Class<?> oneClass = song.getClass();
    
            // 结果:class com.ifdom.reflection.Song
            System.out.println(oneClass);
        }
    }
    
    class Song {
        public void sing() {
            System.out.println("我正在唱歌");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 2.通过每个类都有的属性 AnyClass.class
    package com.ifdom.reflection;
    
    class Song {
        public void sing() {
            System.out.println("我正在唱歌");
        }
    }
    
    /**
     * Reflection
     **/
    public class Reflection1 {
        public static void main(String[] args) {
            Song song = new Song();
    
            Class<Song> oneClass = Song.class;
    
            // 结果:class com.ifdom.reflection.Song
            System.out.println(oneClass);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 3.通过最底层的Class类提供的 forName()方法每个类都有的属性 Class.forName("AnyClassName")
    package com.ifdom.reflection;
    
    class Song {
        public void sing() {
            System.out.println("我正在唱歌");
        }
    }
    
    /**
     * Reflection
     **/
    public class Reflection1 {
        public static void main(String[] args) throws ClassNotFoundException {
            Song song = new Song();
    
            Class<?> oneClass = Class.forName("com.ifdom.reflection.Song");
    
            System.out.println(oneClass);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    如果你不瞎,那么你应该发现了第三种方法要求必须抛出一个异常。
    这是为什么?因为我们是在溯源,溯源,溯源,我们对要找的那个类不甚了解,所以有可能失误,导致要寻找的那个类并不存在
    所以会抛出 ClassNotFoundException 没有找到类异常

    细心的同学应该发现了,在上面三种溯源方法里有一点差异:
    第一种和第二种都是在已经有对象 song 的前提下去溯源。
    而第三种方法,是在我们只知道类的包路径的情况下去溯源。

    此时,思考一下,什么时候使用哪个方法去溯源?

    别犹豫,你可以相信自己的思考结果。

    如何使用反射?- 02 生成实例

    现在我们已经知道如何溯源,接下来该如何通过反射溯源后的对象来使用 源头类的 属性和方法呢?

    1. 调用溯源后的类方法 newInstance()生成实例对象;

    由于第一和第二种溯源方法已经有 song 对象了,可以直接调用对象上的方法 song.sing(),没必要再经过反射。这里使用第三种溯源方法演示

    package com.ifdom.reflection;
    
    class Song {
        private String title;
    
        public void sing() {
            System.out.println("我正在唱歌" + this.title);
        }
    }
    
    /**
     * Reflection
     **/
    public class Reflection1 {
        public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
    
            Class<?> oneClass = Class.forName("com.ifdom.reflection.Song");
    
            System.out.println(oneClass);
    
            Object instance = oneClass.newInstance();
    
            Song song = (Song) instance;
    
            song.sing();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    通过上面的演示会发现,通过反射来创建实例过于麻烦了,但是这种方式有一个好处,避免了使用new,而 new 是代码耦合的元凶。

    比如:再工厂模式中,现在想知道一首歌曲作者的年龄

    package com.ifdom.reflection;
    
    interface Author {
        void age();
    }
    
    class Song implements Author {
        @Override
        public void age() {
            System.out.println("我的年龄18岁");
        }
    }
    
    class Company {
        public static Author getInstance(String className) {
            if ("Song" == className) {
                return new Song();
            }
            return null;
        }
    }
    
    /**
     * Reflection
     **/
    public class Reflection1 {
        public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
    
            Author song = Company.getInstance("Song");
            song.age();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    如果 现在公司又有了舞蹈,想知道一个舞蹈作者的年龄.那么不得不修改 Company.getInstance方法种的内容

    package com.ifdom.reflection;
    
    interface Author {
        void age();
    }
    
    class Song implements Author {
        @Override
        public void age() {
            System.out.println("我的年龄18岁");
        }
    }
    
    class Dance implements Author {
        @Override
        public void age() {
            System.out.println("我的年龄88岁");
        }
    }
    
    class Company {
        public static Author getInstance(String className) {
            if ("Song" == className) {
                return new Song();
            } else if ("Dance" == className) {
                return new Dance();
            }
            return null;
        }
    }
    
    /**
     * Reflection
     **/
    public class Reflection1 {
        public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
    
            Author song = Company.getInstance("Song");
            song.age();
    
            Author dance = Company.getInstance("Dance");
            dance.age();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44

    而使用反射则可以解耦

    package com.ifdom.reflection;
    
    interface Author {
        void age();
    }
    
    class Song implements Author {
        @Override
        public void age() {
            System.out.println("我的年龄18岁");
        }
    }
    
    class Dance implements Author {
        @Override
        public void age() {
            System.out.println("我的年龄88岁");
        }
    }
    
    class Company {
        public static Author getInstance(String className) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
            Author instance = null;
    
            try {
                Class<?> oneClass = Class.forName(className);
                instance = (Author) oneClass.newInstance();
                return instance;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
    }
    
    /**
     * Reflection
     **/
    public class Reflection1 {
        public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
            
            Author song = Company.getInstance("com.ifdom.reflection.Song");
            song.age();
    
            Author dance = Company.getInstance("com.ifdom.reflection.Dance");
            dance.age();
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49

    如何使用反射?- 03 调用构造方法

    在上面使用 newInstance() 创建实例时,Song 都是使用的无参构造函数。
    如果一个类只有有参构造函数,又或者有多个构造函数,构造函数有一个参数,有2个参数,有3个参数…
    那么,想要调用 newInstance() 创建实例,就需要用到 Class类中提供的构造方法,主要有2种

    1. 获取指定构造函数 public Constructor<?> getConstructor() throws NoSuchMethodException,SecurityException;
    2. 获取所有构造函数 public Constructor<?>[] getConstructors() throws SecurityException;
    package com.ifdom.reflection;
    
    import java.lang.reflect.Constructor;
    import java.lang.reflect.InvocationTargetException;
    
    class Song {
        private String title;
        private String city;
    
        public Song(String title) {
            this.title = title;
        }
    
        public Song(String title, String city) {
            this.title = title;
            this.city = city;
        }
        
        public void sing() {
            System.out.println("我正在唱 " + this.city + this.title);
        }
    }
    
    /**
     * Reflection
     **/
    public class Reflection1 {
        public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
            Class<?> oneClass = Class.forName("com.ifdom.reflection.Song");
            
            // 获取一个参数的构造函数
            Constructor<?> constructor = oneClass.getConstructor(String.class);
            Song song = (Song) constructor.newInstance("孤勇者");
            song.sing();
    
            // 获取两个参数的构造函数
            Constructor<?> constructor1 = oneClass.getConstructor(String.class, String.class);
            Song song1 = (Song) constructor1.newInstance("下一站天后", "港台");
            song1.sing();
    
            // 获取所有构造函数
            Constructor<?>[] constructorAll = oneClass.getConstructors();
            Arrays.stream(constructorAll).forEach(System.out::println);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    如何使用反射?-04 调用方法

    在上面演示了如何通过反射获取源头类,如何通过反射获取源头类的构造函数,接下来讲解如何通过反射获取源头类上的方法。

    在Class类里面提供有以下取得类中Method()的操作:

    1. 取得一个类中的全部方法:public Method[] getMethods() throws SecurityException;
    2. 取得类中指定方法:public Method getMethod(String name,Class<?> ... ParameterTypes) throws NoSuchMethodException,SecurityException;
    package com.ifdom.reflection;
    
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    class Song {
        private String title;
        private String city;
    
        public void setTitle(String title) { this.title = title;}
    
        public String getTitle() { return title;}
    
        public void setCity(String city) {this.city = city;}
    
        public String getCity() {return city;}
    
        public void setTitleAndCity(String title, String city) {
            this.title = title;
            this.city = city;
        }
    
        @Override
        public String toString() {
            return "Song{" +
                    "title='" + title + '\'' +
                    ", city='" + city + '\'' +
                    '}';
        }
    }
    
    /**
     * @Author ifredom
     * @Date 2022/6/29
     * @ClassName Reflection1
     * @Version 1.0.0
     * @Description 描述
     **/
    public class Reflection1 {
        public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
            Class<?> oneClass = Class.forName("com.ifdom.reflection.Song");
            Object instance = oneClass.newInstance();
    
            // 获取一个方法并调用该方法
            Method setTitleMethod = oneClass.getMethod("setTitle", String.class);
            setTitleMethod.invoke(instance, "洋葱");
    
            // 获取一个方法并调用该方法
            Method getTitleMethod = oneClass.getMethod("getTitle");
            // 打印结果:洋葱
            System.out.println(getTitleMethod.invoke(instance));
    
            // 获取一个方法并调用该方法
            Method setCityMethod = oneClass.getMethod("setCity", String.class);
            setCityMethod.invoke(instance, "大陆");
    
            // 获取所有
            Method[] methods = oneClass.getMethods();
            for (Method method : methods) {
                System.out.println("迭代:" + method);
            }
    
            Song song = (Song) instance;
            // 打印结果:大陆
            System.out.println(song.getCity());
            System.out.println(song);
        }
    
        public static String generatorMethodName(String str) {
            return str.substring(0, 1).toUpperCase() + str.substring(1);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73

    如何使用反射?- 05 调用属性

    到了这里,其实已经可以预料到接下来讲解什么。

    一个类上能有什么东西呢?无非就是构造方法,普通方法,属性(或者叫成员),当然他们都有修饰符 public, private, static可以进行修饰。

    但是在大类上,只剩下一个属性还没有讲解。那么使用反射时如何获取属性字段?

    Class类中提供了2个API方法:

    1. 取得全部成员:public Filed[] getDeclaredFileds() throws SecurityException;
    2. 取得指定成员:public Filed getDeclaredFiled(String name) throws NoSuchMethodException,SecurityException;
    package com.ifdom.reflection;
    
    import java.lang.reflect.Field;
    
    class Song {
        private String title;
    
        @Override
        public String toString() {
            return "Song{" +
                    "title='" + title + '\'' +
                    '}';
        }
    }
    
    /**
     * @Author ifredom
     * @Date 2022/6/29
     * @ClassName Reflection1
     * @Version 1.0.0
     * @Description 描述
     **/
    public class Reflection1 {
        public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException {
            Class<?> oneClass = Class.forName("com.ifdom.reflection.Song");
            Object instance = oneClass.newInstance();
    
            // 获取一个方法并调用该方法
            Field field = oneClass.getDeclaredField("title");
            // 值为 true 时,则表示取消 Java 语言访问检查(private关键字不在生效)
            field.setAccessible(true);
            field.set(instance, "她");
            System.out.println(field.get(instance));
    
            // 获取所有
            Field[] declaredFields = oneClass.getDeclaredFields();
            Arrays.stream(declaredFields).forEach(System.out::println);
    
            Song song = (Song) instance;
            System.out.println(song);
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    反射在 Spring 中的应用

    • ReflectionUtils.invokeMethod(method, obj, args); // ReflectionUtils 是 spring 中内置的工具类
    • Spring: ApplicationContext.getbean(className) ≈≈ Java: Class.forName(className).newInstance().

    拓展:反射reflection提供的其他API

    以下内容已经不用阅读,大部分人从入行到入土也用不上,仅仅对骨灰级玩家有用。

    1. 获取修饰符
    2. 获取注解
    package com.ifdom.reflection;
    
    import java.lang.annotation.Annotation;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    
    @DemoAnnotation(key = "author", value = "ifredom")
    class Song {
        private String title;
        public String author;
    
        public static void test() {
            System.out.println("test");
        }
    }
    
    /**
     * 自定义注解:这个注解有2个属性
     *
     * @author ifredom
     */
    @Retention(RetentionPolicy.RUNTIME)
    @interface DemoAnnotation {
        public String key();
    
        public String value();
    }
    
    /**
     * @Author ifredom
     * @Date 2022/6/29
     * @ClassName Reflection1
     * @Version 1.0.0
     * @Description 描述
     **/
    public class Reflection1 {
        public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchFieldException, NoSuchMethodException {
            Class<?> oneClass = Class.forName("com.ifdom.reflection.Song");
    
            // 获取属性上的修饰符类型
            Field titleField = oneClass.getDeclaredField("title");
            int modifiers = titleField.getModifiers();
            System.out.println(modifiers);
    
            // 获取属性字段名
            Field authorField = oneClass.getDeclaredField("author");
            String fieldName = authorField.getName();
            System.out.println(fieldName);
    
            // 获取方法上的修饰符类型
            Method testMethod = oneClass.getMethod("test");
            int modifiers2 = testMethod.getModifiers();
    
            // 获取方法的注解
            Annotation testMethodAnnotation1 = testMethod.getAnnotation(DemoAnnotation.class);
            System.out.println(testMethodAnnotation1);
    
            // 获取类的注解
            Annotation testMethodAnnotation2 = oneClass.getAnnotation(DemoAnnotation.class);
            System.out.println(testMethodAnnotation2);
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66

    修饰符属性对照表

    • PUBLIC: 1
    • PRIVATE: 2
    • PROTECTED: 4
    • STATIC: 8
    • FINAL: 16
    • SYNCHRONIZED: 32
    • VOLATILE: 64
    • TRANSIENT: 128
    • NATIVE: 256
    • INTERFACE: 512
    • ABSTRACT: 1024
    • STRICT: 2048

    多个修饰符,getModifiers()获取的值是他们和。 例如:public static final 三个修饰的 就是3 个的加和 为 25

    扩展阅读

    ------ 如果文章对你有用,感谢 >>>点赞 | 收藏 <<<

  • 相关阅读:
    系统编程 day11 信号量 (我也不知道是什么东西,不好解释 )(和之前的信号有一些不同 (函数), 但是也有一些操作相同 ,比如说 p v 操作 , )
    PCB这个工艺,免费了!
    Retrofit+Flow网络请求与Android网络请求的演变
    计算机网络概念入门(十一)
    bitcask论文翻译/笔记
    Dubbo入门实例
    结构型设计模式07-享元模式
    css实现一个炫酷动画loading(四)
    Jeff Dean:深度学习的黄金十年
    gdb调试知识积累——真·详解
  • 原文地址:https://blog.csdn.net/win7583362/article/details/125523945