反射的概念
反射机制通过API取得类的内部信息(包括变量,构造器,方法等),在类完成加载后,会生成Class类型的对象,该对象包含了类的完整结构信息,犹如一面镜子,透过镜子可以看到类的结构,因此称为反射。
反射的引入案例
要求从配置文件中读取并获取类的信息
配置文件:
classPathName=reflectionBeginner.Cat //类名是Cat
method=hi //方法名为hi
Cat类:
public class Cat {
//属性
public String name="cat";
public int age=0;
//构造器
public Cat(){}
public Cat(String name){
this.name=name;
}
//方法
public void hi(){
System.out.println("hello "+name);
}
@Override
public String toString() {
return "WayToGetClass.Cat{" +
"name='" + name + '\'' +
'}';
}
}
通过反射读取配置文件中的信息,创建类的对象并调用类中的方法
1.使用Properties读取并加载配置文件,得到类名和方法名
//使用Properties读取配置文件
Properties properties = new Properties();
//加载配置文件文件
properties.load(new FileInputStream("src/reflectionBeginner/re.properties"));
//类
String classPathName = properties.get("classPathName").toString();
//方法
String methodName = properties.get("method").toString();
2.通过Class.forName()得到对象,并创建对象实例,调用方法
// 调用方法
Class cls = Class.forName(classPathName);//通过类名得到对象
Object o = cls.newInstance(); //对象实例
Method method = cls.getMethod(methodName);//得到方法对象
method.invoke(o);//调用方法 方法.invoke(对象)
3.得到类中的属性
//返回name属性值
Field nameField = cls.getField("name");
System.out.println(nameField.get(o));//属性.get(对象名)
//返回所有属性
Field[] fields = cls.getFields();
for(Field field:fields){
System.out.println(field.getName());
}
- 返回对象实例信息和构造器
//对象实例信息
Cat cat=(Cat)cls.newInstance();
System.out.println("对象实例信息:"+cat); //toString方法
//返回无参构造器
Constructor constructor = cls.getConstructor();
System.out.println(constructor);
//返回有参构造器
Constructor constructor1 = cls.getConstructor(String.class);
System.out.println(constructor1);
获取Class对象的四种方法
//1.通过Class.forName(pathName)
String filePath="WayToGetClass.Cat";
Class cls1 = Class.forName(filePath);
//2.通过类名.Class
Class cls2 = Cat.class;
//3.通过对象.getClass()
Cat cat = new Cat();
Class cls3 = cat.getClass();
//4.通过类加载器得到class对象
//1)先得到类加载器
ClassLoader classLoader = cat.getClass().getClassLoader();
//2)加载得到对象
Class cls4 = classLoader.loadClass(filePath);
类加载的五个阶段
准备阶段的内存分配情况
class A{
//1.a是实例属性,不会在准备阶段分配内存
public int a=10;
//2.b是静态变量,默认初始化分配内存,但不会赋值
public static int b=10;
//3.c是常量,完成分配内存和赋值
public static final int c=10;
}
加载方式
静态加载:编译时完成加载,如果没有该类会报错
动态加载:运行时才加载,如果运行时为使用该类,则不会报错
类加载的四个时机
- new一个对象时
- 子类被加载,父类也被加载
- 调用static成员时
- 通过反射时
类加载后的内存布局
将方法区的二进制字节码数据转移到堆中得到class对象
获取类结构的方法
ShowClassStructure.java中有两个类
//父类
class A{
//属性
public String numbers;
//方法
public void sayOK(){}
//构造器
public A(){}
private A(String numbers){
this.numbers=numbers;
}
}
//子类
class Person extends A{
//属性
public String name;
private int age;
String job;
protected double salary;
//方法
public void sayHi(){}
private void sayNo(){}
void sayYes(){}
protected void sayNothing(){}
//构造器
public Person(){}
private Person(String name){
this.name=name;
}
}
首先得到类的对象
//得到类对象
Class personCls = Class.forName("ShowClassStructure.Person");
类名
//.getName()得到全类名
System.out.println(personCls.getName());
System.out.println(personCls.getSimpleName());//简单类名
属性
//得到所有公有属性,本类及父类
Field[] fields = personCls.getFields();
for (Field field : fields) {
System.out.println("本类和父类的公有属性= "+field.getName());
}
//得到所有公有属性,及修饰值及类型
for (Field field : fields) {
System.out.println("本类和父类的公有属性= "+field.getName()
+" 属性的修饰符值= "+ field.getModifiers()
+" 属性的类型= "+field.getType());
}
//得到本类所有属性
Field[] declaredFields = personCls.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println("本类的所有属性="+declaredField.getName());
}
方法
//得到所有公有方法
Method[] methods = personCls.getMethods();
for (Method method : methods) {
System.out.println("所有公有方法= "+method.getName());
}
//得到本类所有方法
Method[] declaredMethods = personCls.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println("本类所有方法= "+declaredMethod.getName());
}
//得到本类所有方法及修饰值及类型
for (Method declaredMethod : declaredMethods) {
System.out.println("本类所有方法= "+declaredMethod.getName()
+" 修饰值= "+declaredMethod.getModifiers()
+" 返回类型= "+declaredMethod.getReturnType());
}
构造器
//得到本类公有构造器
Constructor[] constructors = personCls.getConstructors();
for (Constructor constructor : constructors) {
System.out.println("本类公有构造器= "+constructor.getName());
}
//本类所有构造器
Constructor[] declaredConstructors = personCls.getDeclaredConstructors();
for (Constructor declaredConstructor : declaredConstructors) {
System.out.println("本类所有构造器= "+declaredConstructor.getName());
}
父类信息
//父类信息
Class superclass = personCls.getSuperclass();
System.out.println("父类信息="+superclass.getSimpleName());
反射爆破
通过反射爆破可以访问私有构造器/属性/方法
User类:
class User{
//私有属性
private String name;
private int age;
//私有含参构造器
private User(String name,int age){
this.name=name;
this.age=age;
}
//私有方法
private void sayHello(String name){
System.out.println("hello "+name);
}
@Override
public String toString() {
return "ReflectionBreaking.User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
1.得到类的对象
//得到类的对象
Class cls = Class.forName("ReflectionBreaking.User");
2.访问构造器
//通过爆破访问私有构造器
Constructor declaredConstructor = cls.getDeclaredConstructor(String.class, int.class);
declaredConstructor.setAccessible(true); //允许访问私有构造器
Object o = declaredConstructor.newInstance("fwj", 19);//创建实例
3.访问属性
//通过爆破访问私有属性
Field name = cls.getDeclaredField("name"); //获取属性对象
Field age = cls.getDeclaredField("age");
name.setAccessible(true);//允许访问
age.setAccessible(true);
name.set(o,"feng"); //设置属性
age.set(o,20);
System.out.println(o);
System.out.println(name.get(o)); //返回属性
System.out.println(age.get(o));
4.访问方法
//通过爆破访问私有方法
Method declaredMethod = cls.getDeclaredMethod("sayHello",String.class);
declaredMethod.setAccessible(true);
declaredMethod.invoke(o,"fwj"); //传参并调用方法