java反射机制

什么是反射

  • Reflection (反射)是被视为动态语言的关键,反射机制允许程序在执行期
    借助于ReflectionAPI取得任何类的内部信息,并能直接操作任意对象的内
    部属性及方法。
  • 加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个
    类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可
    以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看
    到类的结构,所以,我们形象的称之为:反射。

在这里插入图片描述

Java反射机制提供的功能

  1. 在运行时判断任意一个对象所属的类
  2. 在运行时构造任意一个类的对象
  3. 在运行时判断任意-一个类所具有的成员变量和方法
  4. 在运行时获取泛型信息
  5. 在运行时调用任意-一个对象的成员变量和方法
  6. 在运行时处理注解
  7. 生成动态代理

反射的基础----字节码文件

Java源文件是不能直接运行的,.java源文件在编译之后形成字.class的字节码文件,然后使用双亲委派模式被类加载器加载之后形成字节码文件对象,才可以在JVM中运行

在这里插入图片描述
在Java中,一切皆对象,当字节码文件加载到JVM中后,会形成一个Class类对象,即:该类在JVM中变成了一个对象(注意与new T()创建的对象不同)。
字节码文件对象中包含了三部分内容:

  • 构造方法—>Constructor对象
  • 成员方法—>Method对象
  • 成员变量—>Filed对象

注意:

不是所有的对象都在堆里,Class对象(字节码文件对象)存放在方法区,
不在堆里面

反射就是通过配置文件触发类加载拿到字节码文件对象,然后通过字节码文件对象操作类中的成员

双亲委派模式是什么鬼?

双亲委派模式是在Java 1.2后引入的,其工作原理的是,如果一个类加载器收到了
类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行,
如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将
到达顶层的启动类加载器,如果父类加载器可以完成类加载任务,就成功返回,倘
若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委
派模式

双亲委派模式要求除了顶层的启动类加载器外,其余的类加载器都应当有自己的父类加载器,请注意双亲委派模式中的父子关系并非通常所说的类继承关系,而是采用组合关系来复用父类加载器的相关代码,
双亲委派模式要求除了顶层的启动类加载器外,其余的类加载器都应当有自己的父类加载器,请注意双亲委派模式中的父子关系并非通常所说的类继承关系,而是采用组合关系来复用父类加载器的相关代码

优势

  1. 避免类的重复加载,当父亲已经加载了该类时,就没有必要子ClassLoader再加载一次。
  2. 防止核心API库被随意篡改。

反射的实现

  1. 使用Class.forName(“类的全路径名”),该方法是Class中的静态方法,前提时必须知道类的全路径名
Class<?> c3=Class.forName("commenbit.refliction.Student");
  1. 使用.class方法,仅适合在编译器就已经明确要操作的Class
Class<?> c2=Student.class;
System.out.println(c2);
  1. 使用对象的getClass()方法
 Student s1=new Student("luming",20,"男");
//通过getClass获取class对象
Class<?> c1=s1.getClass();
public class Student {
    private String name;
    private int age;
    private String sex;

    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 String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

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

    public Student(String name, int age, String sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }

    public static void main(String[] args) throws ClassNotFoundException {
        Class<?> s0=int.class;
        System.out.println(s0);

        Student s1=new Student("luming",20,"男");
        System.out.println(s1);

        //通过getClass获取class对象
        Class<?> c1=s1.getClass();
        System.out.println(c1.getName());
        System.out.println(c1);


        //直接通过类名.class获取,该方法更为安全可靠,程序性能更高,实际上每个类都有一个隐含的静态实例class
        Class<?> c2=Student.class;
        System.out.println(c2);

        //通过Class的静态方法forName进行获取,路径为全路径,有包名包含包名
        Class<?> c3=Class.forName("commenbit.refliction.Student");
        System.out.println(c3);

        System.out.println(c1.equals(c2));
        System.out.println(c2.equals(c3));
        System.out.println(c3.equals(c1));

    }
}
//运行结果
//class commenbit.refliction.Student
//class commenbit.refliction.Student
//true
//true
//true

反射的使用

  • 先获取要反射类的Class对象,然后获取该对象的构造器来创建实例
    Class.forName()
    在这里插入图片描述
	private static void test1() {
        try {
            Class<?> c=Class.forName("commenbit.refliction.Student");
            // 返回该类所有公有的构造器类对象
            // 如果将Student类中所有的构造函数全部设置成非public类型的,该数组的长度为0
            Constructor[] constructors1=c.getConstructors();
            System.out.println(constructors1.length);

            // 获取类中所有的构造器对象:public和非public
            Constructor[] constructors2=c.getDeclaredConstructors();
            System.out.println(constructors2.length);

            // 获取无参的公有的构造器
            Constructor<?> stuConstruct1 = c.getConstructor();
            System.out.println(stuConstruct1);

            // 获取参数为:String,int的public构造器
            Constructor<?> stuConstruct2 = c.getConstructor(String.class, int.class);
            System.out.println(stuConstruct2);

            // 获取参数为:String,String,int的构造器对象--不管是public还是非public都可以获取到
            Constructor<?> stuConstruct3 = c.getDeclaredConstructor(String.class,
                    String.class, int.class);
            System.out.println(stuConstruct3);

        } catch (Exception e) {
            e.printStackTrace();
        }


    }

  • 创建对象实例
  1. 如果构造方法是公有的,可以直接调用Class类提供的newInstrance方法来构造对象
