基础巩固4-反射机制

框架 = 反射 + 注解 + 设计模式。
加载完类之后, 在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象) , 这个对象就包含了完整的类的结构信息。 我们可以通过这个对象看到类的结构。 这个对象就像一面镜子, 透过这个镜子看到类的结构, 所以, 我们形象的称之为: 反射。
一、获取Class类的对象的四种方法
① Class类的理解
1.类的加载过程:
程序经过javac.exe命令以后,会生成一个或多个字节码文件(.class结尾)。
接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中。此过程就称为类的加载。加载到内存中的类,我们就称为运行时类,此运行时类,就作为Class的一个实例。

2.换句话说,Class的实例就对应着一个运行时类。

3.加载到内存中的运行时类,会缓存一定的时间。在此时间之内,我们可以通过不同的方式来获取此运行时类。

@Getter
@Setter
@NoArgsConstructor
public class Person {

    private Integer id;

    private String name;

    private String gender;
}
public class ReflectTest {

    @Test
    public void reflectTest() throws ClassNotFoundException {
        Person person = new Person();
//      1 获取clazz的方式一:通过类名
        Class<Person> personClass = Person.class;
        System.out.println(personClass);

//     2 获取clazz的方式二:通过对象
        Class<? extends Person> aClass = person.getClass();
        System.out.println(aClass);

//     3 获取clazz的方式三:通过Class类的静态方法和类路径
        Class<?> aClass1 = Class.forName("com.javabase.javabase.reflect.Person");
        System.out.println(aClass1);

//     4 获取clazz的方式四:通过ClassLoader类加载器的静态方法
        Class<?> aClass2 = ClassLoader.getSystemClassLoader().loadClass("com.javabase.javabase.reflect.Person");
        System.out.println(aClass2);
    }
}

Class类的实例描述了其所表示的实体(类、接口、数组类、基本类型或void)的所有信息:
对象照镜子后可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接口。对于每个类而言, JRE 都为其保留一个不变的 Class 类型的对象。一个 Class 对象包含了特定某个结构(class/interface/enum/annotation/primitive type/void/[])的有关信息。
 Class本身也是一个类
 Class 对象只能由系统建立对象
 一个加载的类在 JVM 中只会有一个Class实例
 一个Class对象对应的是一个加载到JVM中的一个.class文件(也就是运行时类)
 每个类的实例都会记得自己是由哪个 Class 实例所生成
 通过Class可以完整地得到一个类中的所有被加载的结构
 Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象

Java类编译、运行的执行的流程(双亲委派机制)
在这里插入图片描述
ps:重写和重载,静态绑定(对应编译期间)和动态绑定(对应执行期间)

重写:
 * 		① 子类重写的方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同
 *         ② 子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符(范围变大)
 *      	>特殊情况:子类不能重写父类中声明为private权限的方法
 *         ③ 返回值类型:(范围变大)
 *      	>父类被重写的方法的返回值类型是void,则子类重写的方法的返回值类型只能是void
 *      	>父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值类型可以是A类或A类的子类
 *      	>父类被重写的方法的返回值类型是基本数据类型(比如:double),则子类重写的方法的返回值类型必须是相同的基本数据类型(必须也是double)
 *		④ 子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型(具体放到异常处理时候讲)(范围变小)
重载,是指允许存在多个同名方法,而这些方法的参数不同。编译器根据方法不同的参数表,对同名方法的名称做修饰。
对于编译器而言,这些同名方法就成了不同的方法。它们的调用地址在编译期就绑定了。
Java的重载是可以包括父类和子类的,即子类可以重载父类的同名不同参数的方法。
所以:对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法,这称为“早绑定”或“静态绑定”;

而对于多态,只等到方法调用的那一刻,解释运行器才会确定所要调用的具体方法,这称为“晚绑定”或“动态绑定”。 

引用一句Bruce Eckel的话:“不要犯傻,如果它不是晚绑定,它就不是多态。”

反射的图片总结:
在这里插入图片描述

哪些类型可以有Class对象
(1) class:
外部类, 成员(成员内部类, 静态内部类), 局部内部类, 匿名内部类
(2) interface: 接口
(3) []:数组
(4) enum:枚举
(5) annotation:注解@interface
(6) primitive type:基本数据类型
(7) void

二、类的加载与classloader的理解
①类的加载器的作用
加载:将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口(即引用地址)。所有需要访问和使用类数据只能通过这个Class象。这个加载的过程需要类加载器参与。
类缓存: 标准的JavaSE类加载器可以按要求查找类, 但一旦某个类被加载到类加载器中, 它将维持加载(缓存) 一段时间。 不过JVM垃圾回收机制可以回收这些Class对象。

②类的加载器的分类
在这里插入图片描述
获取各种类加载器的方法

public class ClassLoaderTest {

