Java反射及动态代理
Posted 早春的树
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java反射及动态代理相关的知识,希望对你有一定的参考价值。
一、反射概述
1、什么是反射:
Reflection是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API获得任何类的内部信息,并能直接操作任意对象的内部属性和方法。
加载完类之后,在堆内存的方法区中,就产生了一个class类型的对象(一个类只有一个Class对象),这个对象包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象地称之为:反射。
2、反射的使用方式:
@Test public void test2() { try { Class clazz = Person.class; //1、通过反射,创建Person类的对象。 Constructor constructor = clazz.getConstructor(String.class, int.class); Person o = (Person) constructor.newInstance("Tom", 22); System.out.println(o); //2、通过反射调用对象指定的属性、方法。 //调用属性。 Field name = clazz.getDeclaredField("name"); name.set(o,"Rock"); System.out.println(o); //调用方法 Method show = clazz.getDeclaredMethod("show"); show.invoke(o); //3、通过反射调用Person私有结构,比如:构造器、方法、属性 //调用私有构造器 Constructor declaredConstructor = clazz.getDeclaredConstructor(String.class); declaredConstructor.setAccessible(true); Person o1 = (Person) declaredConstructor.newInstance("Jerry"); System.out.println(o1); //调用私有属性 Field age = clazz.getDeclaredField("age"); age.setAccessible(true); age.set(o1,19); System.out.println(o1); //调用私有方法 Method showNation = clazz.getDeclaredMethod("showNation",String.class); showNation.setAccessible(true); String nation = (String) showNation.invoke(o1, "China"); System.out.println(nation); } catch (InstantiationException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); } }
public class Person{ public String name; private int age; public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } private Person(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 "Person{" + "name=\'" + name + \'\\\'\' + ", age=" + age + \'}\'; } public void show(){ System.out.println("你看我像人还是像神!?"); } private String showNation(String nation){ System.out.println("国籍:" + nation); return nation; } }
3、是么时候使用反射?
当需要动态创建对象的时候使用反射。
反射的特性:动态性。
4、反射和面向对象中的封装性是否矛盾?
不矛盾。举个例子,比如一个类中提供了私有构造器,和一个返回该类实例的方法,可以通过暴露出的方法创建对象,如果非要使用构造器创建对象则需要用到反射。换句话说,不建议你用构造器创建,如果你非要用也不是不行,可以利用反射调用。
二、关于java.lang.Class的理解
1、类的加载过程
程序通过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾)。接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中。此过程就叫做类的加载。加载到内存中的类就称为运行时类,此运行时类,就作为一个Class的一个实例。换句话说,Class的一个实例就对应着一个运行时类。
2、加载到内存中的运行时类,会缓存一段时间。在此时间内,我们可以通过不同的方式来获取来获取此运行时类。
3、获取Class的实例的方式
@Test public void test3() throws ClassNotFoundException { //方式一:调用运行时类的属性:.class Class clazz1 = Person.class; System.out.println(clazz1); //方式二:通过运行时类的对象 Person person = new Person(); Class clazz2 = person.getClass(); System.out.println(clazz2); //方式三:调用Class类的静态方法:forName( String classPath) Class clazz3 = Class.forName("com.oldking.java.Person"); System.out.println(clazz3); //使用类的加载器:ClassLoader ClassLoader classLoader = ReflectionTest.class.getClassLoader(); Class clazz4 = classLoader.loadClass("com.oldking.java.Person"); System.out.println(clazz4); }
三、类的加载过程
1、加载:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口(即引用地址)。所有需要访问和使用类数据的操作只能通过这个Class对象。这个加载的过程需要类加载器参与。
2、连接:将Java类的二进制代码合并到JVM的运行状态中的过程。
2.1、验证:确保加载的类信息符合JVM规范。
2.2、准备:正式为类变量(static)分配内存并设置变量默认初始值的阶段,这些内存都将在方法区进行分配。
2.3、解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址)的过程。
3、初始化;
3.1、执行类构造器<clinit>( )方法的过程。类构造器<clinit>( )方法是由编译期自动收集类中所有变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信息的,而非构造该类对象的构造器)。
3.2、当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化。
3.3、虚拟机会保证一个类的<clinit>( )方法在多线程环境中被正确加锁和同步。
四、类加载器的理解
1、类加载器的作用:将class文件字节码内容加载到内存中,并将这些静态数据装换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。
2、类缓存:标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象。
3、JVM规范定义了如下类型的类的加载器
3.1、引导类加载器:用C++编写的,是JVM自带的类加载器,负责Java平台核心库,用来装载核心类库。该加载器无法直接获取。
3.2、扩展类加载器:负责jre、lib、ext目录下的jar包或者 -D java.ext.dirs指定目录下的jar包装入工作库。
3.3、系统类加载器:负责 java -classpath 或D -java.class.path所指目录下的类与jar包装入工作,是最常用的加载器。
@Test public void test4(){ //自定义类,使用系统类加载器进行加载 ClassLoader classLoader = Person.class.getClassLoader(); System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2 //调用系统类加载器的getParent(),获取扩展类加载器 ClassLoader classLoader1 = classLoader.getParent(); System.out.println(classLoader1);//sun.misc.Launcher$ExtClassLoader@28a418fc //调用扩展类加载器的getParent(),无法获取引导类加载器 //引导类加载器主要负责加载Java的核心类库,无法加载自定义类。 ClassLoader classLoader2 = classLoader1.getParent(); System.out.println(classLoader2);//null ClassLoader classLoader3 = String.class.getClassLoader(); System.out.println(classLoader3);//null }
4、类加载器读取配置文件
@Test public void test5() throws IOException { Properties properties = new Properties(); //读取配置文件的方式一 /* BufferedInputStream stream = new BufferedInputStream(new FileInputStream("login.properties"));//读取当前module下 properties.load(stream);*/ //读取配置文件的方式二 ClassLoader classLoader = ReflectionTest.class.getClassLoader(); InputStream stream = classLoader.getResourceAsStream("login.properties");//读取当前项目resources目录下 properties.load(stream); String name = properties.getProperty("name"); String pwd = properties.getProperty("pwd"); System.out.println("name = " + name + " ,pwd = " + pwd); }
五、通过反射创建对应的运行时类的对象
@Test public void test6() throws IllegalAccessException, InstantiationException { Class<Person> clazz = Person.class; /* * newInstance():调用此方法,创建对应的运行时类的对象 * 要想此方法正常创建运行时类的对象,要求: * 1、运行时类必须提供空参构造器。 * 2、空参构造器的访问权限得够。通常设置为public。 * * 在javabean中要求提供一个public的空参构造器,原因: * 1、便于通过反射创建运行时类的对象。 * 2、便于子类继承次运行时类时,默认调用super()时保证父类有此构造器。 */ Person person = clazz.newInstance(); System.out.println(person); }
六、获取运行时类的完整结构
public class Creature<T> implements Serializable { private char gender; public double weight; private void breath(){ System.out.println("呼吸"); } public void eat(){ System.out.println("吃东西"); } }
public interface MyInterface { void info(); }
@Target({TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE}) @Retention(RetentionPolicy.RUNTIME) public @interface MyAnnotation { String value() default "hello"; }
@Test public void test1() { Class<Person> clazz = Person.class; //获取属性结构 //getFields():获取当前运行时类机器父类中声明为public访问权限的属性 Field[] fields = clazz.getFields(); for (Field field : fields) { System.out.println(field); } //获取当前运行时类中声明的所有属性(不包含父类中声明的属性) Field[] declaredFields = clazz.getDeclaredFields(); for (Field field : declaredFields) { System.out.println(field); } } @Test public void test2() { Class<Person> clazz = Person.class; Field[] declaredFields = clazz.getDeclaredFields(); for (Field field : declaredFields) { //权限修饰符 int modifiers = field.getModifiers(); System.out.println("权限修饰符 " + Modifier.toString(modifiers)); //数据类型 Class<?> type = field.getType(); System.out.println("数据类型 " + type.getName()); //属性名 String name = field.getName(); System.out.println("属性名 " + name); } } @Test public void test3() { Class clazz = Person.class; //getMethods():获取当前运行时类及其所有父类中声明为public的方法 Method[] methods = clazz.getMethods(); for (Method method : methods) { System.out.println(method); } //getDeclaredMethods():获取当前运行时类中声明的所有方法(不包含父类中声明的方法) Method[] declaredMethods = clazz.getDeclaredMethods(); for (Method declaredMethod : declaredMethods) { System.out.println(declaredMethod); } } @Test public void test4() { Class clazz = Person.class; Method[] declaredMethods = clazz.getDeclaredMethods(); for (Method declaredMethod : declaredMethods) { //获取方法声明的注解 Annotation[] annotations = declaredMethod.getAnnotations(); for (Annotation annotation : annotations) { System.out.println(declaredMethod.toString() + "-------注解--------" + annotation); } //获取每个方法的权限修饰符 System.out.println(declaredMethod.toString() + "-------权限修饰符--------" + Modifier.toString(declaredMethod.getModifiers())); //返回值类型 System.out.println(declaredMethod.toString() + "--------返回值类型-------" + declaredMethod.getReturnType().getName()); //方法名 System.out.println(declaredMethod.toString() + "-------方法名----------" + declaredMethod.getName()); //形参列表 Class<?>[] parameterTypes = declaredMethod.getParameterTypes(); for (Class<?> parameterType : parameterTypes) { System.out.println(declaredMethod.toString() + "---------形参列表类型--------" + parameterType); } Parameter[] parameters = declaredMethod.getParameters(); for (Parameter parameter : parameters) { System.out.println(declaredMethod.toString() + "---------形参列表--------" + parameter); } //抛出的异常类型 Class<?>[] exceptionTypes = declaredMethod.getExceptionTypes(); for (Class<?> exceptionType : exceptionTypes) { System.out.println(declaredMethod.toString() + "---------抛出的异常类型--------" + exceptionType); } System.out.println("========================================================================="); } } @Test public void test5(){ //获取构造器 Class<Person> personClass = Person.class; //getConstructors():获取当前运行时类中声明为public的构造器 Constructor<?>[] constructors = personClass.getConstructors(); for (Constructor<?> constructor : constructors) { System.out.println(constructor); } System.out.println("========================================================"); //getDeclaredConstructors():获取当前运行时类中所有的构造器 Constructor<?>[] declaredConstructors = personClass.getDeclaredConstructors(); for (Constructor<?> declaredConstructor : declaredConstructors) { System.out.println(declaredConstructor); } } @Test public void test6(){ Class<Person> personClass = Person.class; //获取运行时类的父类 Class<? super Person> superclass = personClass.getSuperclass(); System.out.println(superclass); System.out.println(superclass.getSuperclass()); //获取运行时类的带泛型的父类 Type genericSuperclass = personClass.getGenericSuperclass(); System.out.println(genericSuperclass); //获取运行时类的带泛型的父类的泛型 ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass; Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); for (Type actualTypeArgument : actualTypeArguments) { // System.out.println(((Class)actualTypeArgument).getName()); System.out.println(actualTypeArgument.getTypeName()); } } @Test public void test7(){ //获取运行时类实现的接口 Class<Person> personClass = Person.class; Class<?>[] interfaces = personClass.getInterfaces(); for (Class<?> anInterface : interfaces) { System.out.println(anInterface); } System.out.println(); //获取运行时类父类接口 Class<?>[] interfaces1 = personClass.getSuperclass().getInterfaces(); for (Class<?> anInterface : interfaces1) { System.out.println(anInterface); } } @Test public void test8(){ //获取运行时类实所在的包 Class<Person> personClass = Person.class; Package aPackage = personClass.getPackage(); System.out.println(aPackage); } @Test public void test9(){ //获取运行时类的注解 Class<Person> personClass = Person.class; Annotation[] annotations = personClass.getAnnotations(); for (Annotation annotation : annotations) { System.out.println(annotation); } }
七、调用运行时类的指定结构
@Test public void test1() throws IllegalAccessException, InstantiationException, NoSuchFieldException { //调用运行时类的指定属性 Class<Person> personClass = Person.class; //1、创建运行时类的对象。 Person person = personClass.newInstance(); //2、getDeclaredField(String fieldName):获取运行时类中指定变量名的属性。 Field name = personClass.getDeclaredField("name"); //3、保证当前属性可访问。 name.setAccessible(true); //4、获取、设置指定对象的此属性值。 name.set(person,"Tony"); System.out.println(name.get(person)); } @Test public void test2() throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException { //调用运行时类的指定方法 Class<Person> personClass = Person.class; //1、创建运行时类的对象。 Person person = personClass.newInstance(); //2、、获取指定的某个方法 //getDeclaredMethod():参数一:指明获取的方法的名称 参数二:知名获取的方法的形参列表。 Method show = personClass.getDeclaredMethod("show", String.class); //3、保证当前方法可访问 show.setAccessible(true); //4、调用方法的invoke() :参数一:方法的调用者,参数二:给方法形参传入的实参 Object val = show.invoke(person, "CHINA"); System.out.println(val); //调用静态方法 Method showDesc = personClass.getDeclaredMethod("showDesc"); showDesc.setAccessible(true); showDesc.invoke(Person.class);//此处调用者为Person类本身 } @Test public void test3() throws Exception { //如何调用运行时类中指定的构造器 Class<Person> personClass = Person.class; //1、获取指定构造器 Constructor<Person> constructor = personClass.getDeclaredConstructor(String.class); //2、保证构造器可访问 constructor.setAccessible(true); //3、调用此构造器创建运行时类的对象 Person person = constructor.newInstance("TOM"); System.out.println(person); }
八、反射的应用:动态代理
1、代理设计模式的原理:
使用一个代理将对象包装起来,然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。
2、静态代理
/** *静态代理: * 特点:代理类和被代理类在编译期间就确定下来了。 */ //公共接口 interface ClothFactory { void produceCloth(); } //代理类 class ProxyClothFactory implements ClothFactory { private ClothFactory clothFactory; public ProxyClothFactory(ClothFactory factory) { this.clothFactory = factory;//用被代理类对象进行实例化 } @Override public void produceCloth() { System.out.println("代理工厂做准备"); clothFactory.produceCloth(); System.out.println("代理工厂做收尾工作"); } } class NikeClothFactory implements ClothFactory{ @Override public void produceCloth() { System.out.println("Nike工厂生产一批运动服"); } } public class StaticProxyTest { public static void main(String[] args) { NikeClothFactory nike = new NikeClothFactory(); ProxyClothFactory proxyClothFactory = new ProxyClothFactory(nike); proxyClothFactory.produceCloth(); } }
3、动态代理
/** * 动态代理举例 */ 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); } } //想要实现动态代理,需要解决的问题 //1、如何根据加载到内存中的被代理类,动态的创建一个代理类机器对象。 //2、当通过代理类的对象调用方法时,如何动态的去调用被代理类中的同名方法 class ProxyFactory { //调用此方法,返回一个代理类对象 public static Object getProxyInstance(Object obj) {//obj:被代理类对象 MyInvocationHandler handler = new MyInvocationHandler(); handler.bind(obj); return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler); } } class MyInvocationHandler implements InvocationHandler { private Object object;//被代理类的对象 public void bind(Object obj) { this.object = obj; } //当我们通过代理类对象,调用方法a时,就会自动调用下方Invoke()方法。 //将被代理类方法a的功能声明在invoke()中。 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //method:代理类对象调用的方法,此方法也作为了被代理类对象调用的方法 //object:被代理类的对象 Object returnValue = method.invoke(object, args); return returnValue; } } public class ProxyTest { public static void main(String[] args) { SuperMan superMan = new SuperMan(); Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan); proxyInstance.getBelief(); proxyInstance.eat("重庆火锅"); } }
以上是关于Java反射及动态代理的主要内容,如果未能解决你的问题,请参考以下文章