类的加载

当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现这个类进行初始化。

  • 加载

指将class文件读入内存中,并创建一个Class对象。任何类被使用都系统都会创建一个Class对象。

  • 连接
    • 验证 - 是否有正确的内部结构,并和其他类协调一致
    • 准备 - 负责为类的静态成员分配内存,并设置默认初始化值
    • 解析 - 将类的二进制数据中的符号引用替换为直接引用
  • 初始化

jvm负责对类进行初始化
**
反射机制 - 图1

类的初始化

  • 创建类的实例
  • 类的静态变量,或为静态变量赋值
  • 类的静态方法
  • 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
  • 初始化某个类的子类
  • 直接使用java.exe命令来运行某个主类

反射机制 - 图2

反射的概述

java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象都能调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法称为java的反射机制。

java反射机制:**

  • 反射机制是java中底层的一个功能,就是通过这个机制可以进行对象的灵活的调用和处理。
  • 在后期学习中会用到反射的相关内容,但是都是底层的理解,比如:文件的加载,spring框架底层……

目前我们要掌握反射的使用。

  • java中提供了一个api - Class类
  • java.lang包下,不需要导包


反射需要用到的成员:**

  • Class类
  • Constructor 构造器
  • Method 方法
  • Field 字段(属性)
  • newInstance 创建实例
  • invoke 激活方法(执行方法)

**

Class对象

要想解剖一个类,必须先要获取该类的字节码文件对象。而解刨这个类,使用的就是Class类中的方法,所以先要获取到每个字节码文件对应的Class类型对象。

获取Class类对象的常用方式:**

  • 明确类型.class
  • 已知对象.getClass
  • Class.forName(完全限定名) - 完全限定名(包名.类名),获取Class对象 - 比较常用(安全)

同一个类获取到的Class对象是同一个。

  1. import org.junit.Test;
  2. /**
  3. * 当前案例演示利用反射的机制获取对象的属性。
  4. */
  5. public class ReflectDemo {
  6. //对象的获取直接使用反射获取 - 不使用new
  7. @Test
  8. public void test1(){
  9. //已知明确的类型,获取Class对象
  10. Class cla = Student.class;
  11. System.out.println(cla);//class Student
  12. }
  13. @Test
  14. public void test2(){
  15. //已知对象,获取class对象
  16. Student s = new Student();
  17. Class cla = s.getClass();//getClass()是顶级父类Object中的方法
  18. System.out.println(cla);//class Student
  19. }
  20. @Test
  21. public void test3() throws ClassNotFoundException {
  22. //完全限定名(包名.类名),获取Class对象 - 比较常用(安全)
  23. String className = "Student";
  24. //forName()返回这个地址值锁所对应的类
  25. Class cla = Class.forName(className);
  26. System.out.println(cla);//class Student
  27. }
  28. //测试结果:通过不同的反射获取对象判断是不是同一个?
  29. @Test
  30. public void test4() throws ClassNotFoundException {
  31. Class cla1 = Student.class;
  32. Student s1 = new Student();
  33. Class cla2 = s1.getClass();
  34. Class cla3 = Class.forName("Student");
  35. //比较
  36. System.out.println(cla1==cla2);//true
  37. System.out.println(cla2==cla3);//true
  38. System.out.println(cla1==cla3);//true
  39. }
  40. }

**

利用Class对象创建实例对象

反射操作构造方法:

  • 无参构造

Class cla = Student.class;
Object obj = cla.newInstance();//默认调用无参构造 - 创建实例

  • 有参构造

Class cla = Student.class;
Constructor ctor = cla.getConstructor(int.class,String.class);
//通过构造函数为属性赋值
Object obj = ctor.newInstance(20,”Tom”);

import org.junit.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * 通过java反射机制构建对象的测试 - 这些操作平时主要我们创建对象,内存中就偷偷的帮我们做了
 * 步骤:
 * 1.创建该类的Class对象
 * 2.利用Class对象去创建类对象
 * 3.利用Class对象中的newInstance方法创建对象,类中有公共的无参构造
 * 4.最后利用Class类中的构造函数对象,通过构造函数创建实例对象
 *
 */
