Java反射机制

来源:互联网 发布:专业外汇交易软件 编辑:程序博客网 时间:2024/05/29 12:13

一、什么是反射机制

Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

二、Java反射机制提供的功能

(1)在运行时判断任意一个对象所属的类。

(2)在运行时构造任意一个类的对象。

(3)在运行时判断任意一个类所具有的成员变量和方法。

(4)在运行时调用任意一个对象的成员变量和方法。

(5)生成动态代理。

三、反射机制的优点与缺点

优点:

(1)能够运行时动态获取类的实例,大大提高系统的灵活性和扩展性。

(2)与Java动态编译相结合,可以实现无比强大的功能。

缺点:

(1)使用反射的性能较低

(2)使用反射相对来说不安全

(3)破坏了类的封装性,可以通过反射获取这个类的私有方法和属性。

四、如何创建Class实例

要想明白如何创建Class实例需要掌握以下4个知识点

1.创建Class实例的过程:源文件经过编译(javac.exe)以后,得到一个或多个.class文件。.class文件经过运行(java.exe)这步,就需要进行类的加载(通过JVM的类的加载器),记载到内存中的缓存,每一个放入缓存中的.class文件就是一个Class的实例。

2.Class的一个对象,对应着一个运行时类。相当于一个运行时类本身充当了Class的一个实例。

3.java.lang.Class是反射的源头。接下来涉及到反射的类都在java.lang.reflect子包下。

4.实例化Class的方法(三种)

    // 1.调用运行时类本身的.class属性    Class clazz1 = Person.class;    System.out.println(clazz1.getName());    // 2.通过运行时类的对象获取    Person p = new Person();    Class clazz2 = p.getClass();    System.out.println(clazz2.getName());    // 3.通过Class的静态方法来获取,通过此方式,体会一下反射的动态性。    String className = "cn.net.ecode.Person";    Class clazz3 = Class.forName(className);    System.out.println(clazz3.getName());        System.out.println(clazz1 == clazz2);//true    System.out.println(clazz1 == clazz3);//true

注:

(1)结果是 clazz1 、clazz2clazz3为同一个对象,这里请记住一句话:所有的类都是 Class 类的对象。即类也是一个对象,它是Class的对象,它的类型称为类类型。

(2)对于开发而言,我更推荐使用第三种,原因很简单:因为第三种是一个字符串,而不是一个具体的类名,这样的话我们可以把这样的字符串配置在配置文件中去了,这样更方便后期代码的维护。

在这里大家可以根据下面这张图体会一下Java的反射机制。


五、有了Class实例以后可以做什么?

这个知识点我用一个实例来给大家分析一下,这里新建一个 Animal类,在整个案例中,我们都用到这个 Animal类来讲解。

Animal.java

public class Animal {private String name;public int age;static String desc = "我是一个动物";public Animal() {super();}private Animal(String name, int age) {super();this.name = name;this.age = age;}private int getAge() {return age;}public void setAge(int age) {this.age = age;}public static String getDesc() {return desc;}public static void setDesc(String desc) {Animal.desc = desc;}public static void info() {System.out.println("动物");}public void show(String desc) {System.out.println("我是一个:" + desc);}@Overridepublic String toString() {return "Animal [name=" + name + ", age=" + age + "]";}
应用一:可以创建对应的运行时类的对象。

  //调用空参的构造器创建运行时类对象@Testpublic void test1() throws Exception {//获取Class对象,字符串为包名+类名Class clazz = Class.forName("review.Animal");// 创建运行时类的对象。使用newInstance(),实际上是调用了运行时类的空参的构造器。// 要想能够创建成功,①要求对应的运行时类要有空参的构造器。②构造器的权限修饰符的权限要足够。Object obj = clazz.newInstance();Animal animal = (Animal) obj;System.out.println(animal);}//调用指定构造器创建运行时类对象@Testpublic void test2() throws Exception {//获取Class对象Class clazz = Animal.class;//获取两参的构造方法,参数是个可变参数Constructor cons = clazz.getDeclaredConstructor(String.class,int.class);//设置取消访问检查,因为权限修饰符为privatecons.setAccessible(true);//创建对象,参数为构造方法的实参Animal animal = (Animal)cons.newInstance("Tom",10);System.out.println(animal);}

此处有两个异常需要注意:

(1)由于Animal类的两参构造函数的权限修饰符为private,所以需要使用getDeclaredConstructor()来获取。否则会报java.lang.NoSuchMethodException异常。

(2)因为权限修饰符为private,要想访问这个构造函数,需要取消访问检查。否则会报java.lang.IllegalAccessException异常。

应用二:获取对应的运行时类的完整的类的结构。(我们可以获取到类的属性,方法,构造器,接口,泛型,异常,包,父类,内部类等结构,此处我们就用类的属性,方法,构造器来做讲解)

