反射

1. 序列化和反序列化

对象流:

ObjectOutputStream:内存中的对象-----转化为---->存储中的文件、通过网络传输出去:序列化

ObjectInputStream:存储中的文件、通过网络接收过来-----还原为---->内存中的对象:反序列化

对象的序列化机制:

序列化:内存中的Java对象转换成二进制流,把二进制流持久地保存在磁盘上,通过网络将二进制流传输到另一个网络节点。

反序列化:当其它程序获取到了这种二进制流,可以恢复成原来的Java对象。

 

2. 反射(动态性)

  • Reflection(反射)是被视为动态语言的关键,反射机制允许程序在执行期借助于 Reflection API 取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
  • 加载完类之后,在堆内存的方法区中就产生了一个 Class 类型的对象(一个类只有一个 Class 对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射。
  • 类的加载过程:javac.exe命令后,生成一个或多个字节码文件(.class结尾),使用java.exe对某个字节码文件进行解释运行,加载到内存中。
  • Class的实例就对应着一个运行时的类

(1)反射机制的功能:

  • 在运行时判断任意一个对象所属的类

  • 在运行时构造任意一个类的对象

  • 在运行时判断任意一个类所具有的成员变量和方法

  • 在运行时获取泛型信息

  • 在运行时调用任意一个对象的成员变量和方法

  • 在运行时处理注解

  • 生成动态代理  

 

(2)获取Class的实例方法

1)已知具体的类,通过类的 class 属性获取,该方法最为安全可靠,程序性能最高 实例:Class clazz = String.class;

2)已知某个类的实例,调用该实例的 getclass() 方法获取 Class 对象 实例:Class clazz=person.getclass();

3)已知一个类的全类名,且该类在类路径下,可通过 Class 类的静态方法 forName() 获取,可能抛出 ClassNotFoundException(比较常用)

实例:Class clazz = Class.forName(String classPath)

4)通过类加载器 ClassLoader cl = this.getclass().getClassLoader(); Class clazz = cl.loadClass("类的全类名");

Person类
public class Person {
    private  String name;
    public   int age;
    public Person(){    }
    private Person(String name) {
        this.name = name;
    }
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    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("it is my show time");
    }
    private String myNation(String nation){
        System.out.println("祖国"+nation);
        return nation;
    }
}

Reflection测试

public class ReflectTest {
    static void test01(){//反射前对实例对象的操作
        Person person = new Person("张三",8);
        person.show();
        person.age = 12;
        System.out.println(person.toString());
    }

    static void test02() { //反射方法
        //获取Class的实例的方式
        //方式1:调用运行时类的属性
        Class clazz = Person.class; //Class<Person> clazz = Person.class;
        System.out.println(clazz);

        //方式2:通过运行时类的对象获取
        Person person = new Person();
        Class clazz1 = person.getClass();
        System.out.println(clazz1);

        //方式3:调用Class的静态方法:forName(String classPath)
        Class clazz2 = null;
        try {
            clazz2 = Class.forName("Person");
            System.out.println(clazz2);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }

        //方式4:使用类的加载器:ClassLoader
        ClassLoader classLoader = ReflectTest.class.getClassLoader();
        try {
            Class clazz3 = classLoader.loadClass("Person");
            System.out.println(clazz3);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    
    public static void main(String[] args) {
        test01();
        test02();
    }
}

(3)除了类之外哪些结构可以作为Class的实例?

1)class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类

2)interface:接口

3)[]:数组

4)enum:枚举

5)annotation:注解@interface

6)primitive type:基本数据类型

7)void:无返回值

(4)读取配置文件的方式

1)使用IO流读取

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

    //读取配置文件的方式二:使用ClassLoader
    //配置文件默认识别为:当前module的src下
    ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
    InputStream is = classLoader.getResourceAsStream("jdbc1.properties");
    try {
        pros.load(is);
    } catch (IOException e) {
        e.printStackTrace();
    }
    String user = pros.getProperty("user");
    String password = pros.getProperty("password");
    System.out.println("user = " + user + " password =" + password);

(5)创建运行时类的对象 

 反射具有动态性,要创建对象时,不知道具体哪个对象,不能使用new,只能使用newInstance()。

Class<Person> clazz = Person.class;
Person obj = clazz.newInstance(); //调用运行时类的无参的构造方法

(6)反射可以取得的结构:

  • 实现的全部接口: public Class<?>[] getInterfaces() 确定此对象所表示的类或接口实现的接口。

  • 所继承的父类: public Class<? Super T> getSuperclass() 返回表示此 Class 所表示的实体(类、接口、基本类型)的父类的 Class

  • 全部的构造器:

    public Constructor<T>[] getConstructors()

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

    public Constructor<T>[] getDeclaredConstructors()

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

    在Constructor类中:

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

    public Method[] getDeclaredMethods()

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

    public Method[] getMethods()

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

    Method 类中:

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

    public Field[] getFields()

    返回此 Class 对象所表示的类或接口的 publicField

    public Field[] getDeclaredFields()

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

    Field 方法中

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

    get Annotation(Class<T> annotationClass)

    getDeclaredAnnotations()

  • 泛型相关

    获取父类泛型类型:Type getGenericSuperclass()

    泛型类型:ParameterizedType

    获取实际的泛型类型参数数组:getActualTypeArguments()

  • 类所在的包 Package getPackage()

 

获取运行时类的属性filed

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

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

  getType():数据类型

public class FiledTest {
    @Test
    public void test1() {
        Class clazz = Person.class;
        //获取属性结构
        //getFields():获取当前运行时类及其父类中声明为public访问权限的属性
        Field[] fields = clazz.getFields();
        for (Field f :
             fields) {
            System.out.println(f);
        }
        System.out.println();
        //getDeclaredFields():获取当前运行时类中声明的所有属性。(不包含父类中声明的属性)
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field f :
             declaredFields) {
            System.out.println(f);
        }
    }

    //权限修饰符  数据类型 变量名
    @Test
    public void test2() throws ClassNotFoundException {
        Class clazz = Class.forName("Person");
        Field[] declaredFields = clazz.getDeclaredFields();
        for (Field f :
             declaredFields) {
            //1.权限修饰符
            int modifiers = f.getModifiers();
            System.out.print(Modifier.toString(modifiers)+"\t");

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

            //3.变量名
            String fName = f.getName();
            System.out.print(fName);

            System.out.println();
        }
    }
}

获取运行时类的方法Methods

  getMethods():获取当前运行时类及其所有父类中声明为public权限的方法

  getDeclaredMethods():获取当前运行时类中声明的所有方法。(不包含父类中声明的方法)

  getAnnotations():获取方法声明的注解

  getModifiers():权限修饰符

  getReturnType():返回值类型

  getName():方法名

  getParameterTypes():形参类型

  getExceptionTypes():抛出的异常

public class MethodTest {
    @Test
    public void test1() {
        Class<Person> clazz = Person.class;
        //getMethods():获取当前运行时类及其所有父类中声明为public权限的方法
        Method[] methods = clazz.getMethods();
        for (Method m :
             methods) {
            System.out.println(m);
        }
        System.out.println("============");
        //getDeclaredMethods():获取当前运行时类中声明的所有方法。(不包含父类中声明的方法)
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method m :
             declaredMethods) {
            System.out.println(m);
        }
    }

    /*
    @Xxxx
    权限修饰符  返回值类型  方法名(参数类型1 形参名1,...) throws XxxException{}
     */
    @Test
    public void test2() throws ClassNotFoundException {
        Class<?> clazz = Class.forName("Person");
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method m :
             declaredMethods) {
            //1.获取方法声明的注解
            Annotation[] annos = m.getAnnotations();
            for (Annotation a :
                 annos) {
                System.out.println(a);
            }

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

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

            //4.方法名
            System.out.print(m.getName());
            System.out.print("(");

            //5.形参列表
            Class<?>[] parameterTypes = m.getParameterTypes();
            if (!(parameterTypes == null && parameterTypes.length == 0)) {
                for (int i = 0; i < parameterTypes.length; i++) {
                    if (i == parameterTypes.length - 1) {
                        System.out.print(parameterTypes[i].getName() + " args_" + i);
                        break;
                    }
                    System.out.print(parameterTypes[i].getName() + "args_" + i + ",");
                }
            }
            System.out.print(")");

            //6.抛出的异常
            Class<?>[] exceptionTypes = m.getExceptionTypes();
            if (exceptionTypes.length > 0){
                System.out.print("throws ");
                for (int i = 0; i < exceptionTypes.length; i++) {
                    if (i==exceptionTypes.length -1){
                        System.out.print(exceptionTypes[i].getName());
                        break;
                    }
                    System.out.print(exceptionTypes[i].getName()+",");
                }
                System.out.println();
            }
        }
    }
}

 获取构造器结构

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

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

  获取运行时类的父类:getSuperclass()

  获取运行时类的带泛型的父类:getGenericSuperclass()

  获取运行时类的带泛型的父类的泛型:(ParameterizedType :所带的参数类型)

  Class clazz = Person.class;
    Type genericSuperclass = clazz.getGenericSuperclass();
  ParameterizedType paramType = (ParameterizedType) genericSuperclass; //获取泛型类型            Type[] actualTypeArguments = paramType.getActualTypeArguments();
   // System.out.println(actualTypeArguments[0].getTypeName());
  System.out.println(((Class)actualTypeArguments[0]).getName());
  获取运行时类实现的接口:getInterfaces()
  获取运行时类的父类实现的接口:getSuperclass().getInterfaces()
  获取运行时类所在的包:getPackage()
  获取运行时类声明的注解:getAnnotations()
 

调用运行时类中指定的属性:

  Class clazz = Person.class;
  //创建运行时类的对象
  Person p = (Person) clazz.newInstance();
  //1.获取运行时类中指定变量名的属性
  Field name = clazz.getDeclaredField("name");
  //2.保证当前属性是可访问的
  name.setAccessible(true);
  //3.获取(get)、设置(set)指定对象的此属性值
  name.set(p,"Tom");     // 属性.set(属性所属的具体的对象,属性赋的值)
  id.get(p);    //属性.get(属性所属的具体对象)
  System.out.println(name.get(p));

调用运行时类中指定的方法:

public void testMethod() throws Exception {
    Class<Person> clazz = Person.class;

    //创建运行时类的对象
    Person person = clazz.newInstance();
    /*
        1.获取指定的某个方法
        getDeclaredMethod():参数1 :指明获取的方法的名称  参数2:指明获取的方法的形参列表
         */
    Method show = clazz.getDeclaredMethod("show", String.class);
    //2.保证当前方法是可访问的
    show.setAccessible(true); 
    /*
        3. 调用方法的invoke():参数1:方法的调用者  参数2:给方法形参赋值的实参
        invoke()的返回值即为对应类中调用的方法的返回值。
         */
    Object returnValue = show.invoke(person, "CHN");
    System.out.println(returnValue);

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

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

 调用运行时类中指定的构造器

public void testConstructor() throws Exception {
    Class clazz = Person.class;
    //private Person(String name)
    /*
        1.获取指定的构造器
        getDeclaredConstructor():参数:指明构造器的参数列表
         */
    Constructor constructor = clazz.getDeclaredConstructor(String.class);

    //2.保证此构造器是可访问的
    constructor.setAccessible(true);

    //3.调用此构造器创建运行时类的对象
    Person per = (Person) constructor.newInstance("Tom");
    System.out.println(per);
}

3.动态代理

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

(1)静态代理

静态代理的缺点:

① 代理类和目标对象的类都是在编译期间确定下来,不利于程序的扩展。

② 每一个代理类只能为一个接口服务,这样一来程序开发中必然产生过多的代理。

interface ClothFactory{
    void produceCloth();
}
//代理类
class ProxyClothFactory implements ClothFactory{

    private ClothFactory factory;//用被代理类对象进行实例化
    public ProxyClothFactory(ClothFactory factory) {
        this.factory = factory;
    }
    @Override
    public void produceCloth() {
        System.out.println("代理工厂做一些准备工作");
        factory.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) {
        //创建被代理类的对象
        ClothFactory nike = new NikeClothFactory();
        //创建代理类的对象
        ClothFactory proxyClothFactory = new ProxyClothFactory(nike);
        proxyClothFactory.produceCloth();
    }
}

/*
代理工厂做一些准备工作
Nick工厂生产一批运动服
代理工厂做一些后续的收尾工作
*/

 (2)动态代理

两个主要问题:

问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象。

(通过 Proxy.newProxyInstance() 实现)

问题二:当通过代理类的对象调用方法a时,如何动态的去调用被代理类中的同名方法。

(通过 InvocationHandler 接口的实现类及其方法 invoke() )

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

  2. 创建被代理类以及接口

  3. 通过Proxy的静态方法 newProxyInstance(ClassLoader loader, Class<?>...interface, InvocationHandler h) 创建一个接口代理

  4. 通过代理类的实例调用被代理类的方法

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("I like eat " + food);
    }
}

/*
要想实现动态代理,需要解决的问题?
问题一:如何根据加载到内存中的被代理类,动态的创建一个代理类及其对象。
问题二:当通过代理类的对象调用方法a时,如何动态的去调用被代理类中的同名方法a。
 */

//创建继承了InvocationHandler接口的类
class MyInvocationHanlder implements InvocationHandler {
    private Object obj;//需要使用被代理类的对象进行赋值

    public void bind(Object obj) {
        this.obj = obj;
    }
    //当我们通过代理类的对象,调用方法a时,就会自动的调用如下的方法:invoke()
    //将被代理类要执行的方法a的功能就声明在invoke()中
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        //method:即为代理类对象调用的方法,此方法也就作为了被代理类对象要调用的方法
        //obj:被代理类的对象
        Object returnValue = method.invoke(obj, args);

        //上述方法的返回值就作为当前类中的invoke()的返回值。
        return returnValue;
    }
}

class ProxyFactory {
    //调用此方法,返回一个代理类的对象。解决问题一
    public static Object getProxyInstance(Object obj) {
        MyInvocationHanlder hanlder = new MyInvocationHanlder();
        hanlder.bind(obj);
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),hanlder);

    }
}

//测试动态代理
public class ProxyTest {
    public static void main(String[] args) {
        SuperMan superMan = new SuperMan();
        //proxyInstance:代理类的对象
        Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
        //当通过代理类对象调用方法时,会自动的调用被代理类中同名的方法
        String belief = proxyInstance.getBelief();
        System.out.println(belief);
        proxyInstance.eat("火锅");
    }
}

动态代理与AOP

  • 使用 Proxy 生成一个动态代理时,往往并不会凭空产生一个动态代理,这样没有太大的意义。通常都是为指定的目标对象生成动态代理。

  • 这种动态代理在 AOP 中被称为 AOP 代理,AOP 代理可代替目标对象,AOP 代理包含了目标对象的全部方法。但 AOP 代理中的方法与目标对象的方法存在差异:

  • AOP 代理里的方法可以在执行目标方法之前、之后插入一些通用处理

 

posted @ 2022-08-17 10:58  浑浑噩噩一只小迷七  阅读(162)  评论(0)    收藏  举报