Java笔记–反射
什么是反射?
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制
java程序中许多对象在运行时都会出现两种类型:编译时的类型和运行时的类型,比如Person p = new Student() ,这行代码会生成一个p变量,该变量编译时类型为Person,运行时类型为Student,假如程序在接受到外部传入的一个对象,该对象的编译时的类型为Objective,但程序又需要调用该对象运行时类型的方法,此时有,两种解决方法,一种是强制转换,还有一种就是依靠反射了
以Person p = new Student()为例,当我们new Student时,JVM会加载Student.class文件,JVM会去磁盘中找到这个.class文件然后加载到内存中,穿件一个class对象并且分配内存,此时若再new Student,不会再产生一个对象,一个类只产生一个class对象
1,反射的使用
Class对象获取该类的方法:Method对象
Class对象获取该类的构造器:Constructor对象
Class对象获取该类的成员变量:Field对象
1.1,获取该类的方法
1).批量的方法:
public Constructor[] getConstructors():所有”公有的”构造方法
public Constructor[] getDeclaredConstructors():获取所有的构造方法(包括私有、受保护、默认、公有)
2).获取单个的方法,并调用:
public Constructor getConstructor(Class… parameterTypes):获取单个的”公有的”构造方法:
public Constructor getDeclaredConstructor(Class… parameterTypes):获取”某个构造方法”可以是私有的,或受保护、默认、公有;
调用构造方法:
Constructor–>newInstance(Object… initargs)
2、newInstance是 Constructor类的方法(管理构造函数的类)
api的解释为:
newInstance(Object… initargs)使用此 Constructor 对象表示的构造方法来创建该构造方法的声明类的新实例,并用指定的初始化参数初始化该实例。
它的返回值是T类型,所以newInstance是创建了一个构造方法的声明类的新实例对象。并为之调用
public class Student_1 {
//默认构造方法
Student_1(String str){
System.out.println("默认的构造方法:"+str);
}
//无参构造方法
public Student_1() {
System.out.println("这是一个公有无参构造方法");
}
//有一个参数的构造方法
public Student_1(char name) {
System.out.println("姓名:"+name);
}
//有多个参数的构造方法
public Student_1(char name,int age) {
System.out.println("姓名:"+name+"年龄:"+age);
}
//受保护的构造方法
protected Student_1(boolean n){
System.out.println("受保护的构造方法 :" + n);
}
//私有构造方法
private Student_1(int age){
System.out.println("私有的构造方法 年龄:"+ age);
}
}
import java.lang.reflect.Constructor;
public class Constructors {
public static void main(String[] args) throws Exception{
//加载Class对象
Class clazz = Class.forName("day8_7.Student_1");
//获取所有公有的构造方法
System.out.println("*****获取公有构造方法*****");
Constructor[] conArray = clazz.getConstructors();
for(Constructor c : conArray) {
System.out.println(c);
}
System.out.println("*****所有的构造方法*****");
conArray = clazz.getDeclaredConstructors();
for(Constructor c : conArray) {
System.out.println(c);
}
System.out.println("*****获取公有无参构造方法*****");
Constructor con = clazz.getConstructor(null);
System.out.println("con="+con);
//调用构造方法
Object obj = con.newInstance();
System.out.println("obj = " + obj);
Student_1 stu = (Student_1)obj;
System.out.println("******************获取私有构造方法,并调用*******************************");
con = clazz.getDeclaredConstructor(char.class);
System.out.println(con);
//调用构造方法
con.setAccessible(true);//暴力访问(忽略掉访问修饰符)
obj = con.newInstance('男');
}
}
结果
*****获取公有构造方法*****
public day8_7.Student_1(char,int)
public day8_7.Student_1(char)
public day8_7.Student_1()
*****所有的构造方法*****
private day8_7.Student_1(int)
protected day8_7.Student_1(boolean)
public day8_7.Student_1(char,int)
public day8_7.Student_1(char)
public day8_7.Student_1()
day8_7.Student_1(java.lang.String)
*****获取公有无参构造方法*****
con=public day8_7.Student_1()
这是一个公有无参构造方法
obj = day8_7.Student_1@15db9742
******************获取私有构造方法,并调用*******************************
public day8_7.Student_1(char)
姓名:男
1.2,获取该类的成员变量
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:要为字段设置的值;
1.3,获取成员方法并调用
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:调用方式时所传递的实参;
1.4,通过反射运行配置文件内容
Student 类
/*
* 通过反射运行配置文件内容
*/
public class Student {
public void show() {
System.out.println("show");
}
}
配置文件Pro.txt:
className = day8_7.Student
methodName = show
测试类
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Properties;
public class StudentTest {
public static void main(String[] args) throws Exception{
//通过反射获取Class对象
Class stuClass = Class.forName("day8_7.Student");
//获取show方法
Method m = stuClass.getMethod(getValue("methodName"));
//调用show方法
m.invoke(stuClass.getConstructor().newInstance());
}
public static String getValue(String key) throws IOException{
//获取配置文件的对象
Properties pro = new Properties();
//获取输入流
FileReader in = new FileReader("F:\\pro.txt");
//将流加载到配置文件
pro.load(in);
in.close();
//返回根据key获取的value值
return pro.getProperty(key);
}
}
运行结果:
show
利用反射和配置文件,可以使应用程序更新时对源码无需任何修改,只要将新类发送给客户端并修改配置文件
要替换的student2类:
public class Student2 {
public void show2(){
System.out.println("is show2()");
}
}
配置文件更改为:
className = cn.fanshe.Student2
methodName = show2
控制台输出:
is show2();
参考博客点这里