   //获取运行时类的构造器     @Test     public void test5() throws Exception {String className = "review.Animal";Class clazz = Class.forName(className);Constructor[] cons = clazz.getDeclaredConstructors();for (Constructor c : cons) {           System.out.println(c);}     } 

  //获取运行时类中所有的方法     @Testpublic void test3() { Class clazz = Animal.class; Method[] m2 = clazz.getDeclaredMethods(); for (Method m : m2) { //1.获取方法的权限修饰符 String str = Modifier.toString(m.getModifiers()); System.out.print(str + " "); //2.获取方法的返回值类型 Class returnType = m.getReturnType(); System.out.print(returnType.getName() + " "); //3.获取方法的方法名 System.out.print(m.getName() + " "); System.out.println(); }      }
  //获取属性的各个部分的内容  @Testpublic void test4() {Class clazz = Animal.class;Field[] fields1 = clazz.getDeclaredFields();for (Field f : fields1) {//1.获取每个属性的权限修饰符int i = f.getModifiers();String str1 = Modifier.toString(i);System.out.print(str1 + " ");//2.获取每个属性的变量类型Class type = f.getType();System.out.print(type.getName() + " ");//3.获取属性名System.out.print(f.getName());System.out.println();}  }
由此可以看出,有了Class实例后,我们可以获取到运行时类的完整结构。
应用三:可以调用对应的运行时类中指定的结构(某个指定的方法,属性,构造器)。

//1.调用非public的属性Field f1 = clazz.getDeclaredField("name");f1.setAccessible(true);f1.set(animal, "Jerry");System.out.println(animal);//调用public的属性Field f2 = clazz.getField("age");f2.set(animal, 9);System.out.println(animal);//调用静态属性Field f3 = clazz.getDeclaredField("desc");System.out.println(f3.get(animal));
//2.调用非public的方法Method m1 = clazz.getDeclaredMethod("getAge");m1.setAccessible(true);int age = (Integer) m1.invoke(animal);System.out.println(age);//调用public方法Method m2 = clazz.getDeclaredMethod("show",String.class);Object returnValue = m2.invoke(animal, "金毛");System.out.println(returnValue);//调用static方法Method m3 = clazz.getDeclaredMethod("info");m3.setAccessible(true);//对于调用静态方法,一下三种方式都可以。m3.invoke(Animal.class);//m3.invoke(animal);//m3.invoke(null);
//3.调用指定构造器创建运行时类对象Class clazz = Animal.class;Constructor cons = clazz.getDeclaredConstructor(String.class, int.class);cons.setAccessible(true);Animal animal = (Animal) cons.newInstance("Tom", 10);System.out.println(animal);

注:

    (1)在我们获取运行时类的构造函数、成员方法、成员变量时,我们要根据权限去使用对用的方法。在这里我们做一下区分:

①getXXX()方法是获取运行时类及其父类中所有的声明为public方法

②getDeclaredXXX()方法是获取运行时类本身生声明的所有方法,不用去管权限修饰符是什么

如果我们只需要获取运行时类自身的方法,我们直接使用getDeclaredXXX()方法是最为方便、有效的。

     (2)根据上面的代码大家可以看到每次使用构造函数、成员方法、成员变量之前要判断是否要添加取消访问检查,其实我们不用管他们的权限修饰符,每次要使用这些之前都加上取消访问检查就可以了。

反射的基本知识点就介绍到这,上面列举的知识获取的运行时类的构造方法、成员方法、成员变量的信息,其实我们还可以获取到接口、泛型、内部内、异常、包路径、泛型等信息,只要学会了上面方法的使用,那么学习其他方法的使用也是同理的。大家赶紧动手试一下吧!



1 0
原创粉丝点击