zoukankan      html  css  js  c++  java
  • JAVASE(十八) 反射: Class的获取、ClassLoader、反射的应用、动态代理

    个人博客网:https://wushaopei.github.io/    (你想要这里多有)

    1、反射(JAVA Reflection)的理解

    1.1 什么是反射(JAVA Reflection)

      Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

    1.2 Java 反射机制提供的功能

    • 在运行时判断任意一个对象所属的类
    • 在运行时构造任意一个类的对象
    • 在运行时判断任意一个类所具有的成员变量和方法
    • 在运行时调用任意一个对象的成员变量和方法
    • 生成动态代理

    1.3 反射相关的主要API:

    • java.lang.Class:代表一个类
    • java.lang.reflect.Method:代表类的方法
    • java.lang.reflect.Field:代表类的成员变量
    • java.lang.reflect.Constructor:代表类的构造方法

    2、Class 的理解

    2.1 Class 类的 原理

    在Object类中定义了以下的方法,此方法将被所有子类继承:
    ●  public final Class getClass()

    以上的方法返回值的类型是一个Class类,此类是Java反射的源头,实际上所谓反射从程序的运行结果来看也很好理解,即:可以通过对象反射求出类的名称。

    反射获取class区别于以往正常的new 创建对象来对类内部方法、属性进行操作。

    2.2 Class类的存在与定义

    • 对象照镜子后可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接口。对于每个类而言,JRE 都为其保留一个不变的 Class 类型的对象。一个 Class 对象包含了特定某个类的有关信息。
    • Class本身也是一个类
    • Class 对象只能由系统建立对象
    • 一个类在 JVM 中只会有一个Class实例
    • 一个Class对象对应的是一个加载到JVM中的一个.class文件
    • 每个类的实例都会记得自己是由哪个 Class 实例所生成
    • 通过Class可以完整地得到一个类中的完整结构

    3、 Class 的获取

    3.1 Class 类的常用方法

    3.2 应用案例

             

    3.3 实例化Class类对象(四种方法)

            

    4、 ClassLoader (理解)

            

           

    ClassLoader :类加载机制加载流程

           

    案例:

              

              

    5、 创建运行时类的对象

    5.1 有了Class对象,能做什么?

       答案: 可以用来创建类的对象

    5.2 要  求:

        1)类必须有一个无参数的构造器。
        2)类的构造器的访问权限需要足够。

    5.3 难道没有无参的构造器就不能创建对象了吗?

    不是!只要在操作的时候明确的调用类中的构造器,并将参数传递进去之后,才可以实例化操作。

    步骤如下:
    1)通过Class类的getDeclaredConstructor(Class … parameterTypes)取得本类的指定形参类型的构造器
    2)向构造器的形参中传递一个对象数组进去,里面包含了构造器中所需的各个参数。

    3)在Constructor 类中存在一个方法: public T newInstance(Object... initargs)

    5.4 案例

          

    6、获取运行时类的完整结构

    通过Class类获取到类的对象后,有什么用呢?

    答案可以通过类的对象来获取类的完整结构,并进行赋值或更新数据

    6.1 通过反射获取运行时类的完整结构

    Field、Method、Constructor、Superclass、Interface、Annotation
    • 实现的全部接口
    • 所继承的父类
    • 全部的构造器
    • 全部的方法
    • 全部的Field

    6.2 获取完整结构的实现方式:

    (1)实现的全部接口

        public Class<?>[] getInterfaces()   

           确定此对象所表示的类或接口实现的接口。

    (2)所继承的父类

            public Class<? Super T> getSuperclass()

         返回表示此 Class 所表示的实体(类、接口、基本类型)的父类的 Class。

    (3)全部的构造器

            public Constructor<T>[] getConstructors()

          返回此 Class 对象所表示的类的所有public构造方法。

            public Constructor<T>[] getDeclaredConstructors()

          返回此 Class 对象表示的类声明的所有构造方法。

    • Constructor类中:

              取得修饰符: public int getModifiers();
              取得方法名称: public String getName();
              取得参数的类型:public Class<?>[] getParameterTypes();

    (4)全部的方法

            public Method[] getDeclaredMethods()

               返回此Class对象所表示的类或接口的全部方法

            public Method[] getMethods()  

               返回此Class对象所表示的类或接口的public的方法

    • Method类中:

            public Class<?> getReturnType()取得全部的返回值
            public Class<?>[] getParameterTypes()取得全部的参数
            public int getModifiers()取得修饰符
            public Class<?>[] getExceptionTypes()取得异常信息

    (5)全部的Field

            public Field[] getFields()

            返回此Class对象所表示的类或接口的public的Field。

            public Field[] getDeclaredFields()

            返回此Class对象所表示的类或接口的全部Field。

    • Field方法中:

              public int getModifiers()  以整数形式返回此Field的修饰符
              public Class<?> getType()  得到Field的属性类型
              public String getName()  返回Field的名称。

    (6)Annotation相关

    •   get Annotation(Class<T> annotationClass)
    • getDeclaredAnnotations()

    (7)泛型相关
            获取父类泛型类型:Type getGenericSuperclass()
            泛型类型:ParameterizedType
            获取实际的泛型类型参数数组:getActualTypeArguments()

    (8)类所在的包    Package getPackage()

    6.2 调用运行时类的指定属性、指定方法等

    (1)调用指定方法

    通过反射,调用类中的方法,通过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
        3.若原方法形参列表为空,则Object[] args为null
        4.若原方法声明为private,则需要在调用此invoke()方法前,显式调用方法对象的setAccessible(true)方法,将可访问private的方法。

    (2)调用指定属性

    在反射机制中,可以直接通过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的属性内容

    :在类中属性都设置为private的前提下,在使用set()和get()方法时,首先要使用Field类中的setAccessible(true)方法将需要操作的属性设置为可以被外部访问。

    • public void setAccessible(true)访问私有属性时,让这个属性可见。

    6.3 案例:反射的应用

    /*
    	 * 获取类中属性的详细信息
    	 */
    	@Test
    	public void test() throws Exception, Exception{
    		
    		Class<Student> clazz = Student.class;
    		//获取本类中所的属性
    		Field[] declaredFields = clazz.getDeclaredFields();
    		
    		for (Field field : declaredFields) {
    			//获取注解
    			Annotation[] annotations = field.getAnnotations();
    			for (Annotation annotation : annotations) {
    				System.out.println(annotation);
    			}
    			//权限修饰符
    			int modifiers = field.getModifiers();
    			System.out.print(Modifier.toString(modifiers) + " ");
    			
    			//属性的类型
    			Class<?> type = field.getType();
    			System.out.print(type + " ");
    			
    			//获取属性的名字
    			String fieldName = field.getName();
    			System.out.print(fieldName);
    			
    			System.out.println();
    		}
    	}
                    /*
    		 * 需求 : 给对象中的私有属性进行赋值
    		 */
    		//getDeclaredField("age") : 可以获取任何权限修饰符修饰的属性
    		Field declaredField = clazz.getDeclaredField("age");
    		//setAccessible(true) : 获取访问权限
    		declaredField.setAccessible(true);
    		//给属性进行赋值
    		//第一个参数 : 给哪个对象中的该属性进行赋值
    		//第二个参数  : 赋值的内容
    		declaredField.set(student, 123);
    		System.out.println(student.getAge());
    
                    //获取方法 - public修饰且有参
    		/*
    		 * 第一个参数 : 方法名
    		 * 第二个参数 :形参的类型(因为是可变形参也可不写)
    		 */
    		Method declaredMethod = clazz.getDeclaredMethod("info", String.class);
    		//获取访问权限
    		declaredMethod.setAccessible(true);
    		//调用参的方法
    		/*
    		 * 第一个参数 :  通过哪个对象去调用该方法
    		 * 第二个参数 : 实参
    		 */
    		declaredMethod.invoke(student, "ccc");
    
    @Test
    	public void test5(){
    		Class clazz = Student.class;
    		//获取带泛型的父类
    		Type genericSuperclass = clazz.getGenericSuperclass();
    		//将父接口转成子接口
    		ParameterizedType t = (ParameterizedType) genericSuperclass;
    		//获取该类的所的泛型
    		Type[] actualTypeArguments = t.getActualTypeArguments();
    		//遍历数组
    		for (Type type : actualTypeArguments) {
    			System.out.println(type);
    		}
    	}

    7、 动态代理

    代理机制的操作,属于静态代理,特征是代理类和目标对象的类都是在编译期间确定下来,不利于程序的扩展。同时,每一个代理类只能为一个接口服务,这样一来程序开发中必然产生过多的代理。

    7.1 动态代理的概述

         动态代理是指客户通过代理类来调用其它对象的方法,并且是在程序运行时根据需要动态创建目标类的代理对象。

    7.2 动态代理使用场合:

    • 调试
    • 远程方法调用

    7.3 代理设计模式的原理:
         使用一个代理将对象包装起来, 然后用该代理对象取代原始对象. 任何对原始对象的调用都要通过代理. 代理对象决定是否以及何时将方法调用转到原始对象上.

    7.4 代理类介绍-Proxy

    Proxy :专门完成代理的操作类,是所有动态代理类的父类。通过此类为一个或多个接口动态地生成实现类。

    提供用于创建动态代理类和动态代理对象的静态方法

          

    7.5 案例

    (1)创建一个实现接口InvocationHandler的类,它必须实现invoke方法,以完成代理的具体操作。

                

    *
     * 用来创建代理对象的
     */
    public class XiaoMi implements InvocationHandler {
    	// 被代理对象
    	private Object tartetObject;
    	public XiaoMi() {
    	}
    	/*
    	 * 获取代理对象 targetObject : 被代理对象 (根据被代理对象创建代理对象)
    	 */
    	public Object getXiaoMi(Object targetObject){
    		this.tartetObject = targetObject;
    		//创建代理对象
    		/*
    		 * ClassLoader : 类加载器 (被代理对象的类加载器是什么就传什么)
    		 * Class<?>[] interfaces : 被代理对象的所的接口(通过接口代理对象才能知道需要代理的方法)
    		 * InvocationHandler : 是一个接口,传一个实现了该接口的实现类的对象
    		 */
    		
    		Object object = Proxy.newProxyInstance(
    				targetObject.getClass().getClassLoader(), 
    				targetObject.getClass().getInterfaces(),
    				this
    		);	
    		return object;
    	}
    
    	/*
    	 * 用来完成代理的具体操作
    	 * 作用 : 用来调用被代理对象中的方法
    	 */
    	@Override
    	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    		System.out.println("---------代理开始了----------");
    	
    		/*
    		 * proxy : com.sun.proxy.$Proxy4
    		 * 调用方法 : 第一个参数是调用哪个对象的方法,第二个参数是调用方法传递的实参
    		 */
    		Object obj = method.invoke(tartetObject, args);
    		System.out.println("---------代理结束了----------");
    		return obj;
    	}
    }
  • 相关阅读:
    HDU1720 A+B Coming
    HDU1390 ZOJ1383 Binary Numbers
    HDU1390 ZOJ1383 Binary Numbers
    HDU2504 又见GCD
    HDU2504 又见GCD
    HDU1335 POJ1546 UVA389 UVALive5306 ZOJ1334 Basically Speaking
    HDU1335 POJ1546 UVA389 UVALive5306 ZOJ1334 Basically Speaking
    HDU1020 ZOJ2478 Encoding
    HDU1020 ZOJ2478 Encoding
    HDU2097 Sky数
  • 原文地址:https://www.cnblogs.com/wushaopei/p/12259641.html
Copyright © 2011-2022 走看看