动态语言 程序运行时,可以改变程序结构或变量类型。典型的语言:Python、ruby、js等。
C, C++, JAVA不是动态语言,JAVA可以称之为“准动态语言”。但是JAVA有一定的动态性,我们可以利用反射机制、 字节码操作获得类似动态语言的特性。JAVA的动态性让编程的时候更加灵活!
Java反射机制 Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
程序在运行状态中,可以动态加载一个只有名称的类,对于任意一个 已加载的类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性。
加载完类之后,在堆内存中,就产生了一个 Class 类型的对象(一个类只有一个 Class 对象),这个对象就包含了完整的类的结构信息。 我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射 。
Java反射机制提供的功能 在运行时判断任意一个对象所属的类
在运行时构造任意一个类的对象
在运行时判断任意一个类所具有的成员变量和方法
在运行时调用任意一个对象的成员变量和方法
生成动态代理
反射初体验 一个Peason类,里面包含了:
无参数构造方法 有参数构造方法(包括private和public的) public的属性name、private的属性age get和set方法 自定义方法(包括private和public的) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 public  class  Peason      public  String name;       private  int  age;     public  Peason ()       }     public  Peason (String name, int  age)           this .name = name;         this .age = age;     }     private  Peason (String name)           this .name = name;     }     public  String getName ()           return  name;     }     public  void  setName (String name)           this .name = name;     }     public  int  getAge ()           return  age;     }     public  void  setAge (int  age)           this .age = age;     }     @Override      public  String toString ()           return  "Peason{"  +                 "name='"  + name + '\''  +                 ", age="  + age +                 '}' ;     }     private  String show (String nation)          System.out.println("国籍为:"  + nation);         return  nation;     }     public  void  show ()          System.out.println("我是一个人" );     } } 
反射示例,通过反射创建对象,调用对象的属性和方法以及修改私有的属性和调用使用的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 @Test    public  void  test ()  throws  Exception         Class clazz = Peason.class;                Constructor constructor = clazz.getConstructor(String.class, int .class);        Object tom = constructor.newInstance("tom" , 15 );        System.out.println(tom.toString());         System.out.println((tom instanceof  Peason));                Field age = clazz.getDeclaredField("name" );        age.set(tom, "zhu" );        System.out.println(tom.toString());        Field[] declaredFields = clazz.getDeclaredFields();        System.out.println(Arrays.toString(declaredFields));                Method show = clazz.getDeclaredMethod("show" );        show.invoke(tom);        System.out.println("**********************************************" );                Constructor cons1 = clazz.getDeclaredConstructor(String.class);        cons1.setAccessible(true );        Object jack = cons1.newInstance("jack" );        System.out.println(jack.toString());        Field name = clazz.getDeclaredField("name" );        name.setAccessible(true );        name.set(jack, "lihang" );        System.out.println(jack.toString());                Method showNation = clazz.getDeclaredMethod("show" , String.class);        showNation.setAccessible(true );        Object china = showNation.invoke(jack, "China" );        System.out.println(china instanceof  String);        System.out.println(china);    } 
既然使用反射也可以创建对象,那么在开发中是使用new还是使用反射来创建对象呢? 答案是通过new关键字来创建对象。如果在编译之前不知道需要创建哪个对象,则需要通过反射来创建对象,体现了反射的动态性。
封装性的目的就是为了不让外面的对象来调用私有的方法,然而反射却可以调用私有的属性和方法,那么反射机制与面向对象的封装性是不是矛盾的呢?其实不矛盾,因为封装的目的中带有提示的成分,建议程序员不要去调用私有的方法,因为以及提供了public的方法,没有必要去调用的私有的方法,这是封装的一种提示。而反射是可不可以的问题。
Class对象 java.lang.Class类十分特殊,用来表示java中的类型。
class / interface / enum / annotation / [] / primitive type / void
1 2 3 4 5 6 7 8 9 10 11 Class c1 = Object.class; System.out.println(c1);  Class c2 = int [][].class; System.out.println(c2);  Class c3 = int [].class; System.out.println(c3);  int [] a = new  int [10 ];int [] b = new  int [100 ];Class c4 = a.getClass(); Class c5 = b.getClass(); System.out.println(c4 == c5);  
对象照镜子后可以得到的信息:某个类的属性 、方法和构造器 、某个类到底实现了哪些接口 。对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。一个 Class 对象包含了特定某个类的有关信息。Class类是Reflection的根源。针对任何您想动态加载、运行的类,唯有先获得相应的Class 对象。
Class本身也是一个类
Class对象只能由系统建立对象
一个类在JVM 中只会有一个Class实例 (重要: 不管你用哪种方式获取的Class实例,都是同一个)
一个Class对象对应的是一个加载到JVM中的一个.class文件
每个类的实例都会记得自己是由哪个Class实例所生成
通过Class可以完整地得到一个类中的完整结构
类加载的过程 程序进过javac.exe 命令以后,会生成过程一个或者多个字节码文件(.class结尾),接着我们使用java.exe 命令对某个字节码文件进行解释执行。相当于字节码文件加载到内存中。此过程就称为类的加载,我们就称为运行时类 ,此运行时类就作为一个Class的一个实例。
加载到内存中的运行时类,会缓存一段时间。在此时间之内,我们可以通过不同的方式来获取次运行时类。
Class对象的获取 第一种方法 若已知具体的类,通过类的class属性获取,该方法最为安全可靠,程序性能最高。
1 2 Class<PersonC> clazz = PersonC.class; System.out.println(clazz);  
第二种方式 已知某个类的实例,调用该实例的getClass()方法获取Class对象
1 2 3 PersonC p = new  PersonC(); Class clazz1 = p.getClass(); System.out.println(clazz1);  
第三种方式 已知一个类的全类名,且该类在类路径下,可通过Class类的静态方法forName()获取,可能抛出ClassNotFoundException。
1 2 3 4 public  void  test1 ()  throws  ClassNotFoundException         Class clazz2 = Class.forName("PersonC" );         System.out.println(clazz2);     } 
1 2 3 System.out.println(clazz == clazz1);   System.out.println(clazz == clazz2); System.out.println(clazz1 == clazz2); 
第四种方式:类加载器 1 2 3 ClassLoader loader = this .getClass().getClassLoader(); Class clazz4 = loader.loadClass("PersonC" ); System.out.println(clazz4);  
类加载机制 当程序主动使用某个类时,如果该类还未被加载到内存中,则系统会通过如下三个步骤来对该类进行初始化。
加载 将 class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的 java.lang.Class对象,作为方法区中类数据的访问入口(即引用地址)。所有需要访问和使用类数据只能通过这个Class对象。这个加载的过程需要类加载器参与。
链接 将Java类的二进制代码合并到 JVM 的运行状态之中的过程。
验证 :确保加载的类信息符合JVM规范,例如:以cafe开头,没有安全方面的问题
准备 :正式为类变量( static)分配内存并设置类变量默认初始值 的阶段,这些内存都将在方法区中进行分配。比如 static int n; 在这个环节n会被赋值为 0 。
解析 :虛拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。
初始化 执行类构造器<cini>()方法的过程。类构造器< clinit>()方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的 。(类构造器是构造类信息的,不是构造该类对象的构造器)。static int n = 2; //初始化阶段赋值
当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。
虛拟机会保证一个类的< clinit>()方法在多线程环境中被正确加锁和同步。
举例说明类加载 1 2 3 4 5 6 7 8 9 class  A     static  {           m = 300 ;     }     static  int  m = 100 ;  }  public  void  testLoadingClass ()          System.out.println(A.m);    } 
根据类加载机制,首先第一步类加载,将A加载到内存当中,已经有一个class实例了;
第二步是类的链接,进行类变量的默认初始值, 将m 赋值为0, 即链接结束后 m = 0;
第三步是初始化, 由于静态代码块在静态变量定义之前,所以,m 先赋值为300, 然后赋值为100。 m的值由< clinit>()方法执行决定的。 这个A的类构造器< clinit>()方法由类变量的赋值和静态的代码块中的语句按照顺序合并产生,类似于:
1 2 3 4 < clinit>(){ 	m = 300 ;     m = 100 ; } 
故,最终输出的值为100。
类的主动引用 此过程一定会发生类的初始化
new一个类的对象 调用类的静态成员(除了final常量)和静态方法 使用java.lang.reflect包的方法对类进行反射调用 当虚拟机启动,java Hello,则一定会初始化Hello类。说白了就是先启动main方法所在的类 当初始化一个类,如果其父类没有被初始化,则先会初始化他的父类。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 public  class  classLoader      static  {         System.out.println("classLoader 类的静态代码块" );     }     public  static  void  main (String[] args)  throws  ClassNotFoundException          System.out.println("classLoader 中 main方法..." );                           System.out.println(A.b);                   A a = new  A();              } } class  A  extends  Father     public  static  int  k = 100 ;     public  static  final  int  b = 100 ;     static  {         System.out.println("静态初始化类A" );         k = 600 ;     }     public  A ()          System.out.println("类A的构造函数" );     } } class  Father     static  {         System.out.println("父类Father的静态代码块" );     }     public  Father ()          System.out.println("父类Father的构造函数" );     } } 
上述代码执行情况:
classLoader 类的静态代码块父类Father的静态代码块 静态初始化类A 
类的被动引用 不会发生类的初始化
当访问一个静态域时,只有真正声明这个域的类才会被初始化
通过子类引用父类的静态变量,不会导致子类初始化
通过数组定义类引用,不会触发此类的初始化
引用常量不会触发此类的初始化(常量在编译阶段就存入调用类的常量池中了)final
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class  Father     static  int  father = 666 ;     static  {         System.out.println("父类Father的静态代码块" );     }     public  Father ()          System.out.println("父类Father的构造函数" );     } } public  static  void  main (String[] args)  throws  ClassNotFoundException         System.out.println("classLoader 中 main方法..." );                           A[] arr = new  A[10 ];         System.out.println(A.father);     } 
classLoader 类的静态代码块
类加载器ClassLoader 类加载器作用:
类加载的作用:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构 ,然后在堆中生成一个代表这个类的 java.lang.class对象,作为方法区中类数据的访问入口。
类缓存:标准的 JavaSE 类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象。
类加载器是用来把类(class)装载进内存的。JVM 规范定义了两种类型的类加载器:启动类加载器 (bootstrap)和用户自定义加载器 (user-defined class loader)。 JVM在运行时会产生3个类加载器组成的初始化加载器层次结构 ,如下图所示:
引导类加载器 :用C++编写的,是JVM自带的类装载器,负责Java平台核心库 ,用来装载核心类库。该加载器无法直接获取。
扩展类加载器 :负责jre/lib/ext目录下的jar包或 –D java.ext.dirs 指定目录下的jar包装入工作库。
系统类加载器 :负责java –classpath 或 –D java.class.path所指的目录下的类与jar包装入工作 ,是最常用的加载器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 import  org.junit.Test;public  class  TestClassLoader      @Test      public  void  test ()          ClassLoader classLoader = TestClassLoader.class.getClassLoader();                  System.out.println(classLoader);                   ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();         System.out.println(systemClassLoader);                  ClassLoader parent = classLoader.getParent();         System.out.println(parent);          ClassLoader parent1 = parent.getParent();         System.out.println(parent1);                   ClassLoader classloader = Class.forNme("java.lang.Object" ).getClassLoader();         System.out.println(classloader);                     } } 
对于自定义类, 使用的是系统类加载器进行加载。调用系统类加载器的getParent()方法,获取扩展类系统加载器,调用扩展类系统加载器getParent() 无法获取引导类加载器,引导类加载器主要负责加载java的核心类库,无法加载自定义类。
类加载器的代理模式 代理模式 – 交给其他加载器来加载指定的类。
双亲委托机制  : 就是某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次追溯,直到最高的爷爷辈的,如果父类加载器 可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载 任务时,才自己去加载。
双亲委托机制是为了保证 Java 核心库的类型安全
这种机制就保证不会出现用户自己能定义java.lang.Object/String类的情况。
类加载器除了用于加载类,也是安全的最基本的屏障。
双亲委托机制是代理模式的一种。并不是所有的类加载器都采用双亲委托机制。tomcat服务器类加载器也使用代理模式,所不同的是它是首先尝试去加载某个类,如果找不到再代理给父类加载器。 这与一般类加载器的顺序是相反的。
ClassLoader的作用举例:读取配置文件1 2 3 4 5 6 7 8 9 10 public  static  void  main (String[] args)  throws  IOException        Properties properties = new  Properties();        FileInputStream fis = new  		    FileInputStream("./reflect/jdbc.properties" );        properties.load(fis);        String user = properties.getProperty("user" );        String password = properties.getProperty("password" );        System.out.println("user = " +user + ", passeord = " + password);    } 
自定义类加载器 流程 继承 java.lang.ClassLoader
1、首先检查请求的类型是否已经被这个类装载器装载到命名空间中了,如果已经装载,直接返回;否则转入步骤2。
2、委派类加载请求给父类加载器(更准确的说应该是双亲类加载器,真个虚拟机中各种类加载器最终会呈现树状结构),如果父类加载器能够完成,则返回父类加载器加载的Class实例;否则转入步骤3。
3、调用本类加载器的findClass(…)方法,试图获取对应的字节码,如果获取的到,则调用defineClass(…)导入类型到方法区;如 果获取不到对应的字节码或者其他原因失败,返回异常给loadClass(…), loadClass(…)转抛异常,终止加载过程(注意:这里的 异常种类不止一种)。
文件系统类加载器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 import  java.io.*;public  class  fileSystemClassLoader  extends  ClassLoader     private  String rootDir;     public  fileSystemClassLoader (String rootDir)           this .rootDir = rootDir;     }     @Override      protected  Class<?> findClass(String name) throws  ClassNotFoundException {         Class<?> loadedClass = findLoadedClass(name);                  if ( loadedClass != null ){             return  loadedClass;         }else  {             ClassLoader parent = this .getParent();             try  {                 loadedClass = parent.loadClass(name);              }catch  (Exception e){             }             if (loadedClass != null ){                 return  loadedClass;             }else {                                  byte [] classData = getClassData(name);                 if (classData == null ){                     throw  new  ClassNotFoundException();                 }else {                     loadedClass = defineClass(name, classData, 0 , classData.length);                 }             }         }         return  loadedClass;     }          private  byte [] getClassData(String name)  {         String path = rootDir + "/" + name.replace("." , "/" )+".class" ;         byte [] data = new  byte [1024 ];         ByteArrayOutputStream baos = new  ByteArrayOutputStream();         FileInputStream is = null ;         try  {             is = new  FileInputStream(path);             int  len = 0 ;             while  ((len = is.read(data)) != -1 ){                 baos.write(data, 0 , len);             }             return  baos.toByteArray();         } catch  (Exception e) {             e.printStackTrace();         }finally  {             if (is != null ){                 try  {                     is.close();                 } catch  (IOException e) {                     e.printStackTrace();                 }             }             if (baos != null ){                 try  {                     baos.close();                 } catch  (IOException e) {                     e.printStackTrace();                 }             }         }         return  null ;     } } 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public  class  TestFileSystemClassLoader      public  static  void  main (String[] args)  throws  ClassNotFoundException          fileSystemClassLoader loader = new  fileSystemClassLoader("F:/java" );         Class<?> aClass = loader.loadClass("com.test.java.hello" );         Class<?> aClass1 = loader.loadClass("com.test.java.hello" );         fileSystemClassLoader loader1 = new  fileSystemClassLoader("F:/java" );         Class<?> cc = loader1.loadClass("com.test.java.hello" );         Class<?> c1 = loader.loadClass("java.lang.String" );         System.out.println(aClass.hashCode());         System.out.println(aClass1.hashCode());         System.out.println(cc.hashCode());          System.out.println(cc.getClassLoader());          System.out.println(c1.getClassLoader());     } } 
2133927002
注意:被两个类加载器加载的同一个类,JVM不认为是相同的类。 
通过反射创建运行时对象 1 2 3 4 5 Class clazz = PersonC.class;                Constructor constructor = clazz.getConstructor(String.class, int .class);        Object tom = constructor.newInstance("tom" , 15 );        System.out.println(tom.toString());  
获取类的属性和内部结构 首先创建一个结构丰富的类,包含继承、实现接口,有参构造和无参构造、以及权限不同的属性和方法、带返回值的和不带返回值的,还有注解信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 package  com;import  annotation.MyAnnotation;@Myannotation(value = "hi")  public  class  Person  extends  Creature <String > implements  Comparable <String >, Myinterface      private  String name;      int  age;      public  int  id;      public  Person ()       }     private  Person (String name)          this .name = name;     }     Person(String name, int  age){         this .name = name;         this .age = age;     }     private  String show (String nation)          System.out.println("我的国籍为:" + nation);         return  nation;     }     public  String display (String insterests)          return  insterests;     }     @Override      public  void  info ()           System.out.println("这是一个人..." );     }     @Override      public  int  compareTo (String o)           return  0 ;     } } 
父类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package  com;import  java.io.Serializable;public  class  Creature <T > implements  Serializable      private  char  gender;     public  double  weight;     public  void  eat ()          System.out.println("生物在吃东西..." );     }     public  void  breath ()          System.out.println("生物在呼吸..." );     } } 
接口:
1 2 3 public  interface  Myinterface      void  info ()  } 
注解:
1 2 3 4 5 @Target({TYPE, ElementType.CONSTRUCTOR, ElementType.LOCAL_VARIABLE, ElementType.METHOD, ElementType.FIELD, ElementType.TYPE_PARAMETER, ElementType.TYPE_USE}) @Retention(RetentionPolicy.RUNTIME) public  @interface  Myannotation {    String value ()  default  "Hello" ; } 
获取属性结构Field【了解】 getFields() 1 2 3 4 5 6 Class clazz = Person.class; Field[] fields = clazz.getFields(); for (Field f: fields){	System.out.println(f); } 
public int com.Person.id
getFields(): 获取当前运行时类及其父类中所有声明为public访问权限的属性。
getDeclaredFields() 1 2 3 4 Field[] declaredFields = clazz.getDeclaredFields(); for  (Field f: declaredFields){    System.out.println(f); } 
private java.lang.String com.Person.name
getDeclaredFields(): 获取当前运行时类当中声明的所有属性。
获取属性的权限、数据类型和变量名 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public  void  test2 ()         Class clazz = Person.class;         Field[] declaredFields = clazz.getDeclaredFields();         System.out.println("1.权限修饰符:" );         for  (Field f: declaredFields){             int  modifiers = f.getModifiers();             System.out.println(Modifier.toString(modifiers));         }         System.out.println("2. 数据类型" );         for  (Field f: declaredFields){             Class type = f.getType();             System.out.println(type.getName());         }         System.out.println("3. 变量名" );         for  (Field f: declaredFields){             String name = f.getName();             System.out.println(name);         }     } 
1.权限修饰符:
 public
