反射(Reflection)

  • 动态语言的关键,反射机制允许程序在执行期借助Reflection API取得任何类的内部信息,并直接操作任意对象的内部属性及方法,
  • 加载完类之后,在堆内存的方法区就产生了一个完整的Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类结构信息,我们可以通过这个对象看到类的结构,这个对象就像一面镜子,透过这个镜子看到类部的结构,所以我们形象的称其为:反射
  • 动态语言:运行时可以改变其结构的语言
  • 静态语言:运行时结构不可变的语言就是静态语言,Java
  • Java不是动态语言但是可以称之为”准动态语言”,即java有一定的动态性,我们可以利用反射机制,字节码的操作来获得类时动态语言得特性
  • java.long.Class类:反射的源头
    • 类的加载过程:程序经过javac.exe命令以后,会生成一个或者多个字节码文件(.class结尾),接着我们使用java.exe命令对某一字节码文件进行解释运行.相当于将某个字节码文件加载到内存中,此过程就称为类的加载,加载到内存中的类,我们称为运行时类,这个运行时类就作为Class的一个实例.
  • Class实例就对应者一个运行时类
  • 加载到内存中的运行时类,会缓冲一定时间,我们可以通过不同的方式来获取运行时类
    • 获取Class实例
      1. 调用运行类的class属性:例如 Class clazz = Person.class
      2. 通过运行时类的对象: Person p= new Person; class clazz = p.getClass();
      3. 调用Class的静态方法 : Class.forName(“com.adong.java.person”);
      4. 使用类的加载器: CassLoader cl = 当前类.class.getClassLoader(); Class claszz = Cl.loadClass(“com.adong.java.Rerson”)
    • Class方法
      • newInstance() : 创建对应的运行时类的对象:调用空参构造器,空参构造器不能为private
      • Field getField(string name) : 获取运行时类的指定属性,要求属性为public
        • Feild对象: set(Object obj,object obj) : 参数一,为要设置对象,属性二属性值设置为多少.
        • get(object obj):获取属性的值
      • Field getDeclaredField(String name):获取运行时类指定的属性
        • setAccessible(true) :保证当前属性是 可以访问的
      • Field[] getFields(): 获取当前运行时类及其父类中声明为public访问权限的属性
      • Field[] getDeclaredFields():获取当前运行时类中声明的所有属性(不包含父类声明的属性)
        • 可以通过field[] 数组元素知道权限修饰符,数据类型,变量名
        • Field中方法
          1. - int getModifiers(): 返回权限修饰符,可以Modifier.toString(int modifier);获取具体<br />
          2. - Class getType() : 获取数据类型, 获取的class对象.getName()获取具体类型<br />
          3. - String getName(); : 获取变量名<br />
      • Method getDeclareMethod(String name,形参.class) : 参数一获取方法的名称,参数二获取方法的形参列表
        • Object invoke(object obj,String name);参数一为方法的调用者,参数二给方法赋值的实参
          • 静态的化要是使用运行类.class
      • Method[] getMethods() : 获取当前运行时类及其所有父类声明为public权限的方法
      • Method[] getDeclareMethods();:获取当前运动时类中声明的所有方法(不包含父类中声明的方法)
        • Method中方法
          • Annotation[] getAnnotations():获取方法声明 的注解
          • int getModifiers(): 返回权限修饰符,可以Modifier.toString(int modifier);获取具体
          • Class getReturnType() : 返回值类型 .getName() 获取类型字符串
          • String getName() : 方法名
          • Class[] getParameterType() :参数列表
          • class[] getExceptionTypes() : 获取异常类型
      • Constructor getDEclaredConstructor(参数.class):获取指定为构造器,参数为构造器的参数列表
      • Constructor[] getConstructors() : 获取当前运行时类为public权限的构造器
      • Constructor[] getDeclaredConstructors() ; 获取当前运行时类的全部构造器
      • Class getSuperclass():获取运行类的父类
      • Type getGenericSuperclass() : 获取运行时带泛型的父类
      • Class[] getInterface() :获取运行时类实现的接口
      • Package getPackage() : 获取运行时类所在的包
      • Annotation[] getAnnotations():获取运行时类声明的注解
    • 将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。
    • 哪些类型可以有Class 对象?
      • (1)class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类
      • (2)interface:接口
      • (3)[]:数组
      • (4)enum:枚举
      • (5)annotation:注解@interface
      • (6)primitive type:基本数据类型
      • (7)void
      • image.png

//getFields():获取当前运行时类及其父类中声明为public访问权限的属性

  1. Field[] fields = clazz.getFields();

//getDeclaredFields():获取当前运行时类中声明的所有属性。(不包含父类中声明的属性)

    Field[] declaredFields = clazz.getDeclaredFields();

//获取属性的修饰符,数据类型,变量名

Field[] declaredFields = clazz.getDeclaredFields();
for(Field f : declaredFields){
//1.权限修饰符
int modifier = f.getModifiers();
System.out.print(Modifier.toString(modifier) + “\t”);

        //2.数据类型<br />            Class type = f.getType();<br />            System.out.print(type.getName() + "\t");

        //3.变量名<br />            String fName = f.getName();<br />            System.out.print(fName);<br />        }<br />//getMethods():获取当前运行时类及其所有父类中声明为public权限的方法<br />        Method[] methods = clazz.getMethods();<br />//getDeclaredMethods():获取当前运行时类中声明的所有方法。(不包含父类中声明的方法)<br />        Method[] declaredMethods = clazz.getDeclaredMethods();

//获取方法的注解,权限修饰符,返回值,方法名,参数列表,抛出的异常

    Class clazz = Person.class;<br />        Method[] declaredMethods = clazz.getDeclaredMethods();

    for(Method m : declaredMethods){<br />            //1.获取方法声明的注解<br />            Annotation[] annos = m.getAnnotations();<br />          

        //2.权限修饰符<br />            System.out.print(Modifier.toString(m.getModifiers()) + "\t");

        //3.返回值类型<br />            System.out.print(m.getReturnType().getName() + "\t");

        //4.方法名<br />            System.out.print(m.getName());<br />            //5.形参列表<br />            Class[] parameterTypes = m.getParameterTypes();<br />            if(!(parameterTypes == null && parameterTypes.length == 0)){<br />                for(int i = 0;i < parameterTypes.length;i++){

                if(i == parameterTypes.length - 1){<br />                        System.out.print(parameterTypes[i].getName() + " args_" + i);<br />                        break;<br />                    }

                System.out.print(parameterTypes[i].getName() + " args_" + i + ",");<br />                }<br />            }

        System.out.print(")");

        //6.抛出的异常<br />            Class[] exceptionTypes = m.getExceptionTypes();

        if(exceptionTypes.length > 0){<br />                System.out.print("throws ");<br />                for(int i = 0;i < exceptionTypes.length;i++){<br />                    if(i == exceptionTypes.length - 1){<br />                        System.out.print(exceptionTypes[i].getName());<br />                        break;<br />                    }

                System.out.print(exceptionTypes[i].getName() + ",");<br />                }<br />            }

//getConstructors():获取当前运行时类中声明为public的构造器

     Constructor[] constructors = clazz.getConstructors();

//getDeclaredConstructors():获取当前运行时类中声明的所有的构造器

    Constructor[] declaredConstructors = clazz.getDeclaredConstructors();

// 获取运行时类的父类

    Class superclass = clazz.getSuperclass();

// 获取运行时类的带泛型的父类

    Type genericSuperclass = clazz.getGenericSuperclass();

//获取运行时类的带泛型的父类的泛型

    Type genericSuperclass = clazz.getGenericSuperclass();<br />        ParameterizedType paramType = (ParameterizedType) genericSuperclass;<br />        //获取泛型类型<br />        Type[] actualTypeArguments = paramType.getActualTypeArguments();<br />        //二者选一<br />        System.out.println(actualTypeArguments[0].getTypeName());<br />        System.out.println(((Class)actualTypeArguments[0]).getName());

// 获取运行时类实现的接口

    Class[] interfaces = clazz.getInterfaces();

//获取运行时类所在的包

    Package pack = clazz.getPackage();

//获取运行时类声明的注解

    Annotation[] annotations = clazz.getAnnotations();

//创建运行时类的对象

    Class clazz = Person.class;<br />        Person p = (Person) clazz.newInstance();

//操作运行时类中的指定的属性

    Class clazz = Person.class;<br />        //创建运行时类的对象<br />        Person p = (Person) clazz.newInstance();

    //1. getDeclaredField(String fieldName):获取运行时类中指定变量名的属性<br />        Field name = clazz.getDeclaredField("name");

    //2.保证当前属性是可访问的<br />        name.setAccessible(true);<br />        //3.获取、设置指定对象的此属性值<br />        name.set(p,"Tom"); //参数一为要操作属性的对象,参数二修改属性的值,静态的成员变量  :name.set(Person.class,"Tom");修改<br />        //获取变量的值<br />        System.out.println(name.get(p));

//操作运行时类中的指定的方法

    Class clazz = Person.class;<br />        //创建运行时类的对象<br />        Person p = (Person) clazz.newInstance();<br />        <br />        //1.获取指定的某个方法 : getDeclaredMethod():参数1 :指明获取的方法的名称  参数2:指明获取的方法的形参列表<br />        Method show = clazz.getDeclaredMethod("show", String.class);

    //2.保证当前方法是可访问的<br />        show.setAccessible(true);

    //3. 调用方法的invoke():参数1:方法的调用者  参数2:给方法形参赋值的实参,invoke()的返回值即为对应类中调用的方法的返回值。<br />        Object returnValue = show.invoke(p,"CHN"); //String nation = p.show("CHN");<br />        System.out.println(returnValue);

    System.out.println("*************如何调用静态方法*****************");

    // private static void showDesc()

    Method showDesc = clazz.getDeclaredMethod("showDesc");<br />        showDesc.setAccessible(true);<br />        //如果调用的运行时类中的方法没有返回值,则此invoke()返回null<br />        // Object returnVal = showDesc.invoke(null);<br />        Object returnVal = showDesc.invoke(Person.class);<br />        System.out.println(returnVal);//null

//获取指定的构造器:getDeclaredConstructor():参数:指明构造器的参数列表

    Constructor constructor = clazz.getDeclaredConstructor(String.class);<br />        //保证此构造器是可访问的<br />        constructor.setAccessible(true);<br />        //调用此构造器创建运行时类的对象<br />        Person per = (Person) constructor.newInstance("Tom");

//读取配置文件

Properties pros = new Properties();
//此时的文件默认在当前的module下。
//读取配置文件的方式一:
//FileInputStream fis = new FileInputStream(“jdbc.properties”);
//FileInputStream fis = new FileInputStream(“src\jdbc1.properties”);
//pros.load(fis);

    //读取配置文件的方式二:使用ClassLoader<br />        //配置文件默认识别为:当前module的src下<br />        ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();<br />        InputStream is = classLoader.getResourceAsStream("jdbc1.properties");<br />        pros.load(is);

    String user = pros.getProperty("user");<br />        String password = pros.getProperty("password");<br />        System.out.println("user = " + user + ",password = " + password);       

//了解类的加载器

• //1.获取一个系统类加载器
• ClassLoader classloader = ClassLoader.getSystemClassLoader();
• System.out.println(classloader);
• //2.获取系统类加载器的父类加载器,即扩展类加载器
• classloader = classloader.getParent();
• System.out.println(classloader);
• //3.获取扩展类加载器的父类加载器,即引导类加载器
• classloader = classloader.getParent();
• System.out.println(classloader);
• //4.测试当前类由哪个类加载器进行加载
• classloader = Class.forName(“exer2.ClassloaderDemo”).getClassLoader();
• System.out.println(classloader);
• //5.测试JDK提供的Object类由哪个类加载器加载
• classloader = Class.forName(“java.lang.Object”).getClassLoader();
• System.out.println(classloader);
• //*6.关于类加载器的一个主要方法:getResourceAsStream(String str):获取类路径下的指定文件的输入流
• InputStream in = null;
• in = this.getClass().getClassLoader().getResourceAsStream(“exer2\test.properties”);
• System.out.println(in);

反射的应用:动态代理

  • 静态代理:代理类和被代理类在编译期间就去确定下来了,不利于程序的扩展
  • 每一个代理类只能为一个接口服务,程序开发中必然产生过多的代理类
    • 实现动态代理类解决的问题
  • 根据加载到内存中的被代理类,动态创建一个代理类及其对象
  • 代理类的对象调方法时,调用被代理类中同名的方法

package com.adong.java;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
*
* 动态代理的举例
*
* @author shkstart
* @create 2019 上午 10:18
*/

interface Human{

String getBelief();

void eat(String food);

}
//被代理类
class SuperMan implements Human{

@Override<br />    public String getBelief() {<br />        return "I believe I can fly!";<br />    }

@Override<br />    public void eat(String food) {<br />        System._out_.println("我喜欢吃" + food);<br />    }<br />}

class ProxyFactory{
//调用此方法,返回一个代理类的对象。解决问题一
public static Object getProxyInstance(Object obj){//obj:被代理类的对象
MyInvocationHandler handler = new MyInvocationHandler();

    handler.bind(obj);<br />        //被代理类的类加载器,其实现的方法, 第一个类加载器,第二个这个类实现的接口,<br />        return Proxy._newProxyInstance_(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler);<br />    }

}

class MyInvocationHandler implements InvocationHandler{

private Object obj;//需要使用被代理类的对象进行赋值<br />    <br />    public void bind(Object obj){<br />        this.obj = obj;<br />    }

//当我们通过代理类的对象,调用方法a时,就会自动的调用如下的方法:invoke()<br />    //将被代理类要执行的方法a的功能就声明在invoke()中<br />    @Override<br />    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {<br />    //method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法<br />    //obj:被代理类的对象<br />  //args:方法的参数<br />    Object returnValue = method.invoke(obj,args);<br />    //上述方法的返回值就作为当前类中的invoke()的返回值。<br />        return returnValue;

}<br />}

public class ProxyTest {

public static void main(String[] args) {<br />        //代理类的对象<br />        SuperMan superMan = new SuperMan();<br />        //proxyInstance:代理类的对象<br />        Human proxyInstance = (Human) ProxyFactory._getProxyInstance_(superMan);<br />        //当通过代理类对象调用方法时,会自动的调用被代理类中同名的方法<br />        String belief = proxyInstance.getBelief();<br />        System._out_.println(belief);<br />        proxyInstance.eat("四川麻辣烫");

    System._out_.println("*****************************");

    NikeClothFactory nikeClothFactory = new NikeClothFactory();

    ClothFactory proxyClothFactory = (ClothFactory) ProxyFactory._getProxyInstance_(nikeClothFactory);

    proxyClothFactory.produceCloth();

}<br />}