javaSE基础学习笔记 day21 反射

反射

  1. 在反射前,如果想要操作类的属性需要:
    ① new 一个该类的对象
    ②通过对象调用属性或方法,在类的外部无法通过对象调用其内部的私有结构。

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

关于 java.lang.Class 类的理解

  1. 类的加载过程:
    程序经过 javac.exe 编译后,会生成一个或多个字节码文件(.class),接着用 java.exe 对某个字节码文件进行解释运行。相当于把某个字节码文件加载到内存中,这个过程称为类的加载。
    加载到内存中的类,就是运行时类,此类就会作为 Class 类的一个实例。
    换句话说, Class 类的一个实例,就对应着一个运行时的类。
  2. 加载到内存中的运行时类,就会缓存一段时间,在缓冲区时间内,可通过不同方式获取此运行时类。

获取 Class 类的实例的方式

  1. 通过运行时的类:类名.class
  2. 通过类的对象:对象名.getClass()
  3. 调用 Class 类的 static 方法:Class.forName(String classPath)(参数为类的全类名)

Class类的对象可以是那些结构?

  1. class: 外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
  2. interface:接口
  3. []:数组
  4. enum:枚举
  5. annotation:注解@interface
  6. primitive type:基本数据类型
  7. void

注意:只要数组的元素类型和维度一样,就是一个 Class 类的实例。

了解 ClassLoader

简介:类加载器作用是用来把类(class)装载进内存的。JVM 规范定义了如下类型的类的加载器。

  1. 引导类加载器:用C++编写的,是JVM自带的类加载器,负责Java平台核心库,用来装载核心类库。该加载器无法直接获取。
  2. 扩展类加载器:负责jre/lib/ext目录下的jar包或 –D java.ext.dirs 指定目录下的jar包装入工作库
  3. 系统类加载器:负责java –classpath 或 –D java.class.path所指的目录下的类与jar包装入工作库,是最常用的加载器

举例:通过 ClassLoader 加载配置文件
注意:默认路径在当前 Module 的 src 下

    public void test() {
        InputStream is = null;
        try {
            //获取系统类加载器
            ClassLoader scl = ClassLoader.getSystemClassLoader();
            //文件内容为:
            //name="Tom"
            //age="18"
            is = scl.getResourceAsStream("info.properties");

            Properties pro = new Properties();
            //加载输入流
            pro.load(is);
            System.out.println(pro.getProperty("name"));//Tom
            System.out.println(pro.getProperty("age"));//18
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if(is != null){
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

通过反射创建对应的运行时类的对象

public void test1() throws Exception {
        //1.获取运行时类
        Class cls = Person.class;
        /*
        2.调用newInstance()方法
        ① 相当于调用了类的空参构造器
        ② 要想要正常创建,必须要有空参的构造器,访问权限要足够,通常为 public 的
         */
        Person p = (Person) cls.newInstance();
    }

获取运行时类的完整结构

  1. 获取属性
    ① getFields():获取当前运行时类及其父类中声明为 public 的属性。
    ② getDeclaredFields():获取当前运行时类中所有的属性,不包含父类中的。
  2. 获取方法
    ① getMethods():获取当前运行时类及其父类中声明为 public 的方法。
    ② getDeclaredMethods():获取当前运行时类中所有的方法,不包含父类中的。
  3. 获取构造器
    ① getConstructors():获取当前运行时类中声明为 public 的构造器。
    ② getDeclaredConstructors():获取当前运行时类中所有的构造器。
  4. 获取运行时类的父类
    ① getSuperclass()
    ② getGenericSuperclass():获取带泛型的父类
    ③ 如何获取带泛型的父类的泛型:
 public void test1() throws Exception {
        Class cls = Person.class;
        Type gs = cls.getGenericSuperclass();
        ParameterizedType pt = (ParameterizedType) gs;
        Type[] ata = pt.getActualTypeArguments();
        System.out.println(ata[0].getTypeName());
    }

调用运行时类的指定结构

  1. 获取指定的属性
    ① public Field getField(String name) 返回此Class对象表示的类或接口的指定的 public 的属性。()由于类内的属性多为 private 的,故基本不使用此方法。
    ② public Field getDeclaredField(String name)返回此Class对象表示的类或接口的指定的属性。
  2. 获取指定的方法
    ① getDeclaredMethod(String name,Class…parameterTypes)方法取得一个Method对象,并设置此方法操作时所需要的参数类型。

如何调用指定的结构?

先提供一个测试用的 Person 类

public class Person extends Creature<String> {
    private String name;
    public int age;

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

    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 Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Person() {
    }

    private int sleep(int hour){
        System.out.println("睡了" + hour + "小时");
        return hour;
    }
}
    public void test1() throws Exception {
        //1.获取运行时类
        Class cls = Person.class;
        /*
        2.调用newInstance()方法
        ① 相当于调用了类的空参构造器
        ② 要想要正常创建,必须要有空参的构造器,访问权限要足够,通常为 public 的
         */
        Person p = (Person) cls.newInstance();
        p.setName("Tom");
        p.setAge(18);

        //修改指定属性,该属性为私有
        //先获取指定属性的对象
        Field name = cls.getDeclaredField("name");
        //设置该属性为可访问
        name.setAccessible(true);
        //调用set方法修改属性,参数1为要修改的对象,参数2为要修改为的值
        name.set(p, "Alice");
        System.out.println(p);//Person{name='Alice', age=18}

        //调用指定方法,该方法为私有的
        //先获取指定方法的对象
        Method sleep = cls.getDeclaredMethod("sleep", int.class);
        //设置方法为可访问
        sleep.setAccessible(true);
        //通过调用该方法对象的 invoke() 方法来调用该方法
        //如有该方法有返回值,则 invoke 的返回值与该方法返回值一样
        //如无返回值,则返回 null
        int a = (int) sleep.invoke(p, 10);//睡了10小时
        System.out.println(a);//10
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值