Java基础复习——反射

308 阅读5分钟

反射

概念:反射就是将Java类的各个组成部分封装成其他对象

类加载过程

加载 验证 准备 解析 初始化

1. 加载阶段

  1. 通过类的全限定类名获取类的二进制字节流。
  2. 将这个字节流所代表的静态存储结构转换为方法区的运行时数据结构。
  3. 在Java堆中创建一个关于这个类的Class对象,作为方法区数据结构的访问入口。

2. 验证

确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。

  1. 文件格式验证
  2. 元数据验证
  3. 字节码验证
  4. 符号引用验证

3. 准备阶段

准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些内存都将在方法区进行分配。注意:目前值为类变量也就是静态变量分配内存和初始化,并且初始化没有设置值,只是初始化了对应类型的默认值。

4. 解析

解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程

5. 初始化

初始化阶段是类加载的最后一个阶段,到了初始化阶段,才真正开始执行类中定义的Java代码。根据程序员定义的值给各个变量赋值。

Java代码在计算机中经历的三个过程

java在计算机中的几个阶段.png

Source源代码阶段

通过javac的编译命令,将.java文件编译成字节码文件

Class类对象阶段

将类的各个组成部分:如成员属性,构造方法,普通方法封装为不同的三个对象。
一个字节码文件对应着一个类对象Class

Runtime运行时阶段

通过类加载器,将对象创建出来并运行程序

如何获取Class对象

一共有三种方式,分别在三个阶段都可以获取。

在源代码阶段获取

使用场景:多用于配置文件,将类名定义在配置文件中,读取配置文件,加载类。

  Class personClass = Class.forName("com.jianyou.Reflact.Person");

该方法将会返回一个Class对象。

在Class类对象阶段获取

 Class<Person> personClass = Person.class;

该方法也会返回一个Class对象

在运行时阶段获取

Class personClass1 = person.getClass();

注意:同一个字节码文件在一次程序运行过程中只会被创建一个,以上三种方式创建的类对象是相同的

Class类对象的常用方法

反射方法.png

测试获取成员变量

获取出来成员变量有什么用?当然是获取值和设置值! 定义一个Person类

public class Person {

    private String name;
    private int age;
    private boolean ishappy;

    public int a;
    protected int b;
    int c;
    private int d;

    public Person() {
    }

    public Person(String name, int age, boolean ishappy) {
        this.name = name;
        this.age = age;
        this.ishappy = ishappy;
    }

    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 boolean isIshappy() {
        return ishappy;
    }

    public void setIshappy(boolean ishappy) {
        this.ishappy = ishappy;
    }

    public void eat(){
        System.out.println("吃饭.........");
    }
    public void sleep(){
        System.out.println("睡觉.........");
    }

}
  1. 获取类的所有成员变量,不管修饰符
public class Demo02 {

    public static void main(String[] args) throws Exception {
        Class<Person> personClass = Person.class;

        //获取类对象的所有成员属性,不管修饰符如何
        Field[] declaredFields = personClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println(declaredField);
        }
    }
}

结果如下

private java.lang.String com.jianyou.Reflact.Person.name
private int com.jianyou.Reflact.Person.age
private boolean com.jianyou.Reflact.Person.ishappy
public int com.jianyou.Reflact.Person.a
protected int com.jianyou.Reflact.Person.b
int com.jianyou.Reflact.Person.c
private int com.jianyou.Reflact.Person.d
  1. 获取类的所有公开的public修饰的成员变量
    public static void main(String[] args) throws Exception {
        Class<Person> personClass = Person.class;

        //获取类对象的所有public修饰的成员属性

        Field[] fields = personClass.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }
    }

结果如下

public int com.jianyou.Reflact.Person.a    //只能获取一个

获取出来了成员属性如何取值和设置值呢?

使用Filed类的set和get方法

public class Demo02 {

    public static void main(String[] args) throws Exception {
        Class<Person> personClass = Person.class;

        //获取类对象的所有public修饰的成员属性
        Field d = personClass.getDeclaredField("d");
        Person p = new Person();
        d.set(p,999);
        System.out.println(d.get(p));
    }
}

运行发现报错了,为什么?

Exception in thread "main" java.lang.IllegalAccessException: Class com.jianyou.Reflact.Demo02 can not access a member of class com.jianyou.Reflact.Person with modifiers "private"
	at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
	at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)
	at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288)
	at java.lang.reflect.Field.set(Field.java:761)
	at com.jianyou.Reflact.Demo02.main(Demo02.java:14)

发现Person类的d属性是私有的,不能直接设置。我们需要加上一段代码进行暴力破解!注意,反射中没有公有和私有,都可以直接破解。

 d.setAccessible(true);

一运行,发现设置成功了。

测试获取构造方法

构造方法有什么用?我们当然是用来创建对象的啦!

根据一个指定的构造方法获取Constructor对象,然后使用newInstance()方法创建对象

public class Demo02 {

  public static void main(String[] args) throws Exception {
      Class<Person> personClass = Person.class;

      Constructor<Person> constructor = personClass.getConstructor(String.class, int.class, boolean.class);
      Person person = constructor.newInstance("不简单的frank", 23, true); //创建对象
      System.out.println(person);
  }
}

结果如下

Person{name='不简单的frank', age=23, ishappy=true, a=0, b=0, c=0, d=0}

如果是默认的无参数构造方法,那就更简单了!

public class Demo02 {

    public static void main(String[] args) throws Exception {
        Class<Person> personClass = Person.class;
        Person person = personClass.getConstructor().newInstance();
        System.out.println(person);
    }
}

结果如下

Person{name='null', age=0, ishappy=false, a=0, b=0, c=0, d=0}

获取成员方法

获取成员方法后主要是用来执行!我们来测试一下如何执行

修改了一下Person类的sleep方法

    private void sleep(int Day){
        System.out.println("睡觉........."+Day+"天");
    }

开始测试

public class Demo02 {

    public static void main(String[] args) throws Exception {
        Class<Person> personClass = Person.class;
        Method sleepMethod = personClass.getDeclaredMethod("sleep", int.class);
        Person p = new Person();
        sleepMethod.setAccessible(true);    //如果是执行私有的方法,必须加上这一行代码!暴力破解
        sleepMethod.invoke(p,7);    //执行方法需要传递一个对象,还有方法参数
                                          //如果对于一个方法不熟悉的话可以直接点进去看源码!

    }
}

运行结果如下

睡觉.........7天

至此大功告成!

如果这篇文章对您有帮助的话别忘记点赞关注哦!因为自己才疏学浅,如果有错误的地方也欢迎各位码友们批评指正!