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 、clazz2、clazz3为同一个对象,这里请记住一句话:所有的类都是 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)根据上面的代码大家可以看到每次使用构造函数、成员方法、成员变量之前要判断是否要添加取消访问检查,其实我们不用管他们的权限修饰符,每次要使用这些之前都加上取消访问检查就可以了。
反射的基本知识点就介绍到这,上面列举的知识获取的运行时类的构造方法、成员方法、成员变量的信息,其实我们还可以获取到接口、泛型、内部内、异常、包路径、泛型等信息,只要学会了上面方法的使用,那么学习其他方法的使用也是同理的。大家赶紧动手试一下吧!
- 【反射】JAVA反射机制
- 【Java】JAVA反射机制
- Java 反射机制[Field反射]
- Java 反射机制[Method反射]
- Java反射机制笔记-反射机制
- java的反射机制
- Java的反射机制
- java反射机制详解!
- Java反射机制
- Java的反射机制
- java 反射机制--侯捷
- java反射机制
- java反射机制
- [候捷]Java反射机制
- java 反射机制
- java 反射机制初探
- 关于Java反射机制
- java反射机制
- log4j的日志级别记录
- 51Nod-最长递增子序列(nlogn算法)
- linux下错误的捕获:errno和strerror的使用
- openstack 测试感想
- C语言重点知识小结
- Java反射机制
- vncserver的详细配置
- 数组溢出
- IntelliJ IDEA入门系列(2)--IntelliJ IDEA 基本知识
- 关于ListView和EditText的焦点问题
- 《数据之美》读书笔记
- Linux VFS 文件系统结构分析
- Android 中listview的全选、全不选、反选、获得选中条目数据
- 广州海格2016年笔试题,关于链表的操作