一、反射
反射:是框架设计的灵魂
- 框架:半成品软件。可以在框架的基础上进行软件开发,简化编码
- 反射:将类的各个组成部分封装为其他对象,这就是反射机制 (下图蓝色部分)
- 类加载器也见下图
反射的好处:
- 可以在程序运行过程中,操作这些对象。
- 可以解耦,提高程序的可扩展性。
1.1获取Class模板的3种方式:
它们是什么时候获取的可见上图
- Class.forName(“全类名”(需写包名.类名)):将字节码文件加载进内存,返回Class对象(需要传入类名的字符串)(加载、链接、初始化)
eg:Class cls1 = Class.forName(“cn.xxx.domain.Person”);
多用于配置文件,将类名定义在配置文件中。读取文件,加载类
Class.forName 的参数 2 为 false 时不会被初始化
- 类名.class:通过类名的属性class获取(只进行加载,不链接和初始化)
多用于参数的传递
类加载器的 loadClass 方法不会被初始化
- 对象.getClass():getClass()方法在Object类中定义着。(肯定会链接初始化,因为new对象了)
多用于对象的获取字节码的方式 *
结论:同一个字节码文件(*.class)在一次程序运行过程中,只会被加载一次,不论通过哪一种方式获取的Class对象都是同一个。(就是上面三种方法获取的class对象都是同一个)
/**
* @author QLBF
* @version 1.0
* @date 2020/11/10 15:00
*/
class Person{
private String name;
private int age;
public Person() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
public class ReflectDemo1 {
/**
获取Class对象的方式:
1. Class.forName("全类名"):将字节码文件加载进内存,返回Class对象,得抛出异常
2. 类名.class:通过类名的属性class获取
3. 对象.getClass():getClass()方法在Object类中定义着。
*/
public static void main(String[] args) throws Exception {
//下面的泛型可以删
//1.Class.forName("全类名")
Class cls1 = Class.forName("Person");
System.out.println(cls1);
//2.类名.class
Class cls2 = Person.class;
System.out.println(cls2);
//3.对象.getClass()
Person p=new Person();
Class cls3 = p.getClass();
System.out.println(cls3);
//== 比较三个对象
System.out.println(cls1 == cls2);//true
System.out.println(cls1 == cls3);//true
System.out.println(cls2 == cls3);//true
}
}
1.2使用Class
Class对象功能:
* 获取功能:
-
获取成员变量们
-
Field[] getFields()获取所有public修饰的成员变量,不能获得私有的
-
Field getField(String name)获取指定名称的 public修饰的成员变量,也不能获得私有的
-
Field[] getDeclaredFields()获取所有的成员变量,不考虑修饰符,可以获得私有变量,但得忽略访问权限修饰符的安全检查 (protected,default也要):setAccessible(true)(暴力反射)
-
Field getDeclaredField(String name)跟上面类型,指定获得变量(可私有,忽略安全检查就行)
Field(上面获得的都是Field):成员变量
操作:
1.5 设置值 void set(Object obj, Object value)
1.6获取值 get(Object obj) -
-
获取构造方法们
-
Constructor<?>[] getConstructors()
-
Constructor getConstructor(类<?>… parameterTypes)
-
Constructor getDeclaredConstructor(类<?>… parameterTypes)
-
Constructor<?>[] getDeclaredConstructors()
Constructor:构造方法
用来创建对象:
2.1 T newInstance(Object… initargs)
2.2 如果使用空参数构造方法创建对象,操作可以简化:Class对象的newInstance方法(也可以用2.1,麻烦点而已) -
-
获取成员方法们:
-
Method[] getMethods()
-
Method getMethod(String name, 类<?>… parameterTypes)
-
Method[] getDeclaredMethods()
-
Method getDeclaredMethod(String name, 类<?>… parameterTypes)
-
获取方法名:method.getName();
Method:方法对象
执行方法:
-
Object invoke(Object obj, Object… args)
- 获取类名
- String getName()
- 获取类名
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* @author QLBF
* @version 1.0
* @date 2020/11/10 15:00
*/
class Person{
private String name;
private int age;
public String a;
public int b;
protected String c;
String d;
private String e;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
// this.a = a;
// this.b = b;
// this.c = c;
// this.d = d;
// this.e = e;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getA() {
return a;
}
public void setA(String a) {
this.a = a;
}
public int getB() {
return b;
}
public void setB(int b) {
this.b = b;
}
public String getC() {
return c;
}
public void setC(String c) {
this.c = c;
}
public String getD() {
return d;
}
public void setD(String d) {
this.d = d;
}
public String getE() {
return e;
}
public void setE(String e) {
this.e = e;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", a='" + a + '\'' +
", b=" + b +
", c='" + c + '\'' +
", d='" + d + '\'' +
", e='" + e + '\'' +
'}';
}
public void eat(){
System.out.println("eat...");
}
public void eat(String food){
System.out.println("eat--"+food);
}
}
public class ReflectDemo1 {
public static void main(String[] args) throws Exception {
//show_Field();
//show_constryctor();
show_method();
}
private static void show_method() throws Exception {
//0.获取Person的Class对象
Class personClass = Person.class;
/*
3. 获取成员方法们:
* Method[] getMethods()
* Method getMethod(String name, 类<?>... parameterTypes)
* Method[] getDeclaredMethods()
* Method getDeclaredMethod(String name, 类<?>... parameterTypes)
*/
//获取指定名称的方法,得传方法名和参数
Method eat1 = personClass.getMethod("eat");
Person p=new Person();
//执行方法
eat1.invoke(p);//eat...
Method eat2 = personClass.getMethod("eat", String.class);
eat2.invoke(p,"apple");
System.out.println(p);//eat--apple
System.out.println("-----------------");
//获取所有public修饰的方法,Object的方法也会被打印
Method[] methods = personClass.getMethods();
for (Method method : methods) {
//System.out.println(method);//Object的方法也会被打印
//获取方法名:
String name = method.getName();
//System.out.println(name);
}
//获取类名
String className = personClass.getName();
System.out.println(className);//Person,如果还有包名会被输出
}
private static void show_constryctor() throws Exception {
//0.获取Person的Class对象
Class personClass = Person.class;
/*
2. 获取构造方法们
* Constructor<?>[] getConstructors()
* Constructor<T> getConstructor(类<?>... parameterTypes)
* Constructor<T> getDeclaredConstructor(类<?>... parameterTypes)
* Constructor<?>[] getDeclaredConstructors()
*/
//Constructor<T> getConstructor(类<?>... parameterTypes)
//这里传入参数得用类型名.class
Constructor constructor = personClass.getConstructor(String.class, int.class);
System.out.println(constructor);//public Person(java.lang.String,int)
//创建对象
Object per = constructor.newInstance("李四", 29);
System.out.println(per);//Person{name='李四', age=29, a='null', b=0, c='null', d='null', e='null'}
System.out.println("--------");
// Constructor[] constructors = personClass.getConstructors();
// for (Constructor constructor1 : constructors) {
// System.out.println(constructor1);
// }
//无参的构造方法1
Constructor constructor1 = personClass.getConstructor();
System.out.println(constructor1);//public Person()
//空参
Object o = personClass.newInstance();
System.out.println(o);//Person{name='null', age=0, a='null', b=0, c='null', d='null', e='null'}
}
private static void show_Field() throws Exception {
//0.获取Person的Class对象
Class<Person> personClass = Person.class;
/*
1. 获取成员变量们
* Field[] getFields()
* Field getField(String name)
* Field[] getDeclaredFields()
* Field getDeclaredField(String name)
*/
//1.Field[] getFields()获取所有public修饰的成员变量
Field[] fields = personClass.getFields();
for (Field field : fields) {
System.out.println(field);//public java.lang.String Person.a, public int Person.b
}
//2.Field getField(String name)
Field b = personClass.getField("b");
//System.out.println(b);// public int Person.b
//获取成员变量b 的值
Person p=new Person();
Object value = b.get(p);
System.out.println(value);
//设置b的值
b.set(p,128);
System.out.println(p);
System.out.println("-----3-----------");
//3.Field[] getDeclaredFields():获取所有的成员变量,不考虑修饰符
// Field[] declaredFields = personClass.getDeclaredFields();
// for (Field declaredField : declaredFields) {
// System.out.println(declaredField);//啥都获取到了
// }
//Field getDeclaredField(String name)
Field e = personClass.getDeclaredField("e");
//忽略访问权限修饰符的安全检查
e.setAccessible(true);//暴力反射
e.set(p,"张三");
Object value2 = e.get(p);
System.out.println(value2);//张三
System.out.println(p);//Person{name='null', age=0, a='null', b=128, c='null', d='null', e='张三'}
}
}
1.2getClassLoader()
每一个class对象都有一个getClassLoader()方法
eg:ClassLoader classLoader = A.class.getClassLoader(); getClassLoader方法就是获得A的字节码文件的类加载器(把字节码文件加载进内存)
/**
* @author QLBF
* @version 1.0
* @date 2020/11/11 12:30
*/
class Student{
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
'}';
}
public void eat(){
System.out.println("eat..");
}
}
public class fanshe {
public static void main(String[] args) {
//获取class对象
Class class1 = Student.class;
System.out.println(class1);//class Student
//ClassLoader classLoader = Student.class.getClassLoader();
//System.out.println(classLoader);//jdk.internal.loader.ClassLoaders$AppClassLoader@726f3b58
ClassLoader classLoader = class1.getClassLoader();
System.out.println(classLoader);//jdk.internal.loader.ClassLoaders$AppClassLoader@726f3b58
}
}
二、反射的魅力
**反射的魅力在与:只改配置文件,而不用直接修改java代码。**这样的好处在与:上线部署后你要改代码的话,得重新编译执行,再上线。而你修改配置文件就不用编译啥的了,因为它是个物理地址.
我们得不动ReflectTest(相当于一个框架)
首先在src文件新建一个File文件(命名为1.properties,这里命名是随便的,你改为1不要properties后缀也行的,只不过有后缀让别人清楚这是配置文件而已!!!)
1.properties里面写(真实情况下,下面不可以注释的):
className=Student //这里得写Person的全类名(这里每包名,有包名要点,className是自己写的,你只要对应你的ReflectTest就行)
methodName=eat //对应Person类的方法,不用()
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;
/**
* @author QLBF
* @version 1.0
* @date 2020/11/11 12:30
*/
class Student{
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
'}';
}
public void eat(){
System.out.println("eat..");
}
}
class Person{
public void sleep(){
System.out.println("sleep...");
}
}
public class ReflectTest {
public static void main(String[] args) throws Exception {
/*
前提:不能改变该类的任何代码。可以创建任意类的对象,可以执行任意方法
*/
/* Person p = new Person();
p.eat();*/
/*
Student stu = new Student();
stu.sleep();*/
//1.加载配置文件
//1.1创建Properties对象
Properties pro = new Properties();
//1.2加载配置文件,转换为一个集合
//1.2.1获取class目录下的配置文件
ClassLoader classLoader = ReflectTest.class.getClassLoader();
InputStream is = classLoader.getResourceAsStream("1.properties");
pro.load(is);//得抛出异常
//2.获取配置文件中定义的数据
String classname = pro.getProperty("className");//className对应你配置文件的=左边的名字,获得你的全类名
String methodname = pro.getProperty("methodName");//methodName也类似上面的,获得你的方法名
//3.加载该类进内存,看上面的博客方法
Class cls = Class.forName(classname);
//4.创建对象,因为不知道是什么对象,所以创建一个obj的
Object obj = cls.newInstance();
//5.获取方法对象
Method method = cls.getMethod(methodname);
//6.执行方法
method.invoke(obj);
}
}
输出:eat…
我们不需要改动ReflectTest 的代码就可以运行Student类里面的sleep方法,只需要把对应的配置文件改为
就行了,再运行ReflectTest 就会输出:sleep…