1:什么是反射机制?反射机制的应用场景有哪些?
反射机制介绍JAVA 反射机制是在运行状态中,
对于任意一个类,都能够知道这个类的所有属性和方法;
对于任意一个对象,都能够调用它的任意一个方法和属性;
这种动态获取的信息以及动态调用对象的方法的功能称为 java 语言的反射机制。
静态编译和动态编译
静态编译:在编译时确定类型,绑定对象
动态编译:运行时确定类型,绑定对象
反射机制优缺点优点:
运行期类型的判断,动态加载类,提高代码灵活度。
缺点:
性能瓶颈:反射相当于一系列解释操作,通知 JVM 要做的事情,性能比直接的 java 代码要慢很多。
反射是框架设计的灵魂
在我们平时的项目开发过程中,基本上很少会直接使用到反射机制,
但这不能说明反射机制没有用,实际上有很多设计、开发都与反射机制有关;
1: 我们在使用 JDBC 连接数据库时使用 Class.forName()通过反射加载数据库的驱动程序;
2:Spring 框架也用到很多反射机制,最经典的就是 xml 的配置模式。
2:反射的作用
使用的前提条件:必须先得到代表的字节码的Class,Class类用于表示.class文件(字节码)
反射就是把java类中的各种成分映射成一个个的Java对象
例如:一个类有:
构造方法、成员变量、成员方法、包等等信息,
利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象。
(其实:一个类中这些成员方法、构造方法、在加入类中都有一个类来描述)
主要分为以下:
1:获取Class对象的方法
2:获取构造方法
3:获取成员变量
4:获取成员方法
5:通过反射运行配置文件内容
6:通过反射越过泛型检查
3:反射的加载 与 Class类的描述
如图是类的正常加载过程:反射的原理在与class对象。
加载的时候:将class文件读入内存,并为之创建一个Class对象。
Class 类的实例表示正在运行的 Java 应用程序中的类和接口。
也就是jvm中有N多的实例每个类都有该Class对象。(包括基本数据类型)
Class 没有公共构造方法。
Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的defineClass 方法自动构造的。
也就是这不需要我们自己去处理创建,JVM已经帮我们创建好了。


