Java反射详解

转载请注明出处:http://blog.csdn.net/li0978/article/details/53157640
一直想把java反射原理详细的总结一下,昨天看到一位大虾利用许多小栗子阐述的很清楚,我就顺手拈来着手敲了一遍并加以修正补充和说明,以便日后回顾和深入学习。

定义

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

功能

Java反射机制主要提供了以下功能:

  • 在运行时判断任意一个对象所属的类。
  • 在运行时构造任意一个类的对象。
  • 在运行时判断任意一个类所具有的成员变量和方法。
  • 在运行时调用任意一个对象的方法。
  • 生成动态代理。

Class类的使用

Class类是什么?

在面向对象的世界里,万事万物皆对象。其实在java里面有两样东西不是对象。一个是java的普通的数据类型(他们的包装类属于对象),另外一个就是java的静态成员方法或者变量,这个不是对象而是属于某个类。
我们平时写的每一个类都是对象,而类又是java.long.Class的实例对象。说明白点就是任何一个类即有自己的对象(本身new出来的),同时本身又是Class的实例对象(能够从Class中得到该类的实例)。

如何得到Class的实例对象?

先来看看Class类的源码,了解其内部构造:

 /*
     * Constructor. Only the Java Virtual Machine creates Class
     * objects.
     */
    private Class() {}

由源码可知,Class类的构造方法是私有的,根据注释我们知道只有java虚拟机才能创建Class类的对象,并且是处于运行时创建,因此我们不能在代码中直接new出一个Class对象。此路是不通了,但是我们有其他路可走,这里有三条路:
我们先创建一个我们自己的类对象Demo demo = new Demo();
第一条路,通过类的静态成员变量来获取Class类的实例对象(或者叫Demo类的类类型):

Class c = Demo.class;

另外,基本的数据类型和引用类型以及void关键字都可用此方式得到它们的类类型。
第二条路,通过类对象中的getClass()方法来获取。

Class c = demo.getClass();

第三条路,通过Class类中的静态方法forName()来获取,此种形式属于动态加载,在代码中我们无法判定正确与否,只有在运行时才能检测出该类是否存在,调用是否正确。

 Class c = null;  
 try{  
        //这里传入的是全路径,包名路径要写全。
        c = Class.forName("Reflect.Demo");  
  }catch(Exception e){  
        e.printStackTrace();  
  } 
静态加载和动态加载

编译时刻的加载属于静态加载,运行时刻的加载属于动态加载。平时我们采用new创建的对象都是静态加载类,在编译时候就要进行加载,所以只要有一个对象不存在或者方法错误,则其他的对象也都无法加载了,编译就不能通过。而动态加载常常用于在保证整体框架完善编译能够通过,至于我们添加的参数如何也就只能在运行时才能检测出来,一句话:能通过的都过,不能通过的报出来。

获取相关类的对象

通过Class类的实例(Demo类的类类型)来获取Demo类的对象

Demo demo  = null;
try {
            //这里要进行强转一下,并且也要进行一些异常处理。
            demo = (Demo)c.newInstance();
        } catch (InstantiationException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        } catch (IllegalAccessException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }

这里需要注意的是,通过newInstance()调用的是该类的无参构造方法,假如该类中仅仅写有参构造方法,那么默认的无参构造方法将会被覆盖,所以这段代码也就无法运行下去。
java.lang.InstantiationException: Demo
at java.lang.Class.newInstance(Unknown Source)
at Demo.main(Demo.java:8)
所以我们平时写一个类的时候,如果预计到将来这个可能类要被反射获取,最好在该类中写一个无参构造方法。

小栗子

【栗1】通过一个对象获得完整的包名和类名

package com.reflect;

public class Test {

    public static void main(String[] args) {
        Test test =new Test();
        Class<?> c = test.getClass();
        System.out.println(c.getName());  //获取包含完整路径的类的名称
        System.out.println(c.getSimpleName()); //获取不包含包名的类的名称
    }

}

【运行结果】:
com.reflect.Test
Test

【栗2】实例化Class类对象

package com.reflect;

public class Test {