public class ReflectDemo2 {
    @Test
    public void test1() throws IllegalAccessException, InstantiationException {
        //类中有无参构造
        Class cla = Student.class;
        Object obj = cla.newInstance();//默认调用无参构造 - 创建实例
        System.out.println(cla);//class Student
        System.out.println(obj);//Student{id=0, name='null'} - 当前的Object进行了向下转型给了反射获取到的类Student
    }

    @Test
    public void test2() throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException {
        //获取Class类中的构造函数对象
        Class cla = Student.class;
        //获取构造函数 - 有参
        Constructor ctor = cla.getConstructor(int.class,String.class);
        //通过构造函数为属性赋值
        Object obj = ctor.newInstance(20,"Tom");
        System.out.println(obj);//Student{id=20, name='Tom'}
    }

    //演示普通方式实例化对象做不到的事情(私有的set方法)
    @Test
    public void test3(){
        Student s = new Student();
        s.setId(1);
        //s.setName();不能赋值,方法私有
        System.out.println(s);//Student{id=1, name='null'}
    }
}

反射操作成员方法:

操作public方法:

流程:

  1. 获取实例对象
  2. 获取方法,需要明确形参列表
  3. 确定实例对象,并执行方法,传参

    @Test
    public void test(){
     //1.获取实例
     Class cla = Class.forName("package.Bean");
     Object obj = cla.newInstance();//通过无参构造创建对象
     //2.获取方法
     Method method = cla.getMethod("setId",String.class);
     //3.执行方法
     method.invoke(obj,"b002");
    }
    

    操作私有方法:

    步骤:

  4. 获取实例

  5. 获取方法
  6. 强制设置运行访问为私有访问(暴力)
  7. 执行

    @Test
    public void test(){
     //1.获取实例
     Class cla = Class.forName("package.Bean");
     Object obj = cla.newInstance();//通过无参构造创建对象
     //2.获取方法
     Method method = cla.getMethod("show",String.class);
     //3.强制设置运行访问私有访问(暴力)
     method.setAccessible(true);
     //4.执行方法
     method.invoke(obj,"b002");
    }
    

    处理静态方法

    步骤:

  8. 获取Class

  9. 获取方法
  10. 执行方法 ```java import org.junit.Test;

import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method;

/**

  • 反射处理静态方法 */ public class ReflectDemo3 { @Test public void test1(){

     //静态方法的调用 - 普通
     int count = Student.getCount(10);
     System.out.println(count);
    

    }

    @Test public void test2() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {

     //利用反射机制来处理
     //1.创建Class
     String className = "Student";
     Class cla = Class.forName(className);
     //2.调用静态方法
     Method m_getCount = cla.getMethod("getCount", int.class);
     //3.根据类型传参 - invoke方法表示映射,这里因为不需要实例,所以可以传参null
     Object returnValue = m_getCount.invoke(null,2);//(对象,参数)
    

    } }

<a name="e2sy6"></a>
### 操作public字段
**步骤:**

1. **获取实例对象**
1. **获取字段**
1. **给字段赋值**
1. **获取字段值**
```java
@Test
public void test(){
    Class cla = new Class.forName("User");
    Object obj = cla.newInstance();
    Field id = cla.getField("id");
    id.set(obj,20);
    Object id_value = id.get(s1);
    System.out.println(id_value);
}

操作私有字段

步骤:

  1. 获取实例对象
  2. 获取字段
  3. 给字段赋值
  4. 获取字段值
    @Test
    public void test(){
     Class cla = new Class.forName("User");
     Object obj = cla.newInstance();
     Field id = cla.getField("id");
     id.setAccessible(true);
     id.set(obj,20);
     Object id_value = id.get(s1);
     System.out.println(id_value);
    }