4:获取Class对象的三种方式
在运行期间,一个类,只有一个Class对象产生。
三种方式常用第三种:
第一种:对象都有了还要反射干什么。
第二种:需要导入类的包,依赖太强,不导包就抛编译错误。
第三种:一个字符串可以传入也可写在配置文件中等多种方法。
/**
* 获取Class对象的三种方式
* 1 Object ——> getClass();
* 2 任何数据类型(包括基本数据类型)都有一个“静态”的class属性
* 3 通过Class类的静态方法:forName(String className)(常用)
*
*/
public class ClassDemo {
public static void main(String[] args) {
//第一种方式获取Class对象
//这一new 产生一个Student对象,一个Class对象。
Student stu1 = new Student();
//获取Class对象
Class stuClass1 = stu1.getClass();
System.out.println(stuClass1.getName());
//第二种方式获取Class对象
Class stuClass2 = Student.class;
//判断第一种方式获取的Class对象和第二种方式获取的是同一个
System.out.println(stuClass1 == stuClass2);
//第三种方式获取Class对象
try {
//注意此字符串必须是真实路径,就是带包名的类路径,包名.类名
Class stuClass3 = Class.forName("base.model.entity.Student");
//判断三种方式获取的是同一个Class对象
System.out.println(stuClass3 == stuClass2);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
5:获取Class对象的构造方法 并 调用
package base.model.entity ;
public class Student {
//---------------构造方法-------------------
//无参构造方法
public Student(){
System.out.println("调用了公有、无参构造方法执行了。。。");
}
//私有构造方法
private Student(int age){
System.out.println("私有的构造方法 年龄:"+ age);
}
//(默认的构造方法)
Student(String str){
System.out.println("(默认)的构造方法 s = " + str);
}
//受保护的构造方法
protected Student(boolean n){
System.out.println("受保护的构造方法 n = " + n);
}
//有一个参数的构造方法
public Student(char name){
System.out.println("姓名:" + name);
}
//有多个参数的构造方法
public Student(String name ,Integer age){
System.out.println("姓名:"+name+"年龄:"+ age);
}
}
import java.lang.reflect.Constructor;
/*
* 通过Class对象可以获取某个类中的:构造方法、成员变量、成员方法;并访问成员;
*
* 1.获取构造方法:
* 1).批量的方法:
* 所有"公有的"构造方法
* public Constructor[] getConstructors()
* 获取所有的构造方法(包括私有、受保护、默认、公有)
* public Constructor[] getDeclaredConstructors()
*
* 2).获取单个的方法,并调用:
* 获取单个的"公有的"构造方法:
* public Constructor getConstructor(Class... parameterTypes)
* 获取某个构造方法(私有的,或受保护、默认、公有)
* public Constructor getDeclaredConstructor(Class... parameterTypes):
*
* 调用构造方法:
* Constructor-->newInstance(Object... initargs)
*/
public class Constructors {
public static void main(String[] args) throws Exception {
//1.加载Class对象
Class clazz = Class.forName("base.model.entity.Student");
//2.获取所有公有构造方法
Constructor[] pubConArray = clazz.getConstructors();
for(Constructor c : pubConArray){
System.out.println(c);
}
//3.获取所有的构造方法(包括私有、受保护、默认、公有)
Constructor[] allConArray = clazz.getDeclaredConstructors();
for(Constructor c : allConArray){
System.out.println(c);
}
//获取公有、无参的构造方法
//1>、因为是无参的构造方法所以类型是一个null,不写也可以:这里需要的是一个参数的类型,切记是类型
//2>、返回的是描述这个无参构造函数的类对象。
Constructor con = clazz.getConstructor(null);
System.out.println("con = " + con);
//调用构造方法
Object obj = con.newInstance();
System.out.println("******************获取 某个构造方法 并调用*******************************");
con = clazz.getDeclaredConstructor(char.class);
System.out.println("con = " +con);
//调用构造方法
//暴力访问(忽略掉访问修饰符)
con.setAccessible(true);
obj = con.newInstance('hesuijin');
}
}
//控制台打印结果
**********************所有公有构造方法*********************************
public base.model.entity.Student()
public base.model.entity.Student(char)
public base.model.entity.Student(java.lang.String,java.lang.Integer)
************所有的构造方法(包括:私有、默认、受保护、公有)***************
public base.model.entity.Student()
private base.model.entity.Student(int)
base.model.entity.Student(java.lang.String)
protected base.model.entity.Student(boolean
public base.model.entity.Student(char))
public base.model.entity.Student(java.lang.String,int)
*****************获取公有、无参的构造方法*******************************
con = public base.model.entity.Student()
调用了公有、无参构造方法执行了。。。
******************获取私有构造方法,并调用*******************************
con = public base.model.entity.Student(char)
姓名:hesuijin
6:获取成员变量并调用
package base.model.field;
@Data
public class Student {
public Student(){
}
//**********字段*************//
public String name;
protected int age;
char sex;
private String phone;
}
import java.lang.reflect.Field;
/*
* 获取成员变量并调用:
*
* 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:要为字段设置的值;
*
*/
public class Fields {
public static void main(String[] args) throws Exception {
//1.获取Class对象
Class stuClass = Class.forName("base.model.field.Student");
//2.获取字段
System.out.println("************获取所有公有的字段********************");
Field[] fieldArray = stuClass.getFields();
for(Field f : fieldArray){
System.out.println(f);
}
System.out.println("************获取所有的字段(包括私有、受保护、默认的)********************");
fieldArray = stuClass.getDeclaredFields();
for(Field f : fieldArray){
System.out.println(f);
}
//获取一个对象
//产生Student对象--》Student stu = new Student();
Object obj = stuClass.getConstructor().newInstance();
System.out.println("*************获取公有字段**并调用***********************************");
Field f = stuClass.getField("name");
System.out.println(f);
//为字段设置值
//为Student对象中的name属性赋值--》stu.name = "刘德华"
f.set(obj, "刘德华");
//验证
Student stu = (Student)obj;
System.out.println("验证姓名:" + stu.name);
System.out.println("**************获取私有字段****并调用********************************");
f = stuClass.getDeclaredField("phone");
System.out.println(f);
//暴力反射,解除私有限定
f.setAccessible(true);
f.set(obj, "18718747777");
System.out.println("验证电话:" + stu.phone);
}
}
控制台输出
*************获取公有字段**并调用***********************************
public java.lang.String base.model.field.Student.name
验证姓名:刘德华
**************获取私有字段****并调用********************************
private java.lang.String base.model.field.Student.phone
验证电话:18718747777
7:获取成员方法并调用
package base.model.method ;
public class Student {
//**************成员方法***************//
public void show1(String s){
System.out.println("调用了:公有的,String参数的show1(): s = " + s);
}
protected void show2(){
System.out.println("调用了:受保护的,无参的show2()");
}
void show3(){
System.out.println("调用了:默认的,无参的show3()");
}
private String show4(int age){
System.out.println("调用了:私有的,并且有返回值的,int参数的show4(): age = " + age);
return "我是返回值";
}
}
import java.lang.reflect.Method;
/*
* 获取成员方法并调用:
* 1.批量的:
* 获取所有"公有方法";(包含了父类的方法也包含Object类)
* public Method[] getMethods()
* 获取所有的成员方法,包括私有的(私有、受保护、默认、公有)(不包括继承的)
* 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:调用方式时所传递的实参;
):
*/
public class MethodDemo {
public static void main(String[] args) throws Exception {
//1.获取Class对象
Class stuClass = Class.forName("base.model.method.Student");
//2.获取所有公有方法
System.out.println("***************获取所有的”公有“方法*******************");
stuClass.getMethods();
Method[] methodArray = stuClass.getMethods();
for(Method m : methodArray){
System.out.println(m);
}
System.out.println("***************获取所有的方法,包括私有的*******************");
methodArray = stuClass.getDeclaredMethods();
for(Method m : methodArray){
System.out.println(m);
}
//实例化一个Student对象
Object obj = stuClass.getConstructor().newInstance();
Method mehtod;
System.out.println("***************获取公有的show1()方法*******************");
m = stuClass.getMethod("show1", String.class);
//需要两个参数,一个是要调用的对象(获取有反射),一个是实参
System.out.println(m);
m.invoke(obj, "刘德华");
System.out.println("***************获取私有的show4()方法******************");
m = stuClass.getDeclaredMethod("show4", int.class);
System.out.println(m);
//暴力反射,解除私有限定
m.setAccessible(true);
//需要两个参数,一个是要调用的对象(获取有反射),一个是实参
Object result = m.invoke(obj, 20);
System.out.println("返回值:" + result);
}
}
控制台输出
***************获取公有的show1()方法*******************
public void fanshe.method.Student.show1(java.lang.String)
调用了:公有的,String参数的show1(): s = 刘德华
***************获取私有的show4()方法******************
private java.lang.String fanshe.method.Student.show4(int)
调用了,私有的,并且有返回值的,int参数的show4(): age = 20
返回值:我是返回值
8:反射方法的其它使用之—-通过反射运行配置文件内容
public class Student {
public void show(){
System.out.println("is show()");
}
}
//配置文件以txt文件为例子(properties.txt):
className = base.model.property.Student
methodName = show
/*
* 我们利用反射和配置文件,可以使:应用程序更新时,对源码无需进行任何修改
* 我们只需要将新类发送给客户端,并修改配置文件即可
*/
public class Demo {
public static void main(String[] args) throws Exception {
//1:通过反射获取Class对象
//"base.model.property.Student"
Class stuClass = Class.forName(getValue("className"));
//2:获取show()方法
//show
Method m = stuClass.getMethod(getValue("methodName"));
//3:调用show()方法
m.invoke(stuClass.getConstructor().newInstance());
}
//此方法接收一个key,在配置文件中获取相应的value
public static String getValue(String key) throws IOException{
//获取配置文件的对象
Properties pro = new Properties();
//获取输入流
FileReader fr = new FileReader("properties.txt");
//将流加载到配置文件对象中
pro.load(fr);
fr.close();
//返回根据key获取的value值
return pro.getProperty(key);
}
}
9:反射方法的其它使用之—-通过反射越过泛型检查
/*
* 通过反射越过泛型检查
*
* 例如:有一个String泛型的集合,怎样能向这个集合中添加一个Integer类型的值?
*/
public class Demo {
public static void main(String[] args) throws Exception{
ArrayList<String> strList = new ArrayList<>();
strList.add("string");
// strList.add(100);
//获取ArrayList的Class对象,反向的调用add()方法,添加数据
Class listClass = strList.getClass(); //得到 strList 对象的字节码 对象
//获取add()方法
Method m = listClass.getMethod("add", Object.class);
//调用add()方法
m.invoke(strList, 100);
//遍历集合
for(Object obj : strList){
System.out.println(obj);
}
}
}