1.一个需求
根据配置文件re.properties
指定信息,创建对象并调用方法
classfullpath=com.hspedu.Cat
method=hi
package com.hspedu;
public class Cat {
private String name = "招财猫";
public void hi(){
System.out.println(name + " hi~");
}
public void cry(){
System.out.println(name + " cry~");
}
}
public class ReflectionQuestion {
public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
// 根据配合文件re.properties指定信息,创建Cat对象并调用hi方法
//传统的方法
//Cat cat = new Cat();
//cat.hi();
// 1.使用Properties类,可以读写配置文件
Properties properties = new Properties();
properties.load(new FileInputStream("src/re.properties"));
String classfullname = properties.get("classfullpath").toString();
String methodName = properties.get("method").toString();
System.out.println("classfullname => " + classfullname); //classfullname => com.hspedu.Cat
System.out.println("method => " + methodName); // method => hi
// 2.创建对象,传统的方法,行不通
// 3.使用反射机制解决
// (1)加载类, 返回一个Class类型的对象
Class<?> clazz = Class.forName(classfullname);
// (2)通过 clazz 得到你加载的类 com.hspedu.Cat的对象实例
Cat cat = (Cat)clazz.newInstance(); // 类型可转可不转 object运行类型就是Cat
// (3) 通过 clazz 得到加载Cat类的 methodName 的方法对象
// 在反射中,可以把方法看成对象
Method method1 = clazz.getMethod(methodName);
method1.invoke(cat); // 方法.invoke(对象)
}
}
改需求
hi
方法改为cry
不动源码,改配置文件即可!
反射机制允许程序在执行期间借助于Reflection API取得任何类的内部信息 (比如成员变量,构造器,成员方法等等), 并能操作对象的属性及方法。
加载完类之后,在堆中产生了一个Class类型的对象(堆中一个类只有一个Class对象)这个对象包含了类的完整结构信息。这个对象就像一面镜子,透过这个镜子看到类的结构,所以形象的称之为:反射
2. 反射机制
反射可以做的事
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时得到任意一个类所具有的成员变量和方法
- 在运行时调用任意一个对象的成员变量和方法
- 生成动态代理
反射相关主要的类
java.lang.Class
代表一个类,Class对象表示某个类加载后在队中的对象(模板)java.lang.reflect.Method
代表类的方法,Method对象表示某个类的方法java.lang.reflect.Field
: 代表类的成员变量,Field对象表示某个类的成员变量java.lang.reflect.Constructor
: 代表类的构造方法,Constructor对象表示某个类的构造器
package com.hspedu;
public class Cat {
private String name = "招财猫";
public int age = 10;
public Cat(){}
public Cat(String name){
this.name = name;
}
public void hi(){
System.out.println(name + " hi~");
}
public void cry(){
System.out.println(name + " cry~");
}
}
public class Reflect01 {
public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException, NoSuchMethodException {
Properties properties = new Properties();
properties.load(new FileInputStream("src/re.properties"));
String classfullname = properties.get("classfullpath").toString();
String methodName = properties.get("method").toString();
Class<?> clazz = Class.forName(classfullname);
Object obj = clazz.newInstance();
// getField() 不能得到私有的属性
Field ageField = clazz.getField("age");
System.out.println(ageField.get(obj)); // 成员变量对象.get(对象)
//()中可以指定构造器参数类型,返回无参构造器
Constructor<?> constructor1 = clazz.getConstructor();
System.out.println(constructor1);
// String.class 就是 String类的class对象
Constructor<?> constructor2 = clazz.getConstructor(String.class);
System.out.println(constructor2);
}
}
3. 反射优缺点
- 可以动态创建和使用对象(框架底层的核心),使用灵活,没有反射机制,框架技术就失去地城支撑
- 使用反射基本是解释执行,对执行速度有影响
反射调用优化
Method、Field、Constructor对象都有setAccessible()
方法
setAccessible
作用是启动和禁用访问安全检查的开关
参数为true
表示反射的对象在使用时取消访问检查,提高反射的效率。参数值为false
表示开启检查
4. Class类分析
Class也是类,因此也继承Object类
Class类对象不是new出来的,而是系统创建的 ClassLoader.loadClass
反射机制也是通过ClassLoader加载的,如果该类没有加载过,会loadClass一次,否则不会加载了,内存中只有一个此类的Class对象
对于某个类的Class类对象,在内存中只有一份,因此类只加载一次
每个类的实例都会记得自己是由哪个Class实例所生成
通过Class可以完整地得到一个类的完整结构,通过一系列的API
Class对象是放在堆的
5. Class类的相关方法
package com.hspedu.reflection;
import java.lang.reflect.Field;
public class Reflect02 {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {
String classAllPath = "com.hspedu.reflection.Car";
//<?>是通配符,得到car类对应的Class对象
Class<?> clazz = Class.forName(classAllPath);
// (Car)clazz是错误的
// 2.输出clazz
System.out.println(clazz); //clazz对象显示是哪个类的对象 class com.hspedu.reflection.Car
System.out.println(clazz.getClass()); // class java.lang.Class
// 3. 得到类的包名
System.out.println(clazz.getPackage().getName()); // com.hspedu.reflection
// 4. 得到全类名
System.out.println(clazz.getName()); // com.hspedu.reflection.Car
// 5. 通过clazz创建对象实例
Object obj = clazz.newInstance(); //obj运行时就是car对象实例
System.out.println(((Car)obj).brand); // byd
//6. 通过反射获取属性 brand
Field brand = clazz.getField("brand");
System.out.println(brand.get(obj)); //byd
// 7.通过反射给属性赋值
brand.set(obj,"奔驰");
System.out.println(brand.get(obj)); // obj
// 8. 得到所有的属性字段
Field[] fields = clazz.getFields();
for(Field f : fields){
System.out.println(f.getName()); // brand price color
}
}
}
class Car{
public String brand = "byd";
public int price;
public String color;
}
6.获取Class对象的六中方式
Cat cat = new Cat();
// 运行时态 cat对象与Class对象时关联的
Class<? extends Cat> clazz = cat.getClass();
7. 类加载
静态加载: 编译时加载相关的类,依赖性太强
动态加载: 运行时加载需要的类,如果运行时不用该类,则不报错,降低了依赖性
类加载时机
- 当new对象时(静态加载)
- 当子类被加载,父类也加载(静态加载)
- 调用类中的静态成员时 (静态加载)
- 通过反射(动态加载)
8.Field类
9. 反射爆破
暴破创建对象
public class Test {
public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
// 1. 通过public的无参构造器创建实例
Class clazz = User.class;
Object o = clazz.newInstance();
System.out.println(o); // User [age=10,name=superkcl]
// 2. 通过public的有参构造器创建实例
// constructor 对象就是 public User(String name)....
Constructor constructor = clazz.getConstructor(String.class);
Object obj = constructor.newInstance("超级氯化钾");
System.out.println(obj); // User [age=10,name=超级氯化钾]
//3. 通过private有参构造器创建实例
//报错 Constructor constructor2 = clazz.getConstructor(int.class, String.class);
Constructor privateConstructor = clazz.getDeclaredConstructor(int.class, String.class);
//报错, Object obj2 = privateConstructor.newInstance(100, "张三丰");
//暴破,使用反射可以访问private构造器
// 在反射面前都是纸老虎
privateConstructor.setAccessible(true);
Object obj2 = privateConstructor.newInstance(100, "张三丰");
System.out.println(obj2); // User [age=100,name=张三丰]
}
}
class User{
private int age = 10;
private String name = "superkcl";
public User(){
}
public User(String name){
this.name = name;
}
private User(int age,String name){
this.age = age;
this.name = name;
}
public String toString(){
return "User [age="+age + ",name="+name+"]";
}
}
暴破属性
public class Test {
public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
// 1. 通过public的无参构造器创建实例
Class clazz = Student.class;
Object o = clazz.newInstance();
System.out.println(o); // Student [age=0,name=null]
Field age = clazz.getField("age");
age.set(o,88); // 通过反射操作属性
System.out.println(o); // Student [age=88,name=null]
// 通过反射操作private name属性
Field name = clazz.getDeclaredField("name");
// 报错 ! name.set(o,"纸老虎");
name.setAccessible(true);
name.set(o,"纸老虎");
name.set(null,"纸老虎"); //static属性这样做也可以
System.out.println(o); // Student [age=88,name=纸老虎]
}
}
class Student{
public int age;
private static String name;
public Student(){
}
public String toString(){
return "Student [age=" + age + ",name=" + name + "]";
}
}
暴破方法
public class Test {
public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
// 1. 通过public的无参构造器创建实例
Class clazz = Boss.class;
Object o = clazz.newInstance();
System.out.println(o); // Student [age=0,name=null]
Method say = clazz.getDeclaredMethod("say",int.class,String.class,char.class);
say.setAccessible(true);
System.out.println(say.invoke(o,100,"张三",'男')); //100 张三 男
System.out.println(say.invoke(null,100,"张三",'男')); //这样也可以静态方法
}
}
class Boss{
public int age;
private static String name;
public Boss(){
}
private static String say(int n,String s,char c){
return n + " " + s + " " + c;
}
public void hi(String s){
System.out.println("hi " + s);
}
}