反射机制

1. 概述

反射就是把Java类中的各种成分以及Java类本身映射成相应的java类,以方便类加载器或程序员能够直接对Java类进行操作。
例如:每一个加载进内存的Java类都用一个叫Class的类对象来表示,一个类中的各个组成部分:域,构造器,方法,包等等信息也分别用特定的Java类来表示,就像汽车是一个类,汽车中的发动机,变速箱等等也是一个个的类。

反射的本质就是对各个Java类的对象化,即是对Java类进行管理的Java类。对所有的Java类进行抽取其共同点,用一个类来描述,这就是Class类。Class类显然提供了一系列的方法,用以获得其中的域,构造器,方法,修饰符,包等信息,这些信息通过面向对象的思想,毫无疑问也封装成了一个个类,即Field、Contructor、Method、Package等等类。

反射的基石就是Class类。反射能够方便地编写能够动态操纵Java代码的程序,特别是在设计或运行中添加新类时,能够快速地应用开发工具动态地查询添加类的能力。反射是一种功能强大且复杂的机制。使用它的主要人员为框架构造者,而非应用程序猿。

2. 作用


  • 在运行中分析类的能力。
  • 在运行中查看对象,例如,编写一个toString方法供所有类使用。
  • 实现通用的数组操作代码。
  • 利用Method对象,该对象类似C++中的函数指针。



Class类(java.lang)

1. 概述

所有的Java类都属于同一类事物,所以就可以用一个类来描述,Class类就是用于描述Java类的类,是反射的基石。例如:所有的人用Person类来描述,而所有的Java类就用Class类来描述。

Class类实例对应于加载进内存的Java类字节码对象,不同的Java类加载进内存的字节码是不同的,所以用以表示这些字节码的Class实例是不同的,而这些实例显然具有相同的类型,即Class。而同一个类或类型加载进内存,生成的字节码对象也是同一个,不管该类生成了多少个对象,该类对应的字节码在内存中始终是唯一的。

Class类没有构造器,只能通过特定方法或自有静态方法获取实例。

2. 获取Class类实例

有三种方法可以获得Class类实例:


  • 通过Object类中Class<?>  getClass()方法
    必须通过具体对象才能获取字节码文件对象。
  • 使用的任意数据类的一个静态成员class,所有的数据类型都具备的一个属性。
    不需要对象,但是需要具体的类,才能够通过静态方法获取字节码对象。
  • 使用Class类中static Class<?>  forName(String className)方法,通过给定类名来获取对应的字节码文件对象。
    通过类名(字符串)获取字节码文件对象。

private static void getClass_1() {
   
     Employee em = new Employee("jacob", 25, 12000);
   
     Class<? extends Employee> c = em.getClass();
   
     System.out.println(c);
   
   }
   
    
   
   private static void getClass_2() {
   
     Class<Employee> c = Employee.class;
   
     System.out.println(c);
   
   }
   
    
   
   private static Class<?> getClass_3() throws ClassNotFoundException {
   
     Class<?> c = Class.forName("bean.Employee");
   
     // 无法解析字符串,或者没有该类,则报ClassNotFoundException异常。
   
     System.out.println(c);
   
     return c;
   
   }


3. 预定义Class实例

  • 8个基本数据类型(byte,short,int,long,char,float,double,boolean)同样有Class实例与之对应。获取方法为:基本数据类型.class。例如:int.class。
  • void类型也有Class实例与之对应。获取方法为:void.class。
  • 数组本质上相当于一个实例,所以也有Class实例与之对应,获取方法即为普通类获取Class实例的三种方法。
    例如:


int[] arr = new int[3];
• 
     
     
Class clazz1 = int[].class;
• 
     
     
Class clazz2 = arr.getClass();
• 
     
     
Class clazz3 = Class.forName("[I");
• 
     
     
System.out.println(clazz1 == clazz2); // true
• 
     
     
System.out.println(clazz1 == clazz3); // true
•



