Java反射机制

Java反射机制

一、 概述

​ Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

  • 创建对象的正常方式:

    引入需要的“包类”名称 ==> 通过new关键字实例化 ==> 取得实例化对象

  • 反射方式:

    实例化对象==> getClass()方法 ==>得到完整的“包类”名称

  • 简而言之 : 通常的方式是通过类来得到对象 , 而反射是通过对象得到类的过程

动态语言&静态语言

  • 动态语言:是一类在运行时可以改变其结构的语言。通俗讲就是:在运行时代码可以根据某些条件改变自身结构。

    主要动态语言:Object-C、C#、JavaScript、PHP、Python、Erlang。

  • 静态语言:运行时结构不可变的语言就是静态语言。如:Java、C、C++。

Java不是动态语言,但Java可以称之为“准动态语言”。即Java有一定的动态性,我们可以利用反射机制、字节码操作获得类似动态语言的特性。

二、Class类

2.1 概述

在Object类中定义了以下的方法,此方法将被所有子类继承:

  • public final Class getClass():该方法的返回值类型是一个Class类,此类是Java反射的源头,即:可以通过对象反射求出类的名称。

Class类的几点注意事项

  1. Class本身也是一个类。
  2. Class对象只能由系统建立对象。
  3. 一个加载的类在 JVM 中只会有一个Class实例。
  4. 一个Class对象对应的是一个加载到 JVM 中的一个.class文件。
  5. 通过Class可以完整地得到一个类中的所有被加载的结构。
  6. Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象。

2.2 获取Class类对象的三种方式

  1. 已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException

    如:Class aClass1 = Class.forName(“java.lang.String”);

  2. 已知某个类的实例,调用该实例的getClass()方法获取Class对象。

    如:Class aClass2="str".getClass();

  3. 若已知具体的类,通过类的class属性获取,该方法最为安全可靠,程序性能最高。

    如:Class aClass3=String.class;

第4种了解即可:

ClassLoader cl = this.getClass().getClassLoader();

Class aClass4 = cl.loadClass(“类的全类名”);

2.3 常用方法

获取成员变量的方法

  • public Field[] getField(String name):获取指定的公共成员变量。
  • public Field[] getFields():获取所有的成员变量(包含从父类继承过来的)。
  • public Field[] getDeclaredField(String name):获取指定的成员变量(包括私有成员变量)。
  • public Field[] getDeclaredFields():获取所有的成员变量(包含私有的,也包含从父类继承过来的)。

获取构造方法的方法

  • public Constructor<T> getConstructor(Class<?>... parameterTypes):获取单个的构造方法不包含私有构造方法。
  • public Constructor<?>[] getConstructors():获取所有的构造方法(不包含私有构造方法)。
  • public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes):获取单个的构造方法(包含私有构造方法)。
  • public Constructor<?>[] getDeclaredConstructors():获取所有的构造方法(包括私有构造方法)。

获取成员方法的方法

  • public Method getMethod(String name,Class<?>... parameterTypes):获取单个的方法 不包含私有的方法。

  • public Method[] getMethods():获取所有的公共的成员方法不包含私有的,包含从父类继承过来的过来的公共方法。

  • public Method getDeclaredMethod(String name,Class<?>... parameterTypes):获取单个方法包括私有的方法。

  • public Method[] getDeclaredMethods():获取自己的所有成员方法(包含私有的方法)。

2.4 Class对象

哪些类有Class对象?

  1. class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
  2. interface:接口
  3. []:数组
  4. enum:枚举
  5. annotation:注解@interface
  6. primitive type:基本数据类型
  7. void
Class c1 = Object.class;
Class c2 = Comparable.class;
Class c3 = String[].class;
Class c4 = int[][].class;
Class c5 = ElementType.class;
Class c6 = Override.class;
Class c7 = int.class;
Class c8 = void.class;
Class c9 = Class.class;
int[] a = new int[10];
int[] b = new int[100];
Class c10 = a.getClass();
Class c11 = b.getClass();
// 只要元素类型与维度一样,就是同一个Class
System.out.println(c10 == c11);//true

2.5 动态创建对象执行方法

实体类:

public class User{
    private int id;
    private String name;
    private int age;

    public User() {
    }

    public User(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    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;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

测试类

//动态的创建对象,通过反射
public class Demo09 {
    public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException, NoSuchFieldException {
        //获得Class对象
        Class c1 = Class.forName("org.westos.reflection.User");

        //构造一个对象
        /*User user = (User) c1.newInstance(); //本质上是调用了类的无参构造器
        System.out.println(user);*/

        //通过构造器创建对象
        /*Constructor constructor = c1.getDeclaredConstructor(int.class, String.class, int.class);
        User user2 = (User) constructor.newInstance(001, "贾伦", 24);
        System.out.println(user2);*/

        //通过反射调用普通方法
        User user3 = (User) c1.newInstance();
        //通过反射获取一个方法
        Method setName = c1.getDeclaredMethod("setName", String.class);

        //invoke:激活的意思
        //参数(对象,"方法的值")
        setName.invoke(user3, "张三");
        System.out.println(user3.getName());

        //通过反射操作属性
        User user4 = (User) c1.newInstance();
        Field name = c1.getDeclaredField("name");

        //不能直接操作私有属性,我们需要关闭权限检测
        name.setAccessible(true);
        name.set(user4,"李四");
        System.out.println(user4.getName());
    }
}

三、类的加载和类加载器(ClassLoader)

3.1 类的加载过程

​ 当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤来对该类进行初始化。如下图。

3.2 类加 载器

​ 负责将.class文件加载到内在中,并为之生成对应的Class对象。

类加载器的分类

Bootstrap ClassLoader引导类加载器:

​ 是JVM自带的类加载器,负责Java平台核心库,用来装载核心类库。该加载器无法直接获取。

Extension ClassLoader扩展类加载器:

​ 负责JRE的扩展目录中jar包的加载。在JDK中JRE的lib目录下ext目录。

Sysetm ClassLoader系统类加载器:

​ 负责在JVM启动时加载来自java命令的class文件,以及classpath环境变量所指定的jar包和类路径。

代码演示:

public class Demo07 {
    public static void main(String[] args) throws ClassNotFoundException {

        //获取系统类的加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);

        //获取系统类加载器的父类加载器-->扩展类加载器
        ClassLoader parent = systemClassLoader.getParent();
        System.out.println(parent);

        //获取扩展类加载器的父类加载器-->根加载器(C/C++),又叫引导类加载器
        ClassLoader parentParent = parent.getParent();
        System.out.println(parentParent);

        //测试当前类是哪个加载器加载的
        ClassLoader classLoader = Class.forName("org.westos.reflection.Demo07").getClassLoader();
        System.out.println(classLoader);

        //测试JDK内置的类是谁加载的
        classLoader = Class.forName("java.lang.Object").getClassLoader();
        System.out.println(classLoader);
    }
}

总结:

  1. 用户自定义的类由系统类加载器加载
  2. JDK 内置的类由根加载器加载(又叫引导类加载器

3.3 双亲委派机制

  • 简述 : 当系统的某个加载器接收到加载请求时,他会首先将加载认为委托给父类加载器,如果父类加载器能够加载当前类,则任务完成。父类不能完成加载任务,自己才回去完成。

  • 双亲委派机制存在的意义: 防止内存中出现多份同样的字节码。

  • 说明:

    • 若要运行运行自己写的类,如java.lang.String 类(与系统的String类重名),这时如果使用系统的类加载器加载,默认加载的是Java自己的String类,自己写的类不能得到加载。这时想加载自己的String类,需要自定义类加载器完成。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值