2.数据类型
3.变量名
getModifiers(): 获取属性的权限修饰符。
getType(): 获取属性的数据类型。
getName(): 获取属性的名称。
获取运行类的方法结构 getMethods() 1 2 3 4 Method[] methods = clazz.getMethods(); for (Method m: methods){        System.out.println(m); } 
public int com.Person.compareTo(java.lang.String)
getMethods(): 获取当前运行时类及其父类中所有声明为public访问权限的方法。
getDeclaredMethods() 同属性结构:getDeclaredMethods() :获取当前运行时类当中声明的所有属性。(不包含父类中的)
public int com.Person.compareTo(java.lang.String)
方法也能获取一些自身的结构: 权限修饰符、返回值类型、方法名、形参列表、异常信息、返回值。里面都有对应的方法,可以去查询API。
public Class<?> getReturnType()取得全部的返回值
public Class<?>[] getParameterTypes()取得全部的参数
public int getModifiers()取得修饰符
public Class<?>[] getExceptionTypes()取得异常信息
获取构造器 public Constructor<T>[] getConstructors(): 返回此 Class 对象所表示的类的所有public构造方法。
public Constructor<T>[] getDeclaredConstructors(): 返回此 Class 对象表示的类声明的所有构造方法 。
 Constructor类中 :