4. 常用方法


  • 解析字符串以获得Class实例
    static Class<?>    forName(String className)
  • 获取类定义的公有内部类,以及从父类、父接口那里继承来的内部类
    Class<?>[]    getClasses()
  • 获取类定义的所有内部类,不包括继承来的内部类
    Class<?>[]    getDeclaredClasses()
  • 获取加载该类的类加载器
    ClassLoader    getClassLoader()
  • 获取该类的公有构造器、域和方法(包括继承而来的域和方法)
    Constructor<T>    getConstructor(Class<?>... parameterTypes)
    Constructor<?>[]    getConstructors()
    Field    getField(String name)
    Field[]    getFields()
    Method    getMethod(String name, Class<?>... parameterTypes)
    Method[]    getMethods()
  • 获取该类的构造器、域和方法(包括所有权限,但不包括继承而来的域和方法)
    Constructor<T>    getDeclaredConstructor(Class<?>... parameterTypes)
    Constructor<?>[]    getDeclaredConstructors()
    Field    getDeclaredField(String name)
    Field[]    getDeclaredFields()
    Method    getDeclaredMethod(String name, Class<?>... parameterTypes)
    Method[]    getDeclaredMethods()
  • 若该类是内部类,则获取外部类实例
    Class<?>    getDeclaringClass()
  • 获取该类或接口所实现或继承的所有接口
    Class<?>[]    getInterfaces()
  • 获取该类的修饰符、名称和包
    int    getModifiers()
    String    getName()
    Package    getPackage()
  • 载入资源文件
    URL    getResource(String name)
    InputStream    getResourceAsStream(String name)
  • 获取该类的父类
    Class<? super T>    getSuperclass()
  • 判断该类的属性
    boolean    isArray()
    boolean    isEnum()
    boolean    isInstance(Object obj)
    boolean    isInterface()
    boolean    isPrimitive()
  • 通过无参数构造器创建该类实例
    T    newInstance()
  • 返回该类的描述(1.8)
    String    toGenericString()

5. 示例

String str1 = "abc";
    
    Class cls1 = str1.getClass();
    
    Class cls2 = String.class;
    
    Class cls3 = Class.forName("java.lang.String");
    
    System.out.println(cls1 == cls2); // true
    
    System.out.println(cls1 == cls3); // true
    
     
    
    boolean b1 = cls1.isPrimitive(); // false
    
    boolean b2 = int.class.isPrimitive(); // true
    
    boolean b3 = int.class == Integer.class; // false
    
    boolean b4 = int.class == Integer.TYPE; // true
    
    boolean b5 = int[].class.isPrimitive(); // false
    
    boolean b6 = int[].class.isArray(); // true
    
     
    
    String str2 = (String) cls1.newInstance(); // 使用反射通过无参数构造器创建String实例
    
    // 通常被反射的类都会有提供空参数的构造函数。 // 没有对应的构造器,则报InstantiationException异常。 // 对于构造器访问权限不够,报IllegalAccessException异常。 


Constructor类(java.lang.reflect)

1. 概述


Construction类用于描述所有类中的构造器。



 Construction类实例的获取


通过Class类的方法,可以获取对应Java类的所有构造器或指定构造器。


  • 获取该类的构造器(包括所有权限)
    Constructor<T>    getDeclaredConstructor(Class<?>... parameterTypes)
    Constructor<?>[]    getDeclaredConstructors()
  • 获取该类的公有构造器
    Constructor<T>    getConstructor(Class<?>... parameterTypes)
    Constructor<?>[]    getConstructors()


3. 常用方法


  • 获取该构造器的修饰符和名称
    int    getModifiers()
    String    getName()
  • 获取该构造器参数信息
    int    getParameterCount()
    Class<?>[]    getParameterTypes()
  • 该构造器是否带可变参数
    boolean    isVarArgs()
  • 使用特定参数通过该构造器创建该类实例
    T    newInstance(Object... initargs)
  • 获取该构造器的描述(1.8)
    String    toGenericString()