private static void test2() {
        try {
            Class<?> c=Class.forName("commenbit.refliction.Student");
            // 获取到Student无参的构造器对象-调用newInstance实例化对象
            Constructor<?> constructor=c.getConstructor();
            Student student= (Student) constructor.newInstance();
            System.out.println(student);

            // 获取到Student的带有参数的构造器对象-调用newInstance实例化对象
            Constructor<?> constructor1=c.getConstructor(String.class,int.class);
            Student student1= (Student) constructor1.newInstance("lm",18);
            System.out.println(student1);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    //Student{name='null', age=0, sex='null'}
	//Student{name='lm', age=18, sex='null'}

2.如果构造方法是私有的,必须使用Constructor类的setAccessible方法改变构造器权限才可以实例化对象

    private static void test3() {
        Class<?> c=null;

        try {
            c=Class.forName("commenbit.refliction.Student");
            Constructor<?> constructor=c.getDeclaredConstructor(String.class,String.class,int.class);
            constructor.setAccessible(true);// 将构造器的权限改成可在外部访问的,即public
            Student student= (Student) constructor.newInstance("lm","男",18);
            System.out.println(student);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    //Student{name='lm', age=18, sex='男'}
  • 反射属性
    在这里插入图片描述
    注意:该系列函数的返回值必须使用Filed或者Filed[]接收。
    public static void test4() {
        Class<?> classStudent = null;
        try {
            // 获取Student的类对象
            classStudent = Class.forName("commenbit.refliction.Student");
            // 获取所有的属性
            Field[] fieles1 = classStudent.getDeclaredFields();
            System.out.println(fieles1.length);
            // 获取所有公有属性
            Field[] fieles2 = classStudent.getFields();
            System.out.println(fieles2.length);
            // 获取Student的构造器对象-调用newInstance实例化对象
            Constructor<?> stuConstruct = classStudent.getDeclaredConstructor(String.class,
                    String.class, int.class);
            stuConstruct.setAccessible(true);
            Student s = (Student)stuConstruct.newInstance("Peter", "男", 18);
            // 反射私有属性name
            Field name = classStudent.getDeclaredField("name");
            name.setAccessible(true);
            name.set(s, "Peter Wang");
            System.out.println(name.get(s));
            // 反射私有属性gender
            Field gender = classStudent.getDeclaredField("sex");
            gender.setAccessible(true);
            gender.set(s, "女");
            System.out.println(gender.get(s));
            // 反射私有属性age
            Field age = classStudent.getDeclaredField("age");
            age.setAccessible(true);
            age.set(s, 22);
            System.out.println(age.get(s));
            System.out.println(s);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
	//3
	//0
	//Peter Wang
	//女
	//22
Student{name='Peter Wang', age=22, sex='女'}
  • 反射方法
    在这里插入图片描述
    注意:上述方法的返回值必须使用Method或者Method[]进行接收,使用invoke对方法进行调用
public static void test5(){
        try {
            Class<?> classStudent = Class.forName("commenbit.refliction.Student");
			// 获取所有的方法---只获取本类声明的方法
            Method[] methods1 = classStudent.getDeclaredMethods();
            System.out.println(methods1.length);
            System.out.println(methods1);
			// 获取所有方法---处理本类声明的公有方法外,基类中继承的public方法也获取到了
            Method[] methods2 = classStudent.getMethods();
            System.out.println(methods2.length);
            System.out.println(methods2);
			// 获取Student的构造器对象-调用newInstance实例化对象
            Constructor<?> stuConstruct = classStudent.getDeclaredConstructor(String.class,
                    String.class, int.class);
            stuConstruct.setAccessible(true);
            Student s = (Student)stuConstruct.newInstance("Peter", "男", 18);
			// 反射具体的方法-setName
            Method method1 = classStudent.getDeclaredMethod("setName", String.class);
            method1.setAccessible(true);
            method1.invoke(s, "Lily");
			// 反射具体的方法-getName
            Method method2 = classStudent.getDeclaredMethod("getName");
            method2.setAccessible(true);
            System.out.println(method2.invoke(s));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    //14
	//[Ljava.lang.reflect.Method;@6d6f6e28
	//19
	//[Ljava.lang.reflect.Method;@45ee12a7
	//Lily

反射的优缺点

  • 优点
  1. 对于任意一个类,可以知道该类的所有属性和方法;对于任意一个对象,都能调用它的任意一个方法
  2. 增加程序的灵活性和扩展性,降低耦合性,提高自适应能力
  3. 反射已经运用在了很多流行框架如:Struts、Hibernate、Spring 等等。
  • 缺点
  1. Java 反射效率低主要原因是:
	Method#invoke 方法会对参数做封装和解封操作
	需要检查方法可见性
	需要校验参数
	反射方法难以内联
	JIT 无法优化
  1. 反射技术绕过了源代码的技术,因而会带来维护问题。反射代码比相应的直接代码更复杂
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值