动态语言 程序运行时,可以改变程序结构或变量类型。典型的语言: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 类的静态代码块 classLoader 中 main方法… 100父类Father的静态代码块 静态初始化类A 父类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 中 main方法… 父类Father的静态代码块 666
类加载器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 2133927002 1836019240 fileSystemClassLoader@677327b6 null
注意:被两个类加载器加载的同一个类,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 public double com.Creature.weight
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 int com.Person.age public int com.Person.id
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.权限修饰符: private
public
2.数据类型 java.lang.String int int
3.变量名 name age id
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) public int com.Person.compareTo(java.lang.Object) public void com.Person.info() public java.lang.String com.Person.display(java.lang.String) public void com.Creature.breath() public void com.Creature.eat() public final void java.lang.Object.wait() throws java.lang.InterruptedException public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException public boolean java.lang.Object.equals(java.lang.Object) public java.lang.String java.lang.Object.toString() public native int java.lang.Object.hashCode() public final native java.lang.Class java.lang.Object.getClass() public final native void java.lang.Object.notify() public final native void java.lang.Object.notifyAll()
getMethods()
: 获取当前运行时类及其父类中所有声明为public
访问权限的方法。
getDeclaredMethods() 同属性结构:getDeclaredMethods()
:获取当前运行时类当中声明的所有属性。(不包含父类中的)
public int com.Person.compareTo(java.lang.String) public int com.Person.compareTo(java.lang.Object) public void com.Person.info() private java.lang.String com.Person.show(java.lang.String) public java.lang.String com.Person.display(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 interface com.Myinterface
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 反射方法动态调用调用10亿次,耗时:1886 反射方法动态调用调用10亿次,跳过安全检查,耗时:1545
反射的应用–动态代理 代理设计模式:
使用一个代理将对象包装起来, 然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理,代理对象决定是否以及何时将方法调用转到原始对象上。
静态代理 特征是代理类和目标对象的类都是在编译期间 确定下来,不利于程序的扩展。同时,每一个代理类只能为一个接口服务,这样一来程序开发中必然产生过多的代理。最好可以通过一个代理类完成全部的代理功能。
动态代理 是指客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。
静态代理 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(); } }
代理类做一些准备工作… Nike公司生产运动服… 代理类做一些收尾工作…
动态代理 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的静态方法创建一个代理对象。 通过代理对象调用被代理对象的方法。