栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Java系列 - 反射

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Java系列 - 反射

  • 是什么:什么是反射?
  • 为什么:为什么会有反射?
  • 怎么办:如何使用反射
  • 在哪里使用?
什么是反射?
  • 正常情况下:先有类,然后有对象
import java.util.Date;//先有类

public class ReflectTest1 {
    public static void main(String[] args) {
        Date date = new Date();//后有对象
        System.out.println(date);
    }
}
  • 反射中的反意思是: 通过对象来找类
import java.util.Date;

public class ReflectTest2 {
    public static void main(String[] args) {
        // 对象
        Date date = new Date();

        // 找类
        System.out.println(date.getClass());
    }
}

为什么会有反射?

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

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

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

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

package com.ifdom.reflection;


class Song {
    public void sing() {
        System.out.println("我正在唱歌");
    }
}


public class Reflection1 {
    public static void main(String[] args) {
        Song song = new Song();
        song.sing();
    }
}

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

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

如何使用反射?- 01 溯源

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

反射溯源有3种方法:

  • 1.通过对象上的 anyObject.getClass() 方法
package com.ifdom.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("我正在唱歌");
    }
}
  • 2.通过每个类都有的属性 AnyClass.class
package com.ifdom.reflection;

class Song {
    public void sing() {
        System.out.println("我正在唱歌");
    }
}


public class Reflection1 {
    public static void main(String[] args) {
        Song song = new Song();

        Class oneClass = Song.class;

        // 结果:class com.ifdom.reflection.Song
        System.out.println(oneClass);
    }
}
  • 3.通过最底层的Class类提供的 forName()方法每个类都有的属性 Class.forName("AnyClassName")
package com.ifdom.reflection;

class Song {
    public void sing() {
        System.out.println("我正在唱歌");
    }
}


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);
    }
}

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


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();
    }
}

通过上面的演示会发现,通过反射来创建实例过于麻烦了,但是这种方式有一个好处,避免了使用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;
    }
}


public class Reflection1 {
    public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {

        Author song = Company.getInstance("Song");
        song.age();
    }
}

如果 现在公司又有了舞蹈,想知道一个舞蹈作者的年龄.那么不得不修改 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;
    }
}


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();
    }
}

而使用反射则可以解耦

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;
    }
}


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();
    }
}

如何使用反射?- 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);
    }
}


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);
    }
}
如何使用反射?-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 + ''' +
                '}';
    }
}


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);
    }
}

如何使用反射?- 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 + ''' +
                '}';
    }
}


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);
    }

}
反射在 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");
    }
}


@Retention(RetentionPolicy.RUNTIME)
@interface DemoAnnotation {
    public String key();

    public String value();
}


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);
    }

}

修饰符属性对照表

  • 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

扩展阅读
  • Spring ReflectionUtils
  • What is reflection and why is it useful?
  • baeldung: java-reflection
  • Spring 中的反射与反射的原理

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

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/998875.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号