JAVA--反射机制介绍

反射的基本概念

什么是反射?JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法,这种动态获取、调用对象方法的功能称为java语言的反射机制。

在我们一般创建类对象的时候,一般是通过其类中的构造方法来创建对象,也就是通过关键字new,这种通过new关键字创建的对象叫做静态加载,在程序编译期间会对其进行检查,而不是在运行期间。而反射是动态加载,它可以在运行时创建对象,获得对象,调用对象的方法,成员变量。(简单来说new关键字在编译的时候就对类的信息确定了,而反射则是在程序运行的时候才获取类信息。)


我们在运用反射机制的时候,用到了java.lang.Class,以及java.lang.reflect中的Method、Field、Constructor等等classes。

在这里Class类是反射机制中重要的一个类,因为某个类在编译的时候,该类的所有信息被保存在.class字节码文件中(Class对象)。反射机制就是在程序运行时,根据保存在.class字节码文件的Class对象来获得类的信息,然后再创建类的对象或者调用类中的方法等。

对于java.lang包中的Class类理解,可以看上一篇Class类的介绍


反射的使用例子

这里创建了一个测试用的People类,代码如下

public class People {
    //姓名
    private String name;
    //年龄
    private Integer age;
    //性别
    private String sex;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }
    
    public People(){
        System.out.println("People类被实例化");
    }
     public People(String name, Integer age, String sex) {
        this.name = name;
        this.age = age;
        this.sex = sex;
    }
    public void publicMethod(){
        System.out.println("调用了公有方法");
    }
    private void privateMethod(){
        System.out.println("调用了私有方法");
    }
}

1.通过反射创建类对象

public class Test {
    public static void main(String[] args){
        try {
            //这里用类的全限定名来获得该类的Class对象
            Class c = Class.forName("com.example.demo.People");
            //用Class对象的newInstance()方法来创建类对象
            People people=(People)c.newInstance();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        }
    }
}

输出:
在这里插入图片描述
Class对象的newInstance()方法表示调用该类的无参构造函数来创建此 Class 对象所表示的类的一个新实例。所以People无参构造函数被调用,控制台输出内容。

2.通过反射获得类中的所有方法(Method)

public class Test {
    public static void main(String[] args){
        try {
            //这里用类的全限定名来获得该类的Class对象
            Class c = Class.forName("com.example.demo.People");
            //通过Class的getDeclaredMethods()方法获得该类的所有方法
            Method[] methods=c.getDeclaredMethods();
            for (int i=0;i<methods.length;i++){
                System.out.println("方法名称为:"+methods[i].getName());
                System.out.println("方法返回值为:"+methods[i].getReturnType());
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }
}

输出:
在这里插入图片描述
3.通过反射获得类中的所有字段(Field)

public class Test {
    public static void main(String[] args){
        try {
            //这里用类的全限定名来获得该类的Class对象
            Class c = Class.forName("com.example.demo.People");
            //通过Class的getDeclaredFields()方法获得该类的所有字段
            Field[] fields=c.getDeclaredFields();
            for (int i=0;i<fields.length;i++){
                System.out.println("成员变量名称:"+fields[i].getName());
                System.out.println("成员变量类型:"+fields[i].getType().getName());
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

    }
}

输出:
在这里插入图片描述

4.通过反射获得类中的所有构造方法(Constructor)

public class Test {
    public static void main(String[] args){
        try {
            //这里用类的全限定名来获得该类的Class对象
            Class c = Class.forName("com.example.demo.People");
            //通过Class的getDeclaredConstructors()方法获得该类的所有构造方法
            Constructor[] constructors=c.getDeclaredConstructors();
            for (int i=0;i<constructors.length;i++){
                System.out.println("第"+(i+1)+"个构造函数:"+constructors[i].getName());
                //获得构造函数的参数列表,这里获得的是参数类型列表的类类型,也就是Class对象。如int.class
                Class[] classes=constructors[i].getParameterTypes();
                for (int j=0;j<classes.length;j++){
                    System.out.println("参数类型:"+classes[j].getName());
                }
                System.out.println("===========================================");
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

}

输出:
在这里插入图片描述

更多的Class类中的方法到java.lang.Class包下查找,Method和Field还有Constructor类的更多方法到java.reflect.相应的包下查找,可以到https://tool.oschina.net/apidocs/apidoc?api=jdk-zh该网址查询


反射的作用场景

反射作用的最多的场景就是各种框架,比如在JAVA中流行的Spring框架IOC容器,我们在资源文件XML文件或者properties文件中或者使用注解在容器中配置我们的Bean信息,后面我们在程序中需要获取该Bean时,对该Bean进行依赖注入,容器会读取配置,而配置中就有该类的全限定名(如上述People类的全限定名是 com.example.demo.People),拿到这个全限定名后,spring会动态地去加载该类的Class对象,Class对象加载完成时,spring即可用该Class对象动态地创建该类的实例对象。(这里强调突出动态两个字,就是表明这都是在程序运行时所进行的,也就是用了反射进行动态加载。


在我们加载数据库驱动的时候,使用Class.forName(“com.mysql.jdbc.Driver”)来加载驱动类,因为JDBC的规范中规定,Driver类必须向DriverManager注册自己,即任何一个JDBCDriver的Driver类的代码都必须类似如下

public class Driver extends NonRegisteringDriver implements java.sql.Driver {
    public Driver() throws SQLException {
    }

    static {
        try {
            DriverManager.registerDriver(new Driver());
        } catch (SQLException var1) {
            throw new RuntimeException("Can't register driver!");
        }
    }
}

所以在Class.forName()加载完Driver类时,初始化也被完成,类的静态代码块被执行。所以不用获得Driver类的对象(即不用Class类的newInstance()方法),来注册到DriverManager中,因为在加载完成初始化时,类的静态代码块已经被执行了,Driver类也完成了向DriverManager注册。


另外个利用场景就是我们如果需要用到别人写的类,但是别人的类没有完成,那么我们想要我们的代码中运用别人的类并且通过编译的话,就需要用到反射,也就是在程序运行的时候,动态的加载该类的实例对象,这样就能通过编译了。只不过我们没拿到别人的类,在动态加载的时候会获取不到该类的Class对象,但是编译不会错误,也不会影响程序。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值