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();
}
}
(中间有些地方还待完善…)