java反射总结

理解反射对学习Java框架有很大的帮助,如Spring框架的核心就是使用Java反射实现的,而且对做一些Java底层的操作会很有帮助。
(其实是在学JDBC的时候发现用到了一点反射的知识,所以就去补了一下)

一,Class类

1、 在面向对象的世界里,万事万物皆对象,(当然静态的成员、基本数据类型除外(属于类的))所以我们创建的每一个类也都是对象,即类本身是java.lang.Class类的实例对象。
2、 任何一个类都是Class类的实例对象,这个实例对象有三种表示方式。
首先我们新建一个Student类
Student st=new Student();//Student就表示出来了
任何一个类都是Class的实例对象,Student这个类也是一个实例对象。
(1) 第一种表示方式--->实际在告诉我们任何一个类都有一个隐含的静态成员变量class
Class c1=Student.class;
(2)第二种表达方式--->已经知道该类的对象通过getClass方法
Class c2=Student.getClass();
官网解释说:c1,c2表示了Student类的类类型(class type),万事万物皆对象,类也是对象,是Class类的实例对象这个对象我们称为该类的类类型,这里有一点值得注意,当我们执行System.out.println(c1==c2);语句,结果返回的是true。所以不管是c1还是c2都代表了Student类的类类型,一个类只可能是Class类的一个实例对象。
(3)第三种表达方式
Class c3=null;
c3=Class.forName("包名.Student");//会有一个ClassNotFoundException异常

注:

我们完全可以通过类的类类型创建类的对象实例--->通过c1 or c2 or c3创建Student的实例对象。
Student s=(Student)c1.newInstance();//需要有无参数的构造方法

二,动态加载类

1.静态加载类,是编译时刻加载;动态加载类,是运行时刻加载。
2.new创建对象:是静态加载类,在编译时刻就需要加载所有的可能使用到的类。有一个类有问题(如不存在),都不能通过编译,会报错。
3.Class.forName()通过动态加载类,可以用到一个类时,才进行加载。
(功能性的类尽量使用动态加载,而不用静态加载。可以将类的实例转化为接口类型,下次只需要修改接口实现类,重新编译接口实现类,不需要编译接口类就可以直接运行加载)

可以理解为装修房子(编译)买电器,我之前计划的有电视冰箱洗衣机,全都随着装修安装完毕了(静态加载),之后呢(运行),觉得少个微波炉,可是根本就没有规划微波炉的线路,那么就需要拆掉原先的线路为新的电器设置安装.而现在我不想那么麻烦,就在装修的时候预留好电源插座这种接口(interface),只要所有的电器都实现了这个接口,有了两孔或者三孔的插头,那么我后期想增添任何电器都可以随时增加(动态加载)

三,获取方法信息:

基本数据类型,void关键字都存在类类型
class.getMethods()方法获取是该类的所有public方法,包括从父类继承的方法;
class.getDeclareMethods()方法获取该类自行声明的所有方法,不论访问权限;
method.getName()获取方法名
method.getReturnType()获取方法的返回值
method.getParameterTypes(),获取方法的参数类型的类类型数组class[]

四,获取成员变量&构造函数

1、成员变量是java.lang.reflect.Field的对象

Field类封装了关于成员变量的操作

getFields()方法获取的是所有的public的成员变量

getDeclaredField()方法获取的是所有自己声明的成员变量

field.getType()得到成员变量的类型的类类型

field.getName()得到成员变量的名称

2、构造函数是java.lang.Constructor类的对象

getConstructors()获取所有的Public的构造函数

getDeclaredConstructors()获取所有的构造函数,构造方法必须是自己声明的

constructor.getParameterTypes()得到构造函数的参数列表(参数列表的类类型)

注:当一个类中没有定义构造函数时,系统会给该类中加一个默认的空参数的构造函数,方便该类初始化。只是该空构造函数是隐藏不见的。当在该类中自定义了构造函数,默认构造函数就没有了。构造函数不能继承,因为子类继承父类的时候,先运行父类构造函数;具体的说就是运行父类时就会先“调用”父类的构造函数,注意“调用”和继承不是一个含义,实质上是“自动运行”。

五,Java 方法反射的操作

1、如何获取某个方法

方法名称和方法参数列表才能唯一决定某个方法

先获取该类的类类型

A a1 = new A();

Class c = a1.getClass();

再通过类类型获取具体的方法

c.getMethod("print",int.class,int.class);

c.getMethod("print",new Object[]{int.class,int.class});

2、方法反射的操作

Object obj = method.invoke(对象,参数列表);

Object obj = m.invoke(a1,10,20);

Object obj = m.invoke(a1,new Object[]{10,20});

如果该方法没有返回值,则返回null,如果有返回值则返回具体的值

假设程序员A在写一个类A,程序员B在写一个类B,现在程序员要用到A写的类,但是A还没写完,因此肯定是编译不了的(可以假设A类实现了一个接口,但是程序员A还没有将所有方法全部实现),但是B又需要用到A完成了的某个方法,此时通过反射,就可以在非编译的情况下动态调用某个方法。

六、通过反射了解集合泛型的本质

1、通过Class和Method了解泛型的本质

2、Java集合中的泛型,是防止错误输入的,只在编译阶段有效,绕过编译机无效了

ArrayList list1 = new ArrayList();

ArrayList list2 = new ArrayList();

Class c1 = list1.getClass();

Class c2 = list2.getClass();

System.out.Println(c1 == c2);

打印出true,说明编译后集合的泛型是去泛型化的

3、验证

我们可以通过方法的反射来操作,绕过编译

Method m = c1.getMethod("add",Object.class);

m.invoke(list2,100);//绕过编译操作就绕过了泛型

System.out.println(list2.size());

打印出1,说明加进去了

注:此时就不能用foreach来遍历了,否则会报类型转换异常

Last modification:April 25th, 2019 at 05:18 pm

Leave a Comment