Java 反射详解和使用

  • Post author:
  • Post category:java



目录


1.反射的概述


2.Java反射常用API


3.反射的应用


4.反射的优缺点


5.代码实现


总结



1.反射的概述

反射(Reflection)机制是Java语言特性之一,是Java被视为动态(或准动态)语言的一个关键特性;

JAVA反射机制是在【运行状态】中,对于任意一个类,都能够知道这个类的所有属性和方法;

对于任意一个对象,都能够调用它的任意一个方法和属性;

这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

反射的本质理解,就是获取该类的class对象(类类型 class type)后,反向获取该类对象的各种信息

反射就是把java类中的各种成分映射成一个个的Java对象

例如:一个类有:成员变量、方法、构造方法、包等等信息,利用反射技术可以对一个类进行解剖,把个个组成部分映射成一个个对象。


2.Java反射常用API

使用反射技术,常用的类如下。

(1)  java.lang.Class<T>类:反射的核心类,反射所有的操作都是围绕该类来生成的。通过Class类可以获取类的属性,方法等内容信息

(2)  java.lang.reflect.Constructor<T>类:表示类的构造方法

(3)  java.lang.reflect.FieId类:表示类的属性,可以获取和设置类中属性的值

(4)  java.lang.reflect.Method类:表示类的方法,可以用来获取类中方法的信息或执行方法


3.反射的应用

在Java中使用反射的基本步骤如下

(1)  导入java.lang.reflect包中的相关类

(2)  获得需要操作的类的Class实例

(3)  调用Class的实例的方法获取Field,Method等实例

(4)  使用反射API操作实例成员

获取Class实例

Java程序中获得Class实例通常有如下3种方式,可根据实际情况灵活选择。

1.调用类或接口实例的getClass()方法

Class clz = obj.getClass(); // obj为某个类型的实例

2.调用类或接口的class属性

Class clz = Student.Class; // Student 为自定义的学生类型

3.使用Class.forName()方法

Class clz = Class.forName(“com.mysql.cj.jdbc.Driver”);

从Class实例获取信息

在获取到某个类型对应的Class实例之后,就可以调用Class实例的方法来获取信息。

相关方法如下:


4.反射的优缺点



优点:反射提高了程序的灵活性和扩展性,降低耦合性,提高自适应能力。它允许程序创建和控制任何类的对象,无需提前硬编码目标类

缺点:(1) 性能问题。

Java反射机制中包含了一些动态类型,所以Java虚拟机不能够对这些动态代码进行优化。因此,反射操作的效率要比正常操作效率低很多。我们应该避免在对性能要求很高的程序或经常被执行的代码中使用反射。而且,如何使用反射决定了性能的高低。如果它作为程序中较少运行的部分,性能将不会成为一个问题。

(2) 安全限制。

使用反射通常需要程序的运行没有安全方面的限制。如果一个程序对安全性提出要求,则最好不要使用反射。

(3) 程序健壮性。

反射允许代码执行一些通常不被允许的操作,所以使用反射有可能会导致意想不到的后果。反射代码破坏了Java程序结构的抽象性,所以当程序运行的平台发生变化的时候,由于抽象的逻辑结构不能被识别,代码产生的效果与之前会产生差异。


5.代码实现

下面是一个基本实体类Person,该类继承了空类BaseClass,实现了java.io.Serializable类,方便进行测试

package bdqn;

import java.io.IOException;
import java.util.Properties;

public final  class Person extends BaseClass implements java.io.Serializable{
    //成员变量
    private String name;
    static final int age=30;
    protected  String address;
    public String message;

    //成员方法
    public String getName(){return name;}
    public void setName(String name){this.name=name;}
    static final int getAge(){return age;}
    protected String getAddress(){return address;}
    private void silentMethod() throws IOException,NullPointerException{
        System.out.println("这是悄悄话");
    }

    //以下声明用于通过反射获取构造方法学习测试
    public Person(){}
    private Person(String name){this.name=name;}

    protected Person(String name, String address, String message) {
        this.name = name;
        this.address = address;
        this.message = message;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", address='" + address + '\'' +
                ", message='" + message + '\'' +
                '}';
    }
}


