1、反射的介绍html
反射是指程序能够访问,检测,修改它自己状态或行为的一种能力。java的反射机制是指在程序运行状态中,给定任意一个类,均可以获取到这个类的属性和方法;给定任意一个对象均可以调用这个对象的属性和方法,这种动态的获取类的信息和调用对象的方法的功能称之为java的反射机制。 一言以蔽之:反射机制可让你在程序运行时,拿到任意一个类的属性和方法并调用它。java
主要做用:api
a、运行时构造一个类的对象;
b、运行时获取一个类所具备的的成员变量和方法;
c、运行时调用任意一个对象的方法;
d、生成动态代理; 其实反射最主要的功能我以为是与框架搭配使用。数组
其实上面的能够总结为:反射:运行时类信息 安全
e、类型转换前先作检查。oracle
2、Class对象的获取框架
一、对象的getClass()方法。
二、类的.class(最安全/性能最好)属性。
三、运用Class.forName(String className)动态加载类,className须要是类的全限定名(最经常使用)。ide
代码:性能
public class Complet { public static void main(String[] args) throws ClassNotFoundException { Class<?> c = Complet.class; Class<? extends Class> class1 = c.getClass(); Class c1=Class.forName("Complet"); System.out.println(c1.getPackage()); } }
想在运行时使用类型信息,必须获取对象(好比类Base对象)的Class对象的引用,使用功能Class.forName(“Base”)能够实现该目的,或者使用base.class。注意,有一点颇有趣,使用功能”.class”来建立Class对象的引用时,不会自动初始化该Class对象,使用forName()会自动初始化该Class对象。为了使用类而作的准备工做通常有如下3个步骤:this
实例代码:
package bhz.classinfo; public class Base { static int num = 1; static { System.out.println("Base " + num); } }
package bhz.classinfo; public class Main { public static void main(String[] args) throws ClassNotFoundException { // 不会初始化静态块 Class clazz1 = Base.class; System.out.println("------"); // 会初始化 Class clazz2 = Class.forName("bhz.classinfo.Base"); } }
运行结果:
先打印“--------”,而后打印静态代码块的结果。
可是注意并非全部的Class.forName都会触发静态代码块:
Class.forName("bhz.classinfo.Base",false,classLoader);
上面这句代码中,第二个参数传入false,查看源码:
public static Class<?> forName( String name, //若是initialize = false,那么类会被加载,但不会执行静态代码块。 boolean initialize,//whether the class must be initialized; ClassLoader loader ) -------------------------------------------------------------- jdk 7.0 ----------------------------- @CallerSensitive public static Class<?> forName(String name, boolean initialize, ClassLoader loader) throws ClassNotFoundException { Class<?> caller = null; SecurityManager sm = System.getSecurityManager(); if (sm != null) { // Reflective call to get caller class is only needed if a security manager // is present. Avoid the overhead of making this call otherwise. caller = Reflection.getCallerClass(); if (sun.misc.VM.isSystemDomainLoader(loader)) { ClassLoader ccl = ClassLoader.getClassLoader(caller); if (!sun.misc.VM.isSystemDomainLoader(ccl)) { sm.checkPermission( SecurityConstants.GET_CLASSLOADER_PERMISSION); } } } return forName0(name, initialize, loader, caller); }
因此没法加载。查看Class的方法,默认是传的true,自动加载静态代码块的。
总结:
Class对象的生成方式以下:
1.类名.class -----> 说明: JVM将使用类装载器, 将类装入内存(前提是:类尚未装入内存),不作类的初始化工做.返回Class的对象。
2.Class.forName("类名字符串") ----->(注:类名字符串是包名+类名) 说明:装入类,并作类的静态初始化,返回Class的对象。
3.实例对象.getClass() ----> 说明:对类进行静态初始化、非静态初始化;返回引用.运行时真正所指的对象(由于:子对象的引用可能会赋给父对象的引用变量中)所属的类的Class的对象。
当咱们编写一个新的java类时,JVM就会帮咱们编译成class对象,存放在同名的.class文件中。在运行时,当须要生成这个类的对象,JVM就会检查此类是否已经装载内存中。如果没有装载,则把.class文件装入到内存中。如果装载,则根据class文件生成实例对象。
注意:这就要涉及到类的加载机制,下面是补充知识点:
JVM中类的装载是由ClassLoader和它的子类来实现的,Java ClassLoader是一个重要的Java运行时系统组件。它负责在运行时查找和装入类文件的类。
类加载的五个过程:加载、验证、准备、解析、初始化。
从类被加载到虚拟机内存中开始,到卸御出内存为止,它的整个生命周期分为7个阶段,加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)、卸御(Unloading)。其中验证、准备、解析三个部分统称为链接。
3、获取信息
Class
类提供了大量的实例方法来获取该Class对象所对应的详细信息,Class类大体包含以下方法,其中每一个方法都包含多个重载版本,所以咱们只是作简单的介绍,详细请参考JDK文档
获取内容 | 方法签名 |
---|---|
构造器 | Constructor<T> getConstructor(Class<?>... parameterTypes) |
包含的方法 | Method getMethod(String name, Class<?>... parameterTypes) |
包含的属性 | Field getField(String name) |
包含的Annotation |
<A extends Annotation> A getAnnotation(Class<A> annotationClass) |
内部类 | Class<?>[] getDeclaredClasses() |
外部类 | Class<?> getDeclaringClass() |
所实现的接口 | Class<?>[] getInterfaces() |
修饰符 | int getModifiers() |
所在包 | Package getPackage() |
类名 | String getName() |
简称 | String getSimpleName() |
判断内容 | 方法签名 |
---|---|
注解类型? | boolean isAnnotation() |
使用了该Annotation 修饰? |
boolean isAnnotationPresent(Class<? extends Annotation> annotationClass) |
匿名类? | boolean isAnonymousClass() |
数组? | boolean isArray() |
枚举? | boolean isEnum() |
原始类型? | boolean isPrimitive() |
接口? | boolean isInterface() |
obj 是不是该Class 的实例 |
boolean isInstance(Object obj) |
Method Constructor Field这些类都实现了java.lang.reflect.Member接口,程序能够经过Method对象来执行相应的方法,经过Constructor对象来调用对应的构造器建立实例,经过Filed对象直接访问和修改对象的成员变量值。
4、动态代理的使用
4.一、JDK的动态代理
a.实现接口InvocationHandler,而且重写invoke()方法。
public class DynamicProxyHandler implements InvocationHandler{ private Object proxyed; public DynamicProxyHandler(Object proxyed) { this.proxyed = proxyed; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException { System.out.println("代理工做了."); return method.invoke(proxyed, args); } }
b.调用动态代理
public static void main(String[] args) { RealObject real = new RealObject(); //调用动态代理 Interface proxy = (Interface) Proxy.newProxyInstance( Interface.class.getClassLoader(), new Class[] {Interface.class}, new DynamicProxyHandler(real)); //调用实际方法 proxy.doSomething(); proxy.somethingElse("luoxn28"); }
注意:invoke()方法的返回值能够是如何的值(Object),咱们通常是返回方法,而后再调用类中进行调用。假设返回的是“String”类型,将不能调用被代理的相关的方法。因此咱们通常是返回method.invoke(proxyed, args)方法。
举例:
Dubbo源码的动态代理:
public class InvokerInvocationHandler implements InvocationHandler { private final Invoker<?> invoker; public InvokerInvocationHandler(Invoker<?> handler){ this.invoker = handler; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String methodName = method.getName(); Class<?>[] parameterTypes = method.getParameterTypes(); if (method.getDeclaringClass() == Object.class) { return method.invoke(invoker, args); } if ("toString".equals(methodName) && parameterTypes.length == 0) { return invoker.toString(); } if ("hashCode".equals(methodName) && parameterTypes.length == 0) { return invoker.hashCode(); } if ("equals".equals(methodName) && parameterTypes.length == 1) { return invoker.equals(args[0]); } return invoker.invoke(new RpcInvocation(method, args)).recreate(); } }
注意:invoker是dubbo自定义的invoker接口,有利于扩展性。