Java基础-反射
反射反射概述首先我们了解一下JVM,什么是JVM,Java的虚拟机,java之所以能跨平台就是因为这个东西,你可以理解成一个进程,程序,只不过他的作用是用来跑你的代码的。上图是java的内存模型,我们关注的点,一个方法区,一个栈,一个堆,初学的时候老师不深入的话只告诉你java的内存分为堆和栈,易懂点吧!假如你写了一段代码:Object o=new Object();运行了起来!首先JVM会启动,
反射
反射概述
首先我们了解一下JVM,什么是JVM,Java的虚拟机,java之所以能跨平台就是因为这个东西,你可以理解成一个进程,程序,只不过他的作用是用来跑你的代码的。上图是java的内存模型,我们关注的点,一个方法区,一个栈,一个堆,初学的时候老师不深入的话只告诉你java的内存分为堆和栈,易懂点吧!
假如你写了一段代码:Object o=new Object();
运行了起来!
首先JVM会启动,你的代码会编译成一个.class文件,然后被类加载器加载进jvm的内存中,你的类Object加载到方法区中,创建了Object类的class对象到堆中,注意这个不是new出来的对象,而是类的类型对象,每个类只有一个class对象,作为方法区类的数据结构的接口。jvm创建对象前,会先检查类是否加载,寻找类对应的class对象,若加载好,则为你的对象分配内存,初始化也就是代码:new Object()。
上面的流程就是你自己写好的代码扔给jvm去跑,跑完就over了,jvm关闭,你的程序也停止了。
为什么要讲这个呢?因为要理解反射必须知道它在什么场景下使用。
题主想想上面的程序对象是自己new的,程序相当于写死了给jvm去跑。假如一个服务器上突然遇到某个请求哦要用到某个类,哎呀但没加载进jvm,是不是要停下来自己写段代码,new一下,哦启动一下服务器,(脑残)!
反射是什么呢?当我们的程序在运行时,需要动态的加载一些类这些类可能之前用不到所以不用加载到jvm,而是在运行时根据需要才加载,这样的好处对于服务器来说不言而喻,举个例子我们的项目底层有时是用mysql,有时用oracle,需要动态地根据实际情况加载驱动类,这个时候反射就有用了,假设 com.java.dbtest.myqlConnection,com.java.dbtest.oracleConnection这两个类我们要用,这时候我们的程序就写得比较动态化,通过Class tc = Class.forName(“com.java.dbtest.TestConnection”);通过类的全类名让jvm在服务器中找到并加载这个类,而如果是oracle则传入的参数就变成另一个了。这时候就可以看到反射的好处了,这个动态性就体现出java的特性了!举多个例子,大家如果接触过spring,会发现当你配置各种各样的bean时,是以配置文件的形式配置的,你需要用到哪些bean就配哪些,spring容器就会根据你的需求去动态加载,你的程序就能健壮地运行。
Java文件和.class文件的关系
-
Java文件
Java文件中包含代码的所有内容,包括类,接口,成员变量,成员方法等
-
.class字节码文件
.java文件通过 javac 编译工具生成对应的.class字节码文件
如果使用jdk中提供的反编译工具,可以看到.class文件中包含
Class完整的包名和类名
Field成员变量,成员变量的名字和成员变量的数据类型[如果是引用数据类型,也是完整的包名,类名]
Method成员方法,方法权限修饰,返回值类型,方法名,形式参数列表数据类型
总结:
.class字节码文件中,包含了Java文件的所有内容
代码中的每一个类都会生成对应的字节码文件
程序加载过程和.class文件的关系
在Java文件运行过程中,当程序需要哪一个类参与代码运行,就需要加载这个类的.class字节码文件,该.class字节码文件在程序的加载阶段,存在于内存的[代码区]
.class字节码文件既然加载到内存的代码区
.class文件中包含对应Java程序的所有内容
代码区存在一块空间 ==> .class ==> Java程序所有内容
Java中的万物皆对象
在Java代码中,把在内存代码区保存的.class字节码内存空间,看作是一个对象,该对象中包含了对应的Java文件的所有内容
class
反射必会方法-重点
Class Class.forName(String packageNameAndClassName);
Class类的静态成员方法,通过完整的包名,类名获取对应的.class文件的Class对象,同时可以作为.class文件加载的方式
Class 类名.class;
通过类名.class方法,获取对应的Class类对象,通常用于方法的参数类型
Class 类对象.getClass();
通过类对象获取对应的.class的Class类对象,方法参数,或者说数据类型判断
实例:
Person.java,以下均使用此文件
package cn.ocean888_reflect;
/**
* @author Anonymous
*/
public class Person {
//成员变量 Field
/**
* id属性
*/
private int id;
private String name;
public int test = 10;
public static int testStatic = 10;
//构造方法 Constructor
public Person() {}
public Person(int id) {
this.id = id;
}
public Person(int id, String name) {
this.id = id;
this.name = name;
}
private Person(String name) {
this.name = name;
}
//成员方法 Method
public void setId(int id) {
this.id = id;
}
public int getId() {
return id;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void game() {
System.out.println("大吉大利,腾讯倒闭");
}
public void game(String name) {
System.out.println("玩" + name);
}
private void testPrivate() {
System.out.println("类内私有化成员方法!!!");
}
private void testPrivate(String str) {
System.out.println("吃" + str);
}
public static void test() {
System.out.println("静态!!!");
}
@Override
public String toString() {
return "Person [id=" + id + ", name=" + name + ", test=" + test + "]";
}
}
package cn.ocean888_reflect;
public class GetClassObject {
public static void main(String[] args) throws ClassNotFoundException {
System.out.println("test");
Class<?> forName = Class.forName("cn.ocean888_reflect.Person");
Class<cn.ocean888_reflect.Person> cls = Person.class;
Class<? extends Person> class1 = new Person().getClass();
System.out.println(forName == cls);
System.out.println(class1 == cls);
System.out.println(class1 == forName);
}
}
输出结果:
Constructor构造方法涉及的方法
// 1.
public Constructor[] getConstructor();
获取当前Class类对象对应Java文件中,public修饰构造方法类对象数组
// 2.
public Constructor[] getDeclaredConstructors();
[ 暴力反射 ]
获取当前Class类对象对应Java文件中,所有[构造方法的类对象数组],包括私有化构造方法
[回顾]
new Person()
new Person(1)
这里利用了重载的知识点,会根据实际[ 参数类型 ],来选择对应的构造方法
[推理]
通过Class类对象,获取指定构造方法,需要根据构造方法的所需
// 3.
public Constructor getConstructor(Class... initArgumentTypes);
根据指定的数据类型,来选择对应的构造方法,可能会抛出异常
这里有且只能获取类内的指定数据类型public修饰构造方法类对象
Class:约束数据类型
例如:
需要int类型 int.class
需要Stirng类型 String.class
需要Person类型 Person.class
异常:
NoSuchMethodException
... :不定长参数
构造方法需要的参数类型很多,有可能无参数,有可能有参数,使用...不定长参数类约束使用,增强代码的普适性
例如:
无参数 () or (null)
参数类型int (int.class)
参数类型int, String类型 (int.class, String.class)
initArgumentTypes:
参数名 初始化参数类型复数
// 4.
public Constructor getConstructor(Class... initArgumentTypes);
[暴力反射]
根据指定的数据类型,来选择对应的构造方法,可能会抛出异常
这里可以获取指定参数类型私有化构造方法和非私有化构造方法
Class:约束数据类型
例如:
需要int类型 int.class
需要Stirng类型 String.class
需要Person类型 Person.class
异常:
NoSuchMethodException
... :不定长参数
构造方法需要的参数类型很多,有可能无参数,有可能有参数,使用...不定长参数类约束使用,增强代码的普适性
例如:
无参数 () or (null)
参数类型int (int.class)
参数类型int, String类型 (int.class, String.class)
initArgumentTypes:
参数名 初始化参数类型复数
Object newInstance(Object... initArguments);
通过Constructor对象来调用,传入当前构造方法所需创建对象的初始化参数,创建对象
Object:Object类是Java中所有类的基类,这里可以传入任意类型的参数
... :不定长参数,因为Constructor类对象在获取的过程中,约束的参数个数不确定,这里使用不定长参数来传入数据
实例:
package cn.ocean888_reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/*
* 操作Constructor
*/
public class GetConstructorObject {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
/*
* 根据指定的包名.类名 获取对应Class对象
*/
Class<?> cls = Class.forName("cn.ocean888_reflect.Person");
/*
* 获取当前Person类内所有非私有化构造方法
*/
Constructor<?>[] constructors = cls.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println(constructor);
}
System.out.println("=====================================");
System.out.println();
/*
* 暴力反射
*/
Constructor<?>[] declaredConstructors = cls.getDeclaredConstructors();
for (Constructor<?> constructor : declaredConstructors) {
System.out.println(constructor);
}
System.out.println("=====================================");
System.out.println();
/*
* 根据指定参数类型获取Public修饰的构造方法对象
* 如果没有指定参数的构造方法,运行异常NoSuchMethodException
*/
Constructor<?> constructor1 = cls.getConstructor();
Constructor<?> constructor2 = cls.getConstructor(int.class);
Constructor<?> constructor3 = cls.getConstructor(int.class, String.class);
System.out.println(constructor1);
System.out.println(constructor2);
System.out.println(constructor3);
System.out.println("=====================================");
System.out.println();
/*
* 暴力反射
* 通过暴力反射可以获取任意权限修饰符,符合参数要求的构造方法对象
*/
Constructor<?> declaredConstructor1 = cls.getDeclaredConstructor();
Constructor<?> declaredConstructor2 = cls.getDeclaredConstructor(String.class);
System.out.println(declaredConstructor1);
System.out.println(declaredConstructor2);
System.out.println("=====================================");
System.out.println();
/*
* 通过无参数Constructor对象执行newInstance方法
* 这里明确是一个Person类型,可以使用强制类型转换
* 这里使用的是public修饰的构造方法
*/
Person p1 = (Person) constructor1.newInstance(); // 抛出四个异常
System.out.println(p1);
System.out.println(new Person());
System.out.println("=====================================");
System.out.println();
// 给予通过暴力反射获取到的非公开权限成员变量,成员方法,构造方法,操作权限
// 暴力反射的自由化为所欲为的操作
declaredConstructor2.setAccessible(true);
Person p2 = (Person) declaredConstructor2.newInstance("ocean");
System.out.println(p2);
}
}
输出结果:
public cn.ocean888_reflect.Person(int)
public cn.ocean888_reflect.Person()
public cn.ocean888_reflect.Person(int,java.lang.String)
=====================================
private cn.ocean888_reflect.Person(java.lang.String)
public cn.ocean888_reflect.Person(int)
public cn.ocean888_reflect.Person()
public cn.ocean888_reflect.Person(int,java.lang.String)
=====================================
public cn.ocean888_reflect.Person()
public cn.ocean888_reflect.Person(int)
public cn.ocean888_reflect.Person(int,java.lang.String)
=====================================
public cn.ocean888_reflect.Person()
private cn.ocean888_reflect.Person(java.lang.String)
=====================================
Person [id=0, name=null, test=10]
Person [id=0, name=null, test=10]
=====================================
Person [id=0, name=ocean, test=10]
Method成员方法涉及到的方法
问题:
调用执行成员方法时,需要考虑那些内容
调用者
类名,对象
方法名
参数
需要通过Class对象来获取Method对象,有哪些内容需要考虑
参数
方法名
权限修饰符
Method获取方法
// 1.
Method[] getMethods();
获取类内所有public修饰的成员方法,包括从父类继承而来的public修饰方法
// 2.
Method[] getDeclaredMethod();
暴力反射
获取类内的所有成员方法,但不包括从父类继承而来的方法
// 3.
Method getMethod(String methodName, Class... parameterTypes);
根据指定的方法名和对应的参数类型,获取对应的public修饰的成员方法
methodName:
方法名,指定获取的是哪一个方法
parameterTypes:
Class用于约束当前使用的参数类型
... 不定长参数,方法参数个数,顺序,有参无参问题
例如:
cls是Class类对象
cls.getMethod("setName", String.class);
cls.getMethod("getName");
// 4.
Method getDeclaredMethod(String methodName, Class... parameterTypes);
根据指定的方法名和对应的参数类型,获取对应的public修饰的成员方法
methodName:
方法名,指定获取的是哪一个方法
parameterTypes:
Class用于约束当前使用的参数类型
... 不定长参数,方法参数个数,顺序,有参无参问题
例如:
cls是Class类对象
cls.getMethod("setName", String.class);
cls.getMethod("getName");
实例:
package cn.ocean888_reflect;
import java.lang.reflect.Method;
public class getMethodObject {
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException {
/*
* 根据指定的包名.类名,获取对应的Class对象
*/
Class<?> cls = Class.forName("cn.ocean888_reflect.Person");
/*
* 获取类内所有public修饰的成员方法,包括从父类继承的方法
*/
Method[] methods = cls.getMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println("====================================");
System.out.println();
/*
* 暴力反射
* 获取类内所有的成员方法,包括私有化成员方法,不包括父类继承而来的方法
*/
Method[] methods2 = cls.getDeclaredMethods();
for (Method method : methods2) {
System.out.println(method);
}
System.out.println("====================================");
System.out.println();
/*
* 根据指定的方法和参数类型,获取类内public修饰的成员方法
*/
Method game1 = cls.getMethod("game");
Method game2 = cls.getMethod("game", String.class);
System.out.println(game1);
System.out.println(game2);
/*
* 根据指定的方法和参数类型,获取类内的private修饰的成员方法
*/
Method test1 = cls.getDeclaredMethod("testPrivate");
Method test2 = cls.getDeclaredMethod("testPrivate", String.class);
System.out.println(test1);
System.out.println(test2);
}
}
输出结果
Method执行方法和权限
Object invoke(Object obj, Object... argument);
通过method类对象调用,执行对应的方法,需要的参数
obj:
执行当前方法的执行者
argument:
Object... 不定长参数,当前方法执行所需的实际参数
package cn.ocean888_reflect;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class GetMethodObject2 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
Class<?> cls = Class.forName("cn.ocean888_reflect.Person");
Method game1 = cls.getMethod("game");
Method game2 = cls.getMethod("game", String.class);
Method testPrivate1 = cls.getDeclaredMethod("testPrivate");
Method testPrivate2 = cls.getDeclaredMethod("testPrivate", String.class);
Object obj = cls.getConstructor().newInstance();
/*
* 调用public方法
*/
game1.invoke(obj);
game2.invoke(obj, "贪吃蛇大作战");
/*
* 首先基于权限,再调用private方法
*/
testPrivate1.setAccessible(true);
testPrivate1.invoke(obj);
testPrivate2.setAccessible(true);
testPrivate2.invoke(obj, "泡泡");
}
}
Field成员变量涉及到方法
获取方法
Field[] getFields();
获取类内所有public修饰的成员变量
Field[] getDeclaredFields();
获取类内所有成员变量,包括私有化成员变量
Field getField(String fieldName);
获取指定变量名的成员变量对象,要求是public修饰的成员变量
Field getDeclaredField(String fieldName);
获取指定变量名的成员变量对象,包括private私有化修饰的成员变量
实例:
package cn.ocean888_reflect;
import java.lang.reflect.Field;
public class GetFieldObject {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException {
Class<?> cls = Class.forName("cn.ocean888_reflect.Person");
Field[] fields = cls.getFields();
for (Field field : fields) {
System.out.println(field);
}
System.out.println("=====================================");
Field[] declaredFields = cls.getDeclaredFields();
for (Field field : declaredFields) {
System.out.println(field);
}
System.out.println("=====================================");
Field testField = cls.getField("test");
System.out.println(testField);
System.out.println("=====================================");
Field id = cls.getDeclaredField("id");
System.out.println(id);
}
}
设置方法
void set(Object obj, Object value);
设置指定调用者中对应成员变量的数据
obj:调用者
value:对应当前成员变量需要赋值的内容
Object get(Object obj);
获取指定调用者中指定成员变量的数据
obj:调用者
实例:
Object obj = cls.getConstructor().newInstance();
System.out.println(obj);
// 设置public属性的test的参数值
testField.set(obj, 20);
System.out.println(obj);
// 设置private属性的id参数值
id.setAccessible(true);
id.set(obj, 88);
System.out.println(id.get(obj));
给予暴力反射私有化内容的权限操作
setAccessible(boolean flag);
给予Constructor,Method,Field对象,私有化内容,操作权限设置
true 表示可以操作
更多推荐
所有评论(0)