浅识Java反射

63 阅读5分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第19天,点击查看活动详情

反射_概念

我们之前正常使用一个类:这种情况叫:正向加载。缺点:与Student类产生了紧耦合,它改我的类就得改。

Student stu = new Student();

stu.show();

反射:也叫反向加载,是指程序有能力在运行时,动态的加载一个"类",并创建对象,并调用成员属性和成员方法。

在编译时,可以不知道这个类的存在。

反射_意义及示例

示例:

//使用反射,优点:解开两个类的紧耦合
Class c = Class.forName("全类名");
//创建对象
Object o = c.newInstance();
//获取方法
Method m = c.getMethod("show3");
//执行方法
m.invoke(o);

意义: 解开类与类之间的耦合关系。

反射_类的加载

  • 当我们第一次创建一个类的对象时,JVM会去找这个类的class文件。 但之后创建对象时,JVM就不会再去找class文件,而是直接创建对象。

  • 当第一次创建对象时,JVM似乎将这个类的信息全部都"记录下来",当再次创建对象时,不需要找class文件,而是找之前记录的那些信息,就可以创建对象了。

  • 当我们第一次使用(第一次创建对象,第一次访问它的静态成员)某个类时,JVM会在"方法区"中创建一个Class对象,并将这个类信息写入到这个Class对象中。后期通过这个Class对象就可以获取这个类的内部信息。

  • JVM会为每个类都会创建"一个Class对象"。

  • 反射是基于这个Class对象的:

    • 我们让JVM去创建某个类的Class对象;
    • 我们之后可以让JVM将这个Class对象返回到"程序中",之后我们程序员就可以操作这个Class对象,获取那个类的内部的信息。可以创建对象,访问属性、方法。

反射_Class对象的初始化时机

    1. 第一次创建类的实例

Student stu = new Student();//1.创建它的Class对象;2.创建Student对象
...
Student stu2 = new Student();//1.创建Student对象

    2. 第一次类的静态变量,或者为静态变量赋值

System.out.println(Student.PI);//第一次访问静态属性,会创建它的Class对象
...
Student stu = new Student();//不会创建Class对象,会直接创建对象

    3. 第一次调用类的静态方法:

Student.show();//第一次调用静态的show()方法;会创建它的Class对象;
..

    4. 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象

Class.forName(String 全名限定的类名)

    5. 初始化某个类的子类

class Fu{}
class Zi extends Fu{}
main(){
    new Zi();//1.创建Fu的Class对象,2.创建Zi的Class对象,3.创建Fu对象,4.创建Zi对象
}

    6. 直接使用java.exe命令来运行某个主类

class Demo{
    public static void main(String[] args){
    }
}

        ---------------

        命令行:

        C:>java Demo

反射_名词说明

英文中文
Class       
Constructor 构造
Method     方法
Field       字段(成员属性)
instance   示例(对象)
invoke     执行

反射_实现_获取Class对象的三种方式

    1.Object-->getClass()--被所有引用类型继承

    Student stu = new Student();

    Class c1 = stu.getClass();

    2.任何数据类型(包含基本类型)都有一个"静态属性--class",只能通过"类型名"获取,不能通过对象获取

    Class c2 = Student.class;

    3.通过反射

    Class c3 = Class.forName("全类名");

    System.out.println(c1 == c2);//true

    System.out.println(c2 == c3);//true

反射_实现_获取构造方法并创建对象

获取构造方法并调用:

  • 批量获取【了解】:

    • public Constructor[] getConstructors():获取所有的公有构造方法。

    • public Constructor[] getDeclaredContructors():获取所有的构造方法,包括公有、受保护的、默认的、私有的

  • 获取单个【掌握】:

    • public Constructor getConstructor(Class ... parameterTypes):获取某个公有的构造方法

    • public Constructor getDeclaredConstructor(Class ... parameterTypes):获取某个构造方法,可以是:公有、受保护、默认、私有的

调用构造方法,创建对象【重点掌握】:

Constructor newInstance(Object ... 实参列表)

注意:要调用私有构造方法,要先设置暴力访问:

Constructor --> setAccessible(true)

Class --> newInstance():调用这个类的公有、无参构造方法,前提是,这个类必须有这个构造方法


反射_实现_获取成员属性并赋值和取值

  • 获取成员属性:

    • 批量获取:

      • public Field[] getFields():获取所有公有成员属性;

      • public Field[] getDeclaredFields():获取所有的成员属性。包括公有、受保护、默认、私有

    • 单个获取:

      • public Field getField(String fieldName):获取某个公有的成员属性

      • public Field getDeclaredField(String fieldName):获取某个成员属性,可以是公有、受保护、默认、私有。

为属性赋值:注意:一定要先创建对象

Field --> set(Object targetObj,Object value):为targetObj对象中的Field属性赋值为value

Field --> get(Object targetObj):获取targetObj中的Field属性的值。

注意:如果是私有属性,要先设置暴力访问:Field --> setAccessible(true)


反射_实现_获取成员方法并调用

  • 批量获取:

    • public Method[] getMethods():获取所有公有成员方法;包括继承的方法

    • public Method[] getDeclaredMethods():获取所有成员方法,包括公有、受保护、默认、私有

  • 单个获取:

    • public Method getMethod(String methodName,Class ... parameterTypes):获取某个公有的成员方法

    • public Method getDeclaredMethod(String methodName,Class ... ps):获取某个成员方法,包括公有、受保护、默认、私有

  • 调用方法:

Method --> Object invoke(Object targetObj,Class ... parameterTypes):

调用这个Method的invoke()方法,就是去调用Method所封装的那个方法。

调用invoke()的返回值,就是调用Method所封装方法的返回值;

注意:访问私有方法,要设置暴力访问:Method --> setAccessible(true)