反射

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

  • 加载完类之后,在堆内存的方法区就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:“反射”。

动态语言 VS 静态语言

1、动态语言:是一类在运行时可以改变其结构的语言:例如新的函数、对象、甚至代码可以
被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运
行时代码可以根据某些条件改变自身结构。

  • 主要动态语言:Object-C、C#、JavaScript、PHP、Python、Erlang。

2、静态语言:与动态语言相对应的,运行时结构不可变的语言就是静态语言。如Java、C、C++。

注意:Java不是动态语言,但Java可以称之为“准动态语言”。即Java有一定的动
态性,我们可以利用反射机制、字节码操作获得类似动态语言的特性。Java的动态性让编程的时候更加灵活!

Java反射机制研究及应用

  • 在运行时判断任意一个对象所属的类
  • 在运行时构造任意一个类的对象
  • 在运行时判断任意一个类所具有的成员变量和方法
  • 在运行时获取泛型信息
  • 在运行时调用任意一个对象的成员变量和方法
  • 在运行时处理注解
  • 生成动态代理

反射相关的主要API

  • java.lang.Class:代表一个类
  • java.lang.reflect.Method:代表类的方法
  • java.lang.reflect.Field:代表类的成员变量
  • java.lang.reflect.Constructor:代表类的构造器
  • ……

引入

定义一个Person类

  1. /**
  2. * Person类
  3. * @author tongfangping
  4. */
  5. public class Person {
  6. public int age;
  7. private String name;
  8. public Person() {
  9. }
  10. public Person(String name, int age) {
  11. this.name = name;
  12. this.age = age;
  13. }
  14. //私有
  15. private Person(String name) {
  16. this.name = name;
  17. }
  18. public String getName() {
  19. return name;
  20. }
  21. public void setName(String name) {
  22. this.name = name;
  23. }
  24. public int getAge() {
  25. return age;
  26. }
  27. public void setAge(int age) {
  28. this.age = age;
  29. }
  30. public void show(){
  31. System.out.println("你好,我是一个人");
  32. }
  33. public String showNation(String nation){
  34. System.out.println("我的国籍是:"+nation);
  35. return nation;
  36. }
  37. @Override
  38. public String toString() {
  39. return "Persopn{" +
  40. "name='" + name + '\'' +
  41. ", age=" + age +
  42. '}';
  43. }
  44. }

对比使用反射前后的做法

  1. /**
  2. * @author tongfangping
  3. */
  4. public class ReflectionTest {
  5. //反射之前,对于Person的操作
  6. @Test
  7. public void test1(){
  8. //1、创建Person的操作
  9. Person p1=new Person("Tom",12);
  10. //2、通过对象,调用内部的属性、方法
  11. p1.age=10;
  12. System.out.println(p1);
  13. p1.show();
  14. //在Person类的外部不能通过Person类的对象调用其内部私有的结构。
  15. //比如name,shownamtion(),以及私有的构造器
  16. }
  17. @Test
  18. public void test2() throws Exception {
  19. Class<Person> clazz=Person.class;
  20. //1、通过反射,创建Person的对象
  21. Constructor<Person> cons = clazz.getConstructor(String.class, int.class);
  22. Person tom = cons.newInstance("Tom", 12);
  23. System.out.println(tom);
  24. //2、通过反射,调用对象指定的属性和方法
  25. //调用属性
  26. Field age = clazz.getDeclaredField("age");
  27. age.set(tom,10);
  28. System.out.println(tom);
  29. //调用方法
  30. Method show = clazz.getDeclaredMethod("show");
  31. show.invoke(tom);
  32. System.out.println("*************************************");
  33. //通过反射,可以调用Person类的私有结构。比如:私有的构造器、方法、属性
  34. Constructor<Person> cons1 = clazz.getDeclaredConstructor(String.class);
  35. cons1.setAccessible(true);
  36. Person jerry = cons1.newInstance("Jerry");
  37. System.out.println(jerry);
  38. //调用私有的属性
  39. Field name = clazz.getDeclaredField("name");
  40. name.setAccessible(true);
  41. name.set(jerry,"Lilei");
  42. System.out.println(jerry);
  43. //2、调用私有的方法
  44. Method showNation = clazz.getDeclaredMethod("showNation", String.class);
  45. String nation= (String) showNation.invoke(jerry,"中国");
  46. System.out.println(nation);
  47. }
  48. }

