java p43——类加载器、反射、动态代理

类加载器

使用类加载

  1. 创建对象
  2. 使用静态成员、方法
  3. 使用子类会加载父类

三种类加载器

引导类加载器: java.lang.String -> rt.jar JDK基础类
Math、Date、System
扩展类加载器: JDK自带扩展类(现在不存在了)

应用类加载器: ClassPath 自定义类、第三方jar包
ClassPath——类路径——就是src下面的
com.zzxx.utils.DruidUtils -> 自定义类
org.apache.commons.beanutils.BeanUtils -> 自定义类
应用类加载器是可以获得到的,另外两种都是null

public class Demo01ClassLoader {
    public static void main(String[] args) {
        // 应用类加载器, 只有一个
        ClassLoader loader = Demo01ClassLoader.class.getClassLoader();
        ClassLoader loader2 = User.class.getClassLoader();
        System.out.println(loader == loader2);//true
        // 引导类加载器, 和 扩展类加载器 代码获取不到
        ClassLoader loader1 = String.class.getClassLoader();
        System.out.println(loader1);//null

    }

双亲委派模型

  • 加载一个类的时候, 会先由引导类加载器和扩展类加载器 来搜索有没有对应的类
  • 如果有, 那么就加载完成,
  • 如果没有, 会继续使用应用类加载器来完成搜索和加载工作
  • 如果三个类加载器都找不到对应的类, 那么就会抛出 ClassNotFoundException(类未找到异常)

面试题:自己定义一个java.lang.String类, 能不能使用?
String类在使用的时候都要先加载,引导类加载器会找到一个java.lang.String类,加载完之后就不管后面了,自己定义的类就没有机会被加载,也就咩有机会被使用

反射

操作类的字节码对象 Class对象
在这里插入图片描述

获得class字节码对象

		// 1.手动类加载
        Class cls = null;
        try {
            cls = Class.forName("java.lang1.String");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        // 2.获得类的字节码类型
        Class cls2 = int.class;//基本数据类型只能通过这种方式获取
        // 3.通过对象的getClass方法
        Class cls3 = "".getClass();

        // 引用类型有:类, 接口, 注解, 枚举,通过1、3方法获得字节码对象
        // 每一个类的字节码对象只有一个
        System.out.println(cls == cls3); // true

获得所有的父接口

		Class cls = String.class;
        // 1.获得所有的父接口
        Class[] interfaces = cls.getInterfaces();
        System.out.println(Arrays.toString(interfaces));

操作构造器 Constructor

.getConstructor()
newInstance()

创建提供的对象

		// 2.获得类的构造方法
		Class cls = String.class;
        try {
            // 获得一个参数是String类型的构造器 new String("str");
            Constructor constructor = cls.getConstructor(String.class);
            // 通过构造器对象来创建string对象,"hello" 就是调用这个构造方法传递的实际参数
            String ob = (String) constructor.newInstance("hello");
            System.out.println(ob);//hello
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

创建自己写的对象

   // 通过反射创建user对象

        // 1.获得user的字节码对象
        Class cl = User.class;
        // 2.获得user的构造方法
        Constructor con1 = cl.getConstructor();
        // 3.创建对象
        User u1 = (User) con1.newInstance();
        System.out.println(u1);


        // 成员私有时, 通过反射获得构造方法需要使用 getDeclaredxx
        Constructor con2 = cl.getDeclaredConstructor(String.class, int.class);
        // 私有成员可以手动设置可见
        con2.setAccessible(true);
        // 创建对象
        User u2 = (User) con2.newInstance("lucy", 18);
        System.out.println(u2);

//        cl.newInstance(); // -- 直接通过无参构造器创建对象

操作方法 Method

.getMethod()
调用方法:method.invoke()

 public boolean study(String course) {
        System.out.println(this.name + "正在学习" + course);
        return true;
    }
public static void main(String[] args) throws Exception {
        User user = new User("张三");

        // 1.获得user的字节码对象
        Class cls = User.class;
        // 2.获得study这个方法对象,若为私有,则cls.getDeclaredMethod,并设置方法可见
        Method method = cls.getMethod("study", String.class);
        // 2.2设置方法可见
        //method.setAccessible(true);
        // 3.调用方法
        boolean b = (Boolean) method.invoke(user, "Java");
        System.out.println(b);
    }

操作成员变量 Field

通过反射,可以直接调用私有的成员变量
get()
set()

		User user = new User("张三");
        System.out.println(user);
        // 1.获得user的字节码对象
        Class cls = User.class;
        // 2.获得age属性
        Field age = cls.getDeclaredField("age");
        age.setAccessible(true);
        // 3.给user对象的age属性赋值
        age.set(user, 18);
        // 4.获得user对象的age属性
        age.get(user);
        System.out.println(user);

注解

@Override: 重写
只能放在方法上, 没有属性
@Deprecated: 过时的
放在属性/方法/类/构造方法/局部变量/方法参数
@SuppressWarnings: 压制警告
放在属性/方法/类/构造方法/局部变量/方法参数
必须要有属性

元注解

元注解: 标记注解的注解,规定注解的使用位置
@Target(ElementType.METHOD): 规定注解的使用位置
@Retention(RetentionPolicy.RUNTIME)
SOURCE CLASS RUNTIME

注解解析——通过反射(了解)

注意: 注解的可见范围必须是 运行阶段 RUNTIME

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
public @interface MyAnnotation2 {
    // 属性语法  类型 属性名()
    int age() ; // 使用注解时属性必须赋值
}
@MyAnnotation2(age = 1)
public class Demo02 {
//    @MyAnnotation2(age = 1)
    public Demo02() {

    }
    @MyAnnotation2(age = 2)
    public void test01() {

    }
}
// 解析Demo02上的MyAnnotation2注解
public class Demo03 {
    public static void main(String[] args) {
        // 1.获得Demo02对应的字节码对象
        Class cls = Demo02.class;

        // 2.获得Demo02类上的MyAnnotation2注解
        MyAnnotation2 annotation = (MyAnnotation2) cls.getAnnotation(MyAnnotation2.class);
        // 3.获得注解中的属性值
        int age = annotation.age();
        System.out.println(age);
    }

动态代理

装饰者模式

1.和被装饰者拥有相同的父接口
2.用被装饰对象来构建装饰对象
3.重写要增强的方法

动态代理

和装饰者模式一样是为了做方法增强, 代理对象和被代理对象拥有相同的父接口,用被代理对象来构建代理对象
但他的代理对象并不是一个真正存在的类,而且和被代理对象没有直接联系

代理对象: 增强后的对象
被代理对象: 原来的对象

实际上作用:提取公共代码
在这里插入图片描述
如果一个实现类没有父接口,那么他就不能使用动态代理,就不能使用方法增强


版权声明:本文为qq_40905010原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。