第18章 反射

本文深入介绍了Java反射机制,包括其允许在运行时获取类的内部信息和操作对象的能力,以及在框架中的重要角色。通过反射,程序可以在运行时动态加载类、创建对象、访问成员变量和方法。文章还详细阐述了类加载的时机,如创建对象、静态加载和动态加载,并解析了类加载过程的各个阶段,如验证、准备、解析和初始化。此外,文中给出了通过反射创建对象的实例代码,展示了如何访问私有构造器和属性。
摘要由CSDN通过智能技术生成

简单介绍:

(1)反射机制允许程序在执行期间借助于ReflectionAPI取得任何类的内部信息(比如成员变量,构造器,成员方法等等),并能操作对象的属性及方法。反射在设计模式和框架底层都会用到。

(2)加载完类之后,在堆中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象包含了类的完整结构信息。通过这个对象得到类的结构。这个对象就像是一面镜子,透过这个镜子看到类的结构,所以,形象的称之为反射

  • java反射机制可以完成:

(1)在运行时判断任意一个对象所属的类

(2)在运行时构造任意一个类的对象

(3)在运行时得到任意一个类所具有的成员变量和方法

(4)在运行时得到个任意一个对象的成员变量和方法

(5)生成动态代理

反射机制

  • 反射优点和缺点

(1)优点:可以动态的创建和使用对象(也是框架底层核心),使用灵活。如果没有反射机制,框架技术就失去底层支撑

(2)缺点:使用反射,基本是解释执行,对执行速度有影响

Class类

(1)Class类也是类,因此也继承Object类

(2)Class类对象不是new出来的,而是系统创建的。

(3)对于某个类的Class类对象,在内存中只有一份,因为类只加载一次

(4)每个类的实例都会记得自己是由哪个Class实例所生成

(5)通过Class对象可以完整地得到一个类的完整结构,通过一系列API可以实现

(6)Class对象是存放在堆中的

(7)类的字节码二进制数据是放在方法区的,有点地方称为类的元数据

类加载

反射机制 是java实现动态语言的关键,也就是通过反射实现类的动态加载

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

(2)动态加载:运行时加载需要的类,如果运行时不用该类,则不报错,降低了依赖性

  • 例:

import java.util.*;
import java.lang.reflect.*;
public class Hello{
    public static void main(String[] args) throws Exception{
       Scanner scanner = new Scanner(System.in);
       System.out.println("请输入key:");
       String key = scanner.next();
       switch(key){
        case "1":
            Dog dog = new Dog();//静态加载
            dog.cry();
            break;
        case "2":
            Class cls = Class.forName("Person");
            Object o = cls.newInstance();
            Method method = cls.getMethod("hi");
            method.invoke(o);
            break;
        default:
            System.out.println("请重新输入");
       }
    }
}
class Dog{
    public void cry(){
        System.out.println("小猫喵喵叫");
    }
}
class Person{
    public void hi(){
        System.out.println("你好啊");
    }
}
  • 类加载时机:

(1)当创建对象时(new) //静态加载

(2)当子类被加载时 // 静态加载

(3)使用类中的静态成员时 // 静态加载

(4)通过反射 // 动态加载

类加载

  • 加载阶段:JVM在该阶段的主要目的是将字节码从不同的数据源(类,jar包,或者是网络数据)转化为二进制字节流加载到内存中,并生成一个代表该类的java.lang.Class对象

  • 连接阶段--验证:

(1)目的是:为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全

(2)包括:文件格式验证、元数据验证,字节码验证和符号引用验证

(3)可以考虑使用 -Xverify:none参数来关闭大部分的类验证措施,缩短虚拟机类加载的时间

  • 连接阶段--准备

    JVM会在该阶段对静态变量,分配内存并默认初始化。这些变量所使用的内存都将在方法区中进行分配

  • 连接阶段--解析

    虚拟机将常量池内的符号引用替换为直接引用的过程

  • 初始化:

    (1)到初始化阶段,才真正开始执行类中定义的JVA程序代码,此阶段是执行<clinit>()方法的过程

    (2)<clinit>()方法是由编译器按语句在源文件中出现的顺序,依次自动收集类中的所有静态变量的赋值动作和静态代码块中的语句,并进行合作

    (3)虚拟机会保证一个类的<clinit>() 方法在多线程环境中被正确的加锁,同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的<clinit>()方法,其他线程都需要阻塞等待,直到活动线程执行<clinit>()方法完毕

通过反射创建对象

(1)调用类中的public修饰的无参构造器

(2)调用类中的指定构造器

import java.util.*;
import java.lang.reflect.*;
public class Hello{
    public static void main(String[] args) throws Exception{
        //获取类的class对象
        Class cls = Class.forName("User");
        //通过public的无参构造器创建实例
        Object o = cls.newInstance();
        System.out.println(o);
        //通过public的有参构造器创建实例
        Constructor constructor = cls.getConstructor(String.class);
        Object cst = constructor.newInstance("你可真帅");
        System.out.println("cst = "+cst);
        //通过私有的构造器创建实例
        Constructor constructor1 = cls.getDeclaredConstructor(int.class,String.class);
        constructor1.setAccessible(true);//使用爆破可以访问private构造器
        Object cst1 = constructor1.newInstance(18,"我是最帅的");
        System.out.println("cst1 = "+ cst1);

    }
}
class User{
    private int age = 10;
    private String name = "你可真好看";

    public User(){//无参构造器

    }
    public User(String name){
        this.name = name;
    }
    private User(int age,String name){
        this.age = age;
        this.name = name;
    }
    public String toString(){
        return "User age=" + age + ",name = " + name;
    }
}

爆破小练习:

import java.util.*;
import java.lang.reflect.*;
public class Hello{
    public static void main(String[] args) throws Exception{
       Class stucls = Class.forName("Student");
       Object stu = stucls.newInstance();
       System.out.println(stu.getClass());
       //使用反射得到age属性对象
       Field age = stucls.getField("age");
       age.set(stu,18);//通过反射操作属性
       System.out.println(stu);
       //获取私有属性
       Field name = stucls.getDeclaredField("name");
       //因为name是私有属性,采用爆破机制
       name.setAccessible(true);
       // name.set(stu,"roy");
       name.set(null,"roy~");//因为name是静态属性,所以可以为空
       System.out.println(stu);

    }
}
class Student{
    public int age = 10;
    private  static String name = "karry";

    public Student(){//无参构造器

    }
    // public Student(String name,int age){
    //     this.name = name;
    //     this.age = age;
    // }
    // private Student(int age,String name){
    //     this.age = age;
    //     this.name = name;
    // }
    public String toString(){
        return "User age=" + age + ",name = " + name;
    }
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值