Java 反射机制

反射机制

动态获取类中信息,就是java反射。一般需要一个接口!
1. Java反射机制是在运行状态中,对于任意一个类(class文件),都能够知道这个类的所有属性和方法;
2. 对于任意一个对象,都能够调用它的任意一个方法和属性;
3. 这种动态获取的信息以及动态调用对象的方法的功能成为java语言的反射机制。
比如说现在有一个做好的应用程序(Tomcat),并向外提供了Interface Servlet这个接口,当Tomcat运行以后,通过web.xml配置文件找到相应的Servlet.class文件并加载(学习框架的时候,首先要知道这个框架是干嘛的,第二要知道这个框架的配置文件怎么用,其次就是框架中对象的常见用法!注意接口和配置文件(其中包含了变化的参数信息))
这里写图片描述
4. 描述字节码文件的类Class,对于类向上抽取共性就形成了这个类。Class提供了方法从字节码文件中获取.class文件中字段、方法、名称、构造函数、一般函数。反射就是依靠Class类来完成的!
这里写图片描述
5. 那么如何获取一个类的字节码文件对象呢?
要想对字节码文件进行解剖,必须要有字节码文件对象。下面展示了获取对象的几种方式:
1.Object类中的getClass()方法,要用这种方式,必须要明确具体的类并创建对象(麻烦!)
首先写一个Person的bean:

package bean;

public class Person {
    private int age;
    private String name;

    public Person(int age, String name) {
        super();
        this.age = age;
        this.name = name;
        System.out.println("PersonParam run...");
    }
    public Person() {
        super();
        System.out.println("Person run...");
    }

    public void show(){
        System.out.println(name + "...show run..." + age);
    }

    private void method(){
        System.out.println("method run");
    }

    public void paramMethod(String str,int num){
        System.out.println("paramMethod run..." + str + ":" + num);
    }

    public static void staticMethod(){
        System.out.println("staticMethod run...");
    }
} 

然后用以下代码进行测试:

package reflect;

import bean.Person;

public class ReflectDemo {

    public static void main(String[] args) {
        getClassObject_1();
    }
    public static void getClassObject_1(){
        Person p = new Person();
        Class clazz = p.getClass();

        Person p1 = new Person();
        Class clazz1 = p1.getClass();

        System.out.println(clazz==clazz1);
    }
}

然后可以看到输出结果:
这里写图片描述

2.方式二:任何数据类型都具备一个静态的属性.class来获取其对应的Class对象。这个方法相对简单,但是还是要确定类名。用到类中的静态成员
还是不够扩展

private static void getClassObject_2() {
        Class clazz = Person.class;
        Class clazz1 = Person.class;
        //类中的静态成员
        System.out.println(clazz == clazz1);

    }

3.方式三:(需要重点掌握)只要通过给定类的字符串名称就可以获取该类,更为扩展。可以用Class类中的方法完成。该方法就是forName(),这种方式只要有名称即可,更为方便 ,扩展性更强!反射以这种方法为主

public static void getClassObject_3() throws ClassNotFoundException {
        String className = "bean.Person";
        //包名一定要添加上!
        //其实这个String可以写到配置文件中
        Class clazz = Class.forName(className);
        System.out.println(clazz);

    }

下面是程序的输出结果:

这里写图片描述

获取Class中的构造函数

获取默认构造器

  1. 早期创建Person对象的方法是:Person p = new Person();,中间的过程是这样一new,就在所在ClassPath路径中找到Person的字节码文件;并创建这个字节码文件对象,再接着创建Person对象
  2. 现在使用反射的方法来创建这个对象:String name = "bean.Person"; 现在只有一个字符串名字,也想完成这个对象的创建动作(发给配置文件);Class clazz = Class.forName(name); 找寻该名称类文件,并加载进内存,并产生Class对象;Object obj = clazz.newInstance();产生该类的对象(这种方法提高了扩展性),//调用Class中空参构造器
public static void createNewObject() throws ClassNotFoundException, InstantiationException, IllegalAccessException{
        //早期创建Person对象
        //这样一new,就在所在ClassPath路径中找到Person的字节码文件
        //并创建这个字节码文件对象,再接着创建Person对象
        Person p = new Person();

        //现在只有一个字符串名字,也想完成这个对象的创建动作(发给配置文件)
        String name = "bean.Person";
        //找寻该名称类文件,并加载进内存,并产生Class对象
        Class clazz = Class.forName(name);
        //产生该类的对象(这种方法提高了扩展性)
        Object obj = clazz.newInstance();   //调用Class中空参构造器
    } 

获取有参构造器

一般被反射的类都带空参构造器
1. 获取指定名称类的对象,使用有参数的构造器 ,首先获取到指定的构造器,这个方法是getConstructor()
2. 早期的方法是Person p = new Person("张三",20);
3. 现在使用反射的方法:这个方法接收一个可变参数列表,但是都是Class类型的;方法返回一个构造器对象;通过这个对象的newInstance方法产生相应的对象。