4. 示例


package reflect;
    
     
    
    import java.lang.reflect.Constructor;
    
     
    
    import bean.Person;
    
     
    
     
    
    public class Demo {
    
     
    
      public static void main(String[] args) throws Exception {
    
        // 创建字符串
    
        String str1 = new String(new StringBuffer("abc"));
    
        // 用反射实现
    
        Class<?> clazzStr = Class.forName("java.lang.String");
    
        Constructor<?> conStr = clazzStr.getConstructor(StringBuffer.class);
    
        String str2 = (String) conStr.newInstance(new StringBuffer("abc"));
    
        System.out.println(str2);
    
     
    
        // 创建自定义类实例
    
        Person p1 = new Person("jacob", 25);
    
        // 用反射实现
    
     
    
        Class<?> clazz = Class.forName("bean.Person");
    
     
    
        // 获取该类的特定构造器
    
        Constructor<?> con = clazz.getConstructor(String.class, int.class);
    
     
    
        // 根据该构造器创建对象
    
        // 没有对应的构造器或是抽象类,则报InstantiationException异常。
    
        // 对于构造器访问权限不够,报IllegalAccessException异常。
    
        // 给出的参数和构造器参数不匹配,则报IllegalArgumentException异常。
    
        // 如果构造器中抛出异常,则报InvocationTargetException异常。
    
        Person p2 = (Person) con.newInstance("jacob", 25);
    
        System.out.println(p2);
    
      }
    
    }



Field类(java.lang.reflect)

1. 概述


Field类用于描述所有Java类中的域,包括实例域和静态域。



Field类实例的获取


通过Class类的方法,可以获取对应Java类的所有域或指定域。


  • 获取该类的公有域和(包括继承而来的域)
    Field    getField(String name)
    Field[]    getFields()
  • 获取该类域(包括所有权限,但不包括继承而来的域)
    Field    getDeclaredField(String name)
    Field[]    getDeclaredFields()


3. 常用方法


  • 获取该域所属的类的Class实例
    Class<?>    getDeclaringClass()
  • 获取特定实例的该域的值
    Object    get(Object obj)
    boolean    getBoolean(Object obj)
    byte    getByte(Object obj)
    char    getChar(Object obj)
    double    getDouble(Object obj)
    float    getFloat(Object obj)
    int    getInt(Object obj)
    long    getLong(Object obj)
    short    getShort(Object obj)
  • 获取该域的修饰符和名称
    int    getModifiers()
    String    getName()
  • 获取该域的类型
    Class<?>    getType()
  • 对特定实例的该域赋值
    void    set(Object obj, Object value)
    void    setBoolean(Object obj, boolean z)
    void    setByte(Object obj, byte b)
    void    setChar(Object obj, char c)
    void    setDouble(Object obj, double d)
    void    setFloat(Object obj, float f)
    void    setInt(Object obj, int i)
    void    setLong(Object obj, long l)
    void    setShort(Object obj, short s)
  • 获取该域的描述(1.8)
    String    toGenericString()
  • (父类方法)判断及取消权限检查
    boolean    isAccessible()
    void    setAccessible(boolean flag)



4. 示例


ReflectPoint.java


package cn.itcast.day1;
    
     
    
    import java.util.Date;
    
     
    
    public class ReflectPoint {
    
      private Date birthday = new Date();
    
     
    
      private int x;
    
      public int y;
    
      public String str1 = "ball";
    
      public String str2 = "basketball";
    
      public String str3 = "itjava";
    
     
    
      public ReflectPoint(int x, int y) {
    
        super();
    
        this.x = x;
    
        this.y = y;
    
      }
    
     
    
      @Override
    
      public String toString() {
    
        return "ReflectPoint [str1=" + str1 + ", str2=" + str2 + ", str3=" + str3 + "]";
    
      }
    
     
    
      public int getX() {
    
        return x;
    
      }
    
    

    
      public void setX(int x) {
    
        this.x = x;
    
      }
    
    

    
      public int getY() {
    
        return y;
    
      }
    
    

    
      public void setY(int y) {
    
        this.y = y;
    
      }
    
    

    
      public Date getBirthday() {
    
        return birthday;
    
      }
    
     
    
      public void setBirthday(Date birthday) {
    
        this.birthday = birthday;
    
      }
    
     
    
    }


