1.java反射机制是java基础中比较重要的内容
2.java反射机制是在运行状态中,对于任意一个实体类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;
3.java反射机制在许多框架中都有很好的应用,比如hibernate框架等
4.下面我将为大家进行相关演示以及呈现java反射机制原理以及主要方法等
1、JAVA反射机制的简介及其原理
1.1 java反射机制简介
1. java反射机制是在运行状态中,对于任意一个实体类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;
2. java反射机制其实就是通过获取字节码文件来获取你的类对象,也就是.class对象,然后进行暴力对此对象进行操作;
3. java反射机制在获取授权(一行代码)之后,便可以对私有成员和私有方法进行直接获取值或赋值。
4. 如果到这里还没理解,请移步百度查一下,下面主要讲解应用,不准备在原理上花太多时间,因为个人能力问题,怕误导大家,此博客仅作为码农的中转站,并非终点站
1.2 java反射机制原理
一个.java源码文件,进行编译后会生成.class的字节码文件,然后通过ClassLoader将其加载到内存当中,所有的成员变量都加载成为了Field对象,所有的构造方法都加载成为了Constructor对象,所有的成员方法都加载成为了Method对象,那么此时我们便可以对其进行操作。
对于获取.class文件(上文所述的字节码文件对象),主要有三种方式,我们以Person为自己构建的类对象进行举例,如下代码;
(1)通过类名属性进行获取,多用于参数传递,比如hibernate框架中常用此方法
Class person01 = Person.class;
(2)通过Class.forName(“全路径”)进行获取,此方法常用于配置文件,比如在Mybatis框架中便采用了此方法
Class person02 = Class.forName("cn.hnist.ly.model.Person");
(3)通过 .getClass()函数获取,多用于获取字节码。抱歉我才疏学浅,此类方法我没实践过,但是我用上面两种办法写过自己的jdbc连接框架,当然涉及到的知识点不仅仅这些…
Person p = new Person();
Class person03 = p.getClass();
当我们了解这些,并获取了Class对象后便可以对此进行相关操作了,具体代码如下文
2、JAVA反射机制的演示
2.1 对类成员变量(field对象)的操作
1.对于类的成员变量,本处主要介绍获取成员变量,以及获取值和成员对象进行直接赋值的操作
2.不会面面俱到,点到为止说些常用的
3.获取类对象主要采用常用的传参方式,也就是.getClass()方式进行
2.1.1 获取Field成员变量
1 使用 getField 方法获取成员变量(获取的是所有的public修饰的成员变量)
代码:
/**
* 主方法
**/
public static void main(String[]args) throws Exception {
//构造一个Persong对象
Person person = new Person("aContent", "bContent", "cContent", "dContent");
//获取其Class文件
Class personClass = person.getClass();
//通过getField方法获取所有的field对象(成员变量)
Field[] fields = personClass.getFields(); //下文中直接将getField改为getDeclaredFields的地方
System.out.println("*****************************************************************************");
//打印
for (Field field : fields) {
System.out.println("成员变量为:"+field);
System.out.println("成员变量名:"+field.getName());
System.out.println("成员变量类型:"+field.getGenericType().getTypeName());.
System.out.println("访问权限控制符:"+ Modifier.toString(field.getModifiers()) );
System.out.println("*****************************************************************************");
}
}
/**
* Person类
* 1. 此处忽略get set 以及构造方法等
* 2. 后面的例子均以此为例进行操作
*
**/
public class Person {
public String a;
protected String b;
String c;
private String d;
}
运行结果:
2. 使用 getDeclaredFields 方法获取成员变量(获取的是所有的成员变量)
代码:
仅仅将上文中的 Field[] fields = personClass.getFields();
更改为:Field[] fields = personClass.getDeclaredFields();
其他的部分不变 运行结果如下图
结果为:
小结:
1.getField() 方法仅能获取修饰符为public的成员变量
2.getDeclaredFields()能获取所有的的成员变量
3.除上述两点的区别外,其他的区别不大
3. 已知变量名的情况下,获取单个field对象的方法
代码01:
public static void main(String[]args) throws Exception {
//构造一个Persong对象
Person person = new Person("aContent", "bContent", "cContent", "dContent");
//获取其Class文件
Class personClass = person.getClass();
//获取指定变量名为 a 的成员变量
Field aField = personClass.getField("a");
System.out.println("成员变量为:"+aField);
System.out.println("成员变量名:"+aField.getName());
System.out.println("成员变量类型:"+aField.getGenericType().getTypeName());
}
结果:(当然获取别的非公有的变量则报错)
代码02:
public static void main(String[]args) throws Exception {
//构造一个Persong对象
Person person = new Person("aContent", "bContent", "cContent", "dContent");
//获取其Class文件
Class personClass = person.getClass();
Field aField = personClass.getDeclaredField("d");
System.out.println("成员变量为:"+aField);
System.out.println("成员变量名:"+aField.getName());
System.out.println("成员变量类型:"+aField.getGenericType().getTypeName());
System.out.println("访问权限控制符:"+ Modifier.toString(aField.getModifiers()) );
}
结果:
2.1.2 对获取的Field成员对象的值进行操作
1. 对共有的成员变量进行值操作处理
代码:
public static void main(String[]args) throws Exception {
//构造一个Persong对象
Person person = new Person("aContent", "bContent", "cContent", "dContent");
//获取其Class文件
Class personClass = Person.class;
Field aField = personClass.getField("a");
//获取上文构造的person对象的值
Object value = aField.get(person);
//打印
System.out.println("更改前值为:"+value);
//更改
aField.set(person, "aChange");
//打印
System.out.println("更改后值为:"+aField.get(person));
}
结果:
2. 对私有的成员变量进行值操作处理.
代码:
public static void main(String[]args) throws Exception {
//构造一个Persong对象
Person person = new Person("aContent", "bContent", "cContent", "dContent");
//获取其Class文件
Class personClass = Person.class;
Field aField = personClass.getDeclaredField("d");
//授权
aField.setAccessible(true);
//获取上文构造的person对象的值
Object value = aField.get(person);
//打印
System.out.println("更改前值为:"+value);
//更改
aField.set(person, "aChange");
//打印
System.out.println("更改后值为:"+aField.get(person));
}
结果:
小结:
1.原则上是不允许对类成员变量进行直接赋值或者更改的,最好是通过get或set方法
2.对于public修饰的成员变量,我们可以直接进行更改
3.对于非public的成员变量,我们需要进行授权操作才能进行更改
2.2 对构造方法的操作
1.对构造方法Constructor对象进行操作,常用于创建一个对象,比如本文中就可以创建一个Person对象,可以简单理解为Spring框架也使用了此方法
2.创建步骤跟上文演示的Field对象的创建差不多 也是需要先获取类对象之后再逐步实现
3.创建对象的方法主要是获取了他的构造方法
2.2.1 获取构造方法对象(Constructor对象)的方法
代码:
public static void main(String[]args) throws Exception {
//构造一个Persong对象
Person person = new Person("aContent", "bContent", "cContent", "dContent");
//获取其Class文件
Class personClass = Person.class;
System.out.println("*************获取所有的构造方法**************\n");
//获取构造方法之一,获取所有的构造方法
Constructor[] constructors = personClass.getConstructors();
for(Constructor constructor : constructors) {
System.out.println("构造方法:"+constructor);
}
System.out.println("\n*************根据构造方法的参数获取构造方法*************\n");
//根据构造方法的参数获取构造函数
Constructor constructor02 = personClass.getDeclaredConstructor(String.class,String.class,String.class,String.class);
System.out.println(constructor02);
}
结果:
2.2.2 通过获取的构造方法对象创建对象
1.对于非空参数的构造方法创建对象
代码:
public static void main(String[]args) throws Exception {
//构造一个Persong对象
Person person = new Person("aContent", "bContent", "cContent", "dContent");
//获取其Class文件
Class personClass = Person.class;
//根据构造方法的参数获取构造函数
Constructor constructor02 = personClass.getDeclaredConstructor(String.class,String.class,String.class,String.class);
//对获取的构造方法对象进行创建Person对象的操作
Object personObject = constructor02.newInstance("a","b","c","d");
//打印
System.out.println(personObject);
}
结果:
2.对于空参数构造方法创建对象
方法一:就是将上文的方法去掉参数即可,本文不是很推荐使用这个方法,推荐使用方法二
方法二:
代码:
public static void main(String[]args) throws Exception {
//构造一个Persong对象
Person person = new Person("aContent", "bContent", "cContent", "dContent");
//获取其Class文件
Class personClass = Person.class;
//创建一个空参的person对象
Object personObject = personClass.newInstance();
//打印
System.out.println(personObject);
}
结果:
小结(应用):
对于此类方法其实在Hibernate框架经常使用,返回的Object 进行强制转换成我们所需要的各类对象即可,但是也可能因为sql语句问题等造成错误(hibernate框架中)
自己在构建自己的jdbc的框架的时候也应该会经常用到
spring框架管理创建的对象时候,我相信也会用到(本人还没看spring源码)
2.3 对成员方法的操作
2.3.1 获取类对象成员方法
- 获取方法和上文中的差不多,那么我就不再多做累赘,直接上代码
- 特别说明:获取成员方法时,不仅仅会获取自定义的成员方法,还会获取Object类的成员方法
- 获取method对象后,使用invoke方法进行运行,当然非公有的需要授权
2.3.1.1 获取全部的成员方法
代码:
public static void main(String[] args) throws Exception {
//构造一个Persong对象
Person person = new Person("aContent", "bContent", "cContent", "dContent");
//获取其Class文件
Class personClass = Person.class;
//获取所有共有的成员方法
Method[] methods = personClass.getMethods();
//获取所有的成员方法
//Method[] methods = personClass.getDeclaredMethods();
//打印
for (int i = 0; i < methods.length; i++) {
System.out.println(methods[i]);
}
}
结果:
2.3.1.2 获取单个的成员方法
代码:
public static void main(String[]args) throws Exception {
//构造一个Persong对象
Person person = new Person("aContent", "bContent", "cContent", "dContent");
//获取其Class文件
Class personClass = Person.class;
//获取getA()方法
Method getAMethod = personClass.getDeclaredMethod("getA");
//获取setA()方法 , 应为setA方法有个参数 String 故需要多传入一个参数
Method setAMethod = personClass.getDeclaredMethod("setA", String.class);
//打印
System.out.println("getA()方法:"+getAMethod);
System.out.println("setA()方法:"+setAMethod);
}
结果:
2.3.2 运行获取的成员方法
1. 因为获取的是类对象,并非真正运行状态的对象,故需要先new 一个Person对象,然后以他为主体进行操作
2. 延续上文的代码
3. 运行非公有的成员方法时,需要在获取Method对象后使用setAccessible(true);进行授权,例如:setAMethod.setAccessible(true);
代码:
public static void main(String[]args) throws Exception {
//构造一个Persong对象
Person person = new Person("aContent", "bContent", "cContent", "dContent");
//获取其Class文件
Class personClass = Person.class;
//获取getA()方法
Method getAMethod = personClass.getDeclaredMethod("getA");
//获取setA()方法 , 应为setA方法有个参数 String 故需要多传入一个参数
Method setAMethod = personClass.getDeclaredMethod("setA", String.class);
//打印
System.out.println("getA()方法打印结果:"+getAMethod.invoke(person));
//重新设置A值
setAMethod.invoke(person, "AChange");
//重新打印A
System.out.println("getA()再次打印结果:"+getAMethod.invoke(person));
}
结果:
本文总结
- java反射作为java基础内容,也是比较高级的难一点的基础内容,本文仅作为入门文档,在实际应用开发中会遇到各种情况,因为篇幅原因,本文先结束
- java反射的运用在某种情况下会违反一些原则,这些原则在java开发中应该是明文禁止或避免的,但是反射可以暴力执行,也必须需要反射进行暴力执行
- 各种各样的以人为本的框架离不开java反射的这一块内容,我接触比较多的就是spring全家桶以及jdbc的框架。不管你以后是使用市面上比较流行的框架还是自主开发一套,都离不开java反射的扶持,也希望引起大家的重视。
- 关于作者我本人能力有限,水平一般,如果有错误或者不好之处,希望读者看官多多斧正。