private static void createNewObject_1() throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
//      Person p = new Person("张三",20);
        //一般被反射的类都带空参构造器

        /*
         *获取指定名称类的对象,使用有参数的构造器 
         *首先获取到指定的构造器
         *这个方法时getConstructor()
         */

        String name = "bean.Person";
        Class clazz = Class.forName(name);
        //这个方法接收一个可变参数列表,但是都是Class类型的
        Constructor constructor =  clazz.getConstructor(String.class,int.class);//方法返回一个构造器对象

        //通过这个对象的newInstance方法产生相应的对象
        Object obj = constructor.newInstance("张三",20);

用反射的方法创建了对象:
这里写图片描述

获取字段

有两个方法可以字段:Field getField(String name)方法获取公共的方法,包括父类中的;Field getDeclaredField(String name)方法只获取本类中的方法,包括私有的方法;
在字段的访问时,总是存在由于权限控制符所引起的异常,这时候就要找到对应但是方法来解决,这时候就使AccessibleObject类(这个类有三个子类,Constructor、Filed、Method),使用类方法setAccessible()来取消私有四段的访问检查!

  1. 获取字节码文件对象
  2. 调用字节码文件对象的 Field getField(String name)方法,返回字段对象。这个字段可能设置了访问权限,所以先将权限检查取消!field.setAccessible(true)方法
  3. 这个字段对象是类中的,并未与某个具体的类对象相关联!所以想要操作这个字段,就要与具体的对象关联后再操作!
  4. 关联后就可以使用相应的set、get方法对这个对象中的属性(字段)进行了操作!
    /*
     * 获取字节码文件的字段
     */
public static void getFieldDemo() throws Exception {
        //获取字节码文件对象
        Class clazz = Class.forName("bean.Person");
         //获取字段
        Field field = clazz.getDeclaredField("age");
        //对私有字段的访问取消权限检查,暴力访问!
        field.setAccessible(true);
        //创建本类的一个对象
        Object obj = clazz.newInstance();
        //对这个字段设置一个值
        field.set(obj, 20);
        //将字段和对象关联起来并操作
        Object o = field.get(obj);
        //显示这个对象中“age”字段的值
        System.out.println(o);  
    }
}

将设置后的显示:
这里写图片描述

获取方法

获取所有的公共方法:

    /*
     * 获取指定Class中的公共函数
     */
    public static void getMethodDemo() throws Exception {
        Class clazz = Class.forName("bean.Person");
        Method[] ms = clazz.getMethods();
//      System.out.println(Arrays.toString(ms));
        for (Method method : ms) {
            System.out.println(method);
        }
    }
}

将Person中的public方法都打印出来:其中包含了超类Object中的方法!
这里写图片描述

获取本类中的方法,包括私有:不包含父类Object中的方法

private static void getMethodDemo_1() throws Exception {
        /*
         * 获取本类中所有的方法,包括私有
         */
        Class clazz = Class.forName("bean.Person");
        Method[] method = clazz.getDeclaredMethods();
        for (Method method2 : method) {
            System.out.println(method2);
        }
    }

这里写图片描述

调用方法:
直接操作类中的字段来初始化对象(作为内容巩固!)

//方法1:通过设置字段来初始化对象
public static void getMethodDemo_2() throws Exception {
        Class clazz = Class.forName("bean.Person");
        //获取空参数的一般方法
        Method method = clazz.getMethod("show", null);
        //获取两个字段
        Field field1 = clazz.getDeclaredField("name");
        Field field2 = clazz.getDeclaredField("age");
        field1.setAccessible(true);
        field2.setAccessible(true);
        //创建一个对象与两个字段关联
        Object obj = clazz.newInstance();
        //设置字段的值
        field1.set(obj, "wangyue");
        field2.set(obj, 5);
        //调用无参的show()方法!
        method.invoke(obj, null);
    }

这里写图片描述

使用构造器来创建对象(方法回顾):

public static void getMethodDemo_3() throws Exception {
        Class clazz = Class.forName("bean.Person");
        //获取指定构造器
        Constructor constructor = clazz.getConstructor(String.class,int.class);
        //创建一个实例
        Object obj = constructor.newInstance("wangyue",5);
        //获取相应的方法
        Method method = clazz.getMethod("show", null);
        //将方法和实例相关联,并调用
        method.invoke(obj, null);
    }

这里写图片描述

调用有参方法:

private static void getMethodDemo_4() throws Exception {
        Class clazz = Class.forName("bean.Person");
        //获取指定方法
        Method method = clazz.getMethod("paramMethod", String.class,int.class);
        //创建实例
        Constructor constructor = clazz.getConstructor(String.class,int.class);
        Object obj = constructor.newInstance("张三",28);
        //关联实例并调用相关方法
        method.invoke(obj, "李四",5); 
    }

这里写图片描述

本人是菜鸟一枚,当做学习笔记写博客。谢谢各路大咖驻足审阅

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值