    @Test
    public void classLoaderTest() throws ClassNotFoundException {

//        1获取系统类加载器(系统类加载器就是当前类的类加载器)
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println("1" + systemClassLoader);

//        2获取扩展类加载器,即系统类加载器的父类
        ClassLoader systemClassLoaderParent = systemClassLoader.getParent();
        System.out.println("2" + systemClassLoaderParent);

//        3获取引导类加载器,即系统类加载器的父类
        ClassLoader systemClassLoaderParentParent = systemClassLoaderParent.getParent();
        System.out.println("3" + systemClassLoaderParentParent);

//        4测试当前类的对应的Class的实例是由哪个加载器加载的
        ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
        System.out.println("4" + classLoader);

//        5测试object类是由哪个加载器加载的
        ClassLoader classLoader1 = Class.forName("java.lang.Object").getClassLoader();
        System.out.println("5" + classLoader1);

//        6测试String类是由哪个加载器加载的
        ClassLoader classLoader2 = Class.forName("java.lang.String").getClassLoader();
        System.out.println("6" + classLoader2);

//        7类加载器的一个主要的方法,用于获取类路径下的指定文件的输入流
//         方式一、由系统类加载器获取
        InputStream resourceAsStream = ClassLoader.getSystemClassLoader().getResourceAsStream("config.properties");
        System.out.println("7.1" + resourceAsStream);
//         方式二、由当前类的类加载获取
        InputStream resourceAsStream1 = this.getClass().getClassLoader().getResourceAsStream("config.properties");
        System.out.println("7.2" + resourceAsStream1);
    }
}
1sun.misc.Launcher$AppClassLoader@18b4aac2
2sun.misc.Launcher$ExtClassLoader@6f539caf
3null
4sun.misc.Launcher$AppClassLoader@18b4aac2
5null
6null
7.1java.io.BufferedInputStream@3fb4f649
7.2java.io.BufferedInputStream@33833882

三、反射的应用
①获取运行时类的对象,有Class实例得到具体类的对象
②获取运行时类的属性,由Class实例和类的属性名得到唯一的属性对象
//getDeclaredFields():当前类中所有,获取当前运行时类中声明的所属性。(Declared表示不包含继承而来的,不包含父类中声明的属性)
//getFields():当前类的public及其父类的public,获取当前运行时类及其父类中声明为public访问权限的属性
③获取运行时类的方法,由Class实例和类的方法名和方法入参类型可以得到唯一的对应方法的对象(因为重写的方法就是以方法名和入参类型做唯一区分的)
其他应用 如下所示
Person类如下所示

package com.javabase.javabase.reflect;

import lombok.*;
import org.springframework.stereotype.Component;

/**
 * @Author ShawnYang
 * @Date 2019-09-25 9:40
 * @Description TODO
 * 修改人:
 * 修改时间:
 * 修改备注:
 */
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Component
public class Person implements SuperPerson{

    private Integer id;

    private String name;

    private String gender;

    public Integer age;

    private Person(Integer id,String nane,String gender){

    }

    @Override
    public String eat() {
        return "很好吃";
    }
}

测试代码如下所示:

package com.javabase.javabase.reflect;

import lombok.Getter;
import org.junit.Test;

import java.lang.annotation.Annotation;
import java.lang.reflect.*;

/**
 * @Author ShawnYang
 * @Date 2019-09-25 9:39
 * @Description TODO
 * 修改人:
 * 修改时间:
 * 修改备注:
 */
public class ReflectTest {

    @Test
    public void reflectTest() throws Exception {
//        Person person = new Person();
//      1 获取clazz的方式一:通过类名
        Class<Person> personClass = Person.class;
        System.out.println(personClass);

//     2 获取clazz的方式二:通过对象
//        Class<? extends Person> aClass = person.getClass();
//        System.out.println(aClass);

//     3 获取clazz的方式三:通过Class类的静态方法和类路径
        Class<?> aClass1 = Class.forName("com.javabase.javabase.reflect.Person");
        System.out.println(aClass1);

//     4 获取clazz的方式四:通过ClassLoader类加载器的静态方法
        Class<?> aClass2 = ClassLoader.getSystemClassLoader().loadClass("com.javabase.javabase.reflect.Person");
        System.out.println(aClass2);

//     5 反射的应用
//       5.1 应用一、获取运行时类的对象
//        5.1.1 无参构造器,获取运行时类的对象,此方法需要被反射类 Person有一个无参构造器,如果没有无参构造器(最好带上),可通过有参构造器获取对象实例
//         在javabean中要求提供一个public的空参构造器。原因:
//          1.便于通过反射,创建运行时类的对象
//          2.便于子类继承此运行时类时,默认调用super()时,保证父类此构造器
        Object newInstance = aClass1.newInstance();
        System.out.println("5.1"+newInstance);
//        5.1.2 应用二:有参构造器,获取运行时类的对象
//        5.1.2.1 获取有参构造器
        Constructor<?> constructor = aClass1.getConstructor(Integer.class,String.class,String.class,Integer.class);
//        5.1.2.2 获取对象
        Object newInstance1 = constructor.newInstance(1, "yangxuan", "",30);
        System.out.println("5.1.2.2"+newInstance1);
//       5.2 应用二、获取运行时类的属性对象,入参属性名,返回对应的属性对象
//        5.2.1 获取当前运行时类中声明的所有属性。(不包含父类中声明的属性)
        Field name = aClass1.getDeclaredField("name");
        System.out.println("5.2.1.1   "+name);
        Field[] declaredFields = aClass1.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            System.out.println("5.2.1.2   "+declaredField);
        }
//        5.2.2 获取当前运行时类声明为public及其父类中声明为public访问权限的属性
        //只有age属性是public的 所有getField 只能得到age 属性的属性对象
        Field name1 = aClass1.getField("age");
        System.out.println("5.2.2.1   "+name1);
        Field[] fields = aClass1.getFields();
        for (Field field : fields) {
            System.out.println("5.2.2.2   "+field);
        }
//       5.3 应用三、获取运行时类的方法,入参:方法名 方法入参类型  返回:方法对象
//        5.3.1 getDeclaredMethods():获取当前运行时类中声明的所有方法(包括静态方法)。(不包含父类中声明的方法)
        Method getId = aClass1.getDeclaredMethod("getId", null);
        System.out.println("5.3.1.1   "+getId);
        Method setId = aClass1.getDeclaredMethod("setId",Integer.class);
        System.out.println("5.3.1.2   "+setId);
        Method[] declaredMethods = aClass1.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            System.out.println("5.3.1.3   "+declaredMethod);
        }
