反射是一种运行时机制,目的是根据完整类名或者对象来获取类的相关信息。比如方法(Class.getMethods()), 变量(Class.getFilelds()),构造函数(getConstrutors()),父类(getSuperclass()),接口(getInterfaces())等等。

可以这么说,运行时类中所有的信息都可以通过反射来获取。反射实现的原理在于运行时类元信息已经加载进入JVM, 反射相当于根据类索引查找相应信息而已。

反射比较吸引人的一点是它可以突破private访问限制。具体怎么做呢?

比如我们定义一个类 Test:

public class Test{

  private String a = "test";

}

如果我们直接访问: Test test = new Test();  String b = test.a; 无疑是行不通的,因为不能在类外面访问私有变量。

这时就需要用反射了。

Test test = new Test();

Field privateField = test.getClass().getDeclaredFiled("a"); //获取变量a.

privateField.setAccessible(true);

String b = privateField.get(test); //非静态变量依赖于实例,与类实例关联。

System.out.println(b);

这时我们发现我们可以获取到 a的值!

实际上类加载是所有变量都加载到了jvm里,因此反射可以获取到私有变量及私有方法也就不足为奇。

 

另外反射应用较多的场景是由类名实例化对象,以及由类名反调其方法。比如:

package Test;

class Dog{

  private String name;

  public void setName(String s){

    this.name = s;

  }

}

class Main{

  public static void main(String[] args){

    Class dogClass = Class.forName("Test.Dog"); //根据完整类名获取类信息。

    Method method = dogClass.getMethod("setName", String.class); //根据方法名和参数列表识别方法,是不是和方法重载很像?

    method.invoke(dogClass.newInstance(), "Xiao");  //实例化对象是必须的,因为非静态方法依赖于对象。

  }

}

另外我们都知道Java泛型会擦除掉泛型的具体信息。但是反射在一定情况下却可获取到被抹去的信息。这种情况,就是调用泛型方法或属性时。

举例来说:

ArrayList<String> arrayList = new ArrayList<>();

arrayList.add("aaa");

arrayList.get(0).subString(0);

如果说反射会擦除掉泛型信息,那么我们调用get()时,Java怎么知道它是一个String的呢。这就是反射在起作用了。