《疯狂Java讲义(第4版)》-----第18章【类的加载机制与反射】

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ccnuacmhdu/article/details/84979264

JVM进程终止的情况

  1. 程序运行到最后正常结束
  2. 程序运行到System.exit()或Runtime.getRuntime().exit()
  3. 程序执行过程中遇到未捕获的异常或错误而结束
  4. 程序所在平台强制结束了JVM进程

类的加载

下图摘自《深入理解Java虚拟机:JVM高级特性与最佳实践》
在这里插入图片描述
类加载:将类的class文件读入内存,并为之创建一个java.lang.Class对象。类加载过程由类加载器完成。

类加载器无须等到“首次使用”某类时才加载该类,Java虚拟机规范允许系统先加载某些类。

类的连接

连接阶段负责把类的二进制数据合并到JRE中。

(1)验证
(2)准备:为类变量分配内存,设置默认值
(3)解析:把类的二进制数据中的符号引用替换成直接引用

类的初始化

什么情况会导致类的初始化?
(1)创建类的实例。new一个实例,反射创建实例,反序列化创建实例
(2)调用类方法(静态方法)
(3)访问某个类或接口的类变量,或为该类变量赋值
(4)使用发射方式强制创建某个类或接口对应的java.lang.Class对象
(5)初始化某个类的子类
(6)直接使用java.exe命令允许某个主类。

调用final修饰的类变量(编译时已经确定下来)—不会进行类初始化,没调用初始化块

class Test{
	static{
		System.out.println("静态初始化块。。。");
	}
	public static final String s = "我是静态常量,编译时已确定,我类的静态初始化块没调用吧哈!";
}

public class Main{
	public static void main(String[] args){
		System.out.println(Test.s);
	}
}

在这里插入图片描述

调用final修饰的类变量(编译时未确定下来)—会进行类初始化,调用了初始化块

class Test{
	static{
		System.out.println("静态初始化块。。。");
	}
	public static final String s = "我是静态变量,编译时没确定,我类的静态初始化块要调用吧哈!"+ System.currentTimeMillis();
}

public class Main{
	public static void main(String[] args){
		System.out.println(Test.s);
	}
}

在这里插入图片描述

类加载机制

一个类被加载后就不会再次被加载了。

三种类加载机制:

(1)全盘负责
(2)父类委托
(3)缓存机制:所有加载过的Class都会被缓存,当程序需要使用某个Class时,类加载器先从缓冲区找,找不到才会读二进制数据,转成Class对象,并存入缓冲区。
在这里插入图片描述

反射

获得Class对象

方式一:
Class.forName()
方式二:
类名.class

方式三:
对象.getClass()

动态代理

JDK动态代理只能为接口创建动态代理对象。
动态代理是AOP的基础。动态代理实现了:既可以完成某类的某个方法的调用,同时可以在这个调用方法的前后加入一些想加入的代码。动态代理实现了方法增强,还可以重用代码(当多个方法都需要调用某一段代码的时候,可以把共同的代码抽取出来,放到InvocationHandler实现类的invoke方法的前后)。

【示例代码】
(改造《疯狂Java讲义(第4版)》859~862页代码)

import java.lang.reflect.*;

interface A{
	public void fun1(int a);
	public void fun2(int a, int b);
	public void fun3();
}

class B implements A{
	public void fun1(int a){
		System.out.println("aaaaaaaaa");
	}
	public void fun2(int a, int b){
		System.out.println("abababab");
	}
	public void fun3(){
		System.out.println(333333333);
	}
}

class MyInvocationHandler implements InvocationHandler {
	private Object target;//这个就是被代理的对象,因为JDK动态代理只能为接口代理,那就让这个对象实现接口
	public void setTarget(Object target){
		this.target = target;
	}

	public Object invoke(Object proxy, Method method, Object[] args) throws Exception{
		//调用method.invoke()之前(即调用目标方法之前),可以加入代码放在这里
		System.out.println("拦截器代码/公共代码---前置目标方法调用前");

		Object result = method.invoke(target, args);//这是调用目标对象target的method方法,method的参数列表是args

		//调用method.invoke()之后(即调用目标方法之后),可以加入代码放在这里
		System.out.println("拦截器代码/公共代码---后置目标方法调用后");
		System.out.println();
		
		return result;
	}
}

public class Main{
	public static void main(String[] args){
		MyInvocationHandler handler = new MyInvocationHandler();
		A target = new B();
		handler.setTarget(target);
		A proxy = (A)Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), handler);
		proxy.fun1(1);
		proxy.fun2(1, 2);
		proxy.fun3();
	}
}

猜你喜欢

转载自blog.csdn.net/ccnuacmhdu/article/details/84979264