    public static void main(String[] args) { 
        Class<?> c1=null;
        Class<?> c2=null;
        Class<?> c3=null;
        try{
            //这里采用包名加类名(全路径写法)
            c1=Class.forName("com.reflect.Test");
        }catch(Exception e){
            e.printStackTrace();
        }
        c2=new Test().getClass();
        c3=Test.class;

        System.out.println("类名称   "+c1.getName());
        System.out.println("类名称   "+c2.getName());
        System.out.println("类名称   "+c3.getName());
    }

}

【运行结果】:
类名称 com.reflect.Test
类名称 com.reflect.Test
类名称 com.reflect.Test

【栗3】通过Class实例化其他类的对象
通过无参构造实例化对象

package com.reflect;

public class Person {

    private String name;
    private int age;

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    @Override
    public String toString() {
        return "[" + this.name + "  " + this.age + "]";
    }
}

public class Test {

    public static void main(String[] args) {
        Class<?> c = null;
        try {
            c = Class.forName("com.reflect.Person");
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        Person per = null;
        try {
            per = (Person) c.newInstance();
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        per.setName("张三");
        per.setAge(18);
        System.out.print(per);
    }

}

【运行结果】:
[张三 18]
但是注意一下,当我们把Person中的默认的无参构造函数取消的时候,比如自己定义只定义一个有参数的构造函数之后,会出现错误。
比如我定义了一个构造函数:

public Person(String name, int age) {
        this.age=age;
        this.name=name;
}

然后继续运行上面的程序,会出现:

java.lang.InstantiationException: com.reflect.Person
    at java.lang.Class.newInstance(Unknown Source)
    at com.reflect.Test.main(Test.java:16)
Exception in thread "main" java.lang.NullPointerException
    at com.reflect.Test.main(Test.java:25)

所以大家以后再编写使用Class实例化其他类的对象的时候,一定要自己定义无参的构造函数

【栗4】获取某个类的全部构造函数,并根据这些构造函数实例化该类的一个对象。

package com.reflect;

public class Person {

    private String name;
    private int age;

    public Person() {
    }

    public Person(String name) {
        this.name = name;
    }

    public Person(int age) {
        this.age = age;
    }

    public Person(String name, int age) {
        this.age = age;
        this.name = name;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    @Override
    public String toString() {
        return "[" + this.name + "  " + this.age + "]";
    }
}

public class Test{

    public static void main(String[] args) {
        Class<?> c = null;
        try {
            c = Class.forName("com.reflect.Person");
        } catch (ClassNotFoundException e1) {
            // TODO Auto-generated catch block
            e1.printStackTrace();
        }

        Person per1 = null;
        Person per2 = null;
        Person per3 = null;
        Person per4 = null;
        // 取得全部的构造函数
        Constructor<?>[] cons = c.getConstructors();
        for (int i = 0; i < cons.length; i++) {
            System.out.println("cons[" + i + "] : "+cons[i]);
            //获取每个构造函数的相关参数类型
            Class<?> clazzs[] = cons[i].getParameterTypes();
            System.out.print("该构造函数的参数类型有: (");
            for (int j = 0; j < clazzs.length; j++) {
                if (j == clazzs.length - 1)
                    System.out.print(clazzs[j].getName());
                else
                    System.out.print(clazzs[j].getName() + ",");
            }
            System.out.println(")");
        }

        /*
         * 这里需注意:一定要先把所有构造函数获取到,然后根据获取的构造函数进行对号入座正确的去根据特定的构造函数进行实例化对象。
         */
        try {
            per1 = (Person) cons[3].newInstance();
            per2 = (Person) cons[2].newInstance("张三");
            per3 = (Person) cons[1].newInstance(18);
            per4 = (Person) cons[0].newInstance("张三", 18);
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        System.out.println(per1);
        System.out.println(per2);
        System.out.println(per3);
        System.out.println(per4);
    }
}

【运行结果】:
cons[0] : public com.reflect.Person(java.lang.String,int)
该构造函数的参数类型有: (java.lang.String,int)
cons[1] : public com.reflect.Person(int)
该构造函数的参数类型有: (int)
cons[2] : public com.reflect.Person(java.lang.String)
该构造函数的参数类型有: (java.lang.String)
cons[3] : public com.reflect.Person()
该构造函数的参数类型有: ()
[null 0]
[张三 0]
[null 18]
[张三 18]

【栗5】 返回某个类的父类和实现的接口:

package com.reflect;

public interface ISay {
    String name = "xinrun";
    int age = 18;

    public void sayName();

    public void sayAge();

}

public class Test implements ISay{

    public static void main(String[] args) {
        Class<?> c = null;
        try {
            c = Class.forName("com.reflect.Test");
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        Class<?> superClass = c.getSuperclass();
        System.out.println("Test类的父类是:"+superClass.getName());
        // 获取该类中实现的所有接口
        Class<?> intes[] = c.getInterfaces();
        System.out.println("Test类中实现的所有接口有:");
        for (int i = 0; i < intes.length; i++) {
            System.out.println(intes[i].getName());
        }
    }

    @Override
    public void sayName() {
        // TODO Auto-generated method stub
    }

    @Override
    public void sayAge() {
        // TODO Auto-generated method stub
    }

}

【运行结果】:
Test类的父类是:java.lang.Object
Test类中实现的所有接口有:
com.reflect.ISay

【栗6】获取某个类的全部属性以及本类和父类的公有属性

package com.reflect;

public class BaseClass {
    public String baseName;
    private int baseAge;
}

public class Test extends BaseClass{
    public String testName;
    private int testAge;

    public static void main(String[] args) {
        Class<?> c = null;
        try {
            c = Class.forName("com.reflect.Test");
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        System.out.println("==========获取本类的全部属性==========");
        //仅获取本类的全部属性
        Field[] field = c.getDeclaredFields();
        for (int i = 0; i < field.length; i++) {
            // 获取属性权限修饰符
            int mo = field[i].getModifiers();  //每种修饰符都有特定的编号,然后根据编号得到修饰符的名字。
            String priv = Modifier.toString(mo);
            // 获取属性类型
            Class<?> type = field[i].getType();
            System.out.println(priv + " " + type.getName() + " " + field[i].getName() + ";");
        }

        System.out.println("==========获取本类和父类的公有属性==========");
        //获取本类和父类的公有属性(public)
        Field[] field1 = c.getFields();
        for (int i = 0; i < field1.length; i++) {
            // 获取属性权限修饰符
            int mo = field1[i].getModifiers();
            String priv = Modifier.toString(mo);
            // 获取属性类型
            Class<?> type = field1[i].getType();
            System.out.println(priv + " " + type.getName() + " " + field1[i].getName() + ";");
        }   
    }
}

【运行结果】:
==========获取本类的全部属性==========
public java.lang.String testName;
private int testAge;
==========获取本类和父类的公有属性==========
public java.lang.String testName;
public java.lang.String baseName;

【栗7】通过反射操作属性

package com.reflect;

public class BaseClass {
    public String baseName;
    private int baseAge;
}

public class Test extends BaseClass {
    public String testName;
    private int testAge;

    public static void main(String[] args) throws Exception {
        Class<?> c = Class.forName("com.reflect.BaseClass");
        Object obj = c.newInstance();  
        Field field = c.getDeclaredField("baseAge");
        field.setAccessible(true); // 因BaseClass中的baseAge属性是私有的,所以必须对其“解锁”才能操作,公有属性就无关紧要了
        field.set(obj, 1);
        System.out.println("baseAge的值是: "+field.get(obj));
    }
}

【运行结果】:
baseAge的值是: 1
这里需注意一定要事先搞清楚该属性是什么类型才可赋值。

【栗8】获取某个类的和父类的公有方法以及方法所抛出的异常

package com.reflect;

public class BaseClass {
    public String baseName;
    private int baseAge;

    private void basePrivateTest(){
        System.out.println("这是BaseClass的私有方法");
    }

    public void basePublicTest(){
        System.out.println("这是BaseClass的无参公有方法");
    }

    public void basePublicTest(String name,int age) throws Exception{
        System.out.println("这是BaseClass的含参公有方法,名字是:"+name+",年龄是:"+age);
    }
}

public class Test extends BaseClass{
    public String testName;
    private int testAge;

    public static void main(String[] args) throws Exception {
        Class<?> c = Class.forName("com.reflect.BaseClass");

        Method[] methods = c.getMethods();  //获取该类以及父类的所有公有方法
//      Method[] methods = c.getDeclaredMethods(); //获取该类的所有方法
        for (int i = 0; i < methods.length; ++i) {
            Class<?> returnType = methods[i].getReturnType();
            Class<?> para[] = methods[i].getParameterTypes();
            int temp = methods[i].getModifiers();
            System.out.print(Modifier.toString(temp) + " ");
            System.out.print(returnType.getName() + "  ");
            System.out.print(methods[i].getName() + " ");
            System.out.print("(");
            for (int j = 0; j < para.length; ++j) {
                System.out.print(para[j].getName() + " " + "arg" + j);
                if (j < para.length - 1) {
                    System.out.print(",");
                }
            }

            //获取该方法的所有异常类
            Class<?> exce[] = methods[i].getExceptionTypes();
            if (exce.length > 0) {
                System.out.print(") throws ");
                for (int k = 0; k < exce.length; ++k) {
                    System.out.print(exce[k].getName() + " ");
                    if (k < exce.length - 1) {
                        System.out.print(",");
                    }
                }
            } else {
                System.out.print(")");
            }
            System.out.println();
        }
    }
}

【运行结果】:
public void basePublicTest (java.lang.String arg0,int arg1) throws java.lang.Exception
public void basePublicTest ()
public final native void wait (long arg0) throws java.lang.InterruptedException
public final void wait () throws java.lang.InterruptedException
public final void wait (long arg0,int arg1) throws java.lang.InterruptedException
public final native java.lang.Class getClass ()
public native int hashCode ()
public boolean equals (java.lang.Object arg0)
public java.lang.String toString ()
public final native void notifyAll ()
public final native void notify ()

【栗9】调用其他类的方法

public class Test extends BaseClass {
    public String testName;
    private int testAge;

    public static void main(String[] args) throws Exception {
        Class<?> c = Class.forName("com.reflect.BaseClass");
        //调用BaseClass中的无参方法
        Method method = c.getMethod("basePublicTest");
        method.invoke(c.newInstance());
        //调用BaseClass中的有参方法
        Method method1 = c.getMethod("basePublicTest", String.class, int.class);
        method1.invoke(c.newInstance(), "张三", 18);
    }
}

【运行结果】:
这是BaseClass的无参共有方法
这是BaseClass的含参共有方法,名字是:张三,年龄是:18

【栗10】通过反射取得并修改数组的信息和数组大小:

package com.reflect;

import java.lang.reflect.Array;

public class Test extends BaseClass {
    public static void main(String[] args) throws Exception {
        int[] temp = { 0, 1, 2, 3, 4 };
        System.out.println("======================获取数组信息===========");
        Class<?> ctC = temp.getClass().getComponentType();
        System.out.println("数组类型: " + ctC.getName());
        System.out.println("数组长度  " + Array.getLength(temp));
        System.out.println("数组的第一个元素: " + Array.get(temp, 0));
        Array.set(temp, 0, 100);
        System.out.println("修改之后数组第一个元素为: " + Array.get(temp, 0));

        System.out.println("=======================扩大数组长度===========");
        int[] newTemp = (int[]) arrayInc(temp, 15);
        print(newTemp);
    }

    // 修改数组大小
    public static Object arrayInc(Object obj, int len) {
        Class<?> arr = obj.getClass().getComponentType();
        Object newArr = Array.newInstance(arr, len);
        int co = Array.getLength(obj);
        System.arraycopy(obj, 0, newArr, 0, co);
        return newArr;
    }

    // 打印新数组
    public static void print(Object obj) {
        Class<?> c = obj.getClass();
        if (!c.isArray()) {
            return;
        }
        System.out.println("扩展后的数组长度为: " + Array.getLength(obj));
        System.out.println("扩展后的数组为:");
        for (int i = 0; i < Array.getLength(obj); i++) {
            System.out.print(Array.get(obj, i) + " ");
        }
        System.out.println();
    }
}

【运行结果】:
======================获取数组信息===========
数组类型: int
数组长度 5
数组的第一个元素: 0
修改之后数组第一个元素为: 100
=======================扩大数组长度===========
扩展后的数组长度为: 15
扩展后的数组为:
100 1 2 3 4 0 0 0 0 0 0 0 0 0 0

【栗11】动态代理
首先来看看如何获得类加载器:

package com.reflect;

public class Test {
    public static void main(String[] args){
        Test test = new Test();
        System.out.println("类加载器是:"+test.getClass().getClassLoader().getClass().getName());
    }
}

【运行结果】:
类加载器是:sun.misc.Launcher$AppClassLoader

在java中有四类加载器:
1)Bootstrap ClassLoader 负责加载$JAVA_HOME中jre/lib/rt.jar里所有的class,由C++实现,不是ClassLoader子类。
2)Extension ClassLoader 用来进行扩展类的加载,一般对应的是jre\lib\ext目录中的类。
3)AppClassLoader 加载classpath指定的类,是最常用的加载器。同时也是java中默认的加载器。
4)Custom ClassLoader 属于应用程序根据自身需要自定义的ClassLoader,如tomcat、jboss都会根据j2ee规范自行实现ClassLoader。
这一块涉及到JVM的执行和类的生命周期以及类的加载过程,后续专门详细总结一下。

代理是java设计模式中的一种,分为静态代理和动态代理。什么是代理?给大家举个例子:
一个人想要告状,他只知道他要告谁,需要准备的文件有哪些,可是他没有经验,为了保证告状赢的成功率他只好找一个律师帮着他告,这个律师除了说出告状者的心声而且还在公堂之上激情四射把自己的毕生所学发挥的淋漓尽致,这些是告状者不具备的。整个告状的过程是律师帮着告,告状人只是为律师提供相关材料和证据。这里的律师就是代理者。整个过程就是律师代理告状者进行告状。

静态代理和动态代理的最根本的区别就是代理类生成的时间不同,静态代理的代理者必须事先明确,而动态代理可以事先不确定代理者,可在jvm类加载运行时传入。

动态代理要先设置一个动态代理处理器并实现InvocationHandler接口,进而在内部得到一个动态代理的方法。用的时候先拿到代理者实例,再调用动态代理方法,整个动态代理过程如下:

ppackage com.reflect;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 被代理者和代理者所需要实现的接口
 */
interface ISay {
    void say(String name,int age);
}

/**
 * 被代理者
 */
class Person implements ISay {

    @Override
    public void say(String name, int age) {
        System.out.println("名字叫" + name + ",年龄" + age);
    }
}

/**
 * 控制动态代理的处理器
 */
class MyInvocationHandler implements InvocationHandler {

    private Object object = null;

    /*
     * 获取代理者
     */
    public Object bind(Object object) {
        //得到被代理者实例
        this.object = object;
        //根据被代理者返回动态代理类实例,第一个参数是指定类加载器,第二个参数是指定代理类需要实现的一系列接口,第三个参数是控制动态代理的处理器本身实例
        return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(),this);
    }

    /*
     *代理者需要实现的代理方法 
     *参数一:代理者实例(上边bind方法返回的);
     *参数二:被代理者调用的方法对象
     *参数三:被代理者传入的相关参数
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        System.out.println("代理人说出了这个人的信息:");
        method.invoke(this.object, args);
        return null;
    }

}

public class Test {
    public static void main(String[] args) throws Exception {
        MyInvocationHandler invocationHandler = new MyInvocationHandler(); 
        ISay personProxy = (ISay) invocationHandler.bind(new Person());
        personProxy.say("张三", 18);
    }
}

【运行结果】:
代理人说出了这个人的信息:
名字叫张三,年龄18

【栗12】工厂模式
工厂模式也是常用的一种设计模式,从字面意思就能差不多明白其原理了,工厂模式就是根据一些特定条件生产出一些”相同功能”的产品来供使用者去选择。
下面就以选择某个地图导航为例阐述工厂模式:

package com.reflect;

/**
 * 导航定位接口内含一个定位功能
 */
interface INavigation {
    void location();
}

/**
 * 磨具1:百度导航定位
 */
class Baidu implements INavigation {

    @Override
    public void location() {
        System.out.println("我是百度,地图牛逼,已确定您的位置。");
    }
}

/**
 * 磨具2:高德导航定位
 */
class Gaode implements INavigation {

    @Override
    public void location() {
        System.out.println("我是高德,定位准确,已确定您的位置。");

    }
}

/**
 * 工厂,根据特定条件或者标示生产不同磨具的产品
 */
class Factory {
    public static INavigation getInstance(String navigationName){
        System.out.println("选择"+navigationName);
        System.out.println("工厂寻找磨具,准备生产...");
        INavigation navigation = null;
        if ("Baidu".equals(navigationName)) {
            navigation = new Baidu();
        }else if("Gaode".equals(navigationName)) {
            navigation = new Gaode();
        }

        return navigation;
    }
}

public class Test {
    public static void main(String[] args){
        INavigation navigation = Factory.getInstance("Baidu");
        navigation.location();
    }
}

【运行结果】:
选择Baidu
工厂寻找磨具,准备生产…
我是百度,地图牛逼,已确定您的位置。

这中写法有个弊端:当我们在添加一个子类的时候,就需要修改工厂类了。如果我们添加太多的子类的时候,改的就会很多。
稍微修改一下利用反射来实现工厂模式:

package com.reflect;

/**
 * 导航定位接口内含一个定位功能
 */
interface INavigation {
    void location();
}

/**
 * 磨具1:百度导航定位
 */
class Baidu implements INavigation {

    @Override
    public void location() {
        System.out.println("我是百度,地图牛逼,已确定您的位置。");
    }
}

/**
 * 磨具2:高德导航定位
 */
class Gaode implements INavigation {

    @Override
    public void location() {
        System.out.println("我是高德,定位准确,已确定您的位置。");

    }
}

/**
 * 工厂,根据特定条件或者标示生产不同磨具的产品
 */
class Factory {
    public static INavigation getInstance(String navigationName){
        System.out.println("选择"+navigationName);
        System.out.println("工厂寻找磨具,准备生产...");

        INavigation navigation = null;
        try {
            navigation = (INavigation) Class.forName(navigationName).newInstance();
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return navigation;
    }
}

public class Test {
    public static void main(String[] args){
        INavigation navigation = Factory.getInstance("com.reflect.Baidu");
        navigation.location();
    }
}

【运行结果】:
选择com.reflect.Baidu
工厂寻找磨具,准备生产…
我是百度,地图牛逼,已确定您的位置。

这样我们添加任意多个子类的时候工厂类就不需要修改了。但是这里又有一个弊端了:虽然可以通过反射取得接口的实例,但是需要传入完整的包和类名。而且用户也无法知道一个接口有多少个可以使用的子类。
所以我们可以通过属性文件的形式配置所需要的子类,首先在本地创建一个配置文件navigation.properties,内容是:

Gaode=com.reflect.Gaode
Baidu=com.reflect.Baidu

下面是结合属性文件的工厂模式:

package com.reflect;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;

/**
 * 导航定位接口内含一个定位功能
 */
interface INavigation {
    void location();
}

/**
 * 磨具1:百度导航定位
 */
class Baidu implements INavigation {

    @Override
    public void location() {
        System.out.println("我是百度,地图牛逼,已确定您的位置。");
    }
}

/**
 * 磨具2:高德导航定位
 */
class Gaode implements INavigation {

    @Override
    public void location() {
        System.out.println("我是高德,定位准确,已确定您的位置。");

    }
}

/**
 * 工厂,根据特定条件或者标示生产不同磨具的产品
 */
class Factory {
    public static INavigation getInstance(String navigationName){
        System.out.println("选择"+navigationName);
        System.out.println("工厂寻找磨具,准备生产...");

        INavigation navigation = null;
        try {
            navigation = (INavigation) Class.forName(navigationName).newInstance();
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return navigation;
    }
}

public class Test {
    public static void main(String[] args) throws FileNotFoundException, IOException{
        Properties pro=new Properties();
        File f=new File("navigation.properties");
        if(f.exists()){
            pro.load(new FileInputStream(f));
        }else{
            pro.setProperty("Baidu", "com.reflect.Baidu");
            pro.setProperty("Gaode", "com.reflect.Gaode");
            pro.store(new FileOutputStream(f), "NAVIGATION CLASS");
        }
        INavigation navigation = Factory.getInstance(pro.getProperty("Baidu"));
        navigation.location();
    }
}

【运行结果】:
选择com.reflect.Baidu
工厂寻找磨具,准备生产…
我是百度,地图牛逼,已确定您的位置。

总结至此,从上到下算是对java反射有一个清晰的认识,要明白反射最好还是要先弄清类的加载过程,这里仅仅阐述了其使用的方法,所涉及的知识还需慢慢深入,共勉。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值