Demo.java


package reflect;
    
    import java.lang.reflect.Field;
    
    import bean.ReflectPoint;
    
    public class Demo {
    
      public static void main(String[] args) throws Exception {
    
        ReflectPoint pt = new ReflectPoint(3, 5);
    
        // 获取公有域
    
        Field fieldY = pt.getClass().getField("y");
    
        /*
    
         * 问题:fieldY的值是多少?
    
         * 答:fieldY不是对象身上的变量,而是类上,要用它去取某个对象上对应的值。
    
         */
    
        Object objY = fieldY.get(pt); // 5
    
        String objName = objY.getClass().getName(); // "java.lang.Integer"
    
        // 获取私有域
    
        // x为私有域,所以需要使用getDeclaredField方法才可以看到
    
        Field fieldX = pt.getClass().getDeclaredField("x");
    
        // x为私有域,所以需要使用setAccessible方法取消权限检查(暴力访问,不推荐)
    
        fieldX.setAccessible(true);
    
        Object objX = fieldX.get(pt); // 3
    
        // 将所有String类型实例域的值中的字母'a'替换为'x'
    
        changeStringValue(pt);
    
        System.out.println(pt);
    
      }
    
      private static void changeStringValue(Object obj) throws Exception {
    
        // 获取该对象所属类的所有公有域
    
        Field[] fields = obj.getClass().getFields();
    
        for (Field field : fields) {
    
          // 判断获取的域类型是否为String类型
    
          // if(field.getType().equals(String.class)){
    
          if (field.getType() == String.class) {
    
            // 获取实例在该域上的值,并替换
    
            String oldValue = (String) field.get(obj);
    
            String newValue = oldValue.replace('a', 'x');
    
            // 写入替换的新值
    
            // 若抛出IllegalAccessException异常,则该字段无权限访问和更改
    
            field.set(obj, newValue);
    
          }
    
        }
    
      }
    
    }



Method类(java.lang.reflect)


1. 概述



Method类用于描述所有Java类中的方法,包括成员方法和静态方法。





2. Method类实例的获取



通过Class类的方法,可以获取对应Java类的所有域或指定域。



  • 获取该类的公有方法(包括继承而来的方法)
    Method    getMethod(String name, Class<?>... parameterTypes)
    Method[]    getMethods()
  • 获取该类的方法(包括所有权限,但不包括继承而来的方法)
    Method    getDeclaredMethod(String name, Class<?>... parameterTypes)
    Method[]    getDeclaredMethods()



3. 常用方法


  • 获取该方法所属类的Class实例
    Class<?>    getDeclaringClass()
  • 获取该方法的修饰符和名称
    int    getModifiers()
    String    getName()
  • 获取该方法参数个数和类型
    int    getParameterCount()
    Class<?>[]    getParameterTypes()
  • 获取该方法返回值类型
    Class<?>    getReturnType()
  • 执行该方法
    Object    invoke(Object obj, Object... args)



4. 示例


