Java類加載與反射
類加載、連接、初始化
JVM和類
當(dāng)運(yùn)行某個Java程序時左医,將會啟動一個Java虛擬機(jī)進(jìn)程奴紧,不管Java程序里面多么復(fù)雜特姐,都處于該Java虛擬機(jī)進(jìn)程里面。
當(dāng)Java程序運(yùn)行結(jié)束黍氮,JVM進(jìn)程結(jié)束唐含,該進(jìn)程在內(nèi)存中的狀態(tài)將丟失
public class A {
// 定義該類的類變量
public static int a = 6;
}
接下來創(chuàng)建A類的實例
public class ATest1 {
public static void main(String[] args)
{
// 創(chuàng)建A類的實例
A a = new A();
// 讓a實例的類變量a的值自加
a.a ++;
System.out.println(a.a);
}
}
下面這個也進(jìn)行同樣操作
public class ATest2 {
public static void main(String[] args) {
// 創(chuàng)建A類的實例
A b = new A();
// 輸出b實例的類變量a的值
System.out.println(b.a);
}
}
當(dāng)運(yùn)行第二個ATest2時浅浮,程序再次創(chuàng)建了A對象,并輸出a的值捷枯,為6滚秩,這是因為運(yùn)行ATest1和ATest2是兩次運(yùn)行JVM進(jìn)程,第一次JVM結(jié)束后淮捆,對A類的修改全部丟失
類加載
當(dāng)主動使用某個類時郁油,如果該類還未被加載到內(nèi)存中,則系統(tǒng)會通過加載攀痊、連接已艰、初始化三個步驟來對該類進(jìn)行初始化
類加載是將類的class文件讀入內(nèi)存,并為之創(chuàng)建一個java.lang.Class對象
類的加載由類加載器完成蚕苇,類加載器通常由JVM提供
類連接
當(dāng)類被加載后哩掺,系統(tǒng)為之生成一個對應(yīng)的Class對象,接著進(jìn)入連接階段涩笤,類連接又分為:
驗證
準(zhǔn)備
解析
類初始化
虛擬機(jī)負(fù)責(zé)對類進(jìn)行初始化嚼吞,主要是對類變量進(jìn)行初始化
public class Test {
static
{
// 使用靜態(tài)初始化塊為變量b指定出初始值
b = 6;
System.out.println("----------");
}
// 聲明變量a時指定初始值
static int a = 5;
static int b = 9; // ①
static int c;
public static void main(String[] args)
{
System.out.println(Test.b);
}
}
類初始化時機(jī)
當(dāng)Java程序首次通過下面6鐘方式來使用某個類或接口,系統(tǒng)就會初始化該類或接口
創(chuàng)建類實例(通過new操作符蹬碧、反射舱禽、反序列化)
調(diào)用某個類的類方法(靜態(tài)方法)
訪問某個類或接口的類變量
反射強(qiáng)制創(chuàng)建
初始化某個類的子類
直接使用java.exe運(yùn)行某個主類
class MyTest
{
static
{
System.out.println("靜態(tài)初始化塊...");
}
// 使用一個字符串直接量為static final的類變量賦值
static final String compileConstant = "Java";
}
public class CompileConstantTest
{
public static void main(String[] args)
{
// 訪問、輸出MyTest中的compileConstant類變量
System.out.println(MyTest.compileConstant); // ①
}
}
System.out.println(MyTest.compileConstant)不會導(dǎo)致初始化MyTest類
類加載器
類加載器負(fù)責(zé)將.class文件加載到內(nèi)存恩沽,并為之生成對應(yīng)的java.lang.Class對象
類加載機(jī)制
類加載器負(fù)責(zé)加載所有類誊稚,系統(tǒng)為載入內(nèi)存的類生成實例,一旦一個類被載入JVM罗心,同一個類就不會被再次載入
JVM啟動里伯,會形成三個類加載器組成的初始類加載器層次結(jié)構(gòu)
根類加載器
擴(kuò)展類加載器
- 系統(tǒng)類加載器
public class ClassLoaderPropTest {
public static void main(String[] args)
throws IOException
{
// 獲取系統(tǒng)類加載器
ClassLoader systemLoader = ClassLoader.getSystemClassLoader();
System.out.println("系統(tǒng)類加載器:" + systemLoader);
/*
獲取系統(tǒng)類加載器的加載路徑——通常由CLASSPATH環(huán)境變量指定
如果操作系統(tǒng)沒有指定CLASSPATH環(huán)境變量,默認(rèn)以當(dāng)前路徑作為
系統(tǒng)類加載器的加載路徑
*/
Enumeration<URL> em1 = systemLoader.getResources("");
while(em1.hasMoreElements())
{
System.out.println(em1.nextElement());
}
// 獲取系統(tǒng)類加載器的父類加載器:得到擴(kuò)展類加載器
ClassLoader extensionLader = systemLoader.getParent();
System.out.println("擴(kuò)展類加載器:" + extensionLader);
System.out.println("擴(kuò)展類加載器的加載路徑:"
+ System.getProperty("java.ext.dirs"));
System.out.println("擴(kuò)展類加載器的parent: "
+ extensionLader.getParent());
}
}
反射
Java中許多對象在運(yùn)行時都會出現(xiàn)兩種類型
編譯時類型
運(yùn)行時類型
有時我們需要在運(yùn)行時發(fā)現(xiàn)對象和類的真實信息
- 假設(shè)在編譯和運(yùn)行時都完全知道了類型的具體信息渤闷,這樣可以先使用instanceof運(yùn)算符進(jìn)行判斷疾瓮,再進(jìn)行強(qiáng)制類型轉(zhuǎn)換
- 編譯時無法預(yù)知該對象和類可能屬于哪些類,程序只依靠運(yùn)行時信息來發(fā)現(xiàn)該對象和類的真實信息飒箭,這就要使用反射了
獲取Class對象
每個類被加載后狼电,系統(tǒng)會為該類生成一個對應(yīng)的Class對象,通過該Class對象就可以訪問到JVM中的這個類弦蹂,獲取Class對象有三種方式:
Class類的forName()靜態(tài)方法肩碟,傳入字符串參數(shù)
調(diào)用某個類的class屬性來獲取,如Person.class將會返回Person類對應(yīng)的class對象
調(diào)用某個對象的getClass()
從Class獲取信息
例子:
// 定義可重復(fù)注解
@Repeatable(Annos.class)
@interface Anno {}
@Retention(value= RetentionPolicy.RUNTIME)
@interface Annos {
Anno[] value();
}
// 使用4個注解修飾該類
@SuppressWarnings(value="unchecked")
@Deprecated
// 使用重復(fù)注解修飾該類
@Anno
@Anno
public class ClassTest
{
// 為該類定義一個私有的構(gòu)造器
private ClassTest()
{
}
// 定義一個有參數(shù)的構(gòu)造器
public ClassTest(String name)
{
System.out.println("執(zhí)行有參數(shù)的構(gòu)造器");
}
// 定義一個無參數(shù)的info方法
public void info()
{
System.out.println("執(zhí)行無參數(shù)的info方法");
}
// 定義一個有參數(shù)的info方法
public void info(String str)
{
System.out.println("執(zhí)行有參數(shù)的info方法"
+ "凸椿,其str參數(shù)值:" + str);
}
// 定義一個測試用的內(nèi)部類
class Inner
{
}
public static void main(String[] args)
throws Exception
{
// 下面代碼可以獲取ClassTest對應(yīng)的Class
Class<ClassTest> clazz = ClassTest.class;
// 獲取該Class對象所對應(yīng)類的全部構(gòu)造器
Constructor[] ctors = clazz.getDeclaredConstructors();
System.out.println("ClassTest的全部構(gòu)造器如下:");
for (Constructor c : ctors)
{
System.out.println(c);
}
System.out.println("");
System.out.println("======================================================");
System.out.println("");
// 獲取該Class對象所對應(yīng)類的全部public構(gòu)造器
Constructor[] publicCtors = clazz.getConstructors();
System.out.println("ClassTest的全部public構(gòu)造器如下:");
for (Constructor c : publicCtors)
{
System.out.println(c);
}
System.out.println("");
System.out.println("======================================================");
System.out.println("");
// 獲取該Class對象所對應(yīng)類的全部public方法
Method[] mtds = clazz.getMethods();
System.out.println("ClassTest的全部public方法如下:");
for (Method md : mtds)
{
System.out.println(md);
}
System.out.println("");
System.out.println("======================================================");
System.out.println("");
// 獲取該Class對象所對應(yīng)類的指定方法
System.out.println("ClassTest里帶一個字符串參數(shù)的info()方法為:"
+ clazz.getMethod("info" , String.class));
System.out.println("");
System.out.println("======================================================");
System.out.println("");
// 獲取該Class對象所對應(yīng)類的上的全部注解
Annotation[] anns = clazz.getAnnotations();
System.out.println("ClassTest的全部Annotation如下:");
for (Annotation an : anns)
{
System.out.println(an);
}
System.out.println("");
System.out.println("======================================================");
System.out.println("");
System.out.println("該Class元素上的@SuppressWarnings注解為:"
+ Arrays.toString(clazz.getAnnotationsByType(SuppressWarnings.class)));
System.out.println("");
System.out.println("======================================================");
System.out.println("");
System.out.println("該Class元素上的@Anno注解為:"
+ Arrays.toString(clazz.getAnnotationsByType(Anno.class)));
System.out.println("");
System.out.println("======================================================");
System.out.println("");
// 獲取該Class對象所對應(yīng)類的全部內(nèi)部類
Class<?>[] inners = clazz.getDeclaredClasses();
System.out.println("ClassTest的全部內(nèi)部類如下:");
for (Class c : inners)
{
System.out.println(c);
}
System.out.println("");
System.out.println("======================================================");
System.out.println("");
// 使用Class.forName方法加載ClassTest的Inner內(nèi)部類
Class inClazz = Class.forName("C_2.c_2_3.ClassTest$Inner");
// 通過getDeclaringClass()訪問該類所在的外部類
System.out.println("inClazz對應(yīng)類的外部類為:" +
inClazz.getDeclaringClass());
System.out.println("");
System.out.println("======================================================");
System.out.println("");
System.out.println("ClassTest的包為:" + clazz.getPackage());
System.out.println("");
System.out.println("======================================================");
System.out.println("");
System.out.println("ClassTest的父類為:" + clazz.getSuperclass());
System.out.println("");
System.out.println("======================================================");
System.out.println("");
}
}
Java 8 新增方法參數(shù)反射
class Test_1
{
public void replace(String str, List<String> list){}
}
public class MethodParameterTest
{
public static void main(String[] args)throws Exception
{
// 獲取String的類
Class<Test_1> clazz = Test_1.class;
// 獲取String類的帶兩個參數(shù)的replace()方法
Method replace = clazz.getMethod("replace"
, String.class, List.class);
// 獲取指定方法的參數(shù)個數(shù)
System.out.println("replace方法參數(shù)個數(shù):" + replace.getParameterCount());
// 獲取replace的所有參數(shù)信息
Parameter[] parameters = replace.getParameters();
int index = 1;
// 遍歷所有參數(shù)
for (Parameter p : parameters)
{
if (p.isNamePresent())
{
System.out.println("---第" + index++ + "個參數(shù)信息---");
System.out.println("參數(shù)名:" + p.getName());
System.out.println("形參類型:" + p.getType());
System.out.println("泛型類型:" + p.getParameterizedType());
}
}
}
}
使用反射生成并操作對象
創(chuàng)建對象
通過反射生成對象需要先使用Class對象獲取指定的Constructor對象削祈,再用Constructor對象的newInstance()創(chuàng)建該Class對象對應(yīng)類的實例
public class ObjectPoolFactory {
// 定義一個對象池,前面是對象名削饵,后面是實際對象
private Map<String ,Object> objectPool = new HashMap<>();
// 定義一個創(chuàng)建對象的方法
// 該方法只要傳入一個字符串類名岩瘦,程序可以根據(jù)該類名生成Java對象
private Object createObject(String clazzName)
throws Exception
, IllegalAccessException , ClassNotFoundException
{
// 根據(jù)字符串來獲取對應(yīng)的Class對象
Class<?> clazz = Class.forName(clazzName);
// 使用clazz對應(yīng)類的默認(rèn)構(gòu)造器創(chuàng)建實例
return clazz.getConstructor().newInstance();
}
// 該方法根據(jù)指定文件來初始化對象池
// 它會根據(jù)配置文件來創(chuàng)建對象
public void initPool(String fileName)
throws InstantiationException
, IllegalAccessException ,ClassNotFoundException
{
try(
FileInputStream fis = new FileInputStream(fileName))
{
Properties props = new Properties();
props.load(fis);
for (String name : props.stringPropertyNames())
{
// 每取出一對key-value對,就根據(jù)value創(chuàng)建一個對象
// 調(diào)用createObject()創(chuàng)建對象窿撬,并將對象添加到對象池中
objectPool.put(name ,
createObject(props.getProperty(name)));
}
}
catch (Exception ex)
{
System.out.println("讀取" + fileName + "異常");
}
}
public Object getObject(String name)
{
// 從objectPool中取出指定name對應(yīng)的對象
return objectPool.get(name);
}
public static void main(String[] args)
throws Exception
{
ObjectPoolFactory pf = new ObjectPoolFactory();
pf.initPool("obj.txt");
System.out.println(pf.getObject("a")); // ①
System.out.println(pf.getObject("b")); // ②
}
}
調(diào)用方法
獲取某個類對應(yīng)的Class對象后启昧,就可以通過該Class對象的getMethods()獲取全部方法
Method里面有一個invoke()方法:
Object invoke(Object obj,Object...args);
obj是執(zhí)行方法的主調(diào),args是執(zhí)行該方法時傳入該方法的實參
public class ExtendedObjectPoolFactory {
// 定義一個對象池劈伴,前面是對象名密末,后面是實際對象
private Map<String ,Object> objectPool = new HashMap<>();
private Properties config = new Properties();
// 從指定屬性文件中初始化Properties對象
public void init(String fileName)
{
try(
FileInputStream fis = new FileInputStream(fileName))
{
config.load(fis);
}
catch (IOException ex)
{
System.out.println("讀取" + fileName + "異常");
}
}
// 定義一個創(chuàng)建對象的方法
// 該方法只要傳入一個字符串類名,程序可以根據(jù)該類名生成Java對象
private Object createObject(String clazzName)
throws Exception
{
// 根據(jù)字符串來獲取對應(yīng)的Class對象
Class<?> clazz =Class.forName(clazzName);
// 使用clazz對應(yīng)類的默認(rèn)構(gòu)造器創(chuàng)建實例
return clazz.getConstructor().newInstance();
}
// 該方法根據(jù)指定文件來初始化對象池
// 它會根據(jù)配置文件來創(chuàng)建對象
public void initPool()throws Exception
{
for (String name : config.stringPropertyNames())
{
// 每取出一個key-value對跛璧,如果key中不包含百分號(%)
// 這就表明是根據(jù)value來創(chuàng)建一個對象
// 調(diào)用createObject創(chuàng)建對象严里,并將對象添加到對象池中
if (!name.contains("%"))
{
objectPool.put(name ,
createObject(config.getProperty(name)));
}
}
}
// 該方法將會根據(jù)屬性文件來調(diào)用指定對象的setter方法
public void initProperty()throws InvocationTargetException
,IllegalAccessException,NoSuchMethodException
{
for (String name : config.stringPropertyNames())
{
// 每取出一對key-value對,如果key中包含百分號(%)
// 即可認(rèn)為該key用于控制調(diào)用對象的setter方法設(shè)置值
// %前半為對象名字追城,后半控制setter方法名
if (name.contains("%"))
{
// 將配置文件中的key按%分割
String[] objAndProp = name.split("%");
// 取出調(diào)用setter方法的參數(shù)值
Object target = getObject(objAndProp[0]);
// 獲取setter方法名:set + "首字母大寫" + 剩下部分
String mtdName = "set" +
objAndProp[1].substring(0 , 1).toUpperCase()
+ objAndProp[1].substring(1);
// 通過target的getClass()獲取它的實現(xiàn)類所對應(yīng)的Class對象
Class<?> targetClass = target.getClass();
// 獲取希望調(diào)用的setter方法
Method mtd = targetClass.getMethod(mtdName , String.class);
// 通過Method的invoke方法執(zhí)行setter方法
// 將config.getProperty(name)的值作為調(diào)用setter方法的參數(shù)
mtd.invoke(target , config.getProperty(name));
}
}
}
public Object getObject(String name)
{
// 從objectPool中取出指定name對應(yīng)的對象
return objectPool.get(name);
}
public static void main(String[] args)
throws Exception
{
ExtendedObjectPoolFactory epf = new ExtendedObjectPoolFactory();
epf.init("extObj.txt");
epf.initPool();
epf.initProperty();
System.out.println(epf.getObject("a"));
}
}
訪問成員變量值
通過Class對象的getFields()可以獲取該類全部成員變量
class Person
{
private String name;
private int age;
public String toString()
{
return "Person[name:" + name +
" , age:" + age + " ]";
}
}
public class FieldTest
{
public static void main(String[] args)
throws Exception
{
// 創(chuàng)建一個Person對象
Person p = new Person();
// 獲取Person類對應(yīng)的Class對象
Class<Person> personClazz = Person.class;
// 獲取Person的名為name的成員變量
// 使用getDeclaredField()方法表明可獲取各種訪問控制符的成員變量
Field nameField = personClazz.getDeclaredField("name");
// 設(shè)置通過反射訪問該成員變量時取消訪問權(quán)限檢查
nameField.setAccessible(true);
// 調(diào)用set()方法為p對象的name成員變量設(shè)置值
nameField.set(p , "Yeeku.H.Lee");
// 獲取Person類名為age的成員變量
Field ageField = personClazz.getDeclaredField("age");
// 設(shè)置通過反射訪問該成員變量時取消訪問權(quán)限檢查
ageField.setAccessible(true);
// 調(diào)用setInt()方法為p對象的age成員變量設(shè)置值
ageField.setInt(p , 30);
System.out.println(p);
}
}
操作數(shù)組
java.lang.reflect下有一個Array類刹碾,Array對象可以代表所有數(shù)組,可以使用Array動態(tài)創(chuàng)建數(shù)組
public class ArrayTest1 {
public static void main(String[] args) {
try
{
// 創(chuàng)建一個元素類型為String 座柱,長度為10的數(shù)組
Object arr = Array.newInstance(String.class, 10);
// 依次為arr數(shù)組中index為5迷帜、6的元素賦值
Array.set(arr, 5, "Java");
Array.set(arr, 6, "Java EE");
// 依次取出arr數(shù)組中index為5、6的元素的值
Object book1 = Array.get(arr , 5);
Object book2 = Array.get(arr , 6);
// 輸出arr數(shù)組中index為5色洞、6的元素
System.out.println(book1);
System.out.println(book2);
}
catch (Throwable e)
{
System.err.println(e);
}
}
}
下面創(chuàng)建一個三維數(shù)組
public class ArrayTest2 {
public static void main(String[] args) {
/*
創(chuàng)建一個三維數(shù)組戏锹。
根據(jù)前面介紹數(shù)組時講的:三維數(shù)組也是一維數(shù)組,
是數(shù)組元素是二維數(shù)組的一維數(shù)組火诸,
因此可以認(rèn)為arr是長度為3的一維數(shù)組
*/
Object arr = Array.newInstance(String.class, 3, 4, 10);
// 獲取arr數(shù)組中index為2的元素锦针,該元素應(yīng)該是二維數(shù)組
Object arrObj = Array.get(arr, 2);
// 使用Array為二維數(shù)組的數(shù)組元素賦值。二維數(shù)組的數(shù)組元素是一維數(shù)組置蜀,
// 所以傳入Array的set()方法的第三個參數(shù)是一維數(shù)組奈搜。
Array.set(arrObj , 2 , new String[]
{
"Java",
"Java EE"
});
// 獲取arrObj數(shù)組中index為3的元素,該元素應(yīng)該是一維數(shù)組盯荤。
Object anArr = Array.get(arrObj, 3);
Array.set(anArr , 8 , "Android");
// 將arr強(qiáng)制類型轉(zhuǎn)換為三維數(shù)組
String[][][] cast = (String[][][])arr;
// 獲取cast三維數(shù)組中指定元素的值
System.out.println(cast[2][3][8]);
System.out.println(cast[2][2][0]);
System.out.println(cast[2][2][1]);
}
}
動態(tài)代理
java.lang.reflect提供了一個Proxy類和一個InvocationHandler接口
用Proxy和InvocationHandler創(chuàng)建動態(tài)代理
Proxy是所有動態(tài)代理的父類媚污,可以使用它創(chuàng)建動態(tài)代理類以及動態(tài)代理實例
Proxy提供了下面兩個方法創(chuàng)建動態(tài)代理類以及動態(tài)代理實例
- static Class < ? > getProxyClass ( Cl assLoader loader , Class < ? > . . .
interfaces ) 返回實現(xiàn)指定接口的代理類
- staticObject newProxyInstance ( ClassLoader loader , Class < ? > [ ]
interfaces , InvocationHandler handler ) 構(gòu)造實現(xiàn)指定接口的代理類的一個新實例 所有方法會調(diào)用給定處理器對象的 invoke 方法
系統(tǒng)生成的每個代理對象都有一個與之關(guān)聯(lián)的InvocationHandler對象
例子:
interface Person
{
void walk();
void sayHello(String name);
}
class MyInvokationHandler implements InvocationHandler
{
/*
執(zhí)行動態(tài)代理對象的所有方法時,都會被替換成執(zhí)行如下的invoke方法
其中:
proxy:代表動態(tài)代理對象
method:代表正在執(zhí)行的方法
args:代表調(diào)用目標(biāo)方法時傳入的實參廷雅。
*/
public Object invoke(Object proxy, Method method, Object[] args)
{
System.out.println("----正在執(zhí)行的方法:" + method);
if (args != null)
{
System.out.println("下面是執(zhí)行該方法時傳入的實參為:");
for (Object val : args)
{
System.out.println(val);
}
}
else
{
System.out.println("調(diào)用該方法沒有實參耗美!");
}
return null;
}
}
public class ProxyTest
{
public static void main(String[] args)
throws Exception
{
// 創(chuàng)建一個InvocationHandler對象
InvocationHandler handler = new MyInvokationHandler();
// 使用指定的InvocationHandler來生成一個動態(tài)代理對象
Person p = (Person) Proxy.newProxyInstance(Person.class.getClassLoader()
, new Class[]{Person.class}, handler);
// 調(diào)用動態(tài)代理對象的walk()和sayHello()方法
p.walk();
p.sayHello("孫悟空");
}
}
動態(tài)代理和AOP
假設(shè)有三個模塊需要用到一個公共的代碼段,我們可以把該公共代碼段定義成一個方法航缀,然后讓這三個模塊直接調(diào)用就可以了商架,但是這三個模塊又和一個特定的方法耦合了侣诵,最好的情況就是模塊可以使用到這段公共代碼嗜桌,但又不需要在三個模塊中以硬編碼的方式直接調(diào)用這段公共代碼,這個時候就可以使用動態(tài)代理了
先上一個Dog接口
public interface Dog {
// info方法聲明
void info();
// run方法聲明
void run();
}
再來一個獵狗實現(xiàn)類
public class GunDog implements Dog{
// 實現(xiàn)info()方法影所,僅僅打印一個字符串
public void info()
{
System.out.println("我是一只獵狗");
}
// 實現(xiàn)run()方法灿巧,僅僅打印一個字符串
public void run()
{
System.out.println("我奔跑迅速");
}
}
現(xiàn)在需求來了赶袄,假設(shè)info和run就是前面例子中的三個模塊中的兩個揽涮,我們想在這兩個方法中實現(xiàn)一段公共代碼,但又不想以硬編碼方式直接調(diào)用饿肺,怎么做呢蒋困?
假設(shè)下面這個就是需要插入的公共代碼
public class DogUtil {
// 第一個攔截器方法
public void method1()
{
System.out.println("=====模擬第一個通用方法=====");
}
// 第二個攔截器方法
public void method2()
{
System.out.println("=====模擬通用方法二=====");
}
}
關(guān)鍵實現(xiàn):
public class MyInvokationHandler implements InvocationHandler {
// 需要被代理的對象
private Object target;
public void setTarget(Object target)
{
this.target = target;
}
// 執(zhí)行動態(tài)代理對象的所有方法時,都會被替換成執(zhí)行如下的invoke方法
public Object invoke(Object proxy, Method method, Object[] args)
throws Exception
{
DogUtil du = new DogUtil();
// 執(zhí)行DogUtil對象中的method1敬辣。
du.method1();
// 以target作為主調(diào)來執(zhí)行method方法
Object result = method.invoke(target , args);
// 執(zhí)行DogUtil對象中的method2雪标。
du.method2();
return result;
}
}
接下來:
public class MyProxyFactory {
// 為指定target生成動態(tài)代理對象
public static Object getProxy(Object target)
throws Exception
{
// 創(chuàng)建一個MyInvokationHandler對象
MyInvokationHandler handler =
new MyInvokationHandler();
// 為MyInvokationHandler設(shè)置target對象
handler.setTarget(target);
// 創(chuàng)建、并返回一個動態(tài)代理
return Proxy.newProxyInstance(target.getClass().getClassLoader()
, target.getClass().getInterfaces() , handler);
}
}
測試:
public class Test {
public static void main(String[] args)
throws Exception
{
// 創(chuàng)建一個原始的GunDog對象溉跃,作為target
Dog target = new GunDog();
// 以指定的target來創(chuàng)建動態(tài)代理
Dog dog = (Dog)MyProxyFactory.getProxy(target);
dog.info();
dog.run();
}
}
dog對象實際是動態(tài)代理對象村刨,只是該動態(tài)代理對象也實現(xiàn)了Dog接口
反射與泛型
從Java5開始允許使用泛型來限制Class類,如:String.class的類型實際上就是CLass< Sring >
泛型與Class類
使用Class< T >可以避免強(qiáng)制類型轉(zhuǎn)換
下面創(chuàng)建一個簡單對象工廠撰茎,該對象工廠可以根據(jù)指定類來提供該類的實例
public class CrazyitObjectFactory {
public static Object getInstance(String clsName)
{
try
{
// 創(chuàng)建指定類對應(yīng)的Class對象
Class cls = Class.forName(clsName);
// 返回使用該Class對象所創(chuàng)建的實例
return cls.newInstance();
}
catch(Exception e)
{
e.printStackTrace();
return null;
}
}
}
將上面的改寫成使用泛型的Class:
public class CrazyitObjectFactory2 {
public static <T> T getInstance(Class<T> cls)
{
try
{
return cls.newInstance();
}
catch(Exception e)
{
e.printStackTrace();
return null;
}
}
public static void main(String[] args)
{
// 獲取實例后無須類型轉(zhuǎn)換
Date d = CrazyitObjectFactory2.getInstance(Date.class);
JFrame f = CrazyitObjectFactory2.getInstance(JFrame.class);
}
}
對Array的newInstance()進(jìn)行包裝
public class CrazyitArray {
// 對Array的newInstance方法進(jìn)行包裝
@SuppressWarnings("unchecked")
public static <T> T[] newInstance(Class<T> componentType, int length)
{
return (T[]) Array.newInstance(componentType , length); //①
}
public static void main(String[] args)
{
// 使用CrazyitArray的newInstance()創(chuàng)建一維數(shù)組
String[] arr = CrazyitArray.newInstance(String.class , 10);
// 使用CrazyitArray的newInstance()創(chuàng)建二維數(shù)組
// 在這種情況下嵌牺,只要設(shè)置數(shù)組元素的類型是int[]即可。
int[][] intArr = CrazyitArray.newInstance(int[].class , 5);
arr[5] = "Java";
// intArr是二維數(shù)組龄糊,初始化該數(shù)組的第二個數(shù)組元素
// 二維數(shù)組的元素必須是一維數(shù)組
intArr[1] = new int[]{23, 12};
System.out.println(arr[5]);
System.out.println(intArr[1][1]);
}
}
反射獲取泛型信息
public class GenericTest {
private Map<String , Integer> score;
public static void main(String[] args)
throws Exception
{
Class<GenericTest> clazz = GenericTest.class;
Field f = clazz.getDeclaredField("score");
// 直接使用getType()取出的類型只對普通類型的成員變量有效
Class<?> a = f.getType();
// 下面將看到僅輸出java.util.Map
System.out.println("score的類型是:" + a);
// 獲得成員變量f的泛型類型
Type gType = f.getGenericType();
// 如果gType類型是ParameterizedType對象
if(gType instanceof ParameterizedType)
{
// 強(qiáng)制類型轉(zhuǎn)換
ParameterizedType pType = (ParameterizedType)gType;
// 獲取原始類型
Type rType = pType.getRawType();
System.out.println("原始類型是:" + rType);
// 取得泛型類型的泛型參數(shù)
Type[] tArgs = pType.getActualTypeArguments();
System.out.println("泛型信息是:");
for (int i = 0; i < tArgs.length; i++)
{
System.out.println("第" + i + "個泛型類型是:" + tArgs[i]);
}
}
else
{
System.out.println("獲取泛型類型出錯髓梅!");
}
}
}