身为java开发人员的你,一定得好好看看这份关于反射的使用总结

前言

在java中我们使用类的方法时,一般的操作是new一个对象,然后使用这个对象直接调用方法。但是你知道吗?这种形式的调用存在着缺点,试想一下,如果我们有多个Java Bean类,里面的参数类型和个数都相同,它们都有set方法。使用这些Bean类时,我们都需要分别调用它们各自独有的set方法,这种方式的使用增加了我们的代码量,那么我们有什么方法可以减少这些代码量吗?答案肯定是有的,没错,正是我们这次的课题,java 放射,反射的使用除了可以减少不必要的代码量,还可以执行class类私有、保护的变量和方法。

反射的原理

我们知道,java程序的执行过程有两个阶段,它们分别为编译阶段和运行阶段。在编译阶段,jdk会将.java文件编译成.class字节码文件;在运行阶段,java虚拟机(jvm)会去调用业务逻辑对应需要的.class字节码文件,生成对应的class对象,然后调用其中的属性和方法完成业务逻辑。反射的执行是在运行阶段,它主动让jvm去加载.class文件,生成所需要的class对象,使用该class对象调用其属性和方法完成业务逻辑。因此反射可以动态的改变java程序中的属性

反射的使用

获取class对象

  • 方法1:
//使用无参构造获取class类
Class clas = null;

try {
 //获取class类
 //clas = new persion().getClass();
 clas = Class.forName("ObjectPackage.exercise02.persion");
 //生成class对象
 persion pr = (persion) clas.newInstance();
 } catch (ClassNotFoundException e) {
   e.printStackTrace();
 } catch (IllegalAccessException e) {
   e.printStackTrace();
 } catch (InstantiationException e) {
   e.printStackTrace();
 }

这个方法获取到的class对象,它调用的是public修饰的无参构造方法,因此在我们自定义类中我们必须要有public修饰的无参构造方法,如果是private修饰的无参构造方法会抛出ava.lang.IllegalAccessException:Class ObjectPackage.xx.xxr can not access a member of class ObjectPackage.xx.xx with modifiers “private”
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
at java.lang.Class.newInstance(Class.java:436)
如果没有无参构造方法则会抛出异常:
java.lang.InstantiationException:ObjectPackage.xx.xx
at java.lang.Class.newInstance(Class.java:427)

  • 方法2:
//使用带参构造获取class对象
Class clas = null;
persion pr = null;
try {
    //获取class类
    clas = Class.forName("ObjectPackage.exercise02.persion");
    //获取构造方法
    //Constructor c = clas.getConstructor(String.class,String.class); //获取到的是两个String类型的参数构造方法(public)
    //Constructor[] constructor = clas.getConstructors();  //获取的都是public修饰的构造方法
    Constructor[] constructor = clas.getDeclaredConstructors();  //获取的全部构造方法(private、protected、public修饰)
    //生成class对象
    pr = (persion) constructor[0].newInstance("小明","广东xxxxxxx","2020-09-09");
    System.out.println(pr);
} catch (ClassNotFoundException e) {
    e.printStackTrace();
} catch (IllegalAccessException e) {
    e.printStackTrace();
} catch (InstantiationException e) {
    e.printStackTrace();
} catch (InvocationTargetException e) {
    e.printStackTrace();
}

获取构造函数的四种方法:
1、getConstructor(Class… parameterTypes):根据构造函数的参数,返回一个具体的具有public属性的构造函数
2、getDeclaredConstructor(Class… parameterTypes):根据构造函数的参数,返回一个具体的构造函数(public和非public属性)
3、getConstructors():返回所有具有public属性的构造函数数组
4、getDeclaredConstructors():返回该类中所有的构造函数数组(public和非public属性)
注:在获取到的构造函数数组中,构造函数在数组中的存储位置和我们在编程自定类中的构造函数的顺序有关,编程的构造函数越往后,它存储的位置越靠前

获取class类的方法并执行

