java基础 ---- 反射机制
反射机制的理解概述:
- Java程序从编写到运行的三个阶段:
- 反射机制体现在将class字节码文件通过类加载器加载进入内存时,各个组成部分被分别封装成Field[],Construstor[],Method[]对象。正常顺序是“一个类定义好之后,按照固定格式创建实例,实例通过直接调用执行类中定义的方法” ,反射机制相反:通过一个实例化对象通过创建class对象获取自身所属类的定义信息,调用Field[],Construstor[],Method[]对象方法的执行类中定义的方法。实际上,class对象在java程序从编写到运行的三个阶段都可以被创建,创建就能照上述法子用。
反射机制的使用概述:
- 先通过反射机制获取class对象,再调用class对象的方法,方法可分类为:获取成员变量,获取构造方法,获取成员方法,获取类名。获得各部分成分后再进一步操作。
获取Class对象的方式:
-Class.forName("全类名"):将源码阶段的字节码文件(即还未编译)加载进内存,返回Class对象
-类名.class:对已加载进内存的类(即已经编译了)通过类名的属性class获取
-对象.getClass():获取运行时的类(即main方法中创建了实例)的Class对象。
Class对象获取成员变量:
-Field[] getFields() :获取所有public成员变量
-Field getField(String name): 获取指定名称的public成员变量
-Field[] getDeclaredFields(): 不顾修饰符地获取所有成员变量
-Field getDeclaredField(String name):不顾修饰符地获取指定名称的成员变量成员变量Field对象(某一成员变量)可调用的方法:
-void set(Object obj, Object value) :给实例对象obj的该成员变量(调用set方法的field对象)设置值value
-get(Object obj) :获取值
-setAccessible(true):暴力反射 :忽略访问权限修饰符的安全检查
Class对象获取构造方法:
-Constructor[] getConstructors():获取空参构造方法
-Constructor getConstructor(反射类<?>... parameterTypes):获取含指定参数类型的构造方法
-Constructor getDeclaredConstructor(反射类<?>... parameterTypes):不顾访问权限地获取含指定参数类型的构造方法
-Constructor[] getDeclaredConstructors():不顾访问权限地获取空参构造方法构造方法Constructor对象可调用的方法:
-setAccessible(true):暴力反射 :忽略访问权限修饰符的安全检查
-Object newInstance(Object... initargs):基于参数创建对象
Class对象获取成员方法:
-Method[] getMethods():获取所有public成员方法
-Method getMethod(String name, 类<?>... parameterTypes): 根据指定方法名和参数类型获取成员方法
-Method[] getDeclaredMethods(): 不顾访问权限地获取所有成员方法
-Method getDeclaredMethod(String name, 类<?>... parameterTypes):不顾访问权限地获取含指定方法名和参数的成员方法成员方法Method对象可调用的方法:
-setAccessible(true):暴力反射 :忽略访问权限修饰符的安全检查
-Object invoke(Object obj, Object... args) :执行方法
-String getName: 获取方法名
Class对象获取类名:
-String getName()
反射机制的测试代码(整体阅读,摘取运行):
/**运行前提:
* 1.导入符合传参要求的Person,Student两个实体类
* 2. IDEA提示下导入相关包
*/
public class ReflectDemo {
public static void main(String[] args) throws Exception {
show1();
show2();
show3();
}
/**show1():
*分别以三种不同的方式获取class对象
*比较获取结果
*/
static void show1(){
//1.多用于配置文件,将全类名定义在配置文件中。读取文件,加载类
Class cls1 = Class.forName("Mytest.Person");
System.out.println(cls1);
//2.
Class cls2 = Person.class;
System.out.println(cls2);
//3.多用于对象的获取字节码的方式
Person p = new Person();
Class cls3 = p.getClass();
System.out.println(cls3);
//2.同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。
System.out.println(cls1 == cls2);//true
System.out.println(cls1 == cls3);//true
//
//
Class c = Student.class;
System.out.println(c == cls1);//false
}
/**show2();
* 获取Person的Class对象
* 以不同的方式获取成员变量们
*/
static void show2(){
//0.
Class personClass = Person.class;
//1.获取所有public修饰的成员变量
Field[] fields = personClass.getFields();
for (Field field : fields) {
System.out.println(field);
}
//2.获取指定名称的成员变量
Field a = personClass.getField("a");
//
//获取成员变量a 的值
Person p = new Person();
Object value = a.get(p);
System.out.println(value);
//
//设置a的值
a.set(p,"张三");
System.out.println(p);
//3.获取所有的成员变量,不考虑修饰符
Field[] declaredFields = personClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println(declaredField);
}
//4.不考虑修饰符地获取指定名称的成员变量
Field d = personClass.getDeclaredField("d");
//
//对有访问权限的成员变量进一步操作忽略访问权限修饰符的总是需要安全检查
d.setAccessible(true);//暴力反射
//
//获取成员变量d 的值
Object value2 = d.get(p);
System.out.println(value2);
}
/**show3():
* 获取Person的Class对象
* 获取不同类型的构造方法,创建实例
*/
static void show3(){
//0.
Class personClass = Person.class;
//1.按照参数类型获取构造方法
Constructor constructor = personClass.getConstructor(String.class, int.class);
System.out.println(constructor);
//
//创建对象
Object person = constructor.newInstance("张三", 23);
System.out.println(person);
//2.获取空参构造方法
Constructor constructor1 = personClass.getConstructor();
System.out.println(constructor1);
//
//如果使用空参数构造方法创建对象,操作可以简化:Class对象直接调用newInstance方法
Object o = personClass.newInstance();
System.out.println(o);
/*
Object person1 = constructor1.newInstance();
System.out.println(person1);
*/
}
/**show4():
* 获取Person的Class对象
* 各种方式获取成员方法
*/
static void show4(){
//0.
Class personClass = Person.class;
//1.获取指定名称的方法
Method eat_method = personClass.getMethod("eat");
Person p = new Person();
//
//执行方法
eat_method.invoke(p);
//2.获取指定名称,参数的方法
Method eat_method2 = personClass.getMethod("eat", String.class);
// Method eat_method2 = personClass.getMethod("eat", String eat); 错于指定参数的方式也需要通过反射机制
eat_method2.invoke(p,"饭");
//3.获取所有public修饰的所有方法,不仅仅是自己代码定义的,还包括Object父类的一些方法
Method[] methods = personClass.getMethods();
for (Method method : methods) {
System.out.println(method);
String name = method.getName(); //获取方法名
System.out.println(name);
}
//4.获取类名
String className = personClass.getName();
System.out.println(className);//全类名: cn.itcast.domain.Person
}
}
基于反射机制的框架类:
//框架类的配置文件
className=MyTest.Student
methodName=sleep
/**运行前提:
*1. 写好符合运行时参数的配置文件
*2. 导入符合运行时参数要求的实体类
*3. IDEA提示下导入相关包
*/
/**
* 框架类:可以创建任意类的对象,可以执行任意方法
* 1.加载配置文件
* 2.获取配置文件中定义的数据
* 3.加载该类进内存
* 4.创建对象
* 5.获取方法对象
* 6.执行方法
*/
public class ReflectTest {
public static void main(String[] args) throws Exception {
//前提:框架类的前提是不能改变该类的任何代码,所以对象名和方法名需要写进配置文件
//1.
//创建Properties对象
Properties pro = new Properties();
//
//加载配置文件,转换为一个集合:
//通过任何一个类的反射都能获得类加载器对象,类加载器对象能获取class目录下的配置文件
ClassLoader classLoader = ReflectTest.class.getClassLoader();
InputStream is = classLoader.getResourceAsStream("pro.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);
}
}