反射

类的加载

  • 当我们的程序在运行后,第一次使用某个类的时候,会将此类的class文件读取到内存,并将此类的所有信息存储到一个Class对象中

类的加载时机

  1. 创建类的实例。
  2. 类的静态变量,或者为静态变量赋值。
  3. 类的静态方法。
  4. 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象。
  5. 初始化某个类的子类对象。
  6. 直接使用java.exe命令来运行某个主类。

以上六种情况的任何一种,都可以导致JVM将一个类加载到方法区。

public class Test1 {
    public static void main(String[] args) throws Exception{
        //1. 创建类的实例。
        new Person();

        //2. 类的静态变量,或者为静态变量赋值。
        System.out.println(Person.num);

        //3. 类的静态方法。
        Person.method();

        //4. 使用反射方式来强制创建某个类或接口对应的java.lang.Class对象。
        Class<?> c1 = Class.forName("com.itheima.demo2_类加载的时机.Person");

        //5. 初始化某个类的子类对象。
        new Student();

        //6. 直接使用java.exe命令来运行某个主类。
        //   以上六种情况的任何一种,都可以导致JVM将一个类加载到方法区。
    }
}

类加载器

类加载器:是负责将磁盘上的某个class文件读取到内存并生成Class的对象。

  • Java中有三种类加载器,它们分别用于加载不同种类的class:
    • 启动类加载器(Bootstrap ClassLoader):用于加载系统类库<JAVA_HOME>\bin目录下的class,例如:rt.jar。
    • 扩展类加载器(Extension ClassLoader):用于加载扩展类库<JAVA_HOME>\lib\ext目录下的class。
    • 应用程序类加载器(Application ClassLoader):用于加载我们自定义类的加载器。
package com.itheima.demo6_类加载器;

/**

 * @Date: 2020/9/24 9:31
 */
public class Test {
    public static void main(String[] args) {
       // 如何获取类加载器: 使用类的class对象调用getClassLoader()方法
        // 获取Person类的类加载器
        ClassLoader c1 = Person.class.getClassLoader();
        System.out.println(c1);// ClassLoaders$AppClassLoader

        // 获取String类的类加载器
        ClassLoader c2 = String.class.getClassLoader();
        System.out.println(c2);// null
        //API中说明:一些实现可能使用null来表示引导类加载器。 如果此类由引导类加载器加载,则此方法将在此类实现中返回null
    }
}

获取Class对象

  • 通过类名.class获得
  • 通过对象名.getClass()方法获得
  • 通过Class类的静态方法获得: static Class forName(“类全名”)
  • 示例代码
public class Test {
    public static void main(String[] args) throws Exception{
      //Class.forName("全限定类名")
        Class<?> c1 = Class.forName("com.itheima.demo5_获取类的字节码对象.Person");

        //类名.class
        Class<Person> c2 = Person.class;

        //对象名.getClass()
        Person p = new Person();
        Class<? extends Person> c3 = p.getClass();

        System.out.println(c1);
        System.out.println(c2);
        System.out.println(c3);
        System.out.println(c1 == c2);// true
        System.out.println(c2 == c3);// true
    }
}
// 注意: 一个类只有一个Class对象,所以无论通过哪种方式获取的Class对象都是同一个对象

Class类常用方法

String getSimpleName(); 获得类名字符串:类名
String getName();  获得类全名:包名+类名
T newInstance() ;  创建Class对象关联类的对象---相当于调用该类的空参构造方法
  • 示例代码
public class Test {
    public static void main(String[] args) throws Exception{

        // 获取Person类的Class对象
        Class<Person> c = Person.class;

        // String getSimpleName(); 获得类名字符串:类名
        System.out.println("类名: "+c.getSimpleName());// 类名: Person

        // String getName();  获得类全名:包名+类名
        // 类全名: com.itheima.demo6_Class类的常用方法.Person
        System.out.println("类全名: "+c.getName());

        // T newInstance() ;  创建Class对象关联类的对象---相当于调用该类的空参构造方法
        Person p = c.newInstance();// 相当于Person p = new Person();
        
    }
}

反射之操作构造方法

Constructor类概述
    * 类中的每一个构造方法都是一个Constructor类的对象

通过反射获取类的构造方法

Class类的方法 
1. Constructor getConstructor(Class... parameterTypes)
        * 根据参数类型获得对应的Constructor对象。
        * 只能获得public修饰的构造方法
 2. Constructor getDeclaredConstructor(Class... parameterTypes)---推荐
        * 根据参数类型获得对应的Constructor对象
    	* 可以是public、protected、(默认)、private修饰符的构造方法。
 3. Constructor[] getConstructors()
        获得类中的所有构造方法对象,只能获得public的
 4. Constructor[] getDeclaredConstructors()---推荐
        获得类中的所有构造方法对象
    	可以是public、protected、(默认)、private修饰符的构造方法。