通过反射获取Person类的基本信息

    public static void main(String[] args) {
        Class clz=Class.forName("bdqn.Person");
        String fullName=clz.getName();
        String simpleName= clz.getSimpleName();
        System.out.println("以下是 "+fullName+"类的基本信息");
        System.out.println("---------------------------");
        //获取Person类所在的包
        Package pkg=clz.getPackage();
        System.out.println(simpleName+" 定义在: "+pkg.getName()+"包中");
        System.out.println("---------------------------");
        //获得clz所表示的实体(类,接口,基本类型或void)的超类的Class
        //如果clz表示object类,一个接口,一个基本类型或void,则返回null
        //如果clz表示一个数组类,则返回表示object类的class实例
        Class superClass=clz.getSuperclass();
        System.out.println(simpleName+" 类的超类是: "+superClass.getName());
        System.out.println("---------------------------");
        //获得clz所表示的类实现的接口
        //如果clz表示一个不实现任何接口的类或接口,则此方法返回一个长度为0的数组
        //如果clz表示一个基本类型或void,则此方法返回一个长度为0的数组
        Class[] interfaces=clz.getInterfaces();
        System.out.print(simpleName+" 类所实现的接口: ");
        for (Class c:interfaces) {
            System.out.println("\t" + c.getName());
        }
        System.out.println("---------------------------");

        //每个修饰符对应一个int常量,返回的修饰符信息将类所拥有的修饰符以”或“运算组合
        //通过与Modifier类中的常量进行”与“运算即可判断该类型是否拥有某个修饰符
        int modifier = clz.getModifiers();
        System.out.println(simpleName+" 类的修饰符:");
        if((modifier & Modifier.PUBLIC) == Modifier.PUBLIC){
            System.out.println("访问修饰符是:public");
        }else{
            System.out.println("访问修饰符是:default(package)");
        }
        if((modifier & Modifier.FINAL) == Modifier.FINAL){
            System.out.println("这个类是:final的");
        }
        if((modifier & Modifier.ABSTRACT) == Modifier.ABSTRACT){
            System.out.println("这个类是抽象类");
        }
        if((modifier & Modifier.INTERFACE) == Modifier.INTERFACE){
            System.out.println("这是一个接口");
        }
    }

输出结果:


对应类型构造方法


通过反射获取Person类声明的构造方法信息

    public static void main(String[] args) {
        Constructor[] cons= Person.class.getDeclaredConstructors();
        //构造方法的一些信息
        System.out.println("=========构造方法展示=========");
        for(Constructor con:cons){
            System.out.print("访问修饰符:");
            int modifie=con.getModifiers();
            //判断该构造方法的访问修饰符
            if((modifie & Modifier.PUBLIC)==Modifier.PUBLIC){
                System.out.println("public");
            }else if((modifie & Modifier.PROTECTED)==Modifier.PROTECTED){
                System.out.println("protected");
            }else if((modifie & Modifier.PRIVATE)==Modifier.PRIVATE){
                System.out.println("private");
            }else {
                System.out.println("default(package)");
            }

            //获取构造方法的参数列表
            Class[] params=con.getParameterTypes();
            if(params.length==0){
                System.out.println("该构造方法没有参数");
            }else{
                System.out.print("该构造方法的参数列表为:[");
                for(int i=0;i<params.length;i++){
                    if(i!=0)
                        System.out.print(",");
                    System.out.print(params[i].getName());
                }
                System.out.println("]");
            }
            System.out.println("-------------------------------");
        }//构造方法简历
    }


输出结果:


获取对应类型所含属性的方法


通过反射获取Person类中的所有属性

    public static void main(String[] args) {Field[] fields=Person.class.getDeclaredFields();
        //展示属性的一些信息
        System.out.println("=========属性展示=========");
        for (Field field:fields){
            System.out.println("属性名:"+field.getName());
            System.out.println("类型:"+field.getType().getName());

            System.out.print("访问修饰符:");
            int modifier=field.getModifiers();
            //判断该属性的访问修饰符
            if((modifier & Modifier.PUBLIC)==Modifier.PUBLIC){
                System.out.println("public");
            }else if((modifier & Modifier.PROTECTED)==Modifier.PROTECTED){
                System.out.println("protected");
            }else if((modifier & Modifier.PRIVATE)==Modifier.PRIVATE){
                System.out.println("private");
            }else {
                System.out.println("default(package)");
            }
            //判断该属性是否有static修饰符
            if((modifier & Modifier.STATIC)==Modifier.STATIC){
                System.out.println("这是一个静态属性");
            }
            //判断该属性是否由final修饰符
            if((modifier & Modifier.FINAL)==Modifier.FINAL){
                System.out.println("这是一个final属性");
            }
            System.out.println("-------------------");
        }//访问属性结束
    }


输出结果:


访问类包含方法的方法


通过反射获取Person类中的所有方法

