一,反射的概述
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
简单来说,可以总结为:
1.在运行时,我们可以获取任意一个类的所有方法和属性
2.在运行时,我们可以调用任意一个对象的所有方法和属性
反射使用的前提条件:
必须先获取到代表该类字节码的Class,Class类用于表示.Class文件(字节码)
二,Class类的使用
1.在面向对象的世界里,万事万物皆为对象。在java语言中,类也是对象,类是java.lang.Class类的实例对象。,这个实例对象官方称为“该类的类类型”,习惯称作“该类的字节码”。因为一个类在编译之后都会生成一个.Class的字节码文件。
2.创建Class类的实例对象的方式(以Student类举例)
当我们想要创建Student类的实例时,
package cn.reflect.test;
public class ReflectDemo {
public static void main(String[] args) {
//创建Student的实例
Student stu1 = new Student();
}
}
//创建一个Student类
class Student{};
我们知道Student这个类,也是一个Class类的实例对象,那么这个实例对象能不能也通过上面的方式表示呢?
通过查看Class类的源码,我们发现,Class这个类的构造被私有化,并不能通过上面的方式,来进行创建实例。那么我们该怎么创建Class类的实例呢?我们可以查看JDK使用文档。
获取Class类的实例对象的三种方式
1.任何数据类型(包括基本数据类型),都有一个隐含的以static修饰的静态成员变量class属性
2.通过getClass()方式来获取。因为所有的类都继承Object类,该方法存在于Object类中,所以任意一个类的对象都可以调用该方法
3.通过Class类的静态方法:forName(String className)(推荐使用)**
package cn.reflect.test;
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException {
//创建Student的实例
Student stu1 = new Student();
//Student类也是Class类的一个实例对象,这个实例对象有三种表示方式
//方式一----->说明每个类都有一个隐含的静态成员变量class
Class c1 = Student.class;
//方式二:通过Object类的getClass()方法来创建
Class c2 = stu1.getClass();
//方式三:通过Class的静态方法forName(String className)来创建 (推荐使用)
//注意:这种方式创建,传入的方法参数必须是这个类全类名(包名.类名)
Class<?> c3 = Class.forName("cn.reflect.test.Student");
}
}
//创建一个Student类
class Student{};
注意:在运行期间,任意一个类,都只有一个Class对象产生,所以,上述代码中c1,c2,c3这三个对象的地址值是相同的。
这三种方式,推荐使用第三种创建实例的方式。第一种,需要导入类的包,依赖性太强,不导入包,就出抛出编译错误。第二种该类的对象都创建好了,还需要反射干什么。一般使用第三种,方法的参数可以传入,也可以写在配置文件中等多种方式。其实,在spring框架中,创建实例,采用的就是这种方式。
三,通过反射获取构造方法并使用
1.调用构造方法:
1).批量的方法:
public Constructor[] getConstructors():所有"公有的"构造方法
public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
2).获取单个的方法,并调用:
public Constructor getConstructor(Class… parameterTypes):获取单个的"公有的"构造方法:
public Constructor getDeclaredConstructor(Class… parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;
3).使用newInstance方法创建一个类的实例
newInstance方法存在于Class类和Constructor类中,所以创建一个类的实例有两种方式。当你创建一个类的实例,不需要用指定的初始化参数来初始化该实例时,使用Class类中的newInstance()方法更为方便。反之,使用Constructor类中newInstance(Object… initargs)方法 来创建类的实例对象。
student类:
package cn.reflect.domain;
import java.util.Date;
public class Student {
private String name;
private int age;
// 无参公共构造
public Student() {
System.out.println("我是无参公共构造方法");
}
// 有参公共构造
public Student(String name, int age) {
this.name = name;
this.age=age;
System.out.println("姓名: "+name+" 年龄: "+age);
}
// 有参私有构造
private Student(String name) {
this.name = name;
System.out.println("姓名:"+name);
}
// 有多个参数的私有构造
private Student(String name, boolean flag) {
this.name = name;
System.out.println("姓名:"+name+" flag: "+flag);
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
}
测试类:
package cn.reflect.test;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import cn.reflect.domain.Student;
/*
* 通过Class对象可以获取某个类中的:构造方法、成员变量、成员方法;并访问成员;
*
* 1.获取构造方法:
* 1).批量的方法:
* public Constructor[] getConstructors():所有"公有的"构造方法
public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
* 2).获取单个的方法,并调用:
* public Constructor getConstructor(Class... parameterTypes):获取单个的"公有的"构造方法:
* public Constructor getDeclaredConstructor(Class... parameterTypes):获取"某个构造方法"可以是私有的,或受保护、默认、公有;
*
* 调用构造方法:
* Constructor-->newInstance(Object... initargs)
*/
public class ReflectDemo {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
//创建Class类的实例对象
Class<?> clazz = Class.forName("cn.reflect.domain.Student");//注意:参数为Student类的全类名
//2.获取所有的公共构造方法
System.out.println("**********************所有公共构造方法*********************************");
Constructor[] consArrays = clazz.getConstructors();
//遍历数组
System.out.println(Arrays.toString(consArrays));
//3.获取指定的公共构造方法
System.out.println("**********************获取公共,有参构造*********************************");
Constructor<?> cons1 = clazz.getConstructor(String.class,int.class);
System.out.println(cons1);
//4.获取所有的构造方法
System.out.println("**********************获取所有的构造方法*********************************");
Constructor<?>[] allConsArrays = clazz.getDeclaredConstructors();
//遍历数组
for (Constructor<?> c : allConsArrays) {
System.out.println(c);
}
//4.获取指定的私有构造方法,并调用创建Student类的实例
System.out.println("**********************获取私有,有参构造*********************************");
cons1 = clazz.getDeclaredConstructor(String.class,boolean.class);
System.out.println(cons1);
/**由于这个构造是私有的,需要设置取消java的访问检查机制,进行暴力访问,如果不进行设置,会报
java.lang.IllegalAccessException: Class cn.reflect.test.ReflectDemo can not access a member of class cn.reflect.domain.Student with modifiers "private"
没有权限进行访问
**/
cons1.setAccessible(true);
Student stu = (Student) cons1.newInstance("zhangsan",true);
//6.当你创建一个类的实例不需要设置成员变量的值时,可以使用下面这种方式创建
stu = (Student) clazz.newInstance();
}
}
后台输出:
四,获取成员变量并使用
获取成员变量并调用:
1.批量的
1).Field[] getFields():获取所有的"公有字段"
2).Field[] getDeclaredFields():获取所有字段,包括:私有、受保护、默认、公有;
2.获取单个的:
1).public Field getField(String fieldName):获取某个"公有的"字段;
2).public Field getDeclaredField(String fieldName):获取某个字段(可以是私有的)
设置字段的值:
Field --> public void set(Object obj,Object value):
参数说明:
1.obj:要设置的字段所在的对象;
2.value:要为字段设置的值;
student类:
package cn.reflect.domain;
import java.util.Date;
public class Student {
private String name;
public int age;
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
}
测试类:
package cn.reflect.test;
import java.lang.reflect.Field;
import java.util.Arrays;
import cn.reflect.domain.Student;
/**
获取成员变量并调用:
* 1.批量的
* 1).Field[] getFields():获取所有的"公有字段"
* 2).Field[] getDeclaredFields():获取所有字段,包括:私有、受保护、默认、公有;
* 2.获取单个的:
* 1).public Field getField(String fieldName):获取某个"公有的"字段;
* 2).public Field getDeclaredField(String fieldName):获取某个字段(可以是私有的)
*
* 设置字段的值:
* Field --> public void set(Object obj,Object value):
* 参数说明:
* 1.obj:要设置的字段所在的对象;
* 2.value:要为字段设置的值;
**/
public class Fields {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, InstantiationException {
//1.获取Class类的实例对象
Class<?> clazz = Class.forName("cn.reflect.domain.Student");
//1.获取所有的"公有字段"
System.out.println("**********************获取所有的公有字段*********************************");
Field[] fieldsArrays = clazz.getFields();
//遍历数组
System.out.println(Arrays.toString(fieldsArrays));
//2.获取所有的字段
System.out.println("**********************获取所有的字段*********************************");
Field[] allFieldsArrays = clazz.getDeclaredFields();
//遍历数组
System.out.println(Arrays.toString(allFieldsArrays));
//3.获取某个"公有的"字段
System.out.println("**********************获取某个公有的字段 *********************************");
Field field = clazz.getField("age");
//输出结果
System.out.println(field);
//3.获取某个字段(可以私有)
System.out.println("**********************获取某个字段(可以私有) *********************************");
field = clazz.getDeclaredField("name");
//输出结果
System.out.println(field);
System.out.println("**********************设置字段的值*********************************");
//创建student类的实例
Student stu = (Student) clazz.newInstance();
//设置字段的值
//由于要设置的字段name是私有的,所以要设置取消java的访问检查,进行暴力访问
field.setAccessible(true);
field.set(stu, "张三");
System.out.println(stu);
}
}
后台输出:
五,获取成员方法并使用
获取成员方法并调用:
1.批量的:
public Method[] getMethods():获取所有"公有方法";(包含了父类的方法也包含Object类)
public Method[] getDeclaredMethods():获取所有的成员方法,包括私有的(不包括继承的)
2.获取单个的:
public Method getMethod(String name,Class<?>… parameterTypes):
参数:
name : 方法名;
Class … : 形参的Class类型对象
public Method getDeclaredMethod(String name,Class<?>… parameterTypes)
调用方法:
Method --> public Object invoke(Object obj,Object… args):
参数说明:
obj : 要调用方法的对象;
args:调用方式时所传递的实参;
student类:
package cn.reflect.domain;
import java.util.Date;
public class Student {
/************成员方法*******************/
public void method1() {
System.out.println("调用了公共的成员方法");
}
public void method2(String name) {
System.out.println("调用了公共的成员方法,参数name:"+name);
}
private void method3() {
System.out.println("调用了私有的成员方法");
}
private String method4(String name) {
System.out.println("调用了私有的成员方法,参数name:"+name);
return name;
}
}
测试类:
package cn.reflect.test;
import java.lang.reflect.Method;
import java.util.Arrays;
import cn.reflect.domain.Student;
/*
* 获取成员方法并调用:
*
* 1.批量的:
* public Method[] getMethods():获取所有"公有方法";(包含了父类的方法也包含Object类)
* public Method[] getDeclaredMethods():获取所有的成员方法,包括私有的(不包括继承的)
* 2.获取单个的:
* public Method getMethod(String name,Class<?>... parameterTypes):
* 参数:
* name : 方法名;
* Class ... : 形参的Class类型对象
* public Method getDeclaredMethod(String name,Class<?>... parameterTypes)
*
* 调用方法:
* Method --> public Object invoke(Object obj,Object... args):
* 参数说明:
* obj : 要调用方法的对象;
* args:调用方式时所传递的实参;
*/
public class Methods {
public static void main(String[] args) throws Exception {
//创建Class类的实例对象
Class clazz = Class.forName("cn.reflect.domain.Student");
//1.获取所有的公有成员方法
System.out.println("**********************获取所有的公有成员方法*********************************");
Method[] methods = clazz.getMethods();
//遍历数组
for (Method method : methods) {
System.out.println(method);
}
//2.获取指定的method2()公共方法
System.out.println("**********************获取指定的公共方法method2()*********************************");
Method method = clazz.getMethod("method2", String.class);
System.out.println(method);
//3.获取所有的成员方法(包括私有成员方法)
System.out.println("**********************获取所有的成员方法*********************************");
Method[] allMethods = clazz.getDeclaredMethods();
//遍历数组
for (Method method2 : allMethods) {
System.out.println(method2);
}
//4.获取指定的method4()私有方法
System.out.println("**********************获取指定的私有方法method4()*********************************");
method = clazz.getDeclaredMethod("method4", String.class);
System.out.println(method);
//5.调用invoke(Object obj,Object... args)执行方法
System.out.println("**********************调用指定的方法method4*********************************");
//创建Student类的实例对象
Student obj = (Student) clazz.newInstance();
//由于方法的成员方法是私有的,所以要设置取消java的访问检查机制,进行暴力访问
method.setAccessible(true);
//执行方法,并接受方法的返回值
String name = (String) method.invoke(obj, "zhangsan");
System.out.println(name);
}
}
后台输出:
总结:反射在java中占有很大的位置,许多框架的底层都是利用反射来实现的,反射可以称之为框架设计的灵魂,所以理解反射对学习框架有很大的作用!
最后,新手创作,请大家多提批评意见,谢谢!