package reflect;
    
     
    
    import java.lang.reflect.Method;
    
     
    
    public class ReflectMethod {
    
     
    
      public static void main(String[] args) throws Exception {
    
        String str = "abc";
    
        // 获取成员方法并执行
    
        Method methodCharAt = String.class.getMethod("charAt", int.class);
    
        Object obj1 = methodCharAt.invoke(str, 1); // 'b'
    
        // 由于invoke方法接收可变参数,所以第2个参数可以接受若干个,但是对于charAt方法来说,只取用数组的第一个数字
    
        Object obj2 = methodCharAt.invoke(str, new Object[] {2}); // 'c'
    
        
    
        // 获取无参数成员方法并执行
    
        Method methodIsEmpty = String.class.getMethod("isEmpty", null/*new Class[0]*/);
    
        Object obj3 = methodIsEmpty.invoke(str, null/*new Object[0]*/); // false
    
     
    
        // 获取静态方法并执行
    
        Method methodValueOf = String.class.getMethod("valueOf", int.class);
    
        Object obj4 = methodValueOf.invoke(null, 123); // "123"
    
        
    
        // 调用其他类的主函数
    
        ReflectMain.main(new String[]{"111","222","333"});
    
        String ClassName = "reflect.ReflectMain";
    
        Class<?> clazz = Class.forName(ClassName);
    
        Method mainMethod = clazz.getMethod("main", String[].class);
    
        /* 
    
         * 由于invoke方法第二个参数声明为Object... args
    
         * 所以String[]是作为1个Object对象参数传入,或者作为Object[]中的1个元素,并将该Object[]作为参数传入
    
         */
    
        mainMethod.invoke(null, (Object) new String[] {"aa", "bb", "cc"});
    
        mainMethod.invoke(null, new Object[]{new String[]{"aaa","bbb","ccc"}});
    
      }
    
    }


数组的反射

1. 概述


数组也是一种类型,和普通的Java类一样在内存中具有相应的Class实例。



2. 数组的类型提升和转换


  • 数组本身可以看成是一个Java类实例,所有的数组的父类均是Object,所以可以类型提升为1个Object实例。
  • 由于所有的Java类的父类均是Object类,所以除了基本数据类型以外,其他Java类实例数组均可以类型提升为Object数组。
  • 由于基本数据类型不可以被类型提升为Object实例,所以基本数据类型数组不可以类型提升为Object数组。

示例


int[] a1 = new int[] {1, 2, 3};
    
    int[] a2 = new int[4];
    
    int[][] a3 = new int[2][3];
    
    String[] a4 = new String[] {"a", "b", "c"};
    
    boolean b1 = a1.getClass() == a2.getClass(); // true
    
    // boolean b2 = a1.getClass() == a3.getClass(); // 编译错误:Incompatible operand types
    
    // boolean b3 = a1.getClass() == a4.getClass(); // 编译错误:Incompatible operand types
    
    String str1 = a1.getClass().getName(); // "[I"
    
    String str2 = a3.getClass().getName(); // "[[I"
    
    String str3 = a4.getClass().getName(); // "[Ljava.lang.String;"
    
    String str4 = a1.getClass().getSuperclass().getName(); // "java.lang.Object"
    
    String str5 = a3.getClass().getSuperclass().getName(); // "java.lang.Object"
    
    String str6 = a4.getClass().getSuperclass().getName(); // "java.lang.Object"
    
     
    
    Object aObj1 = a1;
    
    Object aObj2 = a3;
    
    Object aObj3 = a4; // 均将数组类型提升为1个Object实例
    
    // Object[] aObj4 = a1; // 编译错误:类型不匹配
    
    Object[] aObj5 = a3; // 二维数组即是数组的数组,类型提升后Object数组中的元素是int型一维数组
    
    Object[] aObj6 = a4; // String类型提升为Object类型,Object数组中的元素是String实例
    
     
    
    // 由于集合中必须存储Java类实例,而基本数据类型不可以类型提升为Object类实例
    
    // 所以基本数据类型数组存入是作为唯一的Object实例存入,而其他数组存入是将其元素存入集合。
    
    System.out.println(Arrays.asList(a1)); // [[I@15db9742]
    
    System.out.println(Arrays.asList(a4)); // [a, b, c]




3. Array类

3.1 概述



Array类用于描述所有的Java数组。该类是个工具类,由静态方法构成,提供了创建和访问Java数组的方法 ,且没有构造器。所以该类的使用无需实例。





