转载于:https://www.cnblogs.com/BRSblackshoot/p/15135178.html
1 反射
封装代码的依据就是反射和泛型,理解反射和泛型能帮助自己更好的掌握手动封装代码的方法,减少代码量,而且对于框架的源码阅读也有帮助。
之前有接触过反射和泛型的概念,现在再系统说明一次以加强对这两个概念的认识
- 反射和泛型属于java的机制,和web无关
- 反射通常与泛型一起使用,因为用反射封装的方法需要适用于所有数据类型,此时数据类型是不确定的,需要用泛型“占位”
- 反射机制最核心的源码如图
1.1 获取Class对象
反射中Class.forName()和ClassLoader.loadClass()的区别 - Jerry迎风 - 博客园 (cnblogs.com)
语法 | 说明 | 静态代码块 | 非静态代码块 |
---|---|---|---|
Class 变量名 = Class.forName ( 全类名 ) | 通过全类名获取全类名指向的类 | 加载 | / |
Class 变量名 = 对象名.getClass() | 通过对象名获取对象所属的类 | 加载 | 加载 |
Class 变量名 = 类名.class | 通过类名获取那个类 | / | / |
Class 变量名 = 任意自定义的类.class.getClassLoader().loadClass(全类名) | 通过类的加载器获取全类名指向的类 | / | / |
注意:
- 全类名即.java文件的绝对路径,外部传入
- 通过类的加载器获取Class时,前面的写法都是固定的,需要传入的参数和方式1相同,两者的区别在于代码块
- 数据库链接时,要使用Class.forName(全类名),因为JDBC Driver中有静态代码块需要被加载
1.1.1 代码块的概念
首先明确一个概念——反射不只用于实体类,也能用于工具类,比如JDBCUtil,或者第三方jar包,比如JDBC Driver
而这些工具类需要在静态代码块里实现类的初始化
因此反射这种类时,就要选择能加载静态代码块的方法。
使用static修饰的代码块即静态代码块;没有static修饰的代码就是非静态代码块,通常非静态代码块用的很少。
实例:
①通过全类名(即类的地址)获取class
可以加载静态代码块,不能加载非静态代码块
②通过对象名获取class
能同时加载静态和非静态代码块
③通过类名获取class
无法加载代码块
④通过类的加载器获取class
无法加载代码块
1.1.2 类的加载器
1.1.3 类的加载器的常见方法
关于类加载的loadClass()方法的讲解_Demon_T的博客-CSDN博客
①loadClass(全类名)
只要知道loadClass(全类名)能根据传入的全类名返回一个Class对象即可
②getResourceAsStream(String name)
通常与getResourceAsStream(String name)结合,用于获取配置文件的信息,返回一个输入流,常用于配置druid连接池
1.2 获取类的构造器
只要获取到类就能获取到类的构造器、成员变量、成员方法。
实际开发中基本只用类的无参构造器,也不需要专门获取类的构造器,因此以下内容了解即可。
语法 | 说明 |
---|---|
Constructor 变量名 = Class对象 . getConstructor() | 获取无参构造方法 |
Constructor 变量名 = Class对象 . getConstructor(String.class, String.class) | 通过参数类型获取有参的构造函数,显然这得先知道想要的构造函数的具体参数表 |
Constructor[] 变量名 = Class对象 . getConstructors() | 获取所有的构造方法,返回的是一个数组,注意方法名最后的“s” |
Constructor对象 . getName() | 获得构造方法的名字,是全类名 |
Constructor对象 . getParameterCount() | 获得构造方法参数表中的参数个数 |
Constructor对象 . getParameterTypes() | 获得一个Class对象数组,每个Class对象指向构造方法参数表中的一个参数类型 |
Constructor对象 . getParameters() | 获得构造方法的参数表,参数类型用全类名表示,参数名用arg加下标组成 |
Object object = Constructor对象 . newInstance() | 使用无参构造函数创建一个Object类型的对象 |
Object object = Constructor对象 . newInstance(参数1,参数2) | 使用有参构造器创建一个Object类型的对象,JAVA会自动调用能对应上参数的构造器 |
注意:
newInstance()除了可以给Class对象用,也能给Constructor对象用,均是使用无参构造获得一个object对象,这也是最常用的创建对象的方法。
实例:
Class对象指向的类的构造方法
①获取无参构造
- 获得的名字是全类名
- 无参构造的参数个数为0
②获取有参构造
在知道某个构造器需要的参数类型后,可以获取到该有参构造方法
当一个类被用来创建对象时,类中的静态代码块和非静态代码块均会被调用
③获取所有构造方法
此时能获得一个对象数组,里面按照代码的先后顺序存放类中所有的构造方法,比如图中的信息就表明类中先写了无参构造再写了有参构造
1.3 获取类的方法
java.lang.reflect.Method类提供了获取类的成员函数的方法,方法通常由修饰符、方法名、返回类型、参数类型构成。
语法 | 说明 |
---|---|
Method[] 变量名 = Class对象 . getMethods() | 获取类中所有的公开方法,返回一个对象数组 1.包括从父类继承的(一直到Object超类)以及从父接口继承的(一直到超接口) 2.注意:JDK新特性,接口中除了抽象方法,也能存在成员函数 |
Method[] 变量名 = Class对象 . getDeclaredMethods() | 获取类中所有的方法(包含私有,需要暴力破解),但只能是类自己的方法,返回一个对象数组 1.包括重载的父类方法以及实现的父接口方法 2.不包括从父类继承或者从父接口继承的方法 |
Method 变量名 = Class对象 . getMethod ("方法名", 指向参数类型的Class对象) | 获取类中某个带参的方法,由于存在方法重载的可能,因此需要给定方法的参数表 |
Method 变量名 = Class对象 . getMethod ("方法名") | 获取类中某个无参的方法 |
Method对象 . getName() | 获得方法名,不是全类名 |
Method对象 . getParameterCount() | 获得方法的参数个数 |
Method对象 . getParameterTypes() | 获得一个Class对象数组,每个Class对象指向方法参数表中的一个参数类型 |
Method对象 . getParameters() | 获得方法的参数表,参数类型用全类名表示,参数名用arg加下标组成 |
Method对象 . getReturnType() | 获得方法的返回类型,无返回类型则返回void |
Method对象 . setAccessible(true) | 在调用私有的方法前需要设置暴力破解 |
Method对象 . invoke(对象名,参数1,参数2) | 调用方法,需要设置哪个对象调用这个方法,并提供这个方法需要的所有参数,参数可以是数组 |
注意:
①获取到私有方法时可以直接打印该方法,但是如果希望使用该方法,必须先设置暴力破解
②invoke的第一个参数是对象,后面的是可变参数,也就是允许传入多个参数,也允许传入数组
-
invoke实现类的源码
-
invoke实现类的接口源码
1.3.1 获取类中某一个公开的方法 无参无返回值
1.3.2 获取类中某一个公开的方法 有参无返回值
指定需要从类中取出的方法名 此时可能存在方法重载 所以还要指定这个方法需要的参数 以精准指定某个方法
1.3.3 获取类中某一个公开的方法 有参有返回值
1.3.4 获取类中所有的公开的方法(包括从父类、父接口继承的)
因为会继承父类并且一直继承到超级父类,因此必定有toString、hashCode、wait、equals等方法,除此之外还会有get/set方法以及明写出来的成员函数
1.3.5 获取类中某一个方法
1.3.6 获取类中所有的方法(不包含从父类或父接口继承的)
1.4 获取类的变量
java.lang.reflect.Filed类提供了获取类中的成员变量(属性)的方法
语法 | 说明 |
---|---|
Field 变量名 = Class对象 . getField( "变量名" ) | 获取类中指定名字的公开的成员变量,返回一个对象 |
Field[] 变量名 = Class对象 . getFields() | 获取类中所有公开的成员变量,包括从父类继承的公开的成员变量,返回一个对象数组 |
Field 变量名 = Class对象 . getDeclaredField( "变量名" ) | 获取类中指定名字的成员变量,即便这个变量没有公开,返回一个对象 |
Field[] 变量名 = Class对象 . getDeclaredFields(); | 获取类中所有的成员变量,但不包括从父类继承的成员变量,返回一个对象数组 |
Field对象 . getName() | 获得成员变量的名字,不是全类名 |
Field对象 . getType() | 返回一个Class对象,指向该成员变量的数据类型 |
Field对象 . set( 对象名 , 参数); | 调用set方法,需要设置哪个对象调用这个方法,并提供这个方法需要的所有参数,参数可以是数组 |
Field对象 . get( 对象名 ) | 调用get方法,需要设置哪个对象调用这个方法 |
Field对象 . setAccessible(true); | 在操作私有的成员变量之前,需要设置暴力破解 |
注意:
①获取到私有变量时可以直接打印该方法,但是如果希望使用该方法,必须先设置暴力破解
②set的第一个参数是对象,后面只允许跟着一个参数。
set实现类的源码
下面分别列举获取属性的四种方法的实例,演示的类存在如图的属性:
1.4.1 获取某一个公开的属性
需要指定属性的名字