public class Test {
    public static void main(String[] args) throws Exception{
        // 获取Person类的Class对象
        Class<Person> c = Person.class;

        // 获得指定的构造方法:
        // 获取第一个构造方法对象
        Constructor<Person> con1 = c.getDeclaredConstructor();
        System.out.println("con1:"+con1);

        // 获取第二个构造方法对象
        Constructor<Person> con2 = c.getDeclaredConstructor(String.class,int.class);
        System.out.println("con2:"+con2);

        // 获取第三个构造方法对象
        Constructor<Person> con3 = c.getDeclaredConstructor(int.class);
        System.out.println("con3:"+con3);

        // 获取第四个构造方法对象
        Constructor<Person> con4 = c.getDeclaredConstructor(String.class);
        System.out.println("con4:"+con4);

        // 获取所有的构造方法
        Constructor<?>[] arr = c.getDeclaredConstructors();
        for (Constructor<?> con : arr) {
            System.out.println(con);
        }

    }

}

通过反射执行构造方法

Constructor对象常用方法
1. T newInstance(Object... initargs)
 	根据指定的参数创建对象
    参数:被执行的构造方法需要的实际参数
2. void setAccessible(true)
   设置"暴力反射"——是否取消权限检查,true取消权限检查,false表示不取消

示例代码

public class Person {
    String name;
    int age;

    // 构造方法
    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public Person(int age) {
        this.age = age;
    }

    private Person(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
public class Test {
    public static void main(String[] args) throws Exception {
        // 获取Person类的Class对象
        Class<Person> c = Person.class;

        // 获得指定的构造方法:
        // 获取第一个构造方法对象
        Constructor<Person> con1 = c.getDeclaredConstructor();
        System.out.println("con1:" + con1);

        // 获取第二个构造方法对象
        Constructor<Person> con2 = c.getDeclaredConstructor(String.class, int.class);
        System.out.println("con2:" + con2);

        // 获取第三个构造方法对象
        Constructor<Person> con3 = c.getDeclaredConstructor(int.class);
        System.out.println("con3:" + con3);

        // 获取第四个构造方法对象
        Constructor<Person> con4 = c.getDeclaredConstructor(String.class);
        System.out.println("con4:" + con4);

        // 获取所有的构造方法
        Constructor<?>[] arr = c.getDeclaredConstructors();
        for (Constructor<?> con : arr) {
            System.out.println(con);
        }
        System.out.println("========");
        // 通过反射执行con1表示的构造方法
        Person p1 = con1.newInstance();
        System.out.println("p1:" + p1);

        // 通过反射执行con2表示的构造方法
        Person p2 = con2.newInstance("张三", 18);
        System.out.println("p2:" + p2);

        // 通过反射执行con3表示的构造方法
        Person p3 = con3.newInstance(19);
        System.out.println("p3:" + p3);

        // 通过反射执行con4表示的构造方法
        con4.setAccessible(true);// 取消权限检查
        Person p4 = con4.newInstance("王五");
        System.out.println("p4:" + p4);
    }

}

动态代理

代理模式的定义:被代理者没有能力或者不愿意去完成某件事情,那么就需要找个人代替自己去完成这件事,这个人就是代理者, 所以代理模式包含了3个角色: 被代理角色 代理角色 抽象角色(协议)

静态代理:

public interface FindHappy {
    void happy();
}


public class JinLian implements FindHappy{

    public void happy(){
        System.out.println("金莲正在happy...");
    }
}


public class WangPo implements FindHappy {

    JinLian jl;

    public WangPo(JinLian jl) {
        this.jl = jl;
    }

    @Override
    public void happy() {
        System.out.println("王婆以做头发的名义,帮2人开好房间...");
        jl.happy();
        System.out.println("王婆打扫战场...");
    }
}

public class Test {

    public static void main(String[] args) {
        JinLian jl = new JinLian();
        //jl.happy();
        WangPo wp = new WangPo(jl);
        wp.happy();
    }

}

动态代理介绍

  • 概述 : 动态代理就是在程序运行期间,直接通过反射生成一个代理对象,代理对象所属的类是不需要存在的
  • 动态代理的获取: jdk提供一个Proxy类可以直接给实现接口类的对象直接生成代理对象

动态代理相关api介绍

Java.lang.reflect.Proxy类可以直接生成一个代理对象  

- Proxy.newProxyInstance(ClassLoader loader, Class<?>[]  interfaces, InvocationHandler h)生成一个代理对象
  - 参数1:ClassLoader loader 被代理对象的类加载器 
  - 参数2:Class<?>[] interfaces 被代理对象的要实现的接口 
  - 参数3:InvocationHandler h (接口)执行处理类
  - 返回值: 代理对象
  - 前2个参数是为了帮助在jvm内部生成被代理对象的代理对象,第3个参数,用来监听代理对象调用方法,帮助我们调用方法
- InvocationHandler中的Object invoke(Object proxy, Method method, Object[] args)方法:调用代理类的任何方法,此方法都会执行
  - 参数1:代理对象(慎用)
  - 参数2:当前执行的方法
  - 参数3:当前执行的方法运行时传递过来的参数
  - 返回值:当前方法执行的返回值