JavaSE高级开发之反射(二)

1. ClassLoader类加载器

Class类描述的是整个类的信息,在Class类中提供的forName()方法会根据ClassPath配置的路径进行类的加载。如果说现在你的类的加载路径可能是网络或者文件,这个时候就必须实现类加载器(ClassLoader)。

1.1 认识ClassLoader

  • 什么是类加载器?

  • 类加载器用来加载Java类到JVM虚拟机中。Java源程序(.java文件)在经过Javac编译之后就会被转换成.class文件,而类加载器的作用就是负责读取Java字节码,并转换成java.lang.Class类的一个实例。

  • 获取加载器的方法:
    ① 获得类加载器:getClassLoader()
    ② 获得父加载器:getClassLoader().getParent()

package www.bittech.com;

class Member {}

public class classloader {
    public static void main(String[] args) {
        Class classz = Member.class;
        System.out.println(classz.getClassLoader());//AppClassLoader
        System.out.println(classz.getClassLoader().getParent());//ExtClassLoader
        System.out.println(classz.getClassLoader().getParent().getParent());//null
        System.out.println(classz.getClassLoader().getParent().getParent().getParent());//NullPointerException

    }
}

在这里插入图片描述
此时出现了两个类加载器:ExtClassLoader(扩展类加载器)和AppClassLoader(应用程序类加载器)。其实第三个也是个类加载器,叫做Bootstrap ClassLoader(启动类加载器)。

Bootstrap:这个类加载器是虚拟机自身的一部分,其他的类加载器都独立于JVM外部,并且都继承于它。它负责将存放于<Java_HOME>\lib目录中(或者被-Xbootclasspath参数指定路径中)能被虚拟机识别的(仅按照文件名识别,如rt.jar,名字不符合的类库即使放在lib目录中也不会被加载)类库加载到JVM内存中。启动类加载器无法被Java程序直接引用。

ExtClassLoader:负责加载<Java_HOME>\lib\ext目录中,或者被java.ext.dirs系统变量指定的路径中的类库。开发者可以直接使用扩展类加载器。

AppClassLoader:负责加载用户类路径(ClassPath)上指定的类库,如果应用程序中没有自定义自己的类加载器,则此加载器就是程序中默认的类加载器。

我们的应用程序都是由这三种加载器互相配合进行加载的,如果有必要,还可以加入自定义的类加载器
在这里插入图片描述

1.2 双亲委派模型

在这里插入图片描述
上图展示的类加载器之间的这种层次关系,就称为类加载器的双亲委派模型。双亲委派模型要求除了顶层的父类加载器外,其余的类加载器都应有自己的父类加载器。

双亲委派模型的工作流程是:如果一个类加载器收到了类加载请求,它首先不会自己去尝试加载这个类,而是把这个请求委托给父类加载器去完成,每一个层次的类加载器都是如此。因此,所有的加载请求都应当传送到顶层的BootStrap加载器中,只有当父加载器反馈无法完成这个加载请求时(在自己搜索范围中没有找到此类),子加载器才会尝试自己去加载。

双亲委派模式对于保证Java程序的稳定运行很重要。有一个显而易见的好处就是Java类随着它的类加载器一起具备了一种带有优先级的层次关系。例如:java.lang.Object类,它存放在rt.jar中,无论哪一个类加载器要加载这个类,最终都是委派给处于顶端的BootStrsap类加载器进行加载。因此,Object类在程序的各种类加载器环境中都是同一个类。

1.3 自定义类加载器

ClassLoader类中提供有如下方法(进行类的加载):loadClass(String name, boolean resolve)

2. 反射与代理设计模式

2.1 基础代理模式

代理设计模式的核心本质在于:一个接口有两个子类,一个负责真实业务,一个负责与真实业务有关的所有辅助性操作。按照这样的原则,一个基础的代理设计模式如下:

package www.bittech.com.proxy;

/**
代理设计模式:
1.代理接口    Subject
2.业务实现类  RealSubject   业务对象  被代理对象
3.代理实现类  ProxySubject  代理对象
*/