3.2 常用方法



  • 创建数组实例
    static Object    newInstance(Class<?> componentType, int... dimensions)
    static Object    newInstance(Class<?> componentType, int length)
  • 获取数组中的元素
    static Object    get(Object array, int index)
    static boolean    getBoolean(Object array, int index)
    static byte    getByte(Object array, int index)
    static char    getChar(Object array, int index)
    static double    getDouble(Object array, int index)
    static float    getFloat(Object array, int index)
    static int    getInt(Object array, int index)
    static long    getLong(Object array, int index)
    static short    getShort(Object array, int index)
  • 获取数组空间长度
    static int    getLength(Object array)
  • 设置数组中元素的值
    static void    set(Object array, int index, Object value)
    static void    setBoolean(Object array, int index, boolean z)
    static void    setByte(Object array, int index, byte b)
    static void    setChar(Object array, int index, char c)
    static void    setDouble(Object array, int index, double d)
    static void    setFloat(Object array, int index, float f)
    static void    setInt(Object array, int index, int i)
    static void    setLong(Object array, int index, long l)
    static void    setShort(Object array, int index, short s)



3.3 示例


Object arr = Array.newInstance(int.class, 3);
     
     int len = Array.getLength(arr); // 3
     
     Array.setInt(arr, 0, 3);
     
     Array.setInt(arr, 1, 2);
     
     Array.setInt(arr, 2, 1);
     
     int i = Array.getInt(arr, 1); // 2



4. 案例


实现任意类型实例的字符串转换(包括数组,多维数组,集合等均能正确将其元素进行字符串转换)。


package reflect;
    
     
    
    import java.lang.reflect.Array;
    
     
    
    public class ReflectArrayString {
    
     
    
      public static void main(String[] args) {
    
        int[] arr1 = new int[] {999, 888, 777, 666};
    
        int[][] arr2 = new int[][] { {1, 11, 111}, {2, 22, 222}};
    
        String[] arr3 = {"java", "eclipse", "windows"};
    
        String oStr1 = objectToString(arr1); // "{999, 888, 777, 666}"
    
        String oStr2 = objectToString(arr2); // "{{1, 11, 111}, {2, 22, 222}}"
    
        String oStr3 = objectToString(arr3); // "{java, eclipse, windows}"
    
        String oStr4 = objectToString("xyz"); // "xyz"
    
      }
    
     
    
      private static String objectToString(Object obj) {
    
        Class<? extends Object> clazz = obj.getClass();
    
        if (clazz.isArray()) {
    
          // 如果参数为数组
    
          int len = Array.getLength(obj);
    
          StringBuilder sb = new StringBuilder();
    
          // 左括号
    
          sb.append("{");
    
          int i = 0;
    
          Object temp = null;
    
          // 遍历元素并转成字符串
    
          for (; i < len - 1; i++) {
    
            temp = Array.get(obj, i);
    
            if (temp.getClass().isArray()) {
    
              // 如果获取的数组元素仍然是个数组,则递归实现字符串转换,并添加到结果缓冲中
    
              sb.append(objectToString(temp)).append(", ");
    
            } else {
    
              // 如果元素非数组,则直接转字符串添加
    
              sb.append(temp.toString()).append(", ");
    
            }
    
          }
    
          // 最后一个元素后不需要分隔符,同理相同的处理
    
          temp = Array.get(obj, i);
    
          if (temp.getClass().isArray()) {
    
            sb.append(objectToString(temp)).append("}");
    
          } else {
    
            sb.append(temp.toString()).append("}");
    
          }
    
          // 返回最终字符串
    
          return sb.toString();
    
     
    
        } else {
    
          // 如果参数不是数组,则直接利用toString返回
    
          return obj.toString();
    
        }
    
      }
    
    }



反射的应用


1. 应用


