Java反射机制详细介绍

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

  • 加载:由类加载器完成,找到对应的字节码,建立一个Class对象。
  • 连接:验证类中的字节码,为静态域分配空间。
  • 初始化:若是该类有超类,则对其初始化,执行静态初始化器和静态初始化块。

实例代码:

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接口,有利于扩展性。

相关文章
相关标签/搜索