java反射机制
反射包:java.lang.reflect
里面包含了常用的Proxy动态代理类,Field成员变量类,Method成员方法类,Constructor构造方法类
首先了解java代码在计算机中经历的阶段:三个阶段
Source 源代码阶段
Person.java 源代码
public class Person{
private int age;
private String name;
public Person(){}
public void eat(){}
}
经过javac 编译成Person.class文件,class文件将源代码的内容分成不同的部分,主要包含以下内容
//成员变量
private String name;
private int age;
//构造方法
public Person(){}
//成员方法
public void eat(){}
此时,无论是源代码还是class文件都在磁盘中,将通过jvm中的类加载器将class文件加载到内存,进入第二阶段:Class类对象阶段
Class类对象阶段
在java中万物皆对象,class文件也一样,被类加载器ClassLoader加载后,class文件中的文件信息将被封装在一个Class类对象中,其中又分成了以下几个对象:
//成员变量对象
Field[] fields
//构造方法对象
Constructor[] cons
//成员方法对象
Method[] methods
此时,我们写的源代码才真正地被存放到了内存中可以通过类对象的行为来创建我们的Person对象了
Runtime运行时阶段
最后是熟悉的创建Person对象
//创建对象
new Person();
反射的概念
将类的各个组成部分封装为其他对象,得以在程序运行过程中,操作这些对象,这就是反射机制(其实就是第一各阶段到第二阶段的过程)
好处:
可以解耦,提高程序的可扩展性(详细可见最后的实例)
获取Class对象的方式:
-
Class.forName(“全类名”):将字节码文件加载进内存,返回Class对象(源代码阶段)
*多用于配置文件,将类名定义在配置文件中,读取文件,加载类
-
类名.class :通过类名的class属性获取(Class对象阶段)
*多用于参数的传递
-
对象.getClass():getClass()方法,在Object对象中定义
*多用于对象的获取字节码的方式
结论:
同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个.
Class对象功能:
主要涉及获取和设置两个方面,以下分开描述:
*获取功能:
-
获取成员变量们
Field[] getFileds() 获取所有public修饰的变量…
Field getFiled(String name) 获取指定名称的公有变量…
Field[] getDeclareFileds() 获取所有变量
Field getDeclareFiled(String name) 获取指定名称的变量
-
获取构造方法们
Constructor<?>[] getConstructors() 获取所有public修饰的构造方法…
Constructor getConstructor(类<?>… parameterTypes) 获取所有public修饰的有参构造方法…
Constructor<?>[] getDeclareConstructors() 获取所有的构造方法…
Constructor getDeclareConstructor(类<?>… parameterTypes) 获取所有的有参构造方法…
-
获取成员方法们
Method[] getMethods()获取所有public修饰的方法…
Method getMethod(String name,类<?>… parameterTyeps)获取public修饰的指定方法…
Method[] getDeclareMethods()获取所有的方法…
Method getDeclareMethod(String name,类<?>… parameterTyeps)获取的指定方法…
-
获取类名
getName()
*设置功能
*暴力反射
成员变量变量名(构造方法变量名,成员方法变量名).setAccessible(true); //忽略访问权限修饰符的安全检查,暴力反射 在反射面前没有什么是隐私的
分别介绍:
前提:创建一个Person类,包括四种修饰的成员变量,有参和无参的构造方法,有参和无参的成员方法,实现get,set和tostring方法
package reflect.bean;
public class Person {
private int age;
private String name;
public int a;
int b;
protected int c;
private int d;
public Person(){}
public Person(String name,int age) {
this.age = age;
this.name = name;
}
public void eat(){
System.out.println("eat...");
}
public void eat(String str){
System.out.println("eat..."+str);
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getA() {
return a;
}
public void setA(int a) {
this.a = a;
}
public int getB() {
return b;
}
public void setB(int b) {
this.b = b;
}
public int getC() {
return c;
}
public void setC(int c) {
this.c = c;
}
public int getD() {
return d;
}
public void setD(int d) {
this.d = d;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
", a=" + a +
", b=" + b +
", c=" + c +
", d=" + d +
'}';
}
*Filed成员变量操作
获取
//获取
//1 获取Class文件
// Field[] getFileds() 获取所有public修饰的变量...
Class cls = Class.forName("reflect.bean.Person");
Field[] fields = cls.getFields();
for (Field field : fields
) {
System.out.println(field);
}
System.out.println("------------------------");
Field field = cls.getField("a");
System.out.println(field);
System.out.println("------------------------");
Field[] fields2= cls.getDeclaredFields();
for (Field field2 : fields2
) {
System.out.println(field2);
}
System.out.println("------------------------");
Field field2 = cls.getDeclaredField("name");
System.out.println(field2);
System.out.println("------------------------");
设置
*get(Object obj)
*set(Object obj, Object value)这些都是Field类中的方法
//获取值
Person p = new Person();
// Can not set int field reflect.bean.Person.a to java.lang.Class
//不能传cls 因为cls是Class类,而a不属于Class,应该传一个Person
// Class reflect.TestFiled can not access a member of class reflect.bean.Person with modifiers "private"
//不能直接获取name,因为name是被private修饰的,需要暴力反射
//name-> 成员变量的变量名
name.setAccessible(true);
Object obj = name.get(p);
System.out.println(obj);
System.out.println("------------------------");
//设置值
//传入一个要修改的对象,修改的值
name.set(p,"张三");
System.out.println(p);
*Constructor:构造方法操作
获取
Class cls = Class.forName("reflect.bean.Person");
//获取
//有参
Constructor cons = cls.getConstructor(String.class,int.class);
System.out.println(cons);
System.out.println("-----------------------");
//无参
Constructor cons1 = cls.getConstructor();
System.out.println(cons1);
System.out.println("-----------------------");
含有declare的修饰和成员变量是一样的,可以获取包括public修饰外的构造方法(private用于单例),获取所有的构造方法也同上,这里不多记录
设置(创建对象):
*这个是重点
*T newInstance(Object… initargs) Constructor //有参的构造
//设置(也就是创建对象,因为构造方法本来就是用来创建对象的)
//有参
Object person = cons.newInstance("张三",23);
System.out.println(person);
System.out.println("-----------------------");
//无参
Object person1 = cons1.newInstance();
System.out.println(person1);
System.out.println("-----------------------");
//如果是用无参的方法,可以直接用Class类中的newInstance,而不需要用Constructor的方法
Object person2 = cls.newInstance();
System.out.println(person2);
System.out.println("-----------------------");
Constructor 也是可以暴力反射的,同上这里不多记录
*Method:方法对象
获取
Class cls = Person.class;
//获取
//这样子会将object里的所有方法都打印出来
Method[] methods = cls.getMethods();
for (Method m:
methods) {
System.out.println(m);
}
System.out.println("----------------------------");
//无参
Method eat = cls.getMethod("eat");
System.out.println(eat);
System.out.println("----------------------------");
//有参
Method eat1 = cls.getMethod("eat",String.class);
System.out.println(eat1);
System.out.println("----------------------------");
设置(执行方法)
invoke(真实的对象,参数…) 这个是Method类里的方法,激活函数
//设置(执行方法)
//同设置成员变量一样,执行成员方法,也需要放入一个真正要执行的类
Person p = new Person();
//无参
eat.invoke(p);
System.out.println("----------------------------");
//有参
eat1.invoke(p,"饭");
暴力反射和declare的问题也不多说了,同成员变量中的描述
反射使用
假设这是一个框架类,要求:可以创建任意类的对象,可以执行任意方法,不改变类的代码
新加一个类:
package reflect.bean;
public class Student {
public void sleep(){
System.out.println("sleep...");
}
}
// 传统方法
// Person p = new Person();
// p.eat();
//如果我要换一个类实现,比如,实现Student里面的sleep方法呢?那么传统方法就得改代码
//Student s = new Student();
//这对线上的代码是不友好的,同时也不符合框架的设计思想
/**
* 实现
* 1.配置文件
* 2.反射
* 步骤:
* 1.将需要创建的对象的全类名和需要执行的方法定义在配置文件中
* 2.在程序中加载读取配置文件
* 3.利用反射机制来加载类文件进内存
* 4.创建对象
* 5.执行方法
*/
//获取配置文件
Properties pro = new Properties();
File file = new File("D:\\IDEA\\LeetCode\\src\\main\\resources\\application.properties");
InputStream is = new FileInputStream(file);
//读取配置文件
pro.load(is);
//获取类名和方法名
String className = pro.getProperty("className");
String methodsName = pro.getProperty("methodsName");
//获取class对象
Class<?> aClass = Class.forName(className);
//实例化对象
Object o = aClass.newInstance();
//获取方法,invoke激活方法
Method method = aClass.getMethod(methodsName);
method.invoke(o);