在框架的设计中,往往是先于程序员自定义类的设计的,这时候就需要实现调用还未实现类的功能。例如:一台笔记本电脑主板设计好后,提供若干USB接口,但是设计主板时并不清楚用户在使用时使用什么USB设备,所以就无法通过一般的方式new出USB设备进行使用(因为此时此刻USB设备还不知道是什么,类的定义也没有)。这时候一般定义好USB接口的使用规则,具体什么设备将记录在配置文件中。通过反射获取之后USB具体设备的类名并调用该设备。这样通过反射,将大大提高程序的可扩展性。



2. 配置文件


  • Java提供了Properties类来处理配置文件,可以直接通过流读取和写入。
  • Class类以及类加载器中也提供了 InputStream    getResourceAsStream(String path) 方法来实现配置文件的读取,但是没有写入方法。
  • 配置文件一般需要和class文件一并交由客户使用,所以配置文件建议放在bin/Resource目录下。
  • Eclipse中,将配置文件放在src目录中时,会自动复制一份副本到bin相应目录下。

3. 配置文件目录


  • 由于工程目录在客户机器上是不存在的(或是不一定的),所以不可以使用相对工程目录的默认路径。
  • 使用InputStream需指定相对工程目录的相对路径或是绝对路径,所以不适合配置文件。
  • 使用类加载器ClassLoader提供的getResourceAsStream方法,需要指定相对CLASSPATH的相对路径,可以使用。
  • 使用Class提供的getResourceAsStream方法,需要指定相对该class对应包目录的相对路径,可以使用,且较为方便。

示例

InputStream ips = new FileInputStream("bin/reflect/resources/config.properties"); // 相对路径:工程目录
    
    InputStream ips = new FileInputStream(
    
      "E:/Documents/个人文档/My Programs/Java_itheima/bin/reflect/resources/config.properties");
    
      // 绝对路径
    
    InputStream ips = ReflectUsb.class.getClassLoader().getResourceAsStream(
    
      "reflect/resources/config.properties"); // 相对路径:CLASSPATH,推荐
    
    InputStream ips = ReflectUsb.class.getResourceAsStream(
    
      "/reflect/resources/config.properties"); // 相对路径:CLASSPATH,推荐
    
    InputStream ips = ReflectUsb.class.getResourceAsStream(
    
      "resources/config.properties"); // 相对路径:该类路径,推荐

4. 笔记本电脑实例


一台笔记本电脑有若干USB接口,设计主板时设计好USB接口,但是还没有具体的USB设备。用户在使用该电脑时购买了若干USB设备记录在配置文件中,电脑应识别USB设备并正常运行。


Usb.java


package reflect.usb;
    
     
    
    public interface Usb {
    
      public abstract void run();
    
      public abstract void close();
    
    }


config.properties


Count = 2
    
    USB1 = reflect.usb.CdRom
    
    USB2 = reflect.usb.Keyboard


CdRom.java


pa

ckage reflect.usb;
    
     
    
    public class CdRom implements Usb {
    
     
    
      @Override
    
      public void run() {
    
        System.out.println("CD-ROM run!");
    
      }
    
     
    
      @Override
    
      public void close() {
    
        System.out.println("CD-ROM close!");
    
      }
    
     
    
    } 
 
 
  Keyboard.java 
 
 
package reflect.usb;
    
     
    
    public class Keyboard implements Usb {
    
     
    
      @Override
    
      public void run() {
    
        System.out.println("Keyboard run!");
    
      }
    
     
    
      @Override
    
      public void close() {
    
        System.out.println("Keyboard close!");
    
      }
    
     
    
    } 
 
 
  ReflectUsb.java 
 
 
