小王Java学习打卡day16——反射

本文详细介绍了Java中的反射机制,包括类的加载过程、Class类与实例、构造器、方法和字段的反射操作,以及类加载器加载Properties文件的方法。通过反射,可以在运行时动态创建对象、调用方法和访问字段,这对于框架开发和动态编程至关重要。
摘要由CSDN通过智能技术生成

反射

类的加载机制

  1. jvm 和 类

    • 运行带有java主方法的类(main)
    • 启动jvm去加载字节码
    • 程序何时结束:
      • 程序正常结束
      • 出现异常,没有捕获异常
      • System.exit()方法
      • 强制杀进程
    • **重点:**jvm进程结束,进程中的所有内存数据丢失。
  2. 加载机制

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s7CCRHd2-1653839171053)(http://i1.go2yd.com/image.php?url=0MA6WvUbsE)]

  3. 初始化操作:

    • 加载类:class文件(字节码文件)载入内存里面,创建一个字节码对象,类加载使用类加载器(ClassLoader,由jvm提供)
    • 连接:字节码加载进内存后,jvm会生成一个class对象,把类中的二进制数据提供给jre
      • 验证:验证加载的内是否有正确的结构
      • 准备:类里面有常量,需要分配内存地址,并且提供默认值
      • 解析:类里面的二进制数据的符号的引用替换成直接引用
        • 符号引用:Class文件中(CONSTANCE_CLASS_INFO, )
        • 直接引用:直接指向目标指针,相对偏移量
    • 初始化:jvm负责对类进行初始化,static
      • 如果类没有被加载,则优先加载,并建立连接
      • 直接父类没有加载,先加载父类
      • 静态代码块优先执行,

反射概述

  1. 问题:Object obj = new String(“abc”);

    • 需求:调用String中的length()方法
      • 使用强制类型转换
      • 不知道真实类型如何解决?
  2. 问题:一切皆对象(类属于什么对象?方法属于什么对象?字段属于什么对象?)

    • Class 表示所有类
    • Method 表示所有的方法
    • Filed 表示所有的字段
    • Constructor 表示所有的构造器

    Class类和Class实例

    1. Class表示所有的类

    2. Class实例:一份份的字节码(类,接口,枚举,注解)

    3. 各种类型如何表示?

      • String: Class<java.lang.String>
      • HashMap: Class<java.util.HashMap>
    4. 如何获取一个字节码Class对象?

      • //直接使用class类
        Class<String> Class1 = String.class;
        //对象形式
         Object obj = new String("abc");
        Class<?> Class2 = obj.getClass();
        //知道类的全限定类名 java.lang.String
        Class<?> Class3 = Class.forName("java.lang.String");
        
        • 重点:同一个jvm中,只存在一个类的一份字节码问价你和一份字节码对象

        • 使用最多的就是Class.forName(“类的全限定名”);

        • 基本数据类型的字节码如何表示?

          • jvm里面已经提前内置好了基本数据类型的实例

          • //基本类型的字节码
            Class<Integer> integerClass = int.class;
            Class<Long> longClass = long.class;
            
          • 包装类型里面提供了一个TYPE常量去获取基本数据类型的类实例

            public static final Class<Integer>  TYPE = (Class<Integer>) Class.getPrimitiveClass("int");
            
          • 使用字节码对象证明 int 和Integer不是同一类型

            Class<Integer> integerClass2 = int.class;
            Class<Integer> integerClass3 = Integer.class;
            System.out.println(integerClass2==integerClass3);//false
            

类的加载,超重要!!!

1,直接使用类.class

Class<String> Class1 = String.class;

2,使用对象的调用getClass();

 Object obj = new String("abc");
Class<?> Class2 = obj.getClass();

3,使用类的全限定名 Class.forName

Class<?> Class3 = Class.forName("java.lang.String");
//自己创建的类
  Class<?> aClass = Class.forName
      ("cn.xiaojun.practice.classdemo.ClassDemo");

反射操作构造器

  1. ​ Class–>Constructor 类

  2. 常用方法(获取私有的加 Declared)

    //获取单个构造器
    Constructor<?> constructor = aClass.getConstructor();
    //获取多个构造器(组)
    Constructor<?>[] constructors = aClass.getConstructors();
    //获取和访问权限无关的构造器
    Constructor<?>[] declaredConstructor = aClass.getDeclaredConstructors();
    //获取带参的构造器
    Constructor<?> constructor1 = aClass.getConstructor(String.class);
    
  3. 代码演示

    public static void main(String[] args) throws NoSuchMethodException {
        //获取字节码对象
        Class<Student> studentClass = Student.class;
        //获取公共的无参构造器
        System.out.println(studentClass.getConstructor());
        //获取私有的带参构造器
        Constructor<Student> constructor = studentClass.getDeclaredConstructor(int.class, String.class);
        //获取全部公共的构造器
        Constructor<?>[] constructors = studentClass.getConstructors();
        for (Constructor<?> constructor1 : constructors) {
            System.out.println(constructor1);
        }
        //获取所有的
        Constructor<?>[] declaredConstructors = studentClass.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor : declaredConstructors) {
            System.out.println(declaredConstructor);
        }
        System.out.println(constructor);
    }
    

通过反射获取的构造器创建对象

  1. 为什么要用反射创建对象,而不使用new创建?

    • 在框架中,提供给我们字符串,只能使用Class.forName()
  2. 如何使用反射创建对象?

    • 获取类的字节码对象Class.forName()
    • 获取构造器对象
    • 通过反射使用获取到的构造器创建对象
    //获取字节码对象
    Class<?> aClass = Class.forName("cn.xiaojun.practice.initobject.Student");
    //通过字节码对象获取构造器
    Constructor<?> constructor = aClass.getConstructor();
    //通过反射构造器创建对象
    Object o = constructor.newInstance();
    
  3. 私有构造器设置可以访问

    declaredConstructor.setAccessible(true);//通过构造器设置可以访问
    

反射操作方法

反射获取方法对象

获取方法:

//获取类字节码对象
Class<?> aClass = Class.forName("cn.xiaojun.practice.classdemo.method.getmethod.Student");
//获取public的全部方法
Method[] methods = aClass.getMethods();
//获取指定方法的第一个参数是方法名,第二个参数是方法参数类型
Method eat = aClass.getMethod("eat");
Method eat1 = aClass.getMethod("eat", String.class);

使用反射操作方法

  1. 获取类字节码对象

  2. 获取方法对象

  3. 使用反射调用方法

    • invoke(Object obj,Object…args)

      • obj表示当前方法的所属对象
      • args表示该方法的参数
      • Object返回就是方法的返回
    • 如果方法私有,需要设置可以接受访问

      //获取类的字节码
      Class<?> aClass = Class.forName("cn.xiaojun.practice.Student");
      //通过字节码对象获取方法对象  带参
      Method eat = aClass.getMethod("eat");
      //通过反射,调用方法
      Object invoke = eat.invoke(aClass.newInstance());
      //调用带参方法
      Method eat1 = aClass.getMethod("eat", String.class);
      Object invoke1 = eat1.invoke(aClass.newInstance(), "王曼七");
      

使用反射操作静态方法

  1. 静态方法不属于实例对象,属于类本身

  2. 如何执行静态方法?

    • 获取类字节码对象
    • 获取方法对象
    • 执行 invoke(null)
    //静态方法的调用
    Class<?> aClass = Class.forName("cn.xiaojun.practice.Student");
    Method method = aClass.getMethod("print", String.class);
    Object invoke2 = method.invoke(null,"王曼七");
    

    反射操作字段

    1. 获取类字节码

    2. 获取字段

      //获取公共的字段
      Class<?> aClass = Class.forName("cn.xiaojun.practice.field.Student");
      Field[] fields = aClass.getFields();
      for (Field field : fields) {
          System.out.println(field);
      }
      //所有的字段
      Field[] declaredFields = aClass.getDeclaredFields();
      //设置字段值
       //类的字节码对象
      Class<?> aClass = Class.forName("cn.xiaojun.practice.field.Student");
      //由字节码对象创建实例对象
      Object o = aClass.newInstance();
      //获取类中私有字段age
      Field age = aClass.getDeclaredField("age");
      //设置允许访问
      age.setAccessible(true);
      //设置值
      age.set(o, 33);
      Student o1 = (Student) o;
      System.out.println(o1.getAge());
      

类加载器加载 Properties

  1. FileInputStream 使用绝对路径

    //使用绝对路径加载
    Properties properties = new Properties();
    FileInputStream inputStream = new FileInputStream
        ("D:\\idea_workspace\\day17\\src\\cn\\xiaojun\\practice\\properties\\db.properties");
    properties.load(inputStream);
    System.out.println(properties);
    
  2. 使用相对路径,字节码的输出目录

    Properties properties = new Properties();
    ClassLoader Loader = Thread.currentThread().getContextClassLoader();
    InputStream resourceAsStream = Loader.getResourceAsStream
            ("cn/xiaojun/practice/properties/db.properties");
    properties.load(resourceAsStream);
    System.out.println(properties);
    
  3. 使用相对路径(相对于当前资源文件的字节码路径)

public class CurrentResourcesDemo {
    public static void main(String[] args) throws IOException {
        Properties properties = new Properties();
        Class<CurrentResourcesDemo> currentResourcesDemoClass = CurrentResourcesDemo.class;
        InputStream resourceAsStream = currentResourcesDemoClass.getResourceAsStream("db.properties");
        properties.load(resourceAsStream);
        System.out.println(properties);
    }
}

s = new Properties();
Class currentResourcesDemoClass = CurrentResourcesDemo.class;
InputStream resourceAsStream = currentResourcesDemoClass.getResourceAsStream(“db.properties”);
properties.load(resourceAsStream);
System.out.println(properties);
}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值