java.lang.Class类的理解

  • 1、类加载的过程:程序经过java.exe命令以后,会生成以一个或多个字节码文件(.class结尾)。接着我们使用java.exe命令对该字节码文件进行解释运行。我们相当于将某个字节码文件加载到内存中。此过程就成为类的加载。加载到内存中的类,我们就称为运行时类,此运行时类,就作为Class的一个实例。
  • 2、换句话说:Class的实例就对应着一个运行时类
  • 3、加载到内存中运行时类,会缓存一定时间。在此时间内,我们可以通过不同的方式来获取此运行时类
  • 4、一个加载的类在 JVM 中只会有一个Class实例
  • 5、一个Class对象对应的是一个加载到JVM中的一个.class文件
  • 6、Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的
    Class对象
  • 描述类的类,类可以造对象、类的本身也是一个对象;反射是个概念,但是概念本身也得需要一个概念去解释。

Class类的常用方法

方法名 功能说明
static Class forName(String name) 返回指定类名 name 的 Class 对象
Object newInstance() 调用缺省构造函数,返回该Class对象的一个实例
getName() 返回此Class对象所表示的实体(类、接口、数组类、基本类型或void)名称
Class getSuperClass() 返回当前Class对象的父类的Class对象
Class [] getInterfaces() 获取当前Class对象的接口
ClassLoader getClassLoader() 返回该类的类加载器
Class getSuperclass() 返回表示此Class所表示的实体的超类的Class
Constructor[] getConstructors() 返回一个包含某些Constructor对象的数组
Field[] getDeclaredFields() 返回Field对象的一个数组
Method getMethod(Stringname,Class … paramTypes) 返回一个Method对象,此对象的形参类型为paramType

获取Class的实例的方式

方式一:调用运行时类的属性:.class

  1. //若已知具体的类,通过类的class属性获取,该方法最为安全可靠,程序性能最高
  2. Class clazz1=Person.class;
  3. System.out.println(clazz1);//class com.tfp.study.Person

方式二:调用运行时类的对象,调用getClass()

  1. //前提:已知某个类的实例,调用该实例的getClass()方法获取Class对象
  2. Person p1=new Person();
  3. Class clazz2 = p1.getClass();
  4. System.out.println(clazz2);//class com.tfp.study.Person

方式三:调用Class的静态方法:forName(String classPath)

  1. //已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException
  2. Class clazz3 = Class.forName("com.tfp.study.Person");
  3. clazz3=Class.forName("java.lang.String");
  4. System.out.println(clazz2);//class com.tfp.study.Person
  5. System.out.println(clazz3);//class java.lang.String

方式四:使用类的加载器:ClassLoader

  1. ClassLoader classLoader=ReflectionTest.class.getClassLoader();
  2. Class clazz4 = classLoader.loadClass("com.tfp.study.Person");
  3. System.out.println(clazz4);

对象对比

  1. System.out.println(clazz1 == clazz2);//true
  2. System.out.println(clazz1 == clazz3);//true

哪些类型可以有Class对象?

  • 1、class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
  • 2、interface:接口
  • 3、[]:数组
  • 4、enum:枚举
  • 5、annotation:注解@interface
  • 6、primitive type:基本数据类型
  • 7、void
  1. @Test
  2. public void test4(){
  3. Class c1 = Object.class;
  4. Class c2 = Comparable.class;
  5. Class c3 = String[].class;
  6. Class c4 = int[][].class;
  7. Class c5 = ElementType.class;
  8. Class c6 = Override.class;
  9. Class c7 = int.class;
  10. Class c8 = void.class;
  11. Class c9 = Class.class;
  12. int[] a = new int[10];
  13. int[] b = new int[100];
  14. Class c10 = a.getClass();
  15. Class c11 = b.getClass();
  16. // 只要数组的元素类型与维度一样,就是同一个Class
  17. System.out.println(c10 == c11);//true
  18. }

