美文网首页
Java反射总结

Java反射总结

作者: ObadiObada | 来源:发表于2017-12-25 14:40 被阅读0次

    Java反射总结

    概述

    通常在Java代码中使用一个类,需要在编译时知道类的位置。但是在某些时候,需要使用的类在编译时是未知的,相关信息需要在运行时确定。此时就可以通过反射获得使用只有在运行时才能确定名称的类,完成创建类的对象,读写/成员,调用函数等操作。本文通过一系列的例子讲述反射常用的方法的使用,用于测试的类如下:

    
    package com.minghui.model;
    
    class Engineer extends Employee implements Comparable<Engineer> {
    
        private String mType;
        private int mTypeCode;
        String mSkill;
    
        private Engineer(String name, String tittle) {
            this(name, tittle, 0);
    
        }
    
        protected Engineer(String name) {
            this(name, "Junior", 0);
        }
    
        public Engineer(String name, String tittle, int typeCode) {
            super(name, tittle);
            setType(typeCode);
            setSkill(typeCode);
            mTypeCode = typeCode;
        }
    
        private String setType(int typeCode) {
            mType = (typeCode == 0) ? "Software Engineer" : " Test Engineer";
            return mType;
        }
    
        void setSkill(int typeCode) {
            mSkill = (typeCode == 0) ? "Code" : " Test";
        }
    
        public String getType() {
            return mType;
        }
    
        public int compareTo(Engineer otherEngineer) {
            return mTypeCode - otherEngineer.mTypeCode;
        }
    
        @Override
        public String toString() {
            return "Name:" + mName + " Tittle:" + mTittle + " Type: " + mType + " Skill: " + mSkill;
        }
    
        public static String getSkillByCode(int typeCode) {
            return (typeCode == 0) ? "Code" : " Test";
        }
    }
    

    它的父类如下:

    package com.minghui.model;
    
    public class Employee {
        public static String descripiton = "EMPLOYEE";
    
        protected String mName;
        protected String mTittle;
    
        public Employee(String name, String tittle) {
            mName = name;
            mTittle = tittle;
        }
    
        public String getName() {
            return mName;
        }
    
        String getSalry() {
            return "Confidential";
        }
    
        public String getTittle() {
            return mTittle;
        }
    }
    

    获取类对象以及父类

    可以通过Class.forName方法获得类对象,相关代码如下:

    try {
        Class<?> engineerClass = Class.forName("com.minghui.model.Engineer");
        Class<?> superClass = engineerClass.getSuperclass();
        System.out.println("engineerClass:" + engineerClass.getName() + " superClass:"
                + superClass.getName());
        Class<?>[] interfaces = engineerClass.getInterfaces();
        for (Class<?> cls : interfaces) {
            System.out.println("engineerClass interface:" + cls.getName());
        }
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
    

    上述代码输出如下:

    engineerClass:com.minghui.model.Engineer superClass:com.minghui.model.Employee

    engineerClass interface:java.lang.Comparable

    获得到类的对象后,即可通过该对象完成创建对象,读写成员等各类操作。

    创建对象

    创建对象首选需要获得构造函数,Class类中有下面几个常用的获得构造函数的方法:

    • getConstructors - 获得所有公有的函数
    • getDeclaredConstructors - 获得所有的构造函数
    • getConstructor - 获得指定签名的构造函数,其参数列表为构造函数参数的类的列表
      注意getConstructors 和 getConstructor 会抛出
    package com.minghui.generic;
    
    Constructor<?>[] engineerConstructors = engineerClass.getConstructors();
    for (Constructor<?> con : engineerConstructors) {
        System.out.println("engineerConstructor con:" + con.toGenericString());
    }
    
    Constructor<?>[] engineerDeclareConstructors = engineerClass.getDeclaredConstructors();
    for (Constructor<?> con : engineerDeclareConstructors) {
        System.out.println("engineerDeclareConstructors con:" + con.toGenericString());
    }
    
    Constructor<?> constructor = engineerClass.getConstructor(String.class, String.class,
            int.class);
    System.out.println("engineerClass  constructor :" + constructor.toGenericString());
    

    上述代码结果为:

    engineerConstructor con:public com.minghui.model.Engineer(java.lang.String,java.lang.String,int)

    engineerDeclareConstructors con:public com.minghui.model.Engineer(java.lang.String,java.lang.String,int)

    engineerDeclareConstructors con:protected com.minghui.model.Engineer(java.lang.String)

    engineerDeclareConstructors con:private com.minghui.model.Engineer(java.lang.String,java.lang.String)

    engineerClass constructor :public com.minghui.model.Engineer(java.lang.String,java.lang.String,int)

    获得构造函数对象后,可以通过newInstance方法创建对象,在之前代码中增加如下几行:

    constructor.setAccessible(true);
    Object employ = constructor.newInstance("Bill", "Senior", 0);
    System.out.println("employ objct is obj :" + employ);
    

    输出如下:

    employ objct is obj :Name:Bill Tittle:Senior Type: Software Engineer Skill: Code

    注意在调用newInstance之前先调用了

    constructor.setAccessible(true)
    

    原因在于Engineer类的可见性为Package,测试类和其不再同一个包中因此无法访问,如果未增加这行代码则会抛出java.lang.IllegalAccessException.这段代码同时也说明反射的另外一个作用: 访问类的不可访问的成员(private,package,protected).

    读/写成员

    上一节的例子中我们通过反射获创建了Engineer对象。这一节介绍通过反射读写类的对象的成员变量的方法,参考如下代码:

    Field nameField = superClass.getDeclaredField("mName");
    nameField.setAccessible(true);
    Field tittleField = superClass.getDeclaredField("mTittle");
    tittleField.setAccessible(true);
    Field skillField = engineerClass.getDeclaredField("mSkill");
    skillField.setAccessible(true);
    String name = (String) nameField.get(employ);
    String tittle = (String) tittleField.get(employ);
    String skill = (String) skillField.get(employ);
    System.out.println("employ objct name is: " + name + " tittle is: " + tittle
            + " skill is: " + skill);
    
    nameField.set(employ, "Tony");
    tittleField.set(employ, "Derictor");
    skillField.set(employ, "Wash-Cut-Blow");
    System.out.println("employ after modifyed by reflect ois: " + employ);
    

    上述代码输出为:

    employ objct name is: Bill tittle is: Senior skill is: Code

    employ after modifyed by reflect ois: Name:Tony Tittle:Derictor Type: Software Engineer Skill: Wash-Cut-Blow

    上面的代码需要注意下面几点:

    1. mNamemTittle定义在父类中,使用父类的class对象获取
    2. mNamemTittle以及mSkill都不是可见性都不是public,在读写之前需要调用setAccessible(true)

    调用方法

    上一节介绍了通过通过反射读/写成员变量,这一节介绍通过反射调用成员方法,参考如下代码:

    Method getSkillByCodeMethod =  engineerClass.getMethod("getSkillByCode", int.class);
    getSkillByCodeMethod.setAccessible(true);
    Method setTypeMethod =  engineerClass.getDeclaredMethod("setType", int.class);
    setTypeMethod.setAccessible(true);
    Method setSkillMethod = engineerClass.getDeclaredMethod("setSkill", int.class);
    setSkillMethod.setAccessible(true);
    
    Object skillByCode = getSkillByCodeMethod.invoke(employ, 0);
    System.out.println("employ getSkillByCode skillByCode class: " + skillByCode.getClass()
            + " value: " + skillByCode);
    
    Object setTypeRetValue = setTypeMethod.invoke(employ, 0);
    System.out.println("employ setType setTypeRetValue class: " + setTypeRetValue.getClass()
            + " value: " + setTypeRetValue);
    System.out.println("employ after setType employ : " + employ);
    
    Object setSkillRetValue = setSkillMethod.invoke(employ, 0);
    System.out.println("employ setSkill setSkillRetValue class : " + setSkillRetValue);
    System.out.println("employ after setSkill employ : " + employ);
    

    输出结果:

    employ getSkillByCode skillByCode class: class java.lang.String value: Code

    employ setType setTypeRetValue class: class java.lang.String value: Software Engineer

    employ after setType employ : Name:Tony Tittle:Derictor Type: Software Engineer Skill: Wash-Cut-Blow

    employ setSkill setSkillRetValue class : null

    employ after setSkill employ : Name:Tony Tittle:Derictor Type: Software Engineer Skill: Code

    上面的代码需要注意的几个点:

    1. getSkillByCodeMethod 方法是public类型需要可以通过getMethod获得,另外2个需要通过getDeclaredMethod获得
    2. 由于Engineer类可见性为package,因此三个方法都需要调用setAccessible(true)后才可以调用invoke

    读取修饰符

    通过反射获得的Class,Field,Method对象都包含一个getModifiers的方法,这个方法返回一个整形,代表了相关对象的修饰符。可以通过Modifier.toString(int)方法将整形的修饰符变成字符串,下面是例子:

    int getSkillByCodeModifier = getSkillByCodeMethod.getModifiers();
    int setTypeModifier = setTypeMethod.getModifiers();
    int setSkillModifier = setSkillMethod.getModifiers();
    
    System.out.println("employ getSkillByCodeModifier is : " + getSkillByCodeModifier + " "
            + Modifier.toString(getSkillByCodeModifier));
    System.out.println("employ setTypeModifier is : " + setTypeModifier
            + Modifier.toString(setTypeModifier));
    System.out.println("employ setSkillModifier is : " + setSkillModifier
            + Modifier.toString(setTypeModifier));
    

    输出结果如下:

    employ getSkillByCodeModifier is : 9 public static

    employ setTypeModifier is : 2 private

    employ setSkillModifier is : 0

    getModifiers返回的整形,不同的位代表了不同的修饰符,定义在Modifier.java中,相关代码如下:

    public static final int PUBLIC           = 0x00000001;
    public static final int PRIVATE          = 0x00000002;
    public static final int PROTECTED        = 0x00000004;
    public static final int STATIC           = 0x00000008;
    public static final int FINAL            = 0x00000010;
    public static final int SYNCHRONIZED     = 0x00000020;
    public static final int VOLATILE         = 0x00000040;
    public static final int TRANSIENT        = 0x00000080;
    public static final int NATIVE           = 0x00000100;
    public static final int INTERFACE        = 0x00000200;
    public static final int ABSTRACT         = 0x00000400;
    public static final int STRICT           = 0x00000800;
    

    总结

    Java反射的机制可以用于在运行时获得 classfieldconstuctormethod 对象。反射式Java中一个重要的辅助机制实际使用的并不多。使用反射回带来性能上的问题并会破坏面向对象编程中的封装性,实际的编码过程中应慎用反射。

    相关文章

      网友评论

          本文标题:Java反射总结

          本文链接:https://www.haomeiwen.com/subject/hoylgxtx.html