public static void main(String[] args){
    Method[] methods=Person.class.getDeclaredMethods();
        //展示方法的一些信息
        System.out.println("=========方法展示=========");
        for(Method method:methods){
            System.out.println("方法名: "+method.getName());
            System.out.println("返回值类型:"+method.getReturnType().getName());
            //获取方法的参数列表
            Class[] params=method.getParameterTypes();
            if(params.length==0){
                System.out.println("该方法没有参数");
            }else{
                System.out.print("该方法的参数列表为:[");
                for (int i=0;i<params.length;i++){
                    if(i!=0){
                        System.out.println(",");
                    }
                    System.out.print(params[i].getName());
                }
                System.out.println("]");
            }
            System.out.print("访问修饰符:");
            int modifier=method.getModifiers();
            //判断该方法的访问修饰符
            if((modifier & Modifier.PUBLIC)==Modifier.PUBLIC){
                System.out.println("public");
            }else if((modifier & Modifier.PROTECTED)==Modifier.PROTECTED){
                System.out.println("protected");
            }else if((modifier & Modifier.PRIVATE)==Modifier.PRIVATE){
                System.out.println("private");
            }else {
                System.out.println("default(package)");
            }
            //判断该方法是否有static修饰符
            if((modifier&Modifier.STATIC)==Modifier.STATIC)
                System.out.println("这是一个静态方法");
            //判断该方法是否有final修饰符
            if((modifier&Modifier.FINAL)==Modifier.FINAL)
                System.out.println("这是一个final方法");
            //判断该方法是否有ABSTRACT修饰符
            if((modifier&Modifier.ABSTRACT)==Modifier.ABSTRACT)
                System.out.println("这是一个抽象方法");
            //判断该方法是否有synchronized修饰符
            if((modifier&Modifier.SYNCHRONIZED)==Modifier.SYNCHRONIZED)
                System.out.println("这是一个同步方法");

            //获取方法所属的类或接口的class实例
            Class declaringClass=method.getDeclaringClass();
            System.out.println("方法声明在:"+declaringClass.getName()+"中");

            //获取方法抛出的异常处理,即throws子句中声明的异常
            Class[] exceptions=method.getExceptionTypes();
            if(exceptions.length>0){
                System.out.print("该方法抛出的异常有:[");
                for(int i=0;i<exceptions.length;i++){
                    if(i!=0)
                        System.out.print(",");
                    System.out.print(exceptions[i].getName());
                }
                System.out.println("]");
            }
            System.out.println("--------------------------------");
        }//遍历方法
}


输出结果:

=========方法展示=========
方法名: toString
返回值类型:java.lang.String
该方法没有参数
访问修饰符:public
方法声明在:bdqn.Person中
--------------------------------
方法名: getAddress
返回值类型:java.lang.String
该方法没有参数
访问修饰符:protected
方法声明在:bdqn.Person中
--------------------------------
方法名: getName
返回值类型:java.lang.String
该方法没有参数
访问修饰符:public
方法声明在:bdqn.Person中
--------------------------------
方法名: setName
返回值类型:void
该方法的参数列表为:[java.lang.String]
访问修饰符:public
方法声明在:bdqn.Person中
--------------------------------
方法名: silentMethod
返回值类型:void
该方法没有参数
访问修饰符:private
方法声明在:bdqn.Person中
该方法抛出的异常有:[java.io.IOException,java.lang.NullPointerException]
--------------------------------
方法名: getAge
返回值类型:int
该方法没有参数
访问修饰符:default(package)
这是一个静态方法
这是一个final方法
方法声明在:bdqn.Person中
--------------------------------

Process finished with exit code 0


通过反射对Person类构造方法进行调用与赋值

public static void main(String []args) throws Exception {
        Class  clz=Class.forName("bdqn.Person");
        //获取Person的无参构造:public Person()
        Constructor c1=null;
        try {
            c1= clz.getDeclaredConstructor();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
        //Person的无参构造为public,这里可以直接访问
        Object obj = c1.newInstance();
        System.out.println(obj);
        Constructor c2=null;
        //获取Person的单参构造 private Person(String)
        c2= clz.getDeclaredConstructor(String.class);
        //Person的单参构造为private,这里已超出其他访问范围,不能直接访问
        //通过setAccessable方法,设定为可以访问
        c2.setAccessible(true);
        obj=c2.newInstance("New Person");
        System.out.println(obj);

        //获取Person的三参构造:protected Person(String,String,String)
        Constructor c3=clz.getDeclaredConstructor(String.class,String.class,String.class);
        //Person的三参构造为protected,这里已超出其访问范围,不能直接访问
        //通过setAccessable方法,设定为可以访问
        c3.setAccessible(true);
        obj=c3.newInstance("张三","长沙","你好!");
        System.out.println(obj);
}


输出结果:


访问属性的方法




通过反射对Person属性进行赋值与取值

public static void main(String[] args) throws Exception{
        //通过反射加载一个Person实例
        Class clz=Person.class;
        Object person=clz.newInstance();

        //获取private String name属性
        Field name=clz.getDeclaredField("name");
        //name属性为private,这里已超出其访问范围,不能直接访问
        //通过setAccessable方法,设定为可以访问
        name.setAccessible(true);
        //先取值看一下
        System.out.println("赋值前的name:"+name.get(person));
        //为name赋值
        name.set(person,"张三");
        //展示赋值效果
        System.out.println("赋值后的name:"+name.get(person));
}


输出结果:


总结

Java反射机制是指在运行状态中,动态获取类型信息及动态访问实例成员的能力

使用反射可以在程序运行时创建类的实例及访问其属性和方法

反射在Java框架技术中有着大量的应用


如果你觉得本文对你有帮助,麻烦动动手指顶一下,可以帮助到更多的开发者,如果文中有什么错误的地方,还望指正,转载请注明转自


AnWen03的博客


,谢谢!



版权声明:本文为m0_58375976原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。