取得修饰符: public int getModifiers();
取得方法名称: public String getName();
取得参数的类型:public Class<?>[] getParameterTypes();
获取运行时类的父类 public Class<? Super T> getSuperclass(): 返回表示此 Class 所表示的实体(类、接口、基本类型)的父类的 Class。
1 2 3 4 5 6 Class clazz = Person.class; Class superclass = clazz.getSuperclass(); System.out.println(superclass);  Type genericSuperclass = clazz.getGenericSuperclass();  System.out.println(genericSuperclass);  
获取运行时带泛型类的父类的泛型 1 2 3 4 5 6 7 Class clazz = Person.class; Type genericSuperclass = clazz.getGenericSuperclass(); ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass; Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); System.out.println(actualTypeArguments[0 ].getTypeName());  
在JDBC中需要使用到。DAO操作。
获取父类泛型类型:Type getGenericSuperclass() 
泛型类型:ParameterizedType 
获取实际的泛型类型参数数组:getActualTypeArguments() 
获取运行时类实现的接口 public Class<?>[] getInterfaces() ;
1 2 3 4 Class[] interfaces = clazz.getInterfaces();         for (Class i: interfaces){             System.out.println(i);         } 
interface java.lang.Comparable
1 2 3 4 Class[] interfaces1 = clazz.getSuperclass().getInterfaces();         for (Class i: interfaces1){             System.out.println(i);         } 
interface java.io.Serializable
获取运行时类所在的包 Package getPackage()
1 2 Package aPackage = clazz.getPackage(); System.out.println(aPackage);   
获取注解 详见《Java注解Annotation》文章反射注解部分。
使用反射生成并操作对象 使用反射获取指定的属性 在反射机制中,可以直接通过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的属性内容
注:在类中属性都设置为**非public**的前提下,在使用set()和get()方法时,首先要使用Field类中的setAccessible(true)方法将需要操作的属性设置为可以被外部访问。
public void setAccessible(true)访问私有属性时,让这个属性可见。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public  void  test4 ()  throws  Exception    Class clazz = Person.class;          Person p = (Person) clazz.getConstructor().newInstance();          Field f = clazz.getField("id" );          String name = f.getName();     System.out.println(name);          f.set(p, 100 );     System.out.println(p.toString());        Field name1 = clazz.getDeclaredField("name" );    name1.setAccessible(true );    name1.set(p,  "zhuhongliang" );    System.out.println(p.toString());            } 
使用反射获取指定的方法 通过反射,调用类中的方法,通过Method类完成。步骤:
1.通过Class类的getMethod(String name, Class … parameterTypes)方法取得一个Method对象,并设置此方法操作时所需要的参数类型。
2.之后使用Object invoke(Object obj, Object[] args)进行调用,并向方法中传递要设置的obj对象的参数信息。
Object invoke(Object obj, Object[] args):
1.Object 对应原方法的返回值,若原方法无返回值,此时返回null
2.若原方法若为静态方法,此时形参Object obj可为null 或者 对于的calss对象。
3.若原方法形参列表为空,则Object[] args为null
4.若原方法声明为private,则需要在调用此invoke()方法前,显式调用方法对象的setAccessible(true)方法,将可访问private的方法。
1 2 3 4 5 6 // 在Person类增加入菜静态方法: // 静态方法  private static String showdesc(String name){      System.out.println("英俊潇洒 ");      return name;  } 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16  @Test   public  void  test5 ()  throws  Exception       Class clazz = Person.class;            Person p = (Person) clazz.getConstructor().newInstance();     Method show = clazz.getDeclaredMethod("show" , String.class);     show.setAccessible(true );     show.invoke(p, "加拿大" );     System.out.println("-------------调用静态方法--------" );     Method showdesc = clazz.getDeclaredMethod("showdesc" , String.class);     showdesc.setAccessible(true );     Object invoke = showdesc.invoke(null ,"应似飞鸿踏雪泥" );      System.out.println(invoke); } 
我的国籍为:加拿大
调用指定的构造器 1 2 3 4 5 6 7 public void test6() throws Exception {     Class clazz = Person.class;     Constructor cons = clazz.getDeclaredConstructor(String.class);     cons.setAccessible(true);     Person zhu = (Person) cons.newInstance("zhu");     System.out.println(zhu.toString()); } 
setAccessible 启用和禁用访问安全检查的开关,值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。值为 false 则指示反射的对象应该实施 Java 语言访问检查。并不是为true 就能访问为false就不能访问。
禁止安全检查,可以提高反射的运行速度。 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 import  com.Person;import  java.lang.reflect.Method;public  class  TestAccessiable      public  static  void  main (String[] args)  throws  Exception          Class clazz = Person.class;         Person p = (Person) clazz.getDeclaredConstructor().newInstance();         Method test = clazz.getDeclaredMethod("test" );         Person p1 = new  Person();         long  start = System.currentTimeMillis();         for (int  i =0  ; i < 1000000000L ; i++){             p1.test();         }         long  end = System.currentTimeMillis();         System.out.println("普通方法调用10亿次,耗时:" + (end - start));         start = System.currentTimeMillis();         for (int  i =0  ; i < 1000000000L ; i++){             test.invoke(p);         }         end = System.currentTimeMillis();         System.out.println("反射方法动态调用调用10亿次,耗时:" + (end - start));         test.setAccessible(true );         start = System.currentTimeMillis();         for (int  i =0  ; i < 1000000000L ; i++){             test.invoke(p);         }         end = System.currentTimeMillis();         System.out.println("反射方法动态调用调用10亿次,跳过安全检查,耗时:" + (end - start));     } } 
普通方法调用10亿次,耗时:396
反射的应用–动态代理 代理设计模式:
使用一个代理将对象包装起来, 然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理,代理对象决定是否以及何时将方法调用转到原始对象上。
静态代理 特征是代理类和目标对象的类都是在编译期间 确定下来,不利于程序的扩展。同时,每一个代理类只能为一个接口服务,这样一来程序开发中必然产生过多的代理。最好可以通过一个代理类完成全部的代理功能。
动态代理 是指客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。
静态代理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 package  com.zhu.proxy;import  java.security.PublicKey;interface  ClothFactory     void  produceCloth ()  } class  ProxyClothFactory  implements  ClothFactory     private  ClothFactory factory;          public  ProxyClothFactory (ClothFactory factory)          this .factory = factory;     }     @Override      public  void  produceCloth ()           System.out.println("代理类做一些准备工作..." );         factory.produceCloth();         System.out.println("代理类做一些收尾工作..." );     } } class  Nike  implements  ClothFactory     @Override      public  void  produceCloth ()           System.out.println("Nike公司生产运动服..." );     } } public  class  StaticProxyTest      public  static  void  main (String[] args)                    Nike nike = new  Nike();                  ProxyClothFactory proxyClothFactory = new  ProxyClothFactory(nike);         proxyClothFactory.produceCloth();     } } 
代理类做一些准备工作…
动态代理 Proxy  : 专门完成代理的操作类,是所有动态代理类的父类。通过此类为一个或多个接口动态地生成实现类。 提供用于创建动态代理类和动态代理对象的静态方法:
static Object newProxyInstance( ClassLoader loader, Class[] interfaces, InvocationHandler h)直接创建一个动态代理对象。
static Class getProxyClass( ClassLoader loader, Class… interfaces) 创建一个动态代理类所对应的Class对象。
创建一个代理对象,将被代理对象的类加载器,Class对象,实现的接口一句执行函数的句柄作为参数传入到代理类对象中。通过调用代理类对象的同名方法就会通过invoke()动态调用代理类中的同名方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 package  com.zhu.proxy;import  java.awt.*;import  java.lang.reflect.InvocationHandler;import  java.lang.reflect.Method;import  java.lang.reflect.Proxy;interface  Human     String getBelief ()  ;      void  eat (String food)  } class  SuperMan  implements  Human     @Override      public  String getBelief ()           return  "I believe I can fly!" ;     }     @Override      public  void  eat (String food)           System.out.println("我喜欢吃" + food);     } } class  ProxyFactory          public  static  Object getProxyInstance (Object obj)          MyInvocationHandler handler = new  MyInvocationHandler();         handler.bind(obj);         return  Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler);     } } class  MyInvocationHandler  implements  InvocationHandler     private  Object obj;      public   void  bind (Object obj)          this .obj = obj;     }               @Override      public  Object invoke (Object proxy, Method method, Object[] args)  throws  Throwable                   Object returnValue = method.invoke(obj, args);         return  returnValue;     } } public  class  ProxyTest      public  static  void  main (String[] args)                    Human proxyInstance = (Human) ProxyFactory.getProxyInstance(new  SuperMan());         String belief = proxyInstance.getBelief();         System.out.println(belief);         proxyInstance.eat("胖哥俩" );         System.out.println("----------------------------------" );         ClothFactory proxyInstance1 = (ClothFactory) ProxyFactory.getProxyInstance(new  Nike());         proxyInstance1.produceCloth();     } } 
I believe I can fly!
我喜欢吃胖哥俩
Nike公司生产运动服…
总结 动态代理步骤  创建一个实现接口InvocationHandler 的类,它必须实现invoke方法,以完成代理的具体操作。 1 2 3 4 5 6 7 8 9 10 11 public  Object invoke (Object theProxy, Method method, Object[] params)  throws  Throwable        try          {          Object retval = method.invoke(targetObj, params);                      System.out.println(retval);             return  retval;         }       catch  (Exception exc){}  } 
Object theProxy: 被代理的对象
Method method: 要调用的方法
Object[] params: 方法调用时所需要的参数
创建被代理的类以及接口 通过Proxy的静态方法创建一个代理对象。 通过代理对象调用被代理对象的方法。