package reflect;
   
    
   
   import java.io.IOException;
   
   import java.io.InputStream;
   
   import java.util.ArrayList;
   
   import java.util.List;
   
   import java.util.Properties;
   
    
   
   import reflect.usb.Usb;
   
    
   
   class MainBoard {
   
     private Usb[] usbs = new Usb[10];
   
     private int count = 0;
   
     private boolean isRun = false;
   
    
   
     /**
   
      * 主板创建时,检查是否有USB设备,并添加进计算机系统
   
      * 
   
      * @throws ClassNotFoundException
   
      * @throws InstantiationException
   
      * @throws IllegalAccessException
   
      * @throws IOException
   
      */
   
     MainBoard() throws ClassNotFoundException, InstantiationException, IllegalAccessException,
   
         IOException {
   
       flushDev();
   
     }
   
    
   
     /**
   
      * 运行计算机
   
      */
   
     public void run() {
   
       System.out.println("MainBoard run!");
   
       for (int i = 0; i < count; i++) {
   
         usbs[i].run();
   
       }
   
       isRun = true;
   
     }
   
    
   
     /**
   
      * 关闭计算机
   
      */
   
     public void close() {
   
       // 先关闭USB设备,再关闭主板
   
       for (int i = 0; i < count; i++) {
   
         usbs[i].close();
   
       }
   
       System.out.println("MainBoard close!");
   
       isRun = false;
   
     }
   
    
   
     /**
   
      * 添加USB设备
   
      * 
   
      * @param dev
   
      */
   
     private boolean addUsb(Usb dev) {
   
       if (dev == null) {
   
         return false;
   
       }
   
       // 如果已有该设备,则直接返回
   
       for (Usb usb : usbs) {
   
         if (usb == dev) {
   
           return false;
   
         }
   
       }
   
       usbs[count] = dev;
   
       count++;
   
       // 如果计算机正在运行,则该设备也开始运行
   
       if (isRun) {
   
         dev.run();
   
       }
   
       return true;
   
     }
   
    
   
     /**
   
      * 移除USB设备
   
      * 
   
      * @param dev
   
      */
   
     private boolean remove(Usb dev) {
   
       if (dev == null) {
   
         return false;
   
       }
   
       for (int i = 0; i < count; i++) {
   
         if (usbs[i] == dev) {
   
           // 如果计算机正在运行,则先停止设备运行,再移除
   
           if (isRun) {
   
             dev.close();
   
           }
   
           for (int j = i; j < count - 1; j++) {
   
             usbs[j] = usbs[j + 1];
   
           }
   
           return true;
   
         }
   
       }
   
       return false;
   
     }
   
    
   
     /**
   
      * 加载配置文件中的USB设备,更新USB设备列表
   
      * 
   
      * @throws IOException
   
      * @throws ClassNotFoundException
   
      * @throws InstantiationException
   
      * @throws IllegalAccessException
   
      */
   
     public void flushDev() throws IOException, ClassNotFoundException, InstantiationException,
   
         IllegalAccessException {
   
       // 读取配置文件
   
       InputStream ips = ReflectUsb.class.getResourceAsStream("resources/config.properties");
   
       Properties config = new Properties();
   
       config.load(ips);
   
       // 读取USB设备数量
   
       int configCount = Integer.parseInt(config.getProperty("Count"));
   
       List<Usb> list = new ArrayList<Usb>();
   
       for (int i = 1; i <= configCount; i++) {
   
         String UsbName = config.getProperty("USB" + i);
   
         // 利用反射创建USB设备
   
         Class<?> clazz = Class.forName(UsbName);
   
         Usb usb = (Usb) clazz.newInstance();
   
         list.add(usb);
   
         // 将还未添加入计算机系统的USB设备加入系统
   
         addUsb(usb);
   
       }
   
       // 将计算机系统上已有,但是配置文件中没有的USB设备移除计算机系统
   
       for (Usb usb : usbs) {
   
         if (!list.contains(usb)) {
   
           remove(usb);
   
         }
   
       }
   
     }
   
    
   
   }
   
    
   
    
   
   public class ReflectUsb {
   
    
   
     public static void main(String[] args) throws IOException {
   
       MainBoard mb;
   
       try {
   
         mb = new MainBoard();
   
         mb.run();
   
         mb.close();
   
       } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
   
         e.printStackTrace();
   
       }
   
     }
   
    
   
   }

运行结果:


MainBoard run!



CD-ROM run!



Keyboard run!



CD-ROM close!



Keyboard close!


MainBoard close!