1. 程式人生 > >《瘋狂Java講義(第4版)》-----第18章【類的載入機制與反射】

《瘋狂Java講義(第4版)》-----第18章【類的載入機制與反射】

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();
	}
}