反射常见面试题

2020/1/1

# 反射常见面试题

# 1.何为反射?

反射就是Reflection,Java的反射是指程序在运行期可以拿到一个对象的所有信息。

反射之所以被称为框架的灵魂,主要是因为它赋予了我们在运行时分析类以及执行类中方法的能力。通过反射你可以获取任意一个类的所有属性和方法,你还可以调用这些方法和属性。

Reflection(反射)是被视为动态语言的关键(本身仍为静态语言,可以说是准动态语言),反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射。

# 2.反射的使用场景

平时开发,大部分时候都在写业务代码,很少会接触到直接使用反射机制的场景。

这并不代表反射没有用。相反,正是因为反射,才能这么轻松地使用各种框架。像 Spring/Spring Boot、MyBatis 等等框架中都大量使用了反射机制。

Java的动态代理的实现也依赖反射。关于动态代理可参看:实例理解JDK动态代理和Cglib动态代理及其区别 (opens new window)

Java 中的一大利器注解的实现也用到了反射。

为什么你使用 Spring 的时候 ,一个@Component注解就声明了一个类为 Spring Bean 呢?为什么你通过一个 @Value注解就读取到配置文件中的值呢?究竟是怎么起作用的呢?

这些都是因为你可以基于反射分析类,然后获取到类/属性/方法/方法的参数上的注解。你获取到注解之后,就可以做进一步的处理。

# 3.反射的优缺点

优点 : 可以让咱们的代码更加灵活、为各种框架提供开箱即用的功能提供了便利

缺点 :让我们在运行时有了分析操作类的能力,这同样也增加了安全问题。比如可以无视泛型参数的安全检查(泛型参数的安全检查发生在编译时)。另外,反射的性能也要稍差点,不过,对于框架来说实际是影响不大的。相关阅读:Java Reflection: Why is it so slow? (opens new window)

# 4. 反射获取Class对象的四种方式

# 4.1四种方式

  1. 调用某个类的class属性来获取该类对应的Class对象。例如Person.class将会返回Person类对应的Class对象
  2. 使用Class类forName()静态方法。该方法需要传入字符串参数,该字符串参数的值是某个类的全限定类名(必须添加完整包名)。
  3. 调用某个对象的getClass()方法,该方法是java.lang.Object类中的一个方法,所以所有Java对象都可以调用该方法,该方法将会返回该对象所属类对应的Class对象。
  4. 使用类的加载器:ClassLoader

# 4.2 推荐直接调用类的class属性获取Class对象

优势如下:

  1. 代码更安全,程序在编译阶段就可以检查需要访问的Class对象是否存在。
  2. 程序性能更高,因为这种方式无须调用方法,所以性能更好。

但如果我们只有一个字符串, 例如“java.lang. String”,如果需要获取该字符串对应的Class对象,则只能使用第二种方 式了,使用Class的forName方法获取Class对象时,该方法可能抛出一个ClasNotFoundException异常

# 4.3代码测试获取Class对象

MyAnnotation注解Person接口Producer类为通用的,后续还会使用

@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {

    String value() default "Annotation测试";

}
1
2
3
4
5
6
7
/**
 * 测试反射的接口
 */
public interface Person {
    //吃饭
    void eat(String food);
}
1
2
3
4
5
6
7
/**
 * 功能描述:测试反射的JavaBean
 * 生产商类
 *
 * @author RenShiWei
 * Date: 2020/6/19 9:16
 **/
@MyAnnotation("反射获取类注解信息")
public class Producer implements Person {

    @MyAnnotation("生产商姓名")
    private String name;

    int age;

    public int id;
    
    //生产的商品map
    public Map<String,String> productMap=new HashMap<>();

    public Producer () {
    }

    private Producer ( String name, int age, int id ) {
        this.name = name;
        this.age = age;
        this.id = id;
    }

    //get和set方法
    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;
    }

    public int getId () {
        return id;
    }

    public void setId ( int id ) {
        this.id = id;
    }

    //生产产品
    @MyAnnotation
    public String produce ( String product ) {
        return "正在生产产品:" + product;
    }

    //查看产品
    void showProducts () {
        System.out.println("正在查看产品");
    }

    //实现接口的方法
    @Override
    public void eat ( String food ) {
        System.out.println("正在吃:" + food);
    }

    //内部类
    class Inner{}
    
    @Override
    public String toString () {
        return "Producer{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", id=" + id +
                '}';
    }
}

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
74
75
76
77
78
79
80
81
82
83
84

测试获取Class对象

public static void main ( String[] args ) {
        System.out.println("**************测试获取Class的对象******************");
        System.out.println("----方式一:调用类的class属性获取----");
        Class<Producer> clazz1 = Producer.class;
        System.out.println(clazz1);

        System.out.println("---方式二:通过Class类的静态方法forName()获取----");
        Class<?> clazz2=null;
        try {
            clazz2 = Class.forName("reflectiontest.Producer");
            System.out.println(clazz2);
        } catch (ClassNotFoundException e) {
            System.out.println("没有通过全限类名找到此类,不能使用反射获取Class对象");
            e.printStackTrace();
        }

        System.out.println("---方式三:调用对象的getClass()方法(此方法为java.lang.Object的方法)获取---");
        Producer producer=new Producer();
        Class<? extends Producer> clazz3 = producer.getClass();
        System.out.println(clazz3);

        System.out.println("---方式四:通过类加载器ClassLoader获取---");
        ClassLoader classLoader=ReflectionTest.class.getClassLoader();
        Class<?> clazz4=null;
        try {
            clazz4 = classLoader.loadClass("reflectiontest.Producer");
            System.out.println(clazz4);
        } catch (ClassNotFoundException e) {
            System.out.println("没有通过全限类名找到此类,不能使用反射获取Class对象");
            e.printStackTrace();
        }
        System.out.println("对比四种方式获取的Class对象是否是同一个");
        System.out.println(clazz1==clazz2);
        System.out.println(clazz1==clazz3);
        System.out.println(clazz1==clazz4);
}
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

结果:

**************测试获取Class的对象******************
----方式一:调用类的class属性获取----
class reflectiontest.Producer
---方式二:通过Class类的静态方法forName()获取----
class reflectiontest.Producer
---方式三:调用对象的getClass()方法(此方法为java.lang.Object的方法)获取---
class reflectiontest.Producer
---方式四:通过类加载器ClassLoader获取---
class reflectiontest.Producer
对比四种方式获取的Class对象是否是同一个
true
true
true
1
2
3
4
5
6
7
8
9
10
11
12
13