interface Subject {
    void eat();
}

class RealSubject implements Subject {
    @Override
    public void eat() {
        System.out.println("饿了要吃饭");
    }
}

class ProxySubject implements Subject {

    private Subject subject;

    public ProxySubject(Subject subject) {
        this.subject = subject;
    }

    private void before() {
        System.out.println("准备食材");
    }

    private void after() {
        System.out.println("收拾碗筷");
    }

    @Override
    public void eat() {
        this.before();
        this.subject.eat();//核心业务
        this.after();
    }
}

public class TestSubject {
    public static void main(String[] args) {
        //1.业务对象
        Subject realSubject = new RealSubject();
        //2.代理对象
        Subject proxySubject = new ProxySubject(realSubject);
        proxySubject.eat();
    }
}

3. 反射与Annotation(注解)

3.1 反射取得Annotation信息

Annotation注解可以定义在类或方法上,我们可以通过反射取得它的信息:
① 取得全部Annotation信息:getAnnotations()
② 取得指定的Annotation信息:getAnnotation(Class annotationClass)

  • 定义在类上的Annotation:
package www.bittech.com.annotation;

import java.lang.annotation.Annotation;

@Deprecated
@SuppressWarnings(value = "unused")
public class TestAnnotation {
    public static void main(String[] args) {

        //取得全部Annotation信息
        Annotation[] annotations = TestAnnotation.class.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println(annotation);
        }

        //取得Deprecated信息
        Deprecated annotation = TestAnnotation.class.getAnnotation(Deprecated.class);
        System.out.println(annotation);
    }
}

在这里插入图片描述
至于为什么第一条只输出了一个Annotation?原因如下:
Annotation本身有自己的保存范围,不同的Annotation的返回也不同。所以此处只出现了一个Annotation。

  • 定义在方法上的Annotation:

3.2 自定义Annotation

不同的Annotation有自己的运行范围,而这些范围就在一个枚举类java.lang.annotation.RetentionPolicy中定义的:
① SOURCE:在源代码中出现的Annotation
② CLASS:在*.class中出现的Annotation
③ RUNTIME:在类执行的时候出现的Annotation

package www.bittech.com.annotation;

import java.lang.annotation.*;

/*
自定义Annotation定义:
1. 注解是接口interface修饰,并且interface前加@
2. 定义抽象方法(可选),提供默认值,如果不提供使用时就得设置值
3. 注意保存范围和作用位置
*/

@Retention(RetentionPolicy.RUNTIME)
@Target(value = {ElementType.TYPE})
@interface MyAnnotation {
    String name() default "Tom";
    int age() default 22;
}

@Deprecated
@SuppressWarnings(value = "unused")
public class TestAnnotation {
    public static void main(String[] args) {
        MyAnnotation myAnnotation=TestAnnotation.class.getAnnotation(MyAnnotation.class);
        System.out.println(myAnnotation.name() + " " + myAnnotation.age());
    }
}

3.3 Annotation与工厂设计模式

在之前编写的工厂类都是通过明确的类信息传递实现的实例化对象,现在就可以用注解的形式配置工厂类。

package www.bittech.com.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface MyFruit {
    Class target();
}

interface Fruit {
    void eat();
}

class Apple implements Fruit {
    @Override
    public void eat() {
        System.out.println("吃苹果");
    }
}

class Orange implements Fruit {
    @Override
    public void eat() {
        System.out.println("吃橘子");
    }
}

@MyFruit(target = Orange.class)
class FruitFactory {

    public static Fruit getFruit() {
        MyFruit myFruit = FruitFactory.class.getAnnotation(MyFruit.class);
        try {
            return (Fruit) myFruit.target().newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }
}

public class TestFactory {
    public static void main(String[] args) {
        Fruit fruit = FruitFactory.getFruit();
        fruit.eat();
    }
}

(中间有些地方还待完善…)

猜你喜欢

转载自blog.csdn.net/qq_43508801/article/details/89139924