ClassLoader读取配置Properties文件

  1. @Test
  2. public void test2(){
  3. properties pros=new Properties();
  4. //此时的文件默认在当前的model下
  5. //读取配置文件的方式一:
  6. FileInputStream fis = new FileInputStream("jdbc.properties");
  7. pros.load(fis);
  8. //读取配置文件的方式二:使用ClassLoader
  9. //配置文件默认识别为:当前model的src下
  10. ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
  11. InputStream is = classLoader.getResourceAsStream("jdbc.properties");
  12. pros.load(is);
  13. String user=pros.getProperty("user");
  14. String password = pros.getProperty("password");
  15. System.out.println("user = "+user + ",password=" + password);
  16. }

创建运行时类的对象:newInstance()

  1. @Test
  2. public void test1() throws IllegalAccessException, InstantiationException {
  3. Class<Person> clazz=Person.class;
  4. /*
  5. newInstance():调用此方法,创建对应的运行时对象。内部调用了运行时类的空参的构造器。
  6. 要想此方法正常的创建运行类的对象,要求:
  7. 1、运行时类必须提供空参的构造器
  8. 2、空参的构造器的访问权限得够。通常,设置为public
  9. 在javabean中要求提供一个public的空参构造器。原因:
  10. 1、便于通过反射,创建运行时类的对象
  11. 2、便于子类继承此运行时类,默认调用super()时,保证父类有此构造器
  12. */
  13. Person obj = clazz.newInstance();
  14. System.out.println(obj);

反射的动态性

只有在运行时才知道锁创建的是哪一个类的对象

  1. @Test
  2. public void test2(){
  3. int num = new Random().nextInt(3);//0,1,2
  4. String classPath="";
  5. switch (num){
  6. case 0:
  7. classPath="java.util.Date";
  8. break;
  9. case 1:
  10. classPath = "java.lang.Object";
  11. break;
  12. case 2:
  13. classPath = "com.tfp.study.Person";
  14. break;
  15. }
  16. try {
  17. Object obj = getInstance(classPath);
  18. System.out.println(obj);
  19. } catch (Exception e) {
  20. e.printStackTrace();
  21. }
  22. }
  23. /*
  24. 创建一个指定类的对象
  25. classPath:指定类的全类名
  26. */
  27. public Object getInstance(String classPath) throws Exception {
  28. Class clazz = Class.forName(classPath);
  29. return clazz.newInstance();
  30. }

获取运行时类的完整结构

Field、Method、Constructor、Superclass、Interface、Annotation

  • 全部的属性:Field
  • 全部的方法:Method
  • 全部的构造器:Constructor
  • 所继承的父类:Superclass
  • 实现的全部接口:Interface
  • 注解:Annotation
  1. package com.tfp.study1;
  2. /**
  3. * Person类
  4. * @author tongfangping
  5. */
  6. @MyAnnotation(value = "Hi")
  7. public class Person extends Creature<String> implements Comparable<String>,MyInterface{
  8. private String name;
  9. int age;
  10. public int id;
  11. public Person() {
  12. }
  13. @MyAnnotation(value = "abc")
  14. private Person(String name) {
  15. this.name = name;
  16. }
  17. public Person(String name, int age) {
  18. this.name = name;
  19. this.age = age;
  20. }
  21. @MyAnnotation(value = "show")
  22. private String show(String nation){
  23. System.out.println("我的国籍是:"+ nation);
  24. return nation;
  25. }
  26. public String display(String interests) throws NullPointerException,ClassCastException{
  27. return interests;
  28. }
  29. @Override
  30. public void info() {
  31. System.out.println("我是一个人");
  32. }
  33. @Override
  34. public int compareTo(String o) {
  35. return 0;
  36. }
  37. private static void showDesc(){
  38. System.out.println("我是一个可爱的人");
  39. }
  40. }

一、全部的Field

- public Field[] getFields();

返回此Class对象所表示的类或接口的 public 的Field。

- public Field[] getDeclaredFields()

返回此Class对象所表示的类或接口的全部 Field。

  1. @Test
  2. public void test1(){
  3. Class clazz = Person.class;
  4. //获取属性结构
  5. //getFields():获取当前运行时类及其父类中声明为 public 访问权限的属性
  6. Field[] fields = clazz.getFields();
  7. for (Field f: fields
  8. ) {
  9. System.out.println(f);
  10. }
  11. System.out.println("***********************************");
  12. //getDeclaredFields():获取当前运行时类中声明的所有属性。(不包含父类中声明的属性)
  13. Field[] declaredFields = clazz.getDeclaredFields();
  14. for (Field f: declaredFields
  15. ) {
  16. System.out.println(f);
  17. }
  18. }

获取属性的 权限修饰符、数据类型、变量名

  1. @Test
  2. public void test2(){
  3. //1、权限修饰符
  4. Class clazz = Person.class;
  5. Field[] declaredFields = clazz.getDeclaredFields();
  6. for (Field f: declaredFields
  7. ) {
  8. int modifiers = f.getModifiers();
  9. System.out.print(Modifier.toString(modifiers) + "\t");
  10. //2、数据类型
  11. Class<?> type = f.getType();
  12. System.out.print(type.getName() + "\t");
  13. //3、变量名
  14. String name = f.getName();
  15. System.out.println(name);
  16. System.out.println();
  17. }
  18. }

二、全部的Method

- public Method[] getDeclaredMethods()

返回此Class对象所表示的类或接口的全部方法

public Method[] getMethods()

返回此Class对象所表示的类或接口的 public 的方法

  1. @Test
  2. public void test1() {
  3. Class clazz = Person.class;
  4. //getMethods():获取当前运行时类及其父类当中声明为 pulic 权限的方法
  5. Method[] methods = clazz.getMethods();
  6. for (Method m : methods
  7. ) {
  8. System.out.println(m);
  9. }
  10. System.out.println("********************************");
  11. //getDeclaredMethods():获取当前运行时类中声明的所有方法,不包含父类中声明的方法
  12. Method[] declaredMethods = clazz.getDeclaredMethods();
  13. for (Method m : declaredMethods
  14. ) {
  15. System.out.println(m);
  16. }
  17. System.out.println("********************************");
  18. }

Method类中:返回值、参数、权限修饰符、异常信息

  • public String getName():方法名
  • public Class<?> getReturnType():返回类型
  • public Class<?>[] getParameterTypes():形参列表
  • public int getModifiers():权限修饰符
  • public Class<?>[] getExceptionTypes():取得异常信息
  • public annotation.Annotation[] getAnnotations():方法声明的注解
  1. /*
  2. @Xxxx
  3. 权限修饰符 返回值类型 方法名(参数类型1 形参名1,...) throws XxxException
  4. */
  5. @Test
  6. public void test2() {
  7. Class clazz = Person.class;
  8. Method[] declaredMethods = clazz.getDeclaredMethods();
  9. for (Method m : declaredMethods
  10. ) {
  11. //System.out.println(m);
  12. //1、获取方法声明的注解
  13. Annotation[] annotations = m.getAnnotations();
  14. for (Annotation a : annotations
  15. ) {
  16. System.out.println(a);
  17. }
  18. //2、权限修饰符
  19. System.out.print(Modifier.toString(m.getModifiers()) + "\t");
  20. //3、返回类型
  21. System.out.print(m.getReturnType().getName() + "\t");
  22. //4、方法名
  23. System.out.print(m.getName());
  24. System.out.print("(");
  25. //5、形参列表
  26. Class[] parameterTypes = m.getParameterTypes();
  27. if (!(parameterTypes == null && parameterTypes.length == 0)) {
  28. for (int i = 0; i < parameterTypes.length; i++) {
  29. {
  30. if(i == parameterTypes.length - 1){
  31. System.out.print(parameterTypes[i].getName() + " args_" + i);
  32. break;
  33. }
  34. System.out.print(parameterTypes[i].getName() + " agrs_" + i + ",");
  35. }
  36. }
  37. System.out.print(")");
  38. //6、抛出的异常
  39. Class[] exceptionTypes = m.getExceptionTypes();
  40. if(!(exceptionTypes == null || exceptionTypes.length == 0)){
  41. System.out.print("throws");
  42. for(int i=0;i<exceptionTypes.length;i++){
  43. if(i == exceptionTypes.length-1){
  44. System.out.print(exceptionTypes[i].getName());
  45. break;
  46. }
  47. System.out.print(exceptionTypes[i].getName() + ",");
  48. }
  49. }
  50. System.out.println();
  51. }
  52. }
  53. }

三、全部的构造器

- public Constructor[] getConstructors()

返回此 Class 对象所表示的类的所有public构造方法。

- public Constructor[] getDeclaredConstructors()

返回此 Class 对象表示的类声明的所有构造方法。

  1. /*
  2. 获取构造器结构
  3. */
  4. @Test
  5. public void test(){
  6. Class clazz = Person.class;
  7. //getConstructors():获取当前运行时类中声明为public的构造器
  8. Constructor[] constructors = clazz.getConstructors();
  9. for (Constructor c:constructors
  10. ) {
  11. System.out.println(c);
  12. }
  13. System.out.println("**************************");
  14. //getDeclaredConstructors():获取当前运行时类中声明的所有构造器
  15. Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
  16. for (Constructor dc:declaredConstructors
  17. ) {
  18. System.out.println(dc);
  19. }
  20. }

四、所继承的父类

- public Class<? Super T> getSuperclass()

返回表示此 Class 所表示的实体(类、接口、基本类型)的父类的Class

- public reflect.Type getGenericSuperclass()

返回表示此 Class 所表示的实体(类、接口、基本类型)的带泛型的父类的Class

- public abstract reflect.Type[] getActualTypeArguments()

获取运行时类的带泛型的父类的泛型

  1. /*
  2. 获取运行时类的父类
  3. */
  4. @Test
  5. public void test2(){
  6. Class clazz = Person.class;
  7. Class superclass = clazz.getSuperclass();
  8. System.out.println(superclass);
  9. }
  1. /*
  2. 获取运行时类的带泛型的父类
  3. */
  4. @Test
  5. public void test3(){
  6. Class clazz = Person.class;
  7. Type genericSuperclass = clazz.getGenericSuperclass();
  8. System.out.println(genericSuperclass);
  9. }
  1. /*
  2. 获取运行时类的带泛型的父类的泛型
  3. */
  4. @Test
  5. public void test4(){
  6. Class clazz = Person.class;
  7. Type genericSuperclass = clazz.getGenericSuperclass();
  8. ParameterizedType paramType = (ParameterizedType)genericSuperclass;
  9. //获取泛型类型
  10. Type[] actualTypeArguments = paramType.getActualTypeArguments();
  11. System.out.println(actualTypeArguments[0].getTypeName());
  12. }

五、获取运行时类实现的接口

public Class<?>[] getInterfaces()

获取运行时类实现的接口

  1. /*
  2. 获取运行时类实现的接口
  3. */
  4. @Test
  5. public void test5(){
  6. Class clazz = Person.class;
  7. Class[] interfaces = clazz.getInterfaces();
  8. for (Class i:interfaces
  9. ) {
  10. System.out.println(i);
  11. }
  12. System.out.println("***************");
  13. /**
  14. * 获取运行时类的父类实现的接口
  15. */
  16. Class superclass = clazz.getSuperclass();
  17. Class[] interfaces1 = superclass.getInterfaces();
  18. for (Class i1:interfaces1
  19. ) {
  20. System.out.println(i1);
  21. }
  22. }

六、获取运行时类所在的包

public Package getPackage()

获取运行时类所在的包

  1. /*
  2. 获取运行时类所在的包
  3. */
  4. @Test
  5. public void test6(){
  6. Class clazz = Person.class;
  7. Package Package = clazz.getPackage();
  8. System.out.println(Package);
  9. }

七、获取运行时类上的注解

public annotation.Annotation[] getAnnotations()

、获取运行时类上的注解

  1. /*
  2. 获取运行时类上的注解
  3. */
  4. @Test
  5. public void test7(){
  6. Class clazz = Person.class;
  7. Annotation[] annotations = clazz.getAnnotations();
  8. for (Annotation a:annotations
  9. ) {
  10. System.out.println(a);
  11. }
  12. }

调用运行时类中指定的结构:属性、方法、构造器

一、如何操作运行时类中指定的属性

在反射机制中,可以直接通过Field类操作类中的属性,通过Field类提供的set()和get()方法就可以完成设置和取得属性内容的操作。

  • public Field getField(String name)

返回此Class对象表示的类或接口的指定的public的Field。

  • public Field getDeclaredField(String name)

返回此Class对象表示的类或接口的指定的Field。

在Field中

  • public Object get(Object obj)

取得指定对象obj上此Field的属性内容

  • public void set(Object obj,Object value)

设置指定对象obj上此Field的属性内容

  1. @Test
  2. public void testField() throws Exception {
  3. Class clazz = Person.class;
  4. //创建运行时类的对象
  5. Person p = (Person) clazz.newInstance();
  6. System.out.println(p);
  7. //获取指定的属性:要求运行时类中的属性为public的
  8. //通常不采用此方法
  9. Field id = clazz.getField("id");
  10. System.out.println(id);
  11. /*
  12. 设置当前属性的值
  13. set():参数1:指明设置哪个对象的属性 参数2:将此属性值设置为多少
  14. */
  15. id.set(p,1001);
  16. /*
  17. 获取当前属性的值
  18. get():参数1:获取哪个对象的当前属性
  19. */
  20. int pId = (int) id.get(p);
  21. System.out.println(pId);
  22. }
  1. /*
  2. 如何操作运行时类中指定的属性:getDeclaredField()
  3. */
  4. @Test
  5. public void testField1() throws Exception{
  6. Class clazz = Person.class;
  7. //创建运行时类的对象
  8. Person p = (Person) clazz.newInstance();
  9. //getDeclaredField(String fieldName):获取运行时类中指定变量名的属性
  10. Field name = clazz.getDeclaredField("name");
  11. //保证当前属性是可以访问的
  12. name.setAccessible(true);
  13. //设置、获取指定对象的属性值
  14. name.set(p,"Tom");
  15. System.out.println(name.get(p));
  16. }

如何操作运行时类中指定的方法

通过反射,调用类中的方法,通过Method类完成。步骤:

①.通过Class类的getMethod(String name,Class…parameterTypes)方法取得一个Method对象,并设置此方法操作时所需要的参数类型。

②.之后使用Object invoke(Object obj, Object[] args)进行调用,并向方法中
传递要设置的obj对象的参数信息。
26.反射机制 - 图1

  1. /*
  2. 如何操作运行时类中指定的方法
  3. */
  4. @Test
  5. public void testMethod() throws Exception {
  6. Class clazz = Person.class;
  7. //创建运行时类的对象
  8. Person p = (Person) clazz.newInstance();
  9. /*
  10. 1、获取指定的某个方法
  11. getDeclaredMethod():参数1:指明获取的方法的名称 参数2:指明获取的方法的形参列表
  12. */
  13. Method show = clazz.getDeclaredMethod("show", String.class);
  14. //2、保证当前方法时可访问的
  15. show.setAccessible(true);
  16. /*
  17. 调用方法的invoke():参数1:方法的调用者 参数2:给方法形参赋值的实参
  18. invoke()的返回值即为对应类中调用的方法的返回值。即下面show()的返回值
  19. */
  20. Object returnValue = show.invoke(p, "CHN");//String nation = p.show("CHN");
  21. System.out.println(returnValue); //CHN
  22. System.out.println("*************如何调用静态方法*************");
  23. // private static void showDesc()
  24. Method showDesc = clazz.getDeclaredMethod("showDesc");
  25. showDesc.setAccessible(true);
  26. //如果调用的运行时类中的方法没有返回值,则此invoke()返回null
  27. Object returnVal = showDesc.invoke(Person.class);
  28. System.out.println(returnVal);//null
  29. }

如何操作运行时类中指定的构造器

  • getDeclaredConstructor(parameterTypes)

返回此Class对象表示的类或接口的指定的constructor。

  1. /*
  2. 如何操作运行时类中指定的构造器
  3. */
  4. @Test
  5. public void testConstructor() throws Exception {
  6. Class clazz = Person.class;
  7. //private Person(String name)
  8. /*
  9. 1、获取指定的构造器
  10. getDeclaredConstructor():参数:指明构造器的参数类型
  11. */
  12. Constructor constructor = clazz.getDeclaredConstructor(String.class);
  13. //2、保证此构造器时可访问的
  14. constructor.setAccessible(true);
  15. //3、调用此构造器,创建运行时类的构造器
  16. Person tom = (Person) constructor.newInstance("Tom");
  17. System.out.println(tom);
  18. }

关于setAccessible方法的使用

  • MethodField、Constructor对象都有setAccessible()方法
  • setAccessible启动和禁用访问安全检查的开关。
  • 参数值为true则指示反射的对象在使用时应该取消Java语言访问检查

    • 提高反射的效率。如果代码中必须用反射,而该句代码需要频繁的被调用,那么请设置为true。
    • 使得原本无法访问的私有成员也可以访问
  • 参数值为false则指示反射的对象应该实施Java语言访问检查。

反射的应用

动态代理

代理设计模式的原理:

使用一个代理将对象包装起来, 然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。

静态代理

静态代理,特征是代理类和目标对象的类都是在编译期间确定下来,不利于程序的扩展。

  1. /**
  2. * 静态代理
  3. * 特点:代理类和被代理类在编译期间,就确定下来了。
  4. * @author tongfangping
  5. */
  6. interface ClothFactory{
  7. void produceCloth();
  8. }
  9. //代理类
  10. class ProxyClothFactory implements ClothFactory{
  11. private ClothFactory factory; //用被代理类对象进行实例化
  12. public ProxyClothFactory(ClothFactory factory) {
  13. this.factory = factory;
  14. }
  15. @Override
  16. public void produceCloth() {
  17. System.out.println("代理工厂做一些准备工作");
  18. factory.produceCloth();
  19. System.out.println("代理工厂做收尾工作");
  20. }
  21. }
  22. //被代理类
  23. class NikeClothFactory implements ClothFactory{
  24. @Override
  25. public void produceCloth() {
  26. System.out.println("NIke工厂生产一批运动服");
  27. }
  28. }
  29. //测试
  30. public class StaticProxy {
  31. public static void main(String[] args) {
  32. ClothFactory nike = new NikeClothFactory();
  33. ClothFactory proxy = new ProxyClothFactory(nike);
  34. proxy.produceCloth();
  35. }
  36. }

26.反射机制 - 图2

动态代理

  • 动态代理是指客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。
  • 动态代理使用场合:

    • 调试
    • 远程方法调用
  • 动态代理相比于静态代理的优点:

    • 抽象角色中(接口)声明的所有方法都被转移到调用处理器一个集中的方法中处理,这样,我们可以更加灵活和统一的处理众多的方法。

Java动态代理相关API

  • Proxy:专门完成代理的操作类,是所有动态代理类的父类。通过此类为一
    个或多个接口动态地生成实现类。
  • 提供用于创建动态代理类和动态代理对象的静态方法

    • static Class getProxyClass(ClassLoader loader, Class… interfaces) 创建一个动态代理类所对应的Class对象
    • static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h) 直接创建一个动态代理对象

      • ClassLoader loader:类加载器(被代理类的类加载器)
      • Class<?>[] interfaces:得到被代理类实现的全部接口
      • InvocationHandler h:得到InvocationHandler接口的实现类实例

