java语言 百分网手机站

Java反射机制应用实践

时间:2020-09-11 15:55:44 java语言 我要投稿

Java反射机制应用实践

  引导语:通过反射机制我们可以在运行期间获取对象的类型信息,利用这一特性我们可以实现工厂模式和代理模式等设计模式,以下是小编整理的Java反射机制应用实践,欢迎参考阅读!

  反射基础

  p.s: 本文需要读者对反射机制的API有一定程度的了解,如果之前没有接触过的话,建议先看一下官方文档的Quick Start。

  在应用反射机制之前,首先我们先来看一下如何获取一个对象对应的反射类Class,在Java中我们有三种方法可以获取一个对象的反射类。

  通过getClass方法

  在Java中,每一个Object都有一个getClass方法,通过getClass方法我们可以获取到这个对象对应的反射类:

  String s = "ziwenxie";

  Class

  通过forName方法

  我们也可以调用Class类的静态方法forName:

  Class

  使用.class

  或者我们也可以直接使用.class:

  Class

  获取类型信息

  在文章开头我们就提到反射的一大好处就是可以允许我们在运行期间获取对象的类型信息,下面我们通过一个例子来具体看一下。

  首先我们在typeinfo.interfacea包下面新建一个接口A:

  package typeinfo.interfacea;

  public interface A { void f(); }

  接着我们在typeinfo.packageaccess包下面新建一个接口C,接口C继承自接口A,并且我们还另外创建了几个用于测试的方法,注意下面几个方法的权限都是不同的。

  package typeinfo.packageaccess;

  import typeinfo.interfacea.A;

  class C implements A {

    public void f() { System.out.println("public C.f()"); }

    public void g() { System.out.println("public C.g()"); }

    protected void v () { System.out.println("protected C.v()"); }

    void u() { System.out.println("package C.u()"); }

    private void w() { System.out.println("private C.w()"); }

  }

  public class HiddenC {

    public static A makeA() { return new C(); }

  }

  在callHiddenMethod()方法中我们用到了几个新的API,其中getDeclaredMethod()根据方法名用于获取Class类指代对象的某个方法,然后我们通过调用invoke()方法传入实际的对象就可以触发对象的相关方法:

  package typeinfo;

  import typeinfo.interfacea.A;

  import typeinfo.packageaccess.HiddenC;

  import java.lang.reflect.Method;

  public class HiddenImplementation {

    public static void main(String[] args) throws Exception {

      A a = HiddenC.makeA();

      a.f();

      System.out.println(a.getClass().getName());

      // Oops! Reflection still allows us to call g():

      callHiddenMethod(a, "g");

      // And even methods that are less accessible!

      callHiddenMethod(a, "u");

      callHiddenMethod(a, "v");

      callHiddenMethod(a, "w");

    }

    static void callHiddenMethod(Object a, String methodName) throws Exception {

      Method g = a.getClass().getDeclaredMethod(methodName);

      g.setAccessible(true);

      g.invoke(a);

    }

  }

  从输出结果我们可以看出来,不管是public,default,protect还是pricate方法,通过反射类我们都可以自由调用。当然这里我们只是为了显示反射的强大威力,在实际开发中这种技巧还是不提倡。

  public C.f()

  typeinfo.packageaccess.C

  public C.g()

  package C.u()

  protected C.v()

  private C.w()

  应用实践

  我们有下面这样一个业务场景,我们有一个泛型集合类List<Class>,我们需要统计出这个集合类中每种具体的Pet有多少个。由于Java的泛型擦除,注意类似List的做法肯定是不行的,因为编译器做了静态类型检查之后,到了运行期间JVM会将集合中的对象都视为Pet,但是并不会知道Pet代表的究竟是Cat还是Dog,所以到了运行期间对象的类型信息其实全部丢失了。p.s: 关于泛型擦除:我在上一篇文章里面有详细解释,感兴趣的朋友可以看一看。

  为了实现我们上面的例子,我们先来定义几个类:

  public class Pet extends Individual {

    public Pet(String name) { super(name); }

    public Pet() { super(); }

  }

  public class Cat extends Pet {

    public Cat(String name) { super(name); }

    public Cat() { super(); }

  }

  public class Dog extends Pet {

    public Dog(String name) { super(name); }

    public Dog() { super(); }

  }

  public class EgyptianMau extends Cat {

    public EgyptianMau(String name) { super(name); }

    public EgyptianMau() { super(); }

  }

  public class Mutt extends Dog {

    public Mutt(String name) { super(name); }

    public Mutt() { super(); }

  }

  上面的Pet类继承自Individual,Individual类的的'实现稍微复杂一点,我们实现了Comparable接口,重新自定义了类的比较规则,如果不是很明白的话,也没有关系,我们已经将它抽象出来了,所以不理解实现原理也没有关系。

  public class Individual implements Comparable{

    private static long counter = 0;

    private final long id = counter++;

    private String name; // name is optional

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

    public Individual() {}

    public String toString() {

      return getClass().getSimpleName() + (name == null ? "" : " " + name);

    }

    public long id() { return id; }

    public boolean equals(Object o) {

      return o instanceof Individual && id == ((Individual)o).id;

    }

    public int hashCode() {

      int result = 17;

      if (name != null) {

        result = 37 * result + name.hashCode();

      }

      result = 37 * result + (int) id;

      return result;

    }

    public int compareTo(Individual arg) {

      // Compare by class name first:

      String first = getClass().getSimpleName();

      String argFirst = arg.getClass().getSimpleName();

      int firstCompare = first.compareTo(argFirst);

      if (firstCompare != 0) {

        return firstCompare;

      }

      if (name != null && arg.name != null) {

        int secendCompare = name.compareTo(arg.name);

        if (secendCompare != 0) {

          return secendCompare;

        }

      }

      return (arg.id < id ? -1 : (arg.id == id ? 0 : 1));

    }

  }