反射基础
概念
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;
这种动态获取的信息以及动态调用对象方法的功能称为java语言的反射机制。
反射机制是构建框架技术的基础
无反射 不框架
IDE Eclipse IEDA 代码提示功能
反射的原理
.java文件 =编译器=> .class 文件 =虚拟机=> 运行 .class文件
反射把该过程 逆向推理
正在运行的.class 文件 =编译器=> ?
反射就是把java类中的各种成分映射成一个个的Java对象
例如:一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象。
(其实:一个类中这些成员方法、构造方法、在加入类中都有一个类来描述)
电脑本身是一个对象
解剖成为 一个一个的组成部分 CPU 硬盘 GPU 内存 风扇 网卡 …
Class类
- Class类的
类
表示正在运行的Java应用程序中的类和接口。
Object 中 有一个方法 getClass() 返回值 是 Class 类型 表示 正在运行的类的实际类型
所有的类都有该方法
三种Class 对象的获取方式
第一种/ 根据对象获取
Student stu1 = new Student();
Student stu2 = new Student();
System.out.println(stu1 == stu2);
Class cla1 = stu1.getClass();
Class cla2 = stu2.getClass();
System.out.println(cla1 == cla2);
程序运行开始 , 在创建对象的时候, 如果同一个类创建多个对象 ,实际运行的类型只有一个
第二种 / 根据类获取
类名.class 属性
Class cla = Student.class;
第三种 / 根据字符串获取
// 通过一个类的 全路径 获取该类的字节码文件对象
Class cla = Class.forName("s0731.ref.Student");
后期使用反射推荐使用第三种, 只依赖于一个字符串 Class类常用方法
属性类 Field
获取属性的方法
getField(属性名) 只能获取公共(public)的属性
getDeclaredField(属性名) 获取任意修饰符的属性
getFields() 获取所有公共(public)的属性 返回值为Field 数组
getDeclaredFields() 获取任意修饰符的属性集合
属性类 Field
构造类 Constructor
获取构造的方法
getConstructor(参数列表类型 类<?>... parameterTypes)
// 方法重载
getDeclaredConstructor(参数列表类型 类<?>... parameterTypes)
getConstructors()
getDeclaredConstructors()
Constructor 构造包装类的方法
newInstance(Object... initargs)
使用此 Constructor对象表示的构造函数,使用指定的初始化参数来创建和初始化构造函数的声明类的新实例。
方法类 Method
invoke(Object obj, Object... args)
在具有指定参数的 方法对象上调用此 方法对象表示的底层方法。
获取方法的方法
getMethod(String name, 类<?>... parameterTypes)
getDeclaredMethod(String name, 类<?>... parameterTypes)
getMethods()
getDeclaredMethods()
反射应用
基础数据
public class Student {
public String name;
int age;
private String address;
public Student(){}
public void study(){
System.out.println("正在学习!!");
}
private void play(){
System.out.println("随便玩");
}
private void play(String thing){
System.out.println("玩"+ thing);
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", address='" + address + '\'' +
'}';
}
}
案例1 公共属性
获取对象的公共属性并且赋值
// 获取到了 该类的字节码文件 .class 文件
Class stuCla = Class.forName("s0731.ref.Student");
// 获取该类的构造方法
Constructor constructor = stuCla.getConstructor();
// 获取的就是 public Student(){} 的包装对象
Object obj1 = constructor.newInstance();
Object obj2 = constructor.newInstance();
// 获取属性
Field nameField = stuCla.getField("name");
nameField.set(obj1,"张三");
nameField.set(obj2,"李四");
System.out.println(obj1);
System.out.println(obj2);
案例2 私有属性
获取对象的私有属性并且赋值
public static void main(String[] args) throws Exception{
Class stuCla = Class.forName("s0731.ref.Student");
Constructor constructor = stuCla.getConstructor();
Object obj = constructor.newInstance();
// 获取私有属性的包装类
Field addressField = stuCla.getDeclaredField("address");
// 虽然能够获取到 private 修饰的属性
// private 不能被外界访问和修改
// 设置 取消 private 的 限制
addressField.setAccessible(true);
addressField.set(obj,"青岛");
Field nameField = stuCla.getField("name");
nameField.set(obj,"张三");
Field ageField = stuCla.getDeclaredField("age");
ageField.set(obj,20);
System.out.println(obj);
}
案例3 方法
获取公共的方法并调用
Class stuCla = Class.forName("s0731.ref.Student");
Constructor constructor = stuCla.getConstructor();
Object obj = constructor.newInstance();
/ 获取公共方法 方法名为 study 的无参方法
Method study = stuCla.getMethod("study");
study.invoke(obj);
获取私有的无参方法
// 获取私有方法 方法名为 play 的无参方法
Method play = stuCla.getDeclaredMethod("play");
play.setAccessible(true);
play.invoke(obj);
获取私有的有参方法
// 获取私有方法 方法名为 play 的有参方法
Method play = stuCla.getDeclaredMethod("play",String.class);
play.setAccessible(true);
play.invoke(obj,"篮球");
获取方法的返回值
如果play 方法有返回值
Object Parma = play.invoke(obj,"篮球");
实际应用
避开list 泛型检查
public static void main(String[] args) throws Exception{
List<String> list = new ArrayList<String>();
list.add("张三");
list.add("李四");
// list.add(123);
// 任务 向 泛型为 String 的 list 集合中 成功添加一个 123 的整数类型
/* 步骤
1- 获取list 的运行时 字节码对象
2- 获取 list 的 add 方法封装对象
3, 使用 封装对象 调用 add 方法添加内容
泛型实际上仅仅是在编译期生效
*/
Class<?> clazz = Class.forName("java.util.ArrayList");
Method add = clazz.getMethod("add", Object.class);
add.invoke(list,123);
System.out.println(list);
}
properties
基本使用
新建文件
abc.properties
name=于老板
age=50
通过程序加载
public static void main(String[] args) throws Exception {
// 1- 创建 properties 对象
// 2- 对象 通过 输入流 加载 本地文件
// 3- 分别通过键 获取值
Properties pro = new Properties();
FileInputStream fis = new FileInputStream("src\\s0731\\abc.properties");
pro.load(fis);
fis.close();
String name = pro.getProperty("name");
String age = pro.getProperty("age");
System.out.println(name + "\t"+age);
通过配置文件自定义执行内容
public class Demo1 {
public static void main(String[] args) throws Exception {
// Test1 t1 = new Test1();
// t1.m1();
Properties pro = new Properties();
FileInputStream fis = new FileInputStream("C:\\Users\\Administrator\\abc.properties");
pro.load(fis);
fis.close();
String className = pro.getProperty("class");
String methodName = pro.getProperty("method");
Class<?> clazz = Class.forName(className);
Object obj = clazz.newInstance();
Method method = clazz.getMethod(methodName);
method.invoke(obj);
}
}
需要把整个项目打成一个可执行 jar 包
可以通过修改配置文件 , 来动态的指定我们需要运行的程序