学习spring提到spring框架中的setter方法是使用反射机制实现的,反射机制到底是什么呢?找了一篇文章
Java基础与提高干货系列——Java反射机制
前言
今天介绍下Java的反射机制,以前我们获取一个类的实例都是使用new一个实例出来。那样太low了,今天跟我一起来学习学习一种更加高大上的方式来实现。
正文
Java反射机制定义
Java反射机制是指在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
用一句话总结就是反射可以实现在运行时可以知道任意一个类的属性和方法。
反射机制的优点与缺点
为什么要用反射机制?直接创建对象不就可以了吗,这就涉及到了动态与静态的概念
- 静态编译:在编译时确定类型,绑定对象,即通过。
-
动态编译:运行时确定类型,绑定对象。动态编译最大限度发挥了java的灵活性,体现了多态的应用,有以降低类之间的藕合性。
-
优点
可以实现动态创建对象和编译,体现出很大的灵活性,特别是在J2EE的开发中它的灵活性就表现的十分明显。比如,一个大型的软件,不可能一次就把把它设计的很完美,当这个程序编译后,发布了,当发现需要更新某些功能时,我们不可能要用户把以前的卸载,再重新安装新的版本,假如这样的话,这个软件肯定是没有多少人用的。采用静态的话,需要把整个程序重新编译一次才可以实现功能的更新,而采用反射机制的话,它就可以不用卸载,只需要在运行时才动态的创建和编译,就可以实现该功能。 - 缺点
对性能有影响。使用反射基本上是一种解释操作,我们可以告诉JVM,我们希望做什么并且它满足我们的要求。这类操作总是慢于只直接执行相同的操作。
理解Class类和类类型
想要了解反射首先理解一下Class类,它是反射实现的基础。
类是java.lang.Class类的实例对象,而Class是所有类的类(There is a class named Class)
对于普通的对象,我们一般都会这样创建和表示:
1
|
Code code1 = new Code();
|
上面说了,所有的类都是Class的对象,那么如何表示呢,可不可以通过如下方式呢:
1
|
Class c = new Class();
|
但是我们查看Class的源码时,是这样写的:
1
|
private Class(ClassLoader loader) {
|
可以看到构造器是私有的,只有JVM可以创建Class的对象,因此不可以像普通类一样new一个Class对象,虽然我们不能new一个Class对象,但是却可以通过已有的类得到一个Class对象,共有三种方式,如下:
1
|
Class c1 = Code.class;
|
这里,c1、c2、c3都是Class的对象,他们是完全一样的,而且有个学名,叫做Code的类类型(class type)。
这里就让人奇怪了,前面不是说Code是Class的对象吗,而c1、c2、c3也是Class的对象,那么Code和c1、c2、c3不就一样了吗?为什么还叫Code什么类类型?这里不要纠结于它们是否相同,只要理解类类型是干什么的就好了,顾名思义,类类型就是类的类型,也就是描述一个类是什么,都有哪些东西,所以我们可以通过类类型知道一个类的属性和方法,并且可以调用一个类的属性和方法,这就是反射的基础。
举个简单例子代码:
1
|
public class ReflectDemo {
|
执行结果:
1
|
com.tengj.reflect.ReflectDemo
|
Java反射相关操作
前面我们知道了怎么获取Class,那么我们可以通过这个Class干什么呢?
总结如下:
- 获取成员方法Method
- 获取成员变量Field
- 获取构造函数Constructor
下面来具体介绍
获取成员方法信息
单独获取某一个方法是通过Class类的以下方法获得的:
1
|
public Method getDeclaredMethod(String name, Class<?>... parameterTypes) // 得到该类所有的方法,不包括父类的
|
两个参数分别是方法名和方法参数类的类类型列表。
例如类A有如下一个方法:
1
|
public void fun(String name,int age) {
|
现在知道A有一个对象a,那么就可以通过:
1
|
Class c = Class.forName("com.tengj.reflect.Person"); //先生成class
|
完整代码如下:
1
|
public class Person {
|
执行结果:
1
|
我叫tengj,今年10岁
|
怎样,是不是感觉很厉害,我们只要知道这个类的路径全称就能玩弄它于鼓掌之间。
有时候我们想获取类中所有成员方法的信息,要怎么办。可以通过以下几步来实现:
1.获取所有方法的数组:
1
|
Class c = Class.forName("com.tengj.reflect.Person");
|
2.然后循环这个数组就得到每个方法了:
1
|
for (Method method : methods)
|
完整代码如下:
person类跟上面一样,这里以及后面就不贴出来了,只贴关键代码
1
|
public class ReflectDemo {
|
执行结果:
1
|
getName
|
这里如果把c.getDeclaredMethods();改成c.getMethods();执行结果如下,多了很多方法,以为把Object里面的方法也打印出来了,因为Object是所有类的父类:
1
|
getName
|
获取成员变量信息
想一想成员变量中都包括什么:成员变量类型+成员变量名
类的成员变量也是一个对象,它是java.lang.reflect.Field
的一个对象,所以我们通过java.lang.reflect.Field
里面封装的方法来获取这些信息。
单独获取某个成员变量,通过Class类的以下方法实现:
1
|
public Field getDeclaredField(String name) // 获得该类自身声明的所有变量,不包括其父类的变量
|
参数是成员变量的名字。
例如一个类A有如下成员变量:
1
|
private int n;
|
如果A有一个对象a,那么就可以这样得到其成员变量:
1
|
Class c = a.getClass();
|
完整代码如下:
1
|
public class ReflectDemo {
|
执行结果:
1
|
hello wrold
|
同样,如果想要获取所有成员变量的信息,可以通过以下几步
1.获取所有成员变量的数组:
1
|
Field[] fields = c.getDeclaredFields();
|
2.遍历变量数组,获得某个成员变量field
1
|
for (Field field : fields)
|
完整代码:
1
|
public class ReflectDemo {
|
执行结果:
1
|
name
|
获取构造函数
最后再想一想构造函数中都包括什么:构造函数参数
同上,类的成构造函数也是一个对象,它是java.lang.reflect.Constructor
的一个对象,所以我们通过java.lang.reflect.Constructor
里面封装的方法来获取这些信息。
单独获取某个构造函数,通过Class类的以下方法实现:
1
|
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) // 获得该类所有的构造器,不包括其父类的构造器
|
这个参数为构造函数参数类的类类型列表。
例如类A有如下一个构造函数:
1
|
public A(String a, int b) {
|
那么就可以通过:
1
|
Constructor constructor = a.getDeclaredConstructor(String.class, int.class);
|
来获取这个构造函数。
完整代码:
1
|
public class ReflectDemo {
|
执行结果:
1
|
tengj
|
注意:Class的newInstance方法,只能创建只包含无参数的构造函数的类,如果某类只有带参数的构造函数,那么就要使用另外一种方式:fromClass.getDeclaredConstructor(String.class).newInstance(“tengj”);
获取所有的构造函数,可以通过以下步骤实现:
1.获取该类的所有构造函数,放在一个数组中:
1
|
Constructor[] constructors = c.getDeclaredConstructors();
|
2.遍历构造函数数组,获得某个构造函数constructor:
1
|
for (Constructor constructor : constructors)
|
完整代码:
1
|
public class ReflectDemo {
|
执行结果:
1
|
public com.tengj.reflect.Person()
|
通过反射了解集合泛型的本质
首先下结论:
Java中集合的泛型,是防止错误输入的,只在编译阶段有效,绕过编译到了运行期就无效了。
下面通过一个实例来验证:
1
|
/**
|
执行结果:
1
|
list2的长度是:1
|
总结
到此,Java反射机制入门的差不多了,我是复习SpringMVC里面IOC/DI的时候,是通过Java反射来实现的,希望这篇笔记也对你有用。