文章目录
1.概述
JAVA反射机制是运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。
反射就是将Java类中的各个成分映射成一个个的Java对象。例如,一个类拥有成员变量、方法、构造方法等信息,可以在获取该类字节码文件(.class)对象的基础上,利用反射技术对该类进行解剖,把类的各个组成部分(成员变量、方法、构造方法等)映射成一个个对象。
因此,反射的本质就是在获取到一个类的Class类对象后(字节码文件对象),反向获取该类的对象的各种各种信息,包括成员变量、方法、构造方法等。
2.java代码在计算机中经历的三个阶段
3.获取字节码文件对象的三种方式
(1)Class.forName(“全类名”)
此方式对应源代码阶段。将字节码文件加载进内存,返回Class对象。多用于配置文件,将类名定义在配置文件中,读取文件,加载类。
(2)类名.class
此方式对应类对象阶段。通过类名的属性class来获取。多用于参数的传递。
(3)对象.getClass()
此方式对应运行时阶段。getClass()方法在Object类中定义,所有对象均有此方法。
package cn.ecarg.reflect;
import cn.ecarg.domain.Person;
public class ReflectDemo1 {
public static void main(String[] args) throws Exception {
//1.Class.forName("全类名")
Class cls1 = Class.forName("cn.ecarg.domain.Person");
System.out.println(cls1);
//2.类名.class
Class cls2 = Person.class;
System.out.println(cls2);
//3.对象.getClass()
Person person = new Person();
Class cls3 = person.getClass();
System.out.println(cls3);
//用 == 比较三个对象:
System.out.println(cls1 == cls2);//true
System.out.println(cls1 == cls3);//true
}
}
输出:
总结:同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次。无论通过哪种方式获取的class对象都是同一个。
4.获取各对象并调用
4.1 定义Person类
package cn.ecarg.domain;
public class Person {
private String name;
private int age;
public String a;
private String b;
public String c;
private String d;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void eat(){
System.out.println("eat...");
}
public void eat(String food){
System.out.println("eat" + food);
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", a='" + a + '\'' +
", b='" + b + '\'' +
", c='" + c + '\'' +
", d='" + d + '\'' +
'}';
}
}
4.2 获取成员变量并调用
4.2.1 获取成员变量方法
(1)获取所有public修饰的成员变量
Field[ ] getFields()
(2)获取指定名称的public修饰的成员变量
Field[ ] getField(String name)
(3) 获取所有成员变量,不考虑权限修饰符
Field[ ] getDeclaredFields()
(4)获取指定名称的成员变量
Field[ ] getclaredField(String name)
4.2.2 调用方法
(1)设置成员变量值
&esmp;void set(Object obj,Object value);
(2)获取成员变量值
Object get(Object obj);
(3)忽略访问权限修饰符的安全检查(暴力反射)
setAccssible(true);
4.2.3 实例测试
package cn.ecarg.reflect;
import cn.ecarg.domain.Person;
import java.lang.reflect.Field;
public class ReflectDemo2 {
public static void main(String[] args) throws Exception {
//0.获取Person的Class对象
Class personClass = Person.class;
//1.获取成员变量们
//1.1 Field[] getFields()
System.out.println("***************获取所有public修饰的成员变量*************");
Field[] fields = personClass.getFields();
for(Field field : fields){
System.out.println(field);
}
//1.2 Field[] getField(String name)
System.out.println("***************获取指定名称的public修饰的成员变量***************");
Field a = personClass.getField("a");
System.out.println(a);
//获取成员变量a的值
System.out.println("***************获取成员变量a的值***************");
Person person = new Person();
Object value = a.get(person);
System.out.println(value);//null
//设置成员变量a的值
System.out.println("***************设置成员变量a的值***************");
a.set(person,"张三");
System.out.println(person);
//1.3 Field[] getDeclaredFields()
System.out.println("***************获取所有的成员变量,不考虑访问权限修饰符***************");
Field[] fields1 = personClass.getDeclaredFields();
for(Field field : fields1){
System.out.println(field);
}
//1.4 Field[] getDeclaredField(String name)
System.out.println("***************获取指定名称的成员变量,并使用暴力反射,从而不考虑访问权限修饰符***************");
Field d = personClass.getDeclaredField("d");
//忽略访问权限修饰符的安全检查
d.setAccessible(true);//暴力反射
Object value2 = d.get(person);
System.out.println(value2);//null
}
}
输出:
4.3 获取构造函数并调用
4.3.1 获取构造函数方法
(1)获取所有“公有”的构造方法
Constructor[ ] getConstructors()
(2)获取单个的“公有的”构造方法
Constructor getConstructor(Class… parameterTypes)
Class… parameterTypes:构造函数形参的Class对象类型
(3) 获取所有的构造方法,不考虑权限修饰符
Constructor[ ] getDeclaredConstructors()
(4)获取"某个构造方法"可以是私有的,或受保护、默认、公有
Constructor getDeclaredConstructor(Class… parameterTypes)
Class… parameterTypes:构造函数形参的Class对象类型
4.3.2 调用方法
(1)创建对象
T newInstance(Object… initargs);
如果使用空参数构造方法创建对象,操作可以简化为Class对象的newInstance方法。
4.3.3 实例测试
package cn.ecarg.reflect;
import cn.ecarg.domain.Person;
import java.lang.reflect.Constructor;
public class ReflectDemo3 {
public static void main(String[] args) throws Exception {
//0.获取Person的Class对象
Class personClass = Person.class;
//1.获取构造方法们
//1.1 Constructor<T> getConstructor(Class<?>...parameterTypes)
System.out.println("************获取指定的构造方法************");
Constructor constructor = personClass.getConstructor(String.class,int.class);
System.out.println(constructor);
//创建对象1
System.out.println("************使用Constructor对象创建Object对象************");
Object person = constructor.newInstance("张三",23);
System.out.println(person);
//获取默认构造方法
System.out.println("************获取默认构造方法************");
Constructor constructor1 = personClass.getConstructor();
System.out.println(constructor1);
//创建对象2
System.out.println("************使用Constructor对象创建Object对象************");
Object person1 = constructor1.newInstance();
System.out.println(person1);
//简化 创建对象2 的操作,使用Class对象中的newInstance()
System.out.println("************空构造函数,直接利用Class对象的newInstance()创建对象2************");
Object person3 = personClass.newInstance();
System.out.println(person3);
}
}
输出:
4.4 获取成员方法并调用
4.4.1 获取成员方法方法
(1)获取所有“公有”的成员方法,包括父类的方法,也包括Object类的方法
Method[ ] getMethods()
(2)获取单个的“公有”成员方法
Method getMethod(String name,Class<?>… parameterTypes)
name:方法名;Class…parameterTypes:形参的Class类型对象
(3) 获取所有的成员方法,不考虑权限修饰符
Method[ ] getDeclaredMethods()
(4)获取"某个成员方法"可以是私有的,或受保护、默认、公有
Method getDeclaredMethod(String name,Class…parameterTypes)
name:方法名;Class…parameterTypes:形参的Class类型对象
4.4.2 调用方法
(1)执行方法
Object invoke(Object obj,Object… args)
obj:要调用方法的对象;args:调用方法时所传递的实参
(2)获取方法名
String getName();
4.4.3 实例测试
package cn.ecarg.reflect;
import cn.ecarg.domain.Person;
import java.lang.reflect.Method;
public class ReflectDemo4 {
public static void main(String[] args) throws Exception {
//0.获取Person的Class对象
Class personClass = Person.class;
//1.获取成员方法
//1.1 Method getMethod(String name,Class<?>...parameterTypes)
System.out.println("**********获取无参的成员方法**********");
Method eat_method = personClass.getMethod("eat");//获取无参方法
Person person = new Person();
//执行方法
System.out.println("**********执行方法**********");
eat_method.invoke(person);
System.out.println();
System.out.println("**********获取有参的成员方法**********");
Method eat_method2 = personClass.getMethod("eat",String.class);
//执行方法2
System.out.println("**********执行方法**********\"");
eat_method2.invoke(person,"饭");
System.out.println();
//1.2 Method[] getMethods()
System.out.println(" 获取所有public修饰的方法(除了定义的Person类的方法,还有直接继承的父类Object中的public修饰的方法)");
Method[] methods = personClass.getMethods();
for(Method method : methods){
System.out.println("方法全限定名:"+ method);
String string = method.getName();
System.out.println("方法名:" + string);
}
}
}
输出:
5. 反射案例(框架实现)
(1)需求
写一个“框架”,在不改变该类任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法。
(2)实现方法
① 配置文件
② 反射
(3)步骤
① 将需要创建的对象的全类名和需要执行的方法定义配置文件中;
② 在程序中加载读取配置文件;
③ 使用反射技术来加载文件进内存;
④ 创建对象;
⑤ 执行方法。
(4)具体实现
Person.java
package cn.ecarg.domain;
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void eat(){
System.out.println("eat...");
}
public void eat(String food){
System.out.println("eat" + food);
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age
'}';
}
}
Student.java
package cn.ecarg.domain;
public class Student {
public void sleep(){
System.out.println("正在执行Student类中的sleep方法");
}
}
ReflectTest.java
package cn.ecarg.reflect;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;
/**
* 框架类
* 前提:不能改变类的任何代码,可以创建任意类的对象,执行任意方法。
*/
public class ReflectTest {
public static void main(String[] args) throws Exception{
//1.加载配置文件
//1.1 创建Properties对象
Properties pro = new Properties();
//1.2 加载配置文件,转化为一个集合
//1.2.1 获取class目录下的配置文件(.properties)
ClassLoader classLoader = ReflectTest.class.getClassLoader();//获取ReflectTest字节码文件对应的类加载器(由类加载器将ReflectTest.class加载进内存)
//返回pro.properties文件的字节流
InputStream is = classLoader.getResourceAsStream("pro.properties");
//加载properties配置文件
pro.load(is);
//2.获取配置文件中定义的数据
String className = pro.getProperty("className");
String methodName = pro.getProperty("methodName");
//3.加载该类进内存
Class cls = Class.forName(className);
//4.创建对象
Object obj = cls.newInstance();
//5.获取方法对象
Method method = cls.getMethod(methodName);
//6.执行方法
method.invoke(obj);
}
}
pro.properties
className=cn.ecarg.domain.Student
methodName=sleep
输出: