【Java】反射机制

获取类对象的方式

方式1:

在编写静态源代码阶段,使用Class.forName+类全路径
应用场景:读取配置文件 ——> 读取类的全路径 ——> 加载类,
这种方式常用在底层框架中

方式2:

在Class类阶段,直接使用 类.class
应用场景:用于把类作为参数传递

方式3:

在代码运行阶段时,
当创建了事例对象时,通过对象名.getClass方法

方式4:

使用类加载器【类加载器一共有4种】
classLoader.loadeClass()

基本数据类型和其包装类的类

基本数据类型都是装箱成其包装类,在需要成为基本数据类型的时候再拆箱为基本数据类型

源代码事例:


public static void main(String[] args) throws ClassNotFoundException {
    //1、Class.forName
    //一般是在读取配置文件获取全路径
    String classAllPath = "classLoader.User";
    Class<?> userClass1 = Class.forName(classAllPath);
    System.out.println(userClass1);
    //获取类名:out:classLoader.User
    System.out.println(userClass1.getName());
    //获取类类型:out:class java.lang.Class
    System.out.println(userClass1.getClass());


    //2、类名.class,用于参数传递
    Class<User> userClass2 = User.class;
    System.out.println(userClass2);

    //3、对象.getClass,在已有对象事例
    User user = new User();
    Class<? extends User> userClass3 = user.getClass();
    System.out.println(userClass3);

    //4、通过类加载器获取类的Class对象,类加载器一共有四种
    ClassLoader classLoader = ClassLoader.getSystemClassLoader();
    Class<?> userClass4 = classLoader.loadClass("classLoader.User");
    System.out.println(userClass4);

    //因为同个类,其实只会存储一个类对象在堆中,这样其实上面我们加载了那么多次,其实都是同一个Class对象。
    System.out.println(userClass1.hashCode());
    System.out.println(userClass2.hashCode());
    System.out.println(userClass3.hashCode());
    System.out.println(userClass4.hashCode());


    //5、基本数据类型获取Class对象
    Class<Integer> integerClass = int.class;
    Class<Character> characterClass = char.class;
    Class<Boolean> booleanClass = boolean.class;
    System.out.println(integerClass);

    //6、利用基本数据类型的包装类.TYPE
    Class<Integer> integerClass1 = Integer.class;
    System.out.println(integerClass1);
    Class<Integer> type = Integer.TYPE;
    System.out.println(type);
    //int 是 通过装箱成Integer再 拆箱出来。
}

具有Class对象的类型

具有Class对象的类型有:

  1. 外部类
  2. 内部类
    • 成员内部类
    • 静态内部类
    • 局部内部类
    • 匿名内部类
  3. 数组
  4. 枚举类 enum
  5. 注解
  6. 基本数据类型
  7. void
//1.外部类
Class<User> userClass = User.class;
//接口
Class<Serializable> serializableClass = Serializable.class;
//2.数组
Class<String[]> aClass = String[].class;
//二维数组
Class<int[][]> iaClass = int[][].class;
//3.注解
Class<Deprecated> deprecatedClass = Deprecated.class;
//4.枚举
Class<Thread.State> stateClass = Thread.State.class;
//5.基本数据类型
Class<Integer> integerClass = int.class;
Class<Integer> integerClass1 = Integer.class;
//6.void
Class<Void> voidClass = void.class;

类加载

之所以java时动态语言,反射机制时关键,而反射机制的实现就是通过动态加载。

静态加载和动态加载

  1. 静态加载:编译时加载相关类,如果没有则报错,依赖性极强

    new User();

  2. 动态加载:编译不会报错,运行时加载相关类,如果在运行时没有使用相关类则不会报错,降低了依赖性。

    Class.forName("User");
    image-20210517001621223

在类加载的3个阶段中,加载和连接都是JVM实现的。

加载过程是将字节码(class文件、jar包、网络等)转换为二进制字节流加载到内存中,并生成一个Class对象。

连接过程中的验证是验证字节流的合法性如:是否以魔数0xcafebabe开头等。

连接过程中的准备是jvm对静态变量分配内存并默认初始化值(比如说int默认初始化就是0),分配在方法区。

连接过程中的解析阶段时jvm将常量池的符号引用转为直接应用的过程,这个概念在汇编可以想象得到,符号引用只是知道了这个标签,但是具体的引用需要去知道对应内存地址才可以进行真正的调用。

初始化过程是clint()方法由编译器按照语句在源文件的出现的顺序,依次自动收集所有静态变量的赋值操作静态代码块的语句,并进行合并。

反射练习题

通过反射修改私有变量,并调用方法获取修改后的值

 public static void main(String[] args)
      throws ClassNotFoundException, IllegalAccessException, Instantiati
onException, NoSuchFieldException, NoSuchMethodException, Invocati
onTargetException {
    Class<?> aClass = Class.forName("classLoad.User");
    Method getName = aClass.getMethod("getName");
    Object o = aClass.newInstance();
     //获取非公有成员对象需要用DeclaredXXX
    Field name = aClass.getDeclaredField("name");
    System.out.println(getName.invoke(o));
     //想要操作非公用的就需要把这个设置为true,构造、方法都一样
    name.setAccessible(true);
    name.set(o,"sutpc");
    System.out.println(getName.invoke(o));
  }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值