反射常见面试题
# 反射常见面试题
# 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四种方式
- 调用某个类的
class属性
来获取该类对应的Class对象。例如Person.class
将会返回Person类
对应的Class对象
。 - 使用
Class类
的forName()静态方法
。该方法需要传入字符串参数,该字符串参数的值是某个类的全限定类名(必须添加完整包名)。 - 调用某个对象的
getClass()
方法,该方法是java.lang.Object
类中的一个方法,所以所有Java对象都可以调用该方法,该方法将会返回该对象所属类对应的Class对象。 - 使用类的加载器:
ClassLoader
# 4.2 推荐直接调用类的class属性获取Class对象
优势如下:
- 代码更安全,程序在编译阶段就可以检查需要访问的Class对象是否存在。
- 程序性能更高,因为这种方式无须调用方法,所以性能更好。
但如果我们只有一个字符串, 例如“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测试";
}
2
3
4
5
6
7
/**
* 测试反射的接口
*/
public interface Person {
//吃饭
void eat(String food);
}
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 +
'}';
}
}
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);
}
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
2
3
4
5
6
7
8
9
10
11
12
13