java反射机制(1)- 知识点总结Java Reflection API操作
2015-11-28 17:02
531 查看
转载需注明出处:java反射机制(1)- 知识点总结Java Reflection API操作
复杂点说就是:通过Class类,可以在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的信息以及动态调用对象的方法的功能酒称为java的反射机制。
Java反射机制主要提供了一下功能:
- 在运行时判断任意一个对象所属的类;
- 在运行时构造任意一个类的对象;
- 在运行时判断任意一个类所具有的成员变量和方法;
- 在运行时调用任意一个对象的方法;
- 生成动态代理;
动态加载类(运行时加载类):所谓动态加载类,只需要通过
本质的区别在于静态加载的类的源程序在编译时期加载(必须存在),而动态加载的类在编译时期可以缺席(源程序不必存在)。
Class:代表一个类
Filed:代表类的成员变量或者说成员属性
Method:代表类的方法
Constructor:代表类的构造方法
Array:提供了动态创建数组,以及访问数组元素的静态方法
下面直接通过几个案例说明相关API操作:
案例1:获取类名、方法、属性已经构造函数等基本信息。
在 java.lang.Object 类中定义了 getClass()方法, 因此对于任意一个 Java 对象, 都可以通过此方法获得对象的类型。Class 类是 Reflection API 中的核心类,它有以下方法。
getName():获得类的完整名字。
getFields():获得类的 public 类型的属性。
getDeclaredFields():获得类的所有属性。
getMethods():获得类的 public 类型的方法。
getDeclaredMethods():获得类的所有方法。
getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes参数指定方法的参数类型。
getConstrutors():获得类的 public类型的构造方法。
getConstrutor(Class[] parameterTypes):获得类的特定构造方法, parameterTypes参数指定构造方法的参数类型。
newInstance():通过类的不带参数的构造方法创建这个类的一个对象。
输出结果为(考虑篇幅,只列出获取setter和getter方法的结果,其他的省略):
案例2:获取类的私有属性和私有方法。
person类的私有属性和方法:
注意:在访问私有属性和私有方法时,需要对访问的私有属性或方法设置
运行结果如下:
案例3:通过调用构造方法创建一个新的对象:。
先调用 Class 类的 getConstructor方法获得一个 Constructor 对象,然后调用 Constructor 对象的 newInstance方法构造一个实例。
案例4:运用反射机制调用对象的方法。
Method 类的 invoke(Object obj,Object args[])方法接收的参数必须为对象, 如果参数为基本类型数据, 必须转换为相应的包装类型的对象。 invoke()方法的返回值总是对象,如果实际被调用的方法的返回类型是基本类型数据, 那么 invoke()方法会把它转换为相应的包装类型的对象,再将其返回。
案例5:Array 类提供了动态创建和访问数组元素的各种静态方法。
以上程序代码很冗长,而且可维护性差。如果在以后软件的升级版本中去除了一个 HelloService4 类,或者增加了一个 HelloService1001 类,都需要修改以上 newInstance()方法。如果运用反射机制,就可以简化程序代码,并且提高软件系统的可维护性和可扩展性:
Java 反射机制在服务器程序和中间件程序中得到了广泛运用。在服务器端,往往需要根据客户的请求 ,动态调用某一个对象的特定方法。此外 ,有一种ORM(Object-Relation Mapping,对象-关系映射,例如Hibernate)中间件能够把任意一个 JavaBean 持久化到关系数据库中。在 ORM 中间件的实现中,运用 Java 反射机制来读取任意一个JavaBean 的所有属性, 或者给这些属性赋值。 (小结摘自:孙卫琴《Java网络编程精解》第10章)
1 什么是反射机制
什么是反射机制?简单点说就是程序在运行时能够获取自身的信息。在java中,只要给定类的全名,就可以通过反射机制来获取类的所有信息。复杂点说就是:通过Class类,可以在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的信息以及动态调用对象的方法的功能酒称为java的反射机制。
Java反射机制主要提供了一下功能:
- 在运行时判断任意一个对象所属的类;
- 在运行时构造任意一个类的对象;
- 在运行时判断任意一个类所具有的成员变量和方法;
- 在运行时调用任意一个对象的方法;
- 生成动态代理;
2 先认识下静态加载类与动态加载类
静态加载类(编译时加载类):大多数情况下都是使用这种形式。比如我们定义了一个类A,实例化采用A a = new A()接着就可以通过a对象调用相关方法或属性,这就是静态加载类的过程。
动态加载类(运行时加载类):所谓动态加载类,只需要通过
Class clazz = Class.forName("类的全名")即可获得类类型,然后通过调用
A a = clazz.newInstance()方法即可实例化这个类。
本质的区别在于静态加载的类的源程序在编译时期加载(必须存在),而动态加载的类在编译时期可以缺席(源程序不必存在)。
3 Java Reflection API简介
JDK中,主要通过一下类实现java反射机制,这些类抖位于java.lang.reflect包中。Class:代表一个类
Filed:代表类的成员变量或者说成员属性
Method:代表类的方法
Constructor:代表类的构造方法
Array:提供了动态创建数组,以及访问数组元素的静态方法
下面直接通过几个案例说明相关API操作:
案例1:获取类名、方法、属性已经构造函数等基本信息。
在 java.lang.Object 类中定义了 getClass()方法, 因此对于任意一个 Java 对象, 都可以通过此方法获得对象的类型。Class 类是 Reflection API 中的核心类,它有以下方法。
getName():获得类的完整名字。
getFields():获得类的 public 类型的属性。
getDeclaredFields():获得类的所有属性。
getMethods():获得类的 public 类型的方法。
getDeclaredMethods():获得类的所有方法。
getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes参数指定方法的参数类型。
getConstrutors():获得类的 public类型的构造方法。
getConstrutor(Class[] parameterTypes):获得类的特定构造方法, parameterTypes参数指定构造方法的参数类型。
newInstance():通过类的不带参数的构造方法创建这个类的一个对象。
[code]package com.markliu.reflection.getinfo; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; public class DumpMethodsConstructorsFields { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException { /* Returns the Class object associated with the classname */ Class<?> clazz = Class.forName("java.util.Stack"); Class<?> clazz1 = Class.forName("java.io.FileInputStream", true, java.io.FileInputStream.class.getClassLoader()); System.out.println(clazz); System.out.println(clazz1); System.out.println("--------interfaces------"); Class<?>[] interfaces = clazz1.getInterfaces(); for(Class<?> iClass : interfaces) { System.out.println(iClass.getName()); } System.out.println("--------------"); /* * 获取所有声明的方法(不包括构造函数),public,protected,缺省和private类型, * 包括返回类型、方法名,参数和抛出异常. * getDeclaredMethods():获得类的所有方法。 */ Method[] methods = clazz.getDeclaredMethods(); for(Method m : methods) { System.out.println(m); } System.out.println("--------------"); /* * getMethods():获得类的 public 类型的方法。 */ Method[] methods1 = clazz1.getMethods(); for(Method m : methods1) { System.out.println(m); } System.out.println("--------------"); /* * getMethod(String name, Class[] parameterTypes):获得类的特定方法, * name 参数指定方法的名字 * parameterTypes 参数指定方法的参数类型。 * 获取:public int java.io.FileInputStream.read(byte[],int,int) throws java.io.IOException */ Method method = clazz1.getMethod("read", new Class[]{byte[].class, int.class, int.class}); System.out.println(method); System.out.println("-------public Constructors-------"); Constructor<?>[] publicConstructors = clazz1.getConstructors(); for(Constructor<?> c : publicConstructors) { System.out.println(c); } System.out.println("-------constructors-------"); Constructor<?>[] constructors = clazz1.getDeclaredConstructors(); for(Constructor<?> c : constructors) { System.out.println(c); } /* 根据参数数组获取指定的public型构造函数*/ Constructor<?> constructor = clazz1.getConstructor(new Class[]{java.io.File.class}); System.out.println(constructor); System.out.println("-------public field-------"); /* 获得类的所有public类型的属性。*/ Field[] fields = clazz1.getFields(); for(Field field : fields) { System.out.println(field); } System.out.println("-------field-------"); /* 获得类的所有属性。*/ Field[] fields1 = clazz1.getDeclaredFields(); for(Field field : fields1) { System.out.println(field); } System.out.println("-------field's setters and getters methods-------"); /* * 获取属性的set和get方法 * 我们不能直接获取setter和getter方法,需要获取到所有的方法, * 再进行循环选择。注意对于boolean类型,默认采用的是isXxx() */ Class<?> clazz2 = Class.forName("com.markliu.reflection.invoke.Person"); Method[] methods2 = clazz2.getMethods(); for(Method m : methods2) { if (isGetter(m)) System.out.println(m); if (isSetter(m)) System.out.println(m); } } public static boolean isGetter(Method method) { String methodName = method.getName(); Class<?> returnType = method.getReturnType(); Class<?> parameterTypes[] = method.getParameterTypes(); if(returnType.equals(void.class)) { return false; } if ( (methodName.startsWith("get") || methodName.startsWith("is")) && parameterTypes.length == 0) { return true; } return false; } public static boolean isSetter(Method method) { String methodName = method.getName(); Class<?> parameterTypes[] = method.getParameterTypes(); if (methodName.startsWith("set") && parameterTypes.length == 1) { return true; } return false; } }
输出结果为(考虑篇幅,只列出获取setter和getter方法的结果,其他的省略):
[code]public boolean com.markliu.reflection.invoke.Person.isGoodman() public void com.markliu.reflection.invoke.Person.setGoodman(boolean) public java.lang.Integer com.markliu.reflection.invoke.Person.getAge() public void com.markliu.reflection.invoke.Person.setAge(java.lang.Integer) public java.lang.String com.markliu.reflection.invoke.Person.getName() public void com.markliu.reflection.invoke.Person.setName(java.lang.String) public final native java.lang.Class java.lang.Object.getClass()
案例2:获取类的私有属性和私有方法。
person类的私有属性和方法:
[code]private String name; private Integer age; private void introduce() { System.out.println("My name is " + name + ", I'm " + age + " years old!"); }
[code]System.out.println("-------private fields and methods-------"); Person person = new Person("SunnyMarkLiu", 22); System.out.println("name:" + person.getName()); Field privateField = Person.class.getDeclaredField("name"); // !important 设置标志为true后,被反射的类会抑制java的访问检查机制 privateField.setAccessible(true); privateField.set(person, "HasChanged!"); System.out.println("反射修改后:" + person.getName()); Method privateMethod = Person.class.getDeclaredMethod("introduce", new Class[]{}); // !important 设置标志为true后,被反射的类会抑制java的访问检查机制 privateMethod.setAccessible(true); privateMethod.invoke(person, new Object[]{});
注意:在访问私有属性和私有方法时,需要对访问的私有属性或方法设置
setAccessible(true)使被反射的类抑制java的访问检查机制。否则会报
IllegalAccessException异常!
运行结果如下:
[code]name:SunnyMarkLiu 反射修改后:HasChanged! My name is HasChanged!, I'm 22 years old!
案例3:通过调用构造方法创建一个新的对象:。
先调用 Class 类的 getConstructor方法获得一个 Constructor 对象,然后调用 Constructor 对象的 newInstance方法构造一个实例。
[code]package com.markliu.reflection.newInstance; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; public class NewInstance { public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { Class<?> clazz = Class.forName("com.markliu.reflection.newInstance.PersonBean"); System.out.println(clazz); // 获取有参构造函数 Constructor<?> constructor = clazz.getConstructor(new Class[]{String.class, Integer.class}); PersonBean person = (PersonBean) constructor.newInstance(new Object[]{"SunnyMarkLiu", 20}); person.info(); // 获取有参构造函数 Constructor<?> constructor1 = clazz.getConstructor(new Class[]{}); PersonBean person1 = (PersonBean) constructor1.newInstance(new Object[]{}); person1.info(); } } class PersonBean implements Person{ private String name; private Integer age; public PersonBean() {} public PersonBean(String name, Integer age) { this.name = name; this.age = age; } // 省略了get和set方法 public void info() { System.out.println(this.name + ":" +this.age); } } interface Person { public void info(); }
案例4:运用反射机制调用对象的方法。
Method 类的 invoke(Object obj,Object args[])方法接收的参数必须为对象, 如果参数为基本类型数据, 必须转换为相应的包装类型的对象。 invoke()方法的返回值总是对象,如果实际被调用的方法的返回类型是基本类型数据, 那么 invoke()方法会把它转换为相应的包装类型的对象,再将其返回。
[code]package com.markliu.reflection.invoke; import java.lang.reflect.Method; public class MethodInvoke { public static void main(String[] args) throws Exception { Class<?> clazz = Class.forName("com.markliu.reflection.invoke.PersonService"); PersonService personService = (PersonService) clazz.getConstructor(new Class<?>[]{}).newInstance(new Object[]{}); // 获取要调用的方法 Method method = clazz.getMethod("add", new Class[]{Person.class}); /* * 调用personService对象的method方法,同时传递参数。 * 返回值result为调用该函数的返回值,如果函数返回类型为void,则result为null */ Object result = method.invoke(personService, new Object[]{new Person("SunnyMarkLiu", 22)}); System.out.println(result); Method method1 = clazz.getMethod("get", new Class[]{int.class}); Object result1 = method1.invoke(personService, new Object[]{0}); System.out.println(result1); Person p = (Person) result1; System.out.println(p.getName()+":"+p.getAge()); } } // PersonService类 package com.markliu.reflection.invoke; import java.util.List; import java.util.ArrayList; public class PersonService { private List<Person> persons = new ArrayList<Person>(); public String add(Person p) { persons.add(p); return "add-done"; } public Person get(int i) { return persons.get(i); } } // Person类 package com.markliu.reflection.invoke; public class Person { private String name; private Integer age; public Person(String name, Integer age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } }
案例5:Array 类提供了动态创建和访问数组元素的各种静态方法。
[code]package com.markliu.reflection.array; import java.lang.reflect.*; public class ArrayTester { public static void main(String args[]) throws Exception { // 设置数组的维度为5x10 int dims[] = new int[] { 5, 10, 15 }; Class<?> classType = Class.forName("java.lang.String"); Object array = Array.newInstance(classType, dims); // Object array = Array.newInstance(Integer.TYPE, dims); // 使 arrayObj 引用 array[3] Object arrayObj = Array.get(array, 3); Class<?> cls = arrayObj.getClass().getComponentType(); System.out.println("ComponentType" + cls); // 使 arrayObj 引用 array[3][5] Object arrayObj1 = Array.get(arrayObj, 5); // 把元素 array[3][5][10]设为 37 Array.set(arrayObj1, 10, "hello"); String arrayCast[][][] = (String[][][]) array; System.out.println(arrayCast[3][5][10]); /* * 输出: * ComponentTypeclass [Ljava.lang.String; * hello */ } }
4 小结
Java 反射机制是 Java 语言的一个重要特性。考虑实现一个 newInstance(String className)方法,它的作用是根据参数 className 指定的类名,通过该类的不带参数的构造方法创建这个类的对象,并将其返回。如果不运用 Java 反射机制,必须在newInstance()方法中罗列参数 className 所有可能的取值,然后创建相应的对象:[code]public Object newInstance(String className) throws Exception{ if(className.equals("HelloService1")) return new HelloService1(); if(className.equals("HelloService2")) return new HelloService2(); if(className.equals("HelloService3")) return new HelloService3(); if(className.equals("HelloService4")) return new HelloService4(); ... if(className.equals("HelloService1000")) return new HelloService1000(); }
以上程序代码很冗长,而且可维护性差。如果在以后软件的升级版本中去除了一个 HelloService4 类,或者增加了一个 HelloService1001 类,都需要修改以上 newInstance()方法。如果运用反射机制,就可以简化程序代码,并且提高软件系统的可维护性和可扩展性:
[code]public Object newInstance(String className) throws Exception{ Class classType=Class.forName(className); return classType.newInstance(); }
Java 反射机制在服务器程序和中间件程序中得到了广泛运用。在服务器端,往往需要根据客户的请求 ,动态调用某一个对象的特定方法。此外 ,有一种ORM(Object-Relation Mapping,对象-关系映射,例如Hibernate)中间件能够把任意一个 JavaBean 持久化到关系数据库中。在 ORM 中间件的实现中,运用 Java 反射机制来读取任意一个JavaBean 的所有属性, 或者给这些属性赋值。 (小结摘自:孙卫琴《Java网络编程精解》第10章)
相关文章推荐
- Spring4.2.0设计思想---总体架构
- myeclipse 没有提示时的设置
- JDK切换版本
- 多线程之:java线程创建
- 关于java中==号的一点了解
- java基础阶段总结
- 我的第六个java程序 spring-bean
- Java虚拟机运行时数据区
- 《疯狂Java讲义》第2章——理解面向对象
- LRU缓存介绍与实现 (Java)
- Spring 中的 classpath*: 与 classpath: 通配符
- SpringMVC注解@RequestParam
- Spring 框架的设计理念与设计模式分析
- java保存图片内容到数据库
- java -> sql 数据类型
- 设计模式(六)原型模式
- eclipse中通过properties->android->add关联项目出错
- Java类加载器总结整理
- Java语法基础
- Eclipse(adb)连不上Android手机 解决方法