动态代理步骤

  • 1、创建一个实现接口 InvocationHandler 的类,它必须实现 invoke 方法,以完成代理的具体操作。

26.反射机制 - 图3

  • 2.创建被代理的类以及接口
    26.反射机制 - 图4
  • 3.通过Proxy的静态方法
  1. newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 创建
  2. 一个Subject接口代理
  3. RealSubject target = new RealSubject();
  4. // Create a proxy to wrap the original implementation
  5. DebugProxy proxy = new DebugProxy(target);
  6. // Get a reference to the proxy through the Subject interface
  7. Subject sub = (Subject) Proxy.newProxyInstance(
  8. Subject.class.getClassLoader(),new Class[] { Subject.class }, proxy);
  • 4.通过 Subject代理调用RealSubject实现类的方法
  1. String info = sub.say(“Peter", 24);
  2. System.out.println(info);

举例

  1. /**
  2. * 动态代理的举例
  3. * @author tongfangping
  4. */
  5. interface Human{
  6. String getBelief();
  7. void eat(String food);
  8. }
  9. //被代理类
  10. class SuperMan implements Human{
  11. @Override
  12. public String getBelief() {
  13. return "I belief I can fly!";
  14. }
  15. @Override
  16. public void eat(String food) {
  17. System.out.println("我喜欢吃"+food);
  18. }
  19. }
  20. /*
  21. 要想实现动态代理,需要解决的问题?
  22. 问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象
  23. 问题二:当通过代理类的对象调用方法时,动态的去调用被代理类中的同名方法。
  24. */
  25. class ProxyFactory{
  26. //调用此方法返回一个代理类对象。解决问题一
  27. public static Object getProxyInstance(Object obj){ //obj:被代理类对象
  28. MyInvocationHandler handler = new MyInvocationHandler();
  29. handler.bind(obj);
  30. return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler);
  31. }
  32. }
  33. class MyInvocationHandler implements InvocationHandler {
  34. private Object obj;//需要使用被代理类对象进行赋值
  35. public void bind(Object obj){
  36. this.obj = obj;
  37. }
  38. //当我们通过代理类的对象,调用方法a时,就会自动的调用如下的方法:invoke()
  39. //将被代理类要执行的方法a的功能就声明在invoke()中
  40. @Override
  41. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  42. //method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法
  43. // obj:被代理类对象
  44. Object returnValue = method.invoke(obj, args);
  45. //上述方法的返回值就作为当前类中的invoke()的返回值
  46. return returnValue;
  47. }
  48. }
  49. public class DynamicProxy {
  50. public static void main(String[] args) {
  51. SuperMan superMan = new SuperMan();
  52. Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
  53. //当通过代理类对象调用方法时,会自动的调用被代理类中同名的方法
  54. proxyInstance.getBelief();
  55. proxyInstance.eat("德州扒鸡");
  56. System.out.println("***************************************");
  57. NikeClothFactory nikeClothFactory = new NikeClothFactory();
  58. ClothFactory proxyClothFactory = (ClothFactory) ProxyFactory.getProxyInstance(nikeClothFactory);
  59. proxyClothFactory.produceCloth();
  60. }
  61. }

26.反射机制 - 图5