Class clas = null;
//获取class类
clas = Class.forName("ObjectPackage.exercise02.persion");
//生成class对象
pr = (persion) constructor[0].newInstance("小明","广东xxxxxxx","2020-09-09");
//获取class对象的方法
/* Method method = clas.getMethod("nation",String.class);  //获取nation方法
//执行nation方法
method.invoke(pr,"中国");*/
//Method[] methods = clas.getMethods();   //获取全部public的方法,包括Object类的方法
Method[] methods = clas.getDeclaredMethods();  //获取class对象的public、private、protected修饰的所有方法(不包括Object类的方法)
/**methods方法存储的顺序:
 * void ObjectPackage.exercise02.persion.type(java.lang.String)
 * public java.lang.String ObjectPackage.exercise02.persion.toString()
 * public void ObjectPackage.exercise02.persion.setTime(java.lang.String)
 * public void ObjectPackage.exercise02.persion.setAddress(java.lang.String)
 * public void ObjectPackage.exercise02.persion.setPersionName(java.lang.String)
 * void ObjectPackage.exercise02.persion.author(java.lang.String)
 * public void ObjectPackage.exercise02.persion.nation(java.lang.String)
 */
//执行方法
//打破封装,实际上setAccessible是启用和禁用访问安全检查的开关,并不是为true就能访问,为false就不能访问
//由于jdk的安全检查耗时较多,所以通过setAccessible(true)的方式关闭安全检查就可以达到提升反射速度的目的
//xxxx.setAccessible(true);  //没有设置这句话时private、protected修饰的类属性不能设值,如设置抛出异常
methods[0].invoke(pr,"文学类");

class类获取方法的四种方法:
getMethod(String name, Class… parameterTypes):根据方法名和参数,返回一个具体的具有public属性的方法,可以获取Object类的方法
getMethods():返回所有具有public属性的方法数组,包括Object类的方法
getDeclaredMethod(String name, Class… parameterTypes):根据方法名和参数,返回一个具体的方法(public和非public属性)
getDeclaredMethods():返回该类中的所有的方法数组(public和非public属性)
其中parameterTypes是方法参数的类型,如果是String,那么它是String.class;如果是int,那么它是int.class;如果是Integer,那么它是Integer.class

获取class类的接口和接口方法并执行方法

Class clas = null;
//获取class类
clas = Class.forName("ObjectPackage.exercise02.persion");
//获取class接口
Class[] interfaces = clas.getInterfaces();
//创建class对象
persion c = (persion) clas.newInstance();
//获取接口的方法
Method[] interfacesMethods = interfaces[0].getDeclaredMethods(); //和使用获取class的方法一样
//执行接口方法
interfacesMethods[0].invoke(c,"韩国");

获取class类的父类和父类的方法并执行方法

Class clas = null;
//获取class类
clas = Class.forName("ObjectPackage.exercise02.persion");
//获取class类的父类
Class superClass = clas.getSuperclass();
//创建class类对象
persion p = (persion) clas.newInstance();
//获取class类的父类方法
Method[] methods=superClass.getDeclaredMethods();
//执行方法
/**父类的所有方法存储顺序:
 * abstract void ObjectPackage.exercise02.book.type(java.lang.String)
 * public void ObjectPackage.exercise02.book.execute(java.lang.String)
 * private void ObjectPackage.exercise02.book.press(java.lang.String)
 * abstract void ObjectPackage.exercise02.book.author(java.lang.String)
 */
methods[1].invoke(p,"");

在(3)、(4)中不能创建接口和父类的对象,因为在java中接口和抽象类不能被实例化,但是可以通过它的继承类来调用它们public方法(接口中的所有方法和属性都是public)、abstract方法,但是不能调用它们的private方法

获取class类的属性并改变属性的值

Class clas = null;
persion pr = null;
//获取class类
clas = Class.forName("ObjectPackage.exercise02.persion");
//创建class类的对象
pr = (persion) clas.newInstance();
//获取class类的属性
Field[] field = clas.getDeclaredFields();
/**属性的存储顺序:
 * private java.lang.String ObjectPackage.exercise02.persion.persionName
 * private java.lang.String ObjectPackage.exercise02.persion.address
 * private java.lang.String ObjectPackage.exercise02.persion.time
 * private java.lang.String ObjectPackage.exercise02.persion.bookName
 * private int ObjectPackage.exercise02.persion.bookPrice
 */
//改变属性的值
//打破封装,实际上setAccessible是启用和禁用访问安全检查的开关,并不是为true就能访问,为false就不能访问
//由于jdk的安全检查耗时较多,所以通过setAccessible(true)的方式关闭安全检查就可以达到提升反射速度的目的
field[1].setAccessible(true);//没有设置这句话时private、protected修饰的类属性不能设值,如设置抛出异常
field[1].set(pr, "2020-8-20 16:58");
System.out.println(pr);

class类获取属性的四种方法:
getField(String name):根据变量名,返回一个具体的具有public属性的成员变量
getFields():返回具有public属性的成员变量的数组
getDeclaredField(String name):根据变量名,返回一个成员变量(public和非public属性)
getDeclaredFields():返回所有成员变量组成的数组(public和非public属性)

获取方法的修饰符、参数类型

Class clas = null;
persion pr = null;
//获取class类
clas = Class.forName("ObjectPackage.exercise02.persion");
//创建class类的对象
pr = (persion) clas.newInstance();
//获取class类的方法
Method[] methods = clas.getDeclaredMethods();
for (int i = 0; i < methods.length; i++) {
    // methods[i].getReturnType()获取方法的返回类型
    //methods[i].getExceptionTypes();  获取这个方法执行后抛出的异常
    //获取参数类型
    Class[] paramenterType = methods[i].getParameterTypes();
    System.out.print("方法: ");
    //获取修饰符
    int mo = methods[i].getModifiers();
    //根据修饰符整数获取相应的修饰符
    System.out.print(Modifier.toString(mo)+" ");
    //获取方法名
    System.out.print(methods[i].getName());
    System.out.print("(");
    //打印参数
    for (int j = 0; j < paramenterType.length; j++) {
        System.out.print(paramenterType[j]+" arg"+j);
        if (j

在modifier.toString(int type)中,type=1表示public;
// type=2表示private;type=3表示public和private;type=4表示protected;type=5表示public和protected;
// type=6表示protected和private
// type=7表示public、protected和private;
// type=8表示static;type=9、10、11、12、13、14、15表示1到7分别与8的组合
// type=16表示的是final,type=17到23表示的是1到7分别与16的组合;
// type=24表示的是static final;type=25到31表示的是1到7分别与24的组合
// type=32表示的是synchronized; type=33到39表示的是1到7分别与32的组合
// type=40表示的是static synchronized;type=41到47表示的是1到7分别与40的组合
// type=48表示的是final synchronized;type=49到55表示的是1到7分别与48的组合
// type=56表示的是static final synchronized;type=57到63表示的是1到7分别与56的组合
// type=64表示的是volatile(让变量每次在使用的时候,都从主存中取);type=65到71表示的是1到7分别与64的组合
// type=72表示的是static volatile;type=73到79表示的是1到7分别与72的组合
// type=80表示的是final volatile;type=81到97表示的是1到7分别于80的组合
// type=88表示的是static final volatile;type=89到95表示的是1到7分别于88的组合
// type=96表示的是volatile synchronized;type=97到103表示的是1到7分别与96的组合
// type=104表示的是static volatile synchronized;type=105到111表示的是1到7分别与104的组合
// type=112表示的是final volatile synchronized;type=113到119表示的是1到7分别与112的组合
// type=120表示的是static final volatile synchronized;type=121到127表示的是1到7分别与120的组合
// type=128表示的是transient(表示序列化时被忽略);type=255表示public protected private static final transient volatile synchronized
// type=256表示的是native(本地方法);type=511表示的是public protected private static final transient volatile synchronized native
// type=512表示的是interface…后面的关键字还有(abstract、strictfp(strictfp 的意思是FP-strict,也就是说精确浮点的意思。在Java虚拟机进行浮点运算时,如果没有指定strictfp关键字时,Java的编译器以及运 行环境在对浮点运算的表达式是采取一种近似于我行我素的行为来完成这些操作,以致于得到的结果往往无法令你满意。而一旦使用了strictfp来声明一个 类、接口或者方法时,那么所声明的范围内Java的编译器以及运行环境会完全依照浮点规范IEEE-754来执行。因此如果你想让你的浮点运算更加精确, 而且不会因为不同的硬件平台所执行的结果不一致的话,那就请用关键字strictfp。
//你可以将一个类、接口以及方法声明为strictfp,但是不允许对接口中的方法以及构造函数声明strictfp关键字))

代码例子(工厂模式)

animal.java

public interface animal {
    String eat(String food);
    String color(String color);
}

cat.java

public class cat implements animal {
    private String food = "鱼";
    private String color = "黑白色";
    /**
     * 构造方法
     */
    public cat(){}

    public cat(String food,String color){
        this.food = food;
        this.color = color;
    }

    //独有的成员方法
    private void Only(){
        System.out.println("猫可以爬树");
    }

    /**
     * 接口方法
     */
    @Override
    public String eat(String food) {
        if (food != " ") this.food = food;
        return this.food;
    }

    @Override
    public String color(String color) {
        if (color != " ") this.color = color;
        return this.color;
    }

    //重写toString方法

    @Override
    public String toString() {
        if (this.food.equals("")) this.food = "鱼";
        if (this.color.equals("")) this.color = "黑白色";
        return "[动物:猫;颜色:"+this.color+";食物:"+this.food+"]";
    }
}

dog.java

public class dog implements animal {
    private String food = "骨头";
    private String color = "白色";

    /**
     *构造方法
     */
    public dog(){}

    public dog(String food,String color){
        this.food = food;
        this.color = color;
    }

    //独有的方法
    private void Only(){
        System.out.println("狗可以根据气味寻人");
    }

    /**
     *接口的方法
     */
    @Override
    public String eat(String food) {
        if (food != "") this.food = food;
        return this.food;
    }

    @Override
    public String color(String color) {
        if (color != "") this.color = color;
        return this.color;
    }

    //重写toString方法
    @Override
    public String toString() {
        if (this.food.equals("")) this.food = "骨头";
        if (this.color.equals("")) this.color = "白色";
        return "[动物:狗;颜色:"+this.color+";食物:"+this.food+"]";
    }
}

Facyory.java

import java.lang.reflect.*;

public class Factory {
    private static int index = 0;
    /**
     *获取class对象的两中形式
     */
    public static animal getInstance(Object object,String[] modifierArray,String[] parameterType){
        index = 0; //初始化index
        Class clas = null;
        animal an= null;
        //获取class类
        clas = object.getClass();
        //创建class类的对象
        try {
            an = (animal) clas.newInstance();
            System.out.println(clas.getName()+":"); //获取类名
            //获取父类和接口
            Factory.getSuperAnInterface(clas);
            //获取变量并改变变量的值
            Factory.setFields(clas,an,modifierArray);
            //获取class类的构造方法
            Factory.getConstruction(clas);
            //获取class类的成员方法并执行
            Factory.getMethods(clas,an,parameterType);
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return an;
    }

    public static animal getInstance(String backageName,String[] modifierArray,String[] parameterType){
        index = 0; //初始化index
        Class clas = null;
        animal an= null;
        try {
         //获取class类
         clas = Class.forName(backageName);
         //创建class类的对象
         an = (animal) clas.newInstance();
            System.out.println(clas.getName()+":"); //获取类名
            //获取父类和接口
            Factory.getSuperAnInterface(clas);
            //获取变量并改变变量的值
            Factory.setFields(clas,an,modifierArray);
            //获取class类的构造方法
            Factory.getConstruction(clas);
            //获取class类的成员方法并执行
            Factory.getMethods(clas,an,parameterType);
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return an;
    }

    /**
     * 获取class类的构造方法
     */
    public static void getConstruction(Class clas){
        Constructor[] constructors = clas.getDeclaredConstructors();
        System.out.println("构造方法:");
        for (int i = 0; i < constructors.length; i++) {
            System.out.println(constructors[i]);
        }
    }

    /**
     *获取class类中的全部成员方法并打印
     */
    public static void getMethods(Class clas,animal an,String[] arrays){
        //获取方法(public和非public)
        Method[] methods = clas.getDeclaredMethods();
        System.out.println("成员方法:");
        for (int i = 0; i < methods.length; i++) {
            //获取方法的参数
            Class[] parameterTypes = methods[i].getParameterTypes();
            //获取方法的修饰符的数字
            int mod = methods[i].getModifiers();
            //打印
            System.out.print(Modifier.toString(mod)+" ");  //根据数字打印修饰符
            System.out.print(methods[i].getName()+"(");  //打印方法名
            for (int j = 0; j < parameterTypes.length; j++) {
                System.out.print(parameterTypes[j]+" arg"+j); //打印参数
                if (j < parameterTypes.length-1)
                System.out.print(",");
            }
            System.out.println("){}");  //打印结束符
            //修饰符判断
            if (Modifier.toString(mod).equals("private")){
                //打破封装
                methods[i].setAccessible(true);
            }
            /**
             * public toString(){}
             * public color(class java.lang.String arg0){}
             * public eat(class java.lang.String arg0){}
             * private Only(){}
             */
            //执行方法
            try{
                if (parameterTypes.length > 0){  //判断方法是否有参数
                    methods[i].invoke(an,arrays[index]);  //执行带有参数的方法
                    index++;
                }else {
                    methods[i].invoke(an);  //执行没有参数的方法
                }
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     *获取class类的接口和父类
     */
    public static void getSuperAnInterface(Class clas){
        //获取父类和接口
        System.out.print("父类:"+clas.getSuperclass()+"\t接口:");
        Class[] interfaces = clas.getInterfaces();  //获取接口
        for (int i = 0; i < interfaces.length; i++) {
            System.out.print(interfaces[i]+"\t");
        }
        System.out.println();
    }

    /**
     *获取class类的属性并改变属性的值
     */
    public static void setFields(Class clas,animal an,String[] arg){
        /**
         * private java.lang.String ObjectPackage.exercise03.cat.food
         * private java.lang.String ObjectPackage.exercise03.cat.color
         */
        Field[] fields = clas.getDeclaredFields();  //获取属性(public和非public)
        for (int i = 0; i < fields.length; i++) {
            //判断属性的修饰符
            if (Modifier.toString(fields[i].getModifiers()).equals("private")){
                //打破封装,关闭检查装置
                fields[i].setAccessible(true);
            }
            try {
                //改变属性值
                fields[i].set(an,arg[i]);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        String[] catModiferArray = new String[]{"煎鱼","黑色"};  //变量值
        String[] catParameter = new String[]{"蒸鱼","亮黑色"};  //参数
        String[] dogModiferArray = new String[]{"鸡骨头","黑白色"};  //变量值
        String[] dogParameter = new String[]{"猪骨头","亮白色"};  //参数
        System.out.println(Factory.getInstance(new cat(),catModiferArray,catParameter));
        System.out.println("\n");
        System.out.println(Factory.getInstance("ObjectPackage.exercise03.dog",dogModiferArray,dogParameter));
    }
}

执行结果:


其中Class.forName()参数的参数是类的具体路径,即包名+类名

最后

感谢你看到这里,看完有什么的不懂的可以在评论区问我,觉得文章对你有帮助的话记得给我点个赞,每天都会分享java相关技术文章或行业资讯,欢迎大家关注和转发文章!

你可能感兴趣的:(身为java开发人员的你,一定得好好看看这份关于反射的使用总结)