//        5.3.2 getMethods():获取当前运行时类及其所父类中声明为public权限的方法
        Method getClass = aClass1.getMethod("getClass", null);
        System.out.println("5.3.2.1   "+getClass);
        Method[] methods = aClass1.getMethods();
        for (Method method : methods) {
            System.out.println("5.3.2.3   "+method);
        }

//        5.4 应用四、获取构造器结构
//        5.4.1 获取当前运行时类中声明的所的构造器
        Constructor<?> declaredConstructor = aClass1.getDeclaredConstructor();
        System.out.println("5.4.1.1   "+declaredConstructor);
        Constructor<?> declaredConstructor1 = aClass1.getDeclaredConstructor(Integer.class, String.class, String.class, Integer.class);
        System.out.println("5.4.1.2   "+declaredConstructor1);
        Constructor<?>[] declaredConstructors = aClass1.getDeclaredConstructors();
        for (Constructor<?> declaredConstructor2 : declaredConstructors) {
            System.out.println("5.4.1.3   "+declaredConstructor2);
        }
//        5.4.2 获取当前运行时类中声明为public的构造器
        Constructor<?> constructor1 = aClass1.getConstructor();
        System.out.println("5.4.2.1   "+constructor1);
        Constructor<?> constructor2 = aClass1.getConstructor(Integer.class, String.class, String.class, Integer.class);
        System.out.println("5.4.2.2   "+constructor2);
        Constructor<?>[] constructors = aClass1.getConstructors();
        for (Constructor<?> constructor3 : constructors) {
            System.out.println("5.4.2.3   "+constructor3);
        }

//        5.5 应用五、获取运行时的父类
//        5.5.1 获取普通父类
        Class<?> superclass = aClass1.getSuperclass();
        System.out.println("5.5.1   "+superclass);
//        5.5.2 获取带泛型的父类
        Type genericSuperclass = aClass1.getGenericSuperclass();
        System.out.println("5.5.2   "+genericSuperclass);
//        5.6 应用六  获取运行时带泛型的父类的泛型(了解)
//        ParameterizedType genericSuperclass1 = (ParameterizedType) aClass1.getGenericSuperclass();
//        Type[] actualTypeArguments = genericSuperclass1.getActualTypeArguments();
//        System.out.println("5.5.6   "+actualTypeArguments);

//        5.7 获取运行类实现的接口
        Class<?>[] interfaces = aClass1.getInterfaces();
        for (Class<?> anInterface : interfaces) {
            System.out.println("5.7     "+anInterface);
        }

//        5.8 获取运行时类所在的包
        Package aPackage = aClass1.getPackage();
        System.out.println("5.8     "+aPackage);

//        5.9 获取运行时类声明的注解(注意只是@Component 是在运行时存在,其他Person 的注解在编译期就别discard了 要注意
        Getter annotation = aClass1.getAnnotation(Getter.class);
        System.out.println("5.9.1     "+annotation);
        Annotation[] annotations = aClass1.getAnnotations();
        System.out.println(annotations.length);
        for (Annotation annotation1 : annotations) {
            System.out.println("5.9.2     "+annotation1);
        }
    }
}


运行结果:

class com.javabase.javabase.reflect.Person
class com.javabase.javabase.reflect.Person
class com.javabase.javabase.reflect.Person
5.1Person(id=null, name=null, gender=null, age=null)
5.1.2.2Person(id=1, name=yangxuan, gender=, age=30)
5.2.1.1   private java.lang.String com.javabase.javabase.reflect.Person.name
5.2.1.2   private java.lang.Integer com.javabase.javabase.reflect.Person.id
5.2.1.2   private java.lang.String com.javabase.javabase.reflect.Person.name
5.2.1.2   private java.lang.String com.javabase.javabase.reflect.Person.gender
5.2.1.2   public java.lang.Integer com.javabase.javabase.reflect.Person.age
5.2.2.1   public java.lang.Integer com.javabase.javabase.reflect.Person.age
5.2.2.2   public java.lang.Integer com.javabase.javabase.reflect.Person.age
5.3.1.1   public java.lang.Integer com.javabase.javabase.reflect.Person.getId()
5.3.1.2   public void com.javabase.javabase.reflect.Person.setId(java.lang.Integer)
5.3.1.3   public java.lang.String com.javabase.javabase.reflect.Person.toString()
5.3.1.3   public java.lang.String com.javabase.javabase.reflect.Person.getName()
5.3.1.3   public java.lang.Integer com.javabase.javabase.reflect.Person.getId()
5.3.1.3   public void com.javabase.javabase.reflect.Person.setName(java.lang.String)
5.3.1.3   public void com.javabase.javabase.reflect.Person.setId(java.lang.Integer)
5.3.1.3   public java.lang.String com.javabase.javabase.reflect.Person.eat()
5.3.1.3   public java.lang.String com.javabase.javabase.reflect.Person.getGender()
5.3.1.3   public static java.lang.String com.javabase.javabase.reflect.Person.staticTest(java.lang.String)
5.3.1.3   public void com.javabase.javabase.reflect.Person.setGender(java.lang.String)
5.3.1.3   public void com.javabase.javabase.reflect.Person.setAge(java.lang.Integer)
5.3.1.3   public java.lang.Integer com.javabase.javabase.reflect.Person.getAge()
5.3.2.1   public final native java.lang.Class java.lang.Object.getClass()
5.3.2.3   public java.lang.String com.javabase.javabase.reflect.Person.toString()
5.3.2.3   public java.lang.String com.javabase.javabase.reflect.Person.getName()
5.3.2.3   public java.lang.Integer com.javabase.javabase.reflect.Person.getId()
5.3.2.3   public void com.javabase.javabase.reflect.Person.setName(java.lang.String)
5.3.2.3   public void com.javabase.javabase.reflect.Person.setId(java.lang.Integer)
5.3.2.3   public java.lang.String com.javabase.javabase.reflect.Person.eat()
5.3.2.3   public java.lang.String com.javabase.javabase.reflect.Person.getGender()
5.3.2.3   public static java.lang.String com.javabase.javabase.reflect.Person.staticTest(java.lang.String)
5.3.2.3   public void com.javabase.javabase.reflect.Person.setGender(java.lang.String)
5.3.2.3   public void com.javabase.javabase.reflect.Person.setAge(java.lang.Integer)
5.3.2.3   public java.lang.Integer com.javabase.javabase.reflect.Person.getAge()
5.3.2.3   public final void java.lang.Object.wait() throws java.lang.InterruptedException
5.3.2.3   public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
5.3.2.3   public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
5.3.2.3   public boolean java.lang.Object.equals(java.lang.Object)
5.3.2.3   public native int java.lang.Object.hashCode()
5.3.2.3   public final native java.lang.Class java.lang.Object.getClass()
5.3.2.3   public final native void java.lang.Object.notify()
5.3.2.3   public final native void java.lang.Object.notifyAll()
5.4.1.1   public com.javabase.javabase.reflect.Person()
5.4.1.2   public com.javabase.javabase.reflect.Person(java.lang.Integer,java.lang.String,java.lang.String,java.lang.Integer)
5.4.1.3   public com.javabase.javabase.reflect.Person(java.lang.Integer,java.lang.String,java.lang.String,java.lang.Integer)
5.4.1.3   private com.javabase.javabase.reflect.Person(java.lang.Integer,java.lang.String,java.lang.String)
5.4.1.3   public com.javabase.javabase.reflect.Person()
5.4.2.1   public com.javabase.javabase.reflect.Person()
5.4.2.2   public com.javabase.javabase.reflect.Person(java.lang.Integer,java.lang.String,java.lang.String,java.lang.Integer)
5.4.2.3   public com.javabase.javabase.reflect.Person(java.lang.Integer,java.lang.String,java.lang.String,java.lang.Integer)
5.4.2.3   public com.javabase.javabase.reflect.Person()
5.5.1   class java.lang.Object
5.5.2   class java.lang.Object
5.7     interface com.javabase.javabase.reflect.SuperPerson
5.8     package com.javabase.javabase.reflect
5.9.1     null
1
5.9.2     @org.springframework.stereotype.Component(value=)

四、用反射方式获取类的属性、方法、构造器
Person类

package com.javabase.javabase.reflect;

import lombok.*;
import org.springframework.stereotype.Component;

/**
 * @Author ShawnYang
 * @Date 2019-09-25 9:40
 * @Description TODO
 * 修改人:
 * 修改时间:
 * 修改备注:
 */
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@ToString
@Component
public class Person implements SuperPerson{

    private Integer id;

    private String name;

    private String gender;

    public Integer age;

    public static String staticTest(String s){
        return "输入给静态方法的字符串内容是:"+s;
    }

    private Person(Integer id,String name,String gender){

    }

    @Override
    public String eat() {
        return "很好吃";
    }
}

测试样例:

package com.javabase.javabase.reflect;

import org.junit.Test;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * @Author ShawnYang
 * @Date 2019-09-25 16:17
 * @Description TODO
 * 修改人:
 * 修改时间:
 * 修改备注:
 */
public class ReflectUse {

    /**
     * @Author ShawnYang
     * @Date 2019/9/25 0025 16:58
     * @Description 调用指定的属性
     * 修改人:
     * 修改时间:
     * 修改备注:
     * 实现注意:
     */
    @Test
    public void test01Field() throws Exception {
//      1 创建运行时类的Class实例
        Class<Person> personClass = Person.class;
//      2 获取运行时类指定变量名的属性,
//         常规获取属性的方法:类创建类的对象 由对象获取对象的属性 先有对象 后有属性 对象占主导地位
//                           类似于:先在内存中获取一片区域,然后从占据这片区域的对象上获取属性
//         反射获取属性的方法:类或者其他方式常见类的对应的Class实例 由Class实例获取类的属性(注意此时和当前类的哪个对象没有任何关系),现有属性结构后有对象,属性结构占主导地位
//                           类上于:通过Class实例在方法区得到类的数据结构中的类的属性这个数据结构,让后关联到具体对象中,也就是关联到具体的内存结构中去
        Field age = personClass.getDeclaredField("age");

        Field name = personClass.getDeclaredField("name");
//      3 保证当前属性是可访问的,属性age的修饰符是public,需要开放权限
        name.setAccessible(true);

//      4 创建运行时类的对象
        Person person = personClass.newInstance();

//      5 给对象的属性赋值
        age.set(person, 30);
        name.set(person, "shawnyang");
//      4 获取属性的值
//        方法一
        Integer ageValue = (Integer) age.get(person);
        String nameValue = (String) name.get(person);
        System.out.println("0101   "+ageValue+","+nameValue);
//        方法二
        Integer ageValue2 = person.getAge();
        String nameValue2=person.getName();

        System.out.println("0102   "+ageValue2+","+nameValue2);
    }

    /**
     * @Author ShawnYang
     * @Date 2019/9/25 0025 16:59
     * @Description 调用指定的方法
     * 修改人:
     * 修改时间:
     * 修改备注:
     * 实现注意:
     */
    @Test
    public void test02Method() throws Exception {
//        1 获取具体类所对应的Class类的实例
        Class<Person> personClass = Person.class;
//        2 获取具体类的方法(静态和非静态的方式异常,因为此时只有数据结构并没有对象这一说)
        Method setName = personClass.getDeclaredMethod("setName", String.class);
        Method staticTest = personClass.getDeclaredMethod("staticTest", String.class);

//        3 保证当前方法时可以访问的
        setName.setAccessible(true);
        staticTest.setAccessible(true);

//        4 创建具体类的对象
        Person person = personClass.newInstance();

//        5 方法的调用 invoke 方法
//        成员方法的调用,返回的内容就是方法的返回值
        Object method = setName.invoke(person, "yangshiying");
//        静态方法的调用,返回的内容就是方法的返回值
        String staticMethod = (String) staticTest.invoke(Person.class, "一只小怪兽");

//        6 输出
        System.out.println("0201    "+person);
        System.out.println("0201    "+method+","+staticMethod);
    }

    /**
     * @Author ShawnYang
     * @Date 2019/9/25 0025 17:58
     * @Description 获取指定的构造器
     * 修改人:
     * 修改时间:
     * 修改备注:
     * 实现注意:
     */
    @Test
    public void constructionTest() throws Exception {
//        1 获取具体类对应的Class实例
        Class<Person> personClass = Person.class;
//        2 获取类的构造器,注意不能用        personClass.getConstructor() 这里的构造去是private的只能用下面的方法
        Constructor<Person> constructor = personClass.getDeclaredConstructor(Integer.class, String.class, String.class);
//        3 设置构造器可以访问
        constructor.setAccessible(true);
//        4 调用构造器创建类的对象
        Person person = constructor.newInstance(22,"zhangsan","nv");
//        5 输出
        System.out.println(person);
    }
}

五、反射的动态代理
1 定义被代理类的接口

package com.javabase.javabase.reflect.dynamicProxy;

/**
 * @Author ShawnYang
 * @Date 2019-09-25 18:29
 * @Description TODO
 * 修改人:
 * 修改时间:
 * 修改备注:
 */
public interface IHuman {

    String eat(String s);

    void tell();
}

2 定义被代理类,继承上面的接口

package com.javabase.javabase.reflect.dynamicProxy;

/**
 * @Author ShawnYang
 * @Date 2019-09-25 18:32
 * @Description TODO
 * 修改人:
 * 修改时间:
 * 修改备注:
 */
public class Human implements IHuman{
    @Override
    public String eat(String s){
        return "i like eat"+s;
    }

    @Override
    public void tell(){
        System.out.println("i am ok");
    }

}

3 有被代理类返回代理类

package com.javabase.javabase.reflect.dynamicProxy;

import java.lang.reflect.Proxy;

/**
 * @Author ShawnYang
 * @Date 2019-09-25 19:02
 * @Description TODO
 * 修改人:
 * 修改时间:
 * 修改备注:
 */
public class ProxyFactory {

//    这里的Object 是被代理的对象
    public static Object getProxyInstance(Object object){
        MyInvocationHandler myInvocationHandler = new MyInvocationHandler();
//        别忘了handler与被代理对象,让后返回代理对象,添加的操作都在MyInvocationHandler里面进行
        myInvocationHandler.bind(object);

        return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), myInvocationHandler);
    }
}

4 定义上面的MyInvocationHandler 实现InvocationHandler,该类用来进行代理类调用的方法并添加额外操作,注意MyInvocationHandler 要聚合被代理类。invoke里面的入参是被代理对象

package com.javabase.javabase.reflect.dynamicProxy;

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

/**
 * @Author ShawnYang
 * @Date 2019-09-25 19:06
 * @Description TODO
 * 修改人:
 * 修改时间:
 * 修改备注:
 */
public class MyInvocationHandler implements InvocationHandler {

    private Object object;

    //需要使用被代理类的对象进行赋值传递给handler
    public void bind(Object object){
        this.object=object;
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("在调用之前做一波骚操作");
        HumanUtil util = new HumanUtil();
        util.method1();

//        注意是object对象,也就是被代理对象,不是上面的proxy对象
        Object invoke = method.invoke(object, args);

        util.method2();
        System.out.println("在调用之后进行一波骚操作");
        return invoke;
    }
}

上面的的代码要用到的一个工具类

package com.javabase.javabase.reflect.dynamicProxy;

/**
 * @Author ShawnYang
 * @Date 2019-09-25 19:57
 * @Description TODO
 * 修改人:
 * 修改时间:
 * 修改备注:
 */
public class HumanUtil {

    public void method1(){
        System.out.println("====================通用方法一====================");

    }

    public void method2(){
        System.out.println("====================通用方法二====================");
    }
}

5 测试效果,注意调用ProxyFactory的静态方法返回的类要强转成接口而不是被代理类

package com.javabase.javabase.reflect.dynamicProxy;

/**
 * @Author ShawnYang
 * @Date 2019-09-25 18:15
 * @Description TODO
 * 修改人:
 * 修改时间:
 * 修改备注:
 */
public class DynamicProxyTest {
    public static void main(String[] args) {

        Human human = new Human();
//        注意这里强制转换的是其接口不是human对象
        IHuman proxyInstance = (IHuman) ProxyFactory.getProxyInstance(human);
        String s = proxyInstance.eat("红烧肉");
        System.out.println(s);

        proxyInstance.tell();

    }

}

运行结果

"C:\Program Files\Java\jdk1.8.0_162\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2018.2\lib\idea_rt.jar=60189:C:\Program Files\JetBrains\IntelliJ IDEA 2018.2\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_162\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_162\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_162\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_162\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_162\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_162\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_162\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_162\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_162\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_162\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_162\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_162\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_162\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_162\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_162\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_162\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_162\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_162\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_162\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_162\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_162\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_162\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_162\jre\lib\rt.jar;D:\github\javabase\javabase-exception\target\classes;D:\java package\apache-maven-3.3.9\.m2\repository\org\springframework\boot\spring-boot-starter-web\2.1.7.RELEASE\spring-boot-starter-web-2.1.7.RELEASE.jar;D:\java package\apache-maven-3.3.9\.m2\repository\org\springframework\boot\spring-boot-starter\2.1.7.RELEASE\spring-boot-starter-2.1.7.RELEASE.jar;D:\java package\apache-maven-3.3.9\.m2\repository\org\springframework\boot\spring-boot\2.1.7.RELEASE\spring-boot-2.1.7.RELEASE.jar;D:\java package\apache-maven-3.3.9\.m2\repository\org\springframework\boot\spring-boot-autoconfigure\2.1.7.RELEASE\spring-boot-autoconfigure-2.1.7.RELEASE.jar;D:\java package\apache-maven-3.3.9\.m2\repository\org\springframework\boot\spring-boot-starter-logging\2.1.7.RELEASE\spring-boot-starter-logging-2.1.7.RELEASE.jar;D:\java package\apache-maven-3.3.9\.m2\repository\ch\qos\logback\logback-classic\1.2.3\logback-classic-1.2.3.jar;D:\java package\apache-maven-3.3.9\.m2\repository\ch\qos\logback\logback-core\1.2.3\logback-core-1.2.3.jar;D:\java package\apache-maven-3.3.9\.m2\repository\org\apache\logging\log4j\log4j-to-slf4j\2.11.2\log4j-to-slf4j-2.11.2.jar;D:\java package\apache-maven-3.3.9\.m2\repository\org\apache\logging\log4j\log4j-api\2.11.2\log4j-api-2.11.2.jar;D:\java package\apache-maven-3.3.9\.m2\repository\org\slf4j\jul-to-slf4j\1.7.26\jul-to-slf4j-1.7.26.jar;D:\java package\apache-maven-3.3.9\.m2\repository\javax\annotation\javax.annotation-api\1.3.2\javax.annotation-api-1.3.2.jar;D:\java package\apache-maven-3.3.9\.m2\repository\org\yaml\snakeyaml\1.23\snakeyaml-1.23.jar;D:\java package\apache-maven-3.3.9\.m2\repository\org\springframework\boot\spring-boot-starter-json\2.1.7.RELEASE\spring-boot-starter-json-2.1.7.RELEASE.jar;D:\java package\apache-maven-3.3.9\.m2\repository\com\fasterxml\jackson\core\jackson-databind\2.9.9\jackson-databind-2.9.9.jar;D:\java package\apache-maven-3.3.9\.m2\repository\com\fasterxml\jackson\core\jackson-annotations\2.9.0\jackson-annotations-2.9.0.jar;D:\java package\apache-maven-3.3.9\.m2\repository\com\fasterxml\jackson\core\jackson-core\2.9.9\jackson-core-2.9.9.jar;D:\java package\apache-maven-3.3.9\.m2\repository\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.9.9\jackson-datatype-jdk8-2.9.9.jar;D:\java package\apache-maven-3.3.9\.m2\repository\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.9.9\jackson-datatype-jsr310-2.9.9.jar;D:\java package\apache-maven-3.3.9\.m2\repository\com\fasterxml\jackson\module\jackson-module-parameter-names\2.9.9\jackson-module-parameter-names-2.9.9.jar;D:\java package\apache-maven-3.3.9\.m2\repository\org\springframework\boot\spring-boot-starter-tomcat\2.1.7.RELEASE\spring-boot-starter-tomcat-2.1.7.RELEASE.jar;D:\java package\apache-maven-3.3.9\.m2\repository\org\apache\tomcat\embed\tomcat-embed-core\9.0.22\tomcat-embed-core-9.0.22.jar;D:\java package\apache-maven-3.3.9\.m2\repository\org\apache\tomcat\embed\tomcat-embed-el\9.0.22\tomcat-embed-el-9.0.22.jar;D:\java package\apache-maven-3.3.9\.m2\repository\org\apache\tomcat\embed\tomcat-embed-websocket\9.0.22\tomcat-embed-websocket-9.0.22.jar;D:\java package\apache-maven-3.3.9\.m2\repository\org\hibernate\validator\hibernate-validator\6.0.17.Final\hibernate-validator-6.0.17.Final.jar;D:\java package\apache-maven-3.3.9\.m2\repository\javax\validation\validation-api\2.0.1.Final\validation-api-2.0.1.Final.jar;D:\java package\apache-maven-3.3.9\.m2\repository\org\jboss\logging\jboss-logging\3.3.2.Final\jboss-logging-3.3.2.Final.jar;D:\java package\apache-maven-3.3.9\.m2\repository\com\fasterxml\classmate\1.4.0\classmate-1.4.0.jar;D:\java package\apache-maven-3.3.9\.m2\repository\org\springframework\spring-web\5.1.9.RELEASE\spring-web-5.1.9.RELEASE.jar;D:\java package\apache-maven-3.3.9\.m2\repository\org\springframework\spring-beans\5.1.9.RELEASE\spring-beans-5.1.9.RELEASE.jar;D:\java package\apache-maven-3.3.9\.m2\repository\org\springframework\spring-webmvc\5.1.9.RELEASE\spring-webmvc-5.1.9.RELEASE.jar;D:\java package\apache-maven-3.3.9\.m2\repository\org\springframework\spring-aop\5.1.9.RELEASE\spring-aop-5.1.9.RELEASE.jar;D:\java package\apache-maven-3.3.9\.m2\repository\org\springframework\spring-context\5.1.9.RELEASE\spring-context-5.1.9.RELEASE.jar;D:\java package\apache-maven-3.3.9\.m2\repository\org\springframework\spring-expression\5.1.9.RELEASE\spring-expression-5.1.9.RELEASE.jar;D:\java package\apache-maven-3.3.9\.m2\repository\org\slf4j\slf4j-api\1.7.26\slf4j-api-1.7.26.jar;D:\java package\apache-maven-3.3.9\.m2\repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar;D:\java package\apache-maven-3.3.9\.m2\repository\org\springframework\spring-core\5.1.9.RELEASE\spring-core-5.1.9.RELEASE.jar;D:\java package\apache-maven-3.3.9\.m2\repository\org\springframework\spring-jcl\5.1.9.RELEASE\spring-jcl-5.1.9.RELEASE.jar;D:\java package\apache-maven-3.3.9\.m2\repository\com\alibaba\fastjson\1.2.58\fastjson-1.2.58.jar;D:\java package\apache-maven-3.3.9\.m2\repository\junit\junit\4.12\junit-4.12.jar" com.javabase.javabase.reflect.dynamicProxy.DynamicProxyTest
在调用之前做一波骚操作
====================通用方法一====================
====================通用方法二====================
在调用之后进行一波骚操作
i like eat红烧肉
在调用之前做一波骚操作
====================通用方法一====================
i am ok
====================通用方法二====================
在调用之后进行一波骚操作

Process finished with exit code 0

补充:
JDK动态代理

package com.javabase.javabase.reflect.dynamicProxy2;

/**
 * @Author ShawnYang
 * @Date 2019-10-09 14:29
 * @Description TODO
 * 修改人:
 * 修改时间:
 * 修改备注:
 */
public interface IPerson {

    void eat();
}

package com.javabase.javabase.reflect.dynamicProxy2;

/**
 * @Author ShawnYang
 * @Date 2019-10-09 14:30
 * @Description TODO
 * 修改人:
 * 修改时间:
 * 修改备注:
 */
public class Person implements IPerson {
    @Override
    public void eat() {
        System.out.println("eat the fruit");
    }
}

package com.javabase.javabase.reflect.dynamicProxy2;

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

/**
 * @Author ShawnYang
 * @Date 2019-10-09 14:31
 * @Description TODO
 * 修改人:
 * 修改时间:
 * 修改备注:
 */
public class ProxyFactory {

    private Person person;

    public ProxyFactory(Person target){
        this.person=target;
    }


    public Object getProxyInstance(){

        return Proxy.newProxyInstance(person.getClass().getClassLoader(), person.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("Proxy start,wash you hand");
                Object invoke = method.invoke(person, args);
                System.out.println("proxy end,clean your mouse");
                return invoke;
            }
        });
    }
}

package com.javabase.javabase.reflect.dynamicProxy2;

/**
 * @Author ShawnYang
 * @Date 2019-10-09 14:47
 * @Description TODO
 * 修改人:
 * 修改时间:
 * 修改备注:
 */
public class Client {

    public static void main(String[] args) {

        IPerson person = (IPerson) new ProxyFactory(new Person()).getProxyInstance();

        person.eat();
    }
}

cglib动态代理

package com.javabase.javabase.reflect.cglibProxy;

/**
 * @Author ShawnYang
 * @Date 2019-10-09 15:07
 * @Description TODO
 * 修改人:
 * 修改时间:
 * 修改备注:
 */
public class Person {

    public void eat() {
        System.out.println("eat the fruit");
    }
}
package com.javabase.javabase.reflect.cglibProxy;


import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * @Author ShawnYang
 * @Date 2019-10-09 15:26
 * @Description TODO
 * 修改人:
 * 修改时间:
 * 修改备注:
 */
public class ProxyFactory implements MethodInterceptor {

    private Person person;

    public ProxyFactory(Person target){
        this.person=target;
    }


    public Object getProxyInstance(){
        //一个工具类
        Enhancer enhancer = new Enhancer();

        enhancer.setSuperclass(Person.class);

        enhancer.setCallback(this);

        return enhancer.create();
    }


    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("wash your hand");
        Object invoke = method.invoke(person, objects);
        System.out.println("clean your mouse");
        return invoke;
    }
}

package com.javabase.javabase.reflect.cglibProxy;

/**
 * @Author ShawnYang
 * @Date 2019-10-09 15:32
 * @Description TODO
 * 修改人:
 * 修改时间:
 * 修改备注:
 */
public class Client {


    public static void main(String[] args) {

        Person proxyInstance = (Person) new ProxyFactory(new Person()).getProxyInstance();

        proxyInstance.eat();
    }
}

补充: new一个对象的过程中发生了什么(比较经典)
装载自:添加链接描述

java在new一个对象的时候,会先查看对象所属的类有没有被加载到内存,如果没有的话,就会先通过类的全限定名来加载。加载并初始化类完成后,再进行对象的创建工作。

我们先假设是第一次使用该类,这样的话new一个对象就可以分为两个过程:加载并初始化类和创建对象。

一、类加载过程(第一次使用该类)
  java是使用双亲委派模型来进行类的加载的,所以在描述类加载过程前,我们先看一下它的工作过程:

双亲委托模型的工作过程是:如果一个类加载器(ClassLoader)收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委托给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父类加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需要加载的类)时,子加载器才会尝试自己去加载。
使用双亲委托机制的好处是:能够有效确保一个类的全局唯一性,当程序中出现多个限定名相同的类时,类加载器在执行加载时,始终只会加载其中的某一个类。

1、加载

由类加载器负责根据一个类的全限定名来读取此类的二进制字节流到JVM内部,并存储在运行时内存区的方法区,然后将其转换为一个与目标类型对应的java.lang.Class对象实例

2、验证
格式验证:验证是否符合class文件规范
语义验证:检查一个被标记为final的类型是否包含子类;检查一个类中的final方法是否被子类进行重写;确保父类和子类之间没有不兼容的一些方法声明(比如方法签名相同,但方法的返回值不同)
操作验证:在操作数栈中的数据必须进行正确的操作,对常量池中的各种符号引用执行验证(通常在解析阶段执行,检查是否可以通过符号引用中描述的全限定名定位到指定类型上,以及类成员信息的访问修饰符是否允许访问等)

3、准备
为类中的所有静态变量分配内存空间,并为其设置一个初始值(由于还没有产生对象,实例变量不在此操作范围内)
被final修饰的static变量(常量),会直接赋值;

4、解析
将常量池中的符号引用转为直接引用(得到类或者字段、方法在内存中的指针或者偏移量,以便直接调用该方法),这个可以在初始化之后再执行。
解析需要静态绑定的内容。 // 所有不会被重写的方法和域都会被静态绑定

以上2、3、4三个阶段又合称为链接阶段,链接阶段要做的是将加载到JVM中的二进制字节流的类数据信息合并到JVM的运行时状态中。

5、初始化(先父后子)
4.1 为静态变量赋值

4.2 执行static代码块

注意:static代码块只有jvm能够调用
   如果是多线程需要同时初始化一个类,仅仅只能允许其中一个线程对其执行初始化操作,其余线程必须等待,只有在活动线程执行完对类的初始化操作之后,才会通知正在等待的其他线程。

因为子类存在对父类的依赖,所以类的加载顺序是先加载父类后加载子类,初始化也一样。不过,父类初始化时,子类静态变量的值也有有的,是默认值。

最终,方法区会存储当前类类信息,包括类的静态变量、类初始化代码(定义静态变量时的赋值语句 和 静态初始化代码块)、实例变量定义、实例初始化代码(定义实例变量时的赋值语句实例代码块和构造方法)和实例方法,还有父类的类信息引用。

二、创建对象
1、在堆区分配对象需要的内存

分配的内存包括本类和父类的所有实例变量,但不包括任何静态变量

2、对所有实例变量赋默认值

将方法区内对实例变量的定义拷贝一份到堆区,然后赋默认值

3、执行实例初始化代码

初始化顺序是先初始化父类再初始化子类,初始化时先执行实例代码块然后是构造方法

4、如果有类似于Child c = new Child()形式的c引用的话,在栈区定义Child类型引用变量c,然后将堆区对象的地址赋值给它

需要注意的是,每个子类对象持有父类对象的引用,可在内部通过super关键字来调用父类对象,但在外部不可访问

补充:
通过实例引用调用实例方法的时候,先从方法区中对象的实际类型信息找,找不到的话再去父类类型信息中找。

如果继承的层次比较深,要调用的方法位于比较上层的父类,则调用的效率是比较低的,因为每次调用都要经过很多次查找。这时候大多系统会采用一种称为虚方法表的方法来优化调用的效率。

所谓虚方法表,就是在类加载的时候,为每个类创建一个表,这个表包括该类的对象所有动态绑定的方法及其地址,包括父类的方法,但一个方法只有一条记录,子类重写了父类方法后只会保留子类的。当通过对象动态绑定方法的时候,只需要查找这个表就可以了,而不需要挨个查找每个父类。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值