高新技術(shù)的重要性
這里的高新技術(shù)指的是Java基礎(chǔ)中的知識(shí)炭分,比如:反射桃焕、注解、代理捧毛、線程池观堂、依賴注入等等让网。
市面上的開源框架大多都是使用了這些Java基礎(chǔ)的知識(shí)去實(shí)現(xiàn)的,掌握了這些Java基礎(chǔ)的知識(shí)师痕,能幫助我們更好的理解一些好的開源框架的實(shí)現(xiàn)原理寂祥。
反射
JAVA反射機(jī)制是在運(yùn)行狀態(tài)中,對(duì)于任意一個(gè)類七兜,都能夠知道這個(gè)類的所有屬性和方法;對(duì)于任意一個(gè)對(duì)象福扬,都能夠調(diào)用它的任意一個(gè)方法腕铸;這種動(dòng)態(tài)獲取的信息以及動(dòng)態(tài)調(diào)用對(duì)象的方法的功能稱為java語言的反射機(jī)制。
說白了就是在Android中給我們提供了一個(gè)android.jar可以調(diào)用android的api铛碑,但是還有一部分api的方法是沒有暴露出來的狠裹,那么如果我們想要調(diào)用這些方法,就需要通過反射來調(diào)用汽烦。
用處:
l 在運(yùn)行時(shí)判斷任意一個(gè)對(duì)象所屬的類涛菠;
? obj instanceof Object => obj.getClass().equals(Object.class)
l 在運(yùn)行時(shí)構(gòu)造任意一個(gè)類的對(duì)象;
l 在運(yùn)行時(shí)判斷任意一個(gè)類所具有的成員變量和方法撇吞;
l 在運(yùn)行時(shí)調(diào)用任意一個(gè)對(duì)象的方法俗冻;
l 生成動(dòng)態(tài)代理。
注意:
一般如果不是必要情況下牍颈,盡量不要使用反射迄薄,反射會(huì)影響app的性能。
獲取Class對(duì)象
萬事萬物皆對(duì)象煮岁,每個(gè)類中都具有成員變量讥蔽,構(gòu)造方法,成員方法画机,所以可以使用Class類來表示每個(gè)類冶伞,每個(gè)類是Class類的實(shí)例對(duì)象。
Class的實(shí)例對(duì)象是各個(gè)類在內(nèi)存中的那份字節(jié)碼文件步氏。
基本數(shù)據(jù)類型响禽,String,void戳护,數(shù)組金抡,引用類型都存在類類型Class。
Class常見方法
l newInstance(); 創(chuàng)建實(shí)例對(duì)象
l getName(); 獲取類的名字腌且,帶包名的
l getSimpleName(); 獲取不帶包名的類名
l getMethod(方法名梗肝,方法參數(shù)的類類型); 獲取指定公有的方法
l getDeclaredMethod(方法名,方法參數(shù)的類類型); 獲取所有的指定的方法
l getMethods(); 獲取所有公有地方法
l getDelcaredMethods(); 獲取類上面所有的方法
l getFields(); 獲取所有公有的成員變量
l getField("成員變量的名稱"); 通過成員變量的名字獲取公有的成員變量
l getDeclaredField("成員變量的名稱"); 通過成員變量的名字獲取成員變量
l getDeclaredFields(); 獲取所有的成員變量
l getConstructor("","","");
l getConstructors();
l getDeclaredConstructor(parameterTypes);
獲取Class文件的三種方式
獲取Class文件的三種方式:
- 類名.class;
- 對(duì)象名.getClass();
- Class.forName(“類的包名+類的名字”);
/**
* 獲取Class的三種方式:
* 類名.class
* 對(duì)象.getClass()
* Class.forName("類名")
*/
public class GetClass {
public static void main(String[] args) throws Exception {
//方式1:Class.forName("類名")
Class clazz1= Class.forName("com.fuyuan.example.bean.People");
System.out.println("Class.forName()方式 :" + clazz1);
//第二種:對(duì)象.getClass()方法
People people=new People();
Class clazz2 = people.getClass();
System.out.println("對(duì)象.getClass()方式 :" + clazz2 + ", " + (clazz1 == clazz2));
//第三種:類名.class方法
Class clazz3 = People.class;
System.out.println("類名.class方式 :" + clazz3 + ", " + (clazz1 == clazz3));
}
}
運(yùn)行結(jié)果:
.]
動(dòng)態(tài)加載類和靜態(tài)加載類
靜態(tài)加載:在編譯時(shí)期加載的類铺董,叫靜態(tài)加載巫击。
動(dòng)態(tài)加載:在運(yùn)行時(shí)期加載的類禀晓,叫動(dòng)態(tài)加載。
簡單來說:
在代碼中寫死的坝锰,直接new出來的類粹懒,就屬于是靜態(tài)加載;
運(yùn)行期間通過配置信息來動(dòng)態(tài)獲取相關(guān)類的實(shí)例顷级,就屬于動(dòng)態(tài)加載凫乖。
動(dòng)態(tài)加載的優(yōu)點(diǎn):提高程序的可擴(kuò)展性
動(dòng)態(tài)加載范例
現(xiàn)有Excel、Word弓颈、PPT 3個(gè)類帽芽,他們都實(shí)現(xiàn)了Office接口,重寫了Office接口的startWork方法翔冀。
現(xiàn)在通過動(dòng)態(tài)加載的方式导街,使用相應(yīng)的類去執(zhí)行相關(guān)的操作。
接口Office纤子,包含一個(gè)startWork方法:Office.java
public interface Office {
public void startWork();
}
Office的實(shí)現(xiàn)類Excel:
public class Excel implements Office{
@Override
public void startWork() {
System.out.println("Excel start work......");
}
}
Office的實(shí)現(xiàn)類Word:
public class Word implements Office{
@Override
public void startWork() {
System.out.println("Word start work......");
}
}
寫一個(gè)測(cè)試類搬瑰,演示動(dòng)態(tài)加載的使用場(chǎng)景:DynamicLoad.java
/**
* 動(dòng)態(tài)加載
*/
public class DynamicLoad {
public static void main(String[] args) {
String worker = "com.fuyuan.example.bean." + "Excel";
startWord(worker);
}
public static void startWord(String worker) {
try {
// 1. 獲取字節(jié)碼文件
Class clazz = Class.forName(worker);
// 2. 獲取類的對(duì)象
Office office = (Office) clazz.newInstance();
// 3. 調(diào)用相關(guān)方法
office.startWork();
} catch (Exception e) {
e.printStackTrace();
}
}
}
打印類中方法的信息
Class中用于獲取方法的API為:
l getMethod(方法名,方法參數(shù)的類類型); 獲取指定公有的方法
l getDeclaredMethod(方法名控硼,方法參數(shù)的類類型); 獲取所有的指定的方法
l getMethods(); 獲取所有公有地方法
l getDelcaredMethods(); 獲取類上面所有的方法
Method的常見API:
l method.getReturnType(); 獲取返回值的類類型
l method.getName(); 獲取方法的名字
l method.getParameterTypes(); 獲取所有參數(shù)的類類型
需求:給定一個(gè)對(duì)象泽论,打印出這個(gè)對(duì)象身上所有的方法的返回值、方法名和參數(shù)
/**
* 打印方法的信息卡乾,包括: 返回值 方法名 參數(shù)
*/
public static void printMethodMessage(Object object){
String temp="";
//1.獲取字節(jié)碼文件
Class c=object.getClass();
//2.獲取類上面的方法
/**
* 獲取一個(gè)方法
* 參數(shù)1:方法的名字
* 參數(shù)2:方法參數(shù)的類類型
*/
// c.getMethod(name, parameterTypes);
//獲取所有的公有地方法
Method[] methods = c.getMethods();
//獲取類上面的所有方法
// c.getDeclaredMethod(name, parameterTypes)
// c.getDeclaredMethods();
for (Method method : methods) {
//返回值的類類型
Class<?> returnType = method.getReturnType();
temp+=returnType.getName()+" ";
//獲取方法的名字
String name=method.getName();
temp+=name+"(";
//獲取參數(shù)
Class<?>[] parameterTypes = method.getParameterTypes();
for (Class<?> class1 : parameterTypes) {
String parameterName=class1.getName();
temp+=parameterName+",";
}
temp+=")";
// 打印方法信息
System.out.println(temp);
temp="";
}
}
調(diào)用此方法打印String身上的所有方法信息佩厚,運(yùn)行結(jié)果:
打印類中成員變量的信息
Class中用于獲取成員變量的API為:
l getFields(); 獲取所有公有的成員變量
l getField("成員變量的名稱"); 通過成員變量的名字獲取公有的成員變量
l getDeclaredFields (); 獲取所有的成員變量(包括private的)
l getDeclaredFields("成員變量的名稱"); 通過成員變量的名字獲取成員變量
Field的常見API:
l getName(); 獲取成員變量的名稱
l getType(); 返回成員變量的類類型
需求:給定一個(gè)對(duì)象,打印出這個(gè)對(duì)象身上所有的成員變量名稱和類類型
/**
* 打印類中成員變量的信息
*/
public static void printFieldMessage(Object obj){
//1.獲取字節(jié)碼文件
Class c1=obj.getClass();
//2.拿到字節(jié)碼文件中所有的變量
// 獲取到所有的公有的成員變量
Field[] fields = c1.getFields();
// 獲取到指定公有的成員變量
// c1.getField("成員變量的名稱");
// 獲取到指定的成員變量
// c1.getDeclaredField("成員變量的名稱");
//可以獲取到所有的成員變量
// c1.getDeclaredFields();
for (Field field : fields) {
//3.獲取成員變量的名稱
String name=field.getName();
//4.獲取成員變量的類型
Class typeClass=field.getType();
System.out.println(typeClass+" "+name+";");
}
}
調(diào)用此方法打印int身上所有的公有的成員變量的信息说订,運(yùn)行結(jié)果:
打印類中的構(gòu)造方法的信息
可變參數(shù)
l 可變參數(shù)的出現(xiàn)解決了一個(gè)方法接受的參數(shù)個(gè)數(shù)不固定的問題抄瓦;
l 可變參數(shù)只能出現(xiàn)在參數(shù)列表的最后;
l ...位于變量類型和變量名之間陶冷,前后有無空格都可以钙姊;
l 調(diào)用可變參數(shù)的方法時(shí),編譯器為該可變參數(shù)隱含創(chuàng)建了一個(gè)數(shù)組埂伦;
l 在方法體中煞额,可以以數(shù)組的形式訪問可變參數(shù);
可變參數(shù)使用范例:
/**
* 可變參數(shù)
*/
public class ChangeArgs {
public static void main(String[] args) {
System.out.println(add(2,5));
System.out.println(add(2,5,9,7));
}
/**
* 可變參數(shù)的使用(可變參數(shù)實(shí)質(zhì)就是數(shù)組的形式)
*/
public static int add(int... args){
int sum=0;
for (int i=0;i<args.length;i++) {
sum+=args[i];
}
return sum;
}
}
打印構(gòu)造方法的信息
Class中用于獲取構(gòu)造方法的API為:
l getConstructor("","",""); 獲取指定公有的構(gòu)造方法沾谜,參數(shù)是可變參數(shù)
l getConstructors(); 獲取所有的公有構(gòu)造方法
l getDeclaredConstructor(parameterTypes); 獲取指定的構(gòu)造方法
l getDeclaredConstructors(); 獲取所有的構(gòu)造方法
Constructor的常見API:
l getName(); 獲取構(gòu)造方法的名字
l getParameterTypes(); 獲取所有參數(shù)的類類型
需求:給定一個(gè)對(duì)象膊毁,打印出這個(gè)對(duì)象身上所有的構(gòu)造方法的名稱和參數(shù)類型
/**
* 打印構(gòu)造方法信息
*/
public static void printConstructorMessage(Object obj){
String temp="";
//1.獲取字節(jié)碼文件
Class c1=obj.getClass();
//2.獲取構(gòu)造方法
//參數(shù)是可變參數(shù),可以理解為數(shù)組,代表的是構(gòu)造方法的參數(shù)的類類型
//獲取指定的構(gòu)造方法
// c1.getConstructor("","","");
//獲取所有公有的構(gòu)造方法
// c1.getConstructors();
//獲取指定的構(gòu)造方法
// c1.getDeclaredConstructor(parameterTypes);
//獲取所有構(gòu)造方法
Constructor[] constructors = c1.getDeclaredConstructors();
for (Constructor constructor : constructors) {
//獲取構(gòu)造方法的名稱
String name=constructor.getName();
temp+=name+"(";
//獲取構(gòu)造的參數(shù)的類類型
Class[] parameterTypes = constructor.getParameterTypes();
for (Class class1 : parameterTypes) {
//獲取構(gòu)造方法的參數(shù)的類類型的名字
String paramName=class1.getName();
temp+=paramName+",";
}
temp+=")";
System.out.println(temp);
temp="";
}
}
調(diào)用此方法打印String身上所有的公有的成員變量的信息基跑,運(yùn)行結(jié)果:
方法和成員變量的反射
測(cè)試用Number類:Number.java
public class Number {
private static String num="number start...";
public void add(int a,int b){
System.out.println(a+b);
}
public static void printNum(){
System.out.println(num);
}
}
通過反射調(diào)用某個(gè)類的方法
通過反射調(diào)用某個(gè)類的方法的步驟:
獲取Class字節(jié)碼
獲取構(gòu)造方法
通過構(gòu)造方法創(chuàng)建對(duì)象
獲取方法(可以是私有的)
方法設(shè)置Accessible為true(暴力反射)
調(diào)用invoke方法
/**
* 通過反射調(diào)用方法
*/
public static void reflectionMethod() throws Exception {
// 1. 獲取字節(jié)碼文件
Class clazz = Number.class;
// 2. 獲取類的對(duì)象
Number number = (Number) clazz.newInstance();
// 3. 獲取方法
Method method = clazz.getDeclaredMethod("add", int.class, int.class);
// 4. 設(shè)置方法訪問權(quán)限(暴力反射)
method.setAccessible(true);
// 5. 執(zhí)行方法
method.invoke(number, 2, 6);
}
通過反射給某個(gè)類的成員變量賦值
通過反射給某個(gè)類的成員變量賦值的步驟:
獲取Class字節(jié)碼
獲取構(gòu)造方法
通過構(gòu)造方法創(chuàng)建對(duì)象
獲取成員變量(可以是私有的)
方法設(shè)置Accessible為true(暴力反射)
調(diào)用Field.set(對(duì)象婚温,參數(shù)值) 方法賦值
/**
* 通過反射為成員變量賦值
*/
public static void reflectionField() throws Exception {
// 1. 獲取字節(jié)碼文件
Class clazz = Number.class;
// 2. 獲取類的對(duì)象
Number number = (Number) clazz.newInstance();
// 調(diào)用方法打印賦值前的成員變量的值
System.out.print("修改前:");
number.printNum();
// 3. 獲取成員變量
Field field = clazz.getDeclaredField("num");
// 4. 設(shè)置成員變量的訪問權(quán)限(暴力反射)
field.setAccessible(true);
// 5. 為成員變量賦值
field.set(number, "我是被修改后的Number值");
// 調(diào)用方法打印賦值后的成員變量的值,看是否修改成功
System.out.print("修改后:");
number.printNum();
}
方法執(zhí)行結(jié)果:
注解
Annotion(注解)是一個(gè)接口媳否,程序可以通過反射來獲取指定程序元素的Annotion對(duì)象栅螟,然后通過Annotion對(duì)象來獲取注解里面的元數(shù)據(jù)荆秦。
Annotation(注解)是JDK5.0及以后版本引入的。它可以用于創(chuàng)建文檔力图,跟蹤代碼中的依賴性步绸,甚至執(zhí)行基本編譯時(shí)檢查。從某些方面看吃媒,Annotation就像修飾符一樣被使用瓤介,并應(yīng)用于包、類型赘那、構(gòu)造方法惑朦、方法、成員變量漓概、參數(shù)、本地變量的聲明中病梢。
Annotation的行為十分類似public胃珍、final這樣的修飾符。每個(gè)Annotation具有一個(gè)名字和成員(成員的個(gè)數(shù)>=0)蜓陌。每個(gè)Annotation的成員具有被稱為name=value對(duì)的名字和值(就像javabean一樣)觅彰,name=value裝載了Annotation的信息。也就是說注解中可以不存在成員钮热。
使用注解的基本規(guī)則:
Annotation不能影響程序代碼的執(zhí)行填抬,無論增加、刪除 Annotation隧期,代碼都始終如一的執(zhí)行飒责。
Annotation類型:
Annotation類型定義了Annotation的名字、類型仆潮、成員默認(rèn)值宏蛉。一個(gè)Annotation類型可以說是一個(gè)特殊的java接口,它的成員變量是受限制的性置,而聲明Annotation類型時(shí)需要使用新語法拾并。當(dāng)我們通過java反射api訪問Annotation時(shí),返回值將是一個(gè)實(shí)現(xiàn)了該 annotation類型接口的對(duì)象鹏浅,通過訪問這個(gè)對(duì)象我們能方便的訪問到其Annotation成員嗅义。
簡而言之:
l 一個(gè)注解就是一個(gè)類,使用注解隐砸,就相當(dāng)于創(chuàng)建了一個(gè)類的實(shí)例對(duì)象之碗。
l 注解相當(dāng)于一種標(biāo)記,在程序中加了注解就等于為程序打上了某種標(biāo)記季希。
l Java編譯器继控、開發(fā)工具或者其他程序可以用反射來了解你的類及各種元素上有無何種標(biāo)記械馆,通過不同的標(biāo)記,就去干相應(yīng)的事武通。
l 標(biāo)記可以加在包霹崎,類,字段冶忱,方法尾菇,方法的參數(shù)以及局部變量上。
注解是一個(gè)特殊的類囚枪,他的格式同接口派诬,只是在接口前加了”@“
注解的分類
根據(jù)注解參數(shù)的個(gè)數(shù),我們可以將注解分為三類:
1.標(biāo)記注解:一個(gè)沒有成員定義的Annotation類型被稱為標(biāo)記注解链沼,@Override;
2.單值注解
3.完整注解
根據(jù)注解使用方法和用途默赂,我們可以將Annotation分為三類:
1.系統(tǒng)注解
2.元注解
3.自定義注解
系統(tǒng)注解
@Override
@Override 用來表示實(shí)現(xiàn)或者重寫接口或父類中的方法。
@Override 是一個(gè)標(biāo)記注解類型括勺,它被用作標(biāo)注方法缆八。它說明了被標(biāo)注的方法重載了父類的方法,起到了斷言的作用疾捍。如果我們使用了這種Annotation在一個(gè)沒有覆蓋父類方法的方法時(shí)奈辰,java編譯器將以一個(gè)編譯錯(cuò)誤來警示。這個(gè)annotaton常常在我們?cè)噲D覆蓋父類方法而確又寫錯(cuò)了方法名時(shí)發(fā)揮威力乱豆。使用方法極其簡單:在使用此annotation時(shí)只要在被修飾的方法前面加上@Override即可奖恰。
@Deprecated
@Deprecated用來標(biāo)記已過時(shí)的類型或者類型成員。
@Deprecated也是一個(gè)標(biāo)記注解宛裕。當(dāng)一個(gè)類型或者類型成員使用@Deprecated修飾的話瑟啃,編譯器將不鼓勵(lì)使用這個(gè)被標(biāo)注的程序元素。而且這種修飾具有一定的“延續(xù)性”:如果我們?cè)诖a中通過繼承或者覆蓋的方式使用了這個(gè)過時(shí)的類型或者成員揩尸,雖然繼承或者覆蓋后的類型或者成員并不是被聲明為@Deprecated翰守,但編譯器仍然要報(bào)警。
@SuppressWarnings
@SuppressWarnings是用來警告用戶的疲酌,它用于通知java編譯器禁止特定的編譯警告蜡峰。
@SuppressWarnings被用于有選擇的關(guān)閉編譯器對(duì)類、方法朗恳、成員變量湿颅、變量初始化的警告。
元注解
元注解就是注解的注解粥诫,它的作用就是負(fù)責(zé)注解其他注解油航。
Java5.0定義了4個(gè)標(biāo)準(zhǔn)的元注解類型,它們被用來提供對(duì)其它注解類型作說明怀浆。
@Target
作用:
用于描述注解的使用范圍谊囚,即被描述的注解可以用在什么地方
取值(ElementType):
1.CONSTRUCTOR:用于描述構(gòu)造器
2.FIELD:用于描述域
3.LOCAL_VARIABLE:用于描述局部變量
4.METHOD:用于描述方法
5.PACKAGE:用于描述包
6.PARAMETER:用于描述參數(shù)
7.TYPE:用于描述類怕享、接口(包括注解類型) 或enum聲明
使用范例:
/**
* Created by Fuyuan on 2016/6/15.
*
* @Target用于表示注解應(yīng)用的范圍
* ElementType中包含方法、接口镰踏、包函筋、注解、類奠伪、構(gòu)造方法等等
*
* 這里指定自定義注解TestAnnotation只能被聲明在方法上和成員變量上
*/
@Target({ElementType.METHOD, ElementType.FIELD})
public @interface TestTargetAnnotation {
}
@Retention
作用:
@Retention 用于說明注解的保留期跌帐,表示需要在什么級(jí)別保存該注釋信息,用于描述注解的生命周期(即:被描述的注解在什么范圍內(nèi)有效)
取值(RetentionPoicy):
l SOURCE:在源文件中有效(即源文件保留)
l CLASS:在class文件中有效(即class保留绊率,在加載到JVM虛擬機(jī)時(shí)丟棄)
l RUNTIME:在運(yùn)行時(shí)有效(即運(yùn)行時(shí)保留谨敛,此時(shí)可以通過反射獲得定義在某個(gè)類上的所有注解)
默認(rèn)值在Class階段
@Override在SOURCE階段(給編譯器看的)
@SuppressWarning在SOURCE階段(給編譯器看)
@Deprecated在RUNTIME階段
使用范例:
/**
* Created by Fuyuan on 2016/6/15.
*
* 注解的生命周期分為三個(gè)階段:
* java源文件、class文件滤否、內(nèi)存中的字節(jié)碼
* 對(duì)應(yīng)的@Retention元注解:
* RetentionPolicy.SOURCE脸狸、RetentionPolicy.CLASS、RetentionPolicy.RUNTIME
*
* 默認(rèn)值在Class階段
* @Override在SOURCE階段(給編譯器看的)
* @SuppressWarning在SOURCE階段(給編譯器看)
* @Deprecated在RUNTIME階段(調(diào)進(jìn)內(nèi)存后掃描二進(jìn)制碼來查看方法藐俺,所以是RUNTIME)
*
* 這里指定自定義注解的聲明生命為運(yùn)行時(shí)保留
*/
@Retention(RetentionPolicy.RUNTIME)
public @interface TestRetentionAnnotation {
}
@Documented
@Documented 注解用于生成文檔的時(shí)候炊甲,帶有@Documented的注解會(huì)被顯示在文檔中。
@Inherited
@Inherited 表示父類的注解可以被子類繼承紊搪, 前提是Retention必須是RUNTIME的。
@Inherited 元注解是一個(gè)標(biāo)記注解全景,@Inherited闡述了某個(gè)被標(biāo)注的類型是被繼承的耀石。
如果一個(gè)使用了@Inherited修飾的注解類型被用于一個(gè)class,則這個(gè)注解將被用于該class的子類爸黄。
注意:@Inherited annotation類型是被標(biāo)注過的class的子類所繼承滞伟。類并不從它所實(shí)現(xiàn)的接口繼承annotation,方法并不從它所重載的方法繼承annotation炕贵。
當(dāng)@Inherited annotation類型標(biāo)注的annotation的Retention是RetentionPolicy.RUNTIME梆奈,則反射API增強(qiáng)了這種繼承性。如果我們使用java.lang.reflect去查詢一個(gè)@Inherited annotation類型的annotation時(shí)称开,反射代碼檢查將展開工作:檢查class和其父類亩钟,直到發(fā)現(xiàn)指定的annotation類型被發(fā)現(xiàn),或者到達(dá)類繼承結(jié)構(gòu)的頂層鳖轰。
簡而言之:
@Inherited表示父類的注解可以被子類繼承清酥,但是這個(gè)可被繼承的前提是注解的生命周期是運(yùn)行時(shí)注解,且@Inherited代表的是子類可以繼承父類類級(jí)別的注解蕴侣,父類的方法如果被子類重寫焰轻,子類不繼承父類方法上的注解。
范例:
定義一個(gè)注解昆雀,指明直接是@Inherited的:TestInheritedAnnotation.java
/**
* Created by Fuyuan on 2016/6/15.
*
* @Inherited 表示父類的注解可以被子類繼承辱志,
*/
@Target({ElementType.METHOD, ElementType.FIELD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface TestInheritedAnnotation {
String value();
}
創(chuàng)建一個(gè)父類蝠筑,在父類的方法、抽象方法揩懒、類上都使用了自定義的@Inherited的注解:Parent.java
@TestInheritedAnnotation("I'm parent")
public abstract class Parent {
@TestInheritedAnnotation("I'm parent method1")
public void method1() {
System.out.println(" Parent method1......");
}
@TestInheritedAnnotation("I'm parent method2")
public void method2() {
System.out.println("Parent method2......");
}
@TestInheritedAnnotation("I'm parent absMethod")
public abstract void absMethod();
}
定義一個(gè)子類什乙,繼承Parent,實(shí)現(xiàn)其抽象方法absMethod旭从,重寫其普通方法method1:Child.java
public class Child extends Parent{
@Override
public void method1() {
super.method1();
}
@Override
public void absMethod() {
System.out.println("子類實(shí)現(xiàn)抽象父類的抽象方法absMethod");
}
}
寫一個(gè)測(cè)試方法稳强,判斷Child上面的各個(gè)方法和類上是否有我們的自定義注解@ TestInheritedAnnotation:
/**
* 測(cè)試@Inherited注解
*/
private static void testInheritedAnnotation() throws NoSuchMethodException {
// 1. 獲取子類的class
Class clazz=Child.class;
//2. 獲取子類重寫的父類抽象方法
Method method = clazz.getMethod("absMethod");
// 3. 判斷子類實(shí)現(xiàn)的父類的抽象方法是否繼承了注解
if(method.isAnnotationPresent(TestInheritedAnnotation.class)){
TestInheritedAnnotation inheritedAnnotation = method.getAnnotation(TestInheritedAnnotation.class);
System.out.println("子類實(shí)現(xiàn)的抽象方法繼承到父類抽象方法中的Annotation,其信息如下:");
System.out.println(inheritedAnnotation.value());
}else{
System.out.println("子類實(shí)現(xiàn)的抽象方法沒有繼承到父類抽象方法中的Annotation");
}
// 4. 判斷子類重寫父類的方法
Method methodOverride = clazz.getMethod("method1");
if(methodOverride.isAnnotationPresent(TestInheritedAnnotation.class)){
TestInheritedAnnotation inheritedAnnotation = methodOverride.getAnnotation(TestInheritedAnnotation.class);
System.out.println("子類method1方法繼承到父類method1方法中的Annotation,其信息如下:");
System.out.println(inheritedAnnotation.value());
}else{
System.out.println("子類method1方法沒有繼承到父類method1方法中的Annotation");
}
// 5. 判斷子類不重寫父類的方法
Method methodParent = clazz.getMethod("method2");
if(methodParent.isAnnotationPresent(TestInheritedAnnotation.class)){
TestInheritedAnnotation inheritedAnnotation = methodParent.getAnnotation(TestInheritedAnnotation.class);
System.out.println("子類method2方法繼承到父類method2方法中的Annotation,其信息如下:");
System.out.println(inheritedAnnotation.value());
}else{
System.out.println("子類method2方法沒有繼承到父類method2方法中的Annotation");
}
// 6. 判斷子類繼承自父類的類上的注解
if(Child.class.isAnnotationPresent(TestInheritedAnnotation.class)){
TestInheritedAnnotation annotation = (TestInheritedAnnotation) clazz.getAnnotation(TestInheritedAnnotation.class);
System.out.println("子類繼承到父類類上Annotation,其信息如下:");
System.out.println(annotation.value());
}else{
System.out.println("子類沒有繼承到父類類上Annotation");
}
}
打印日志:
去掉@ TestInheritedAnnotation中的@Inherited標(biāo)記,打印日志:
結(jié)論:
對(duì)于方法上的注解和悦,加不加@Inherited沒有影響退疫;但是對(duì)于類上的注解,加了@Inherited的鸽素,父類的注解會(huì)被子類繼承褒繁。
自定義注解
注解是一個(gè)特殊的類,他的格式同接口馍忽,只是在接口前加了”@“
定義注解格式:
public @interface 注解名 {定義體}
注解參數(shù)的可支持?jǐn)?shù)據(jù)類型:
l 所有基本數(shù)據(jù)類型(int,float,boolean,byte,double,char,long,short)
l String類型
l Class類型
l enum類型
l Annotation類型
l 以上所有類型的數(shù)組
注意:
l 只能用public或默認(rèn)(default)這兩個(gè)訪問權(quán)修飾棒坏。
例如:String value();這里把方法設(shè)為defaul默認(rèn)類型;
l 參數(shù)成員只能用基本類型byte,short,char,int,long,float,double,boolean八種基本數(shù)據(jù)類型和 String,Enum,Class,annotations等數(shù)據(jù)類型,以及這一些類型的數(shù)組遭笋。
例如:String value();這里的參數(shù)成員就為String;
l 如果只有一個(gè)參數(shù)成員, 方法名需要定義成value,后加小括號(hào)坝冕。
簡而言之:
l 當(dāng)只有一個(gè)變量的時(shí)候,方法名最好定義成value瓦呼。
原因:
假如方法名定義成value喂窟,那么用的時(shí)候可以直接@注解(“value值”);
假如方法名定義的其他的央串,比如name()磨澡,那么用的時(shí)候必須寫成key-value形式:@注解(name = “value值”)
l 當(dāng)多個(gè)的時(shí)候可以隨便定義。
l 注解可以有默認(rèn)值质和。
自定義注解范例:@CustomAnnotation
**
* 自定義注解
*
* 假如方法名定義成value稳摄,那么用的時(shí)候可以直接@注解(“value值”);
* 假如方法名定義的其他的饲宿,比如name()厦酬,那么用的時(shí)候必須寫成key -value形式:@注解(name = “value值”)
*/
public @interface CustomAnnotation {
/** 假如注解中只有一個(gè)屬性,建議名字定義成value */
// String value();
/** 為注解的屬性定義一個(gè)默認(rèn)值 */
String name() default "zhangsan";
int age();
}
使用范例:
@CustomAnnotation(age = 25)
private String custom = "hello";
注解的處理器
l <T extends Annotation> T getAnnotation(Class<T>annotationClass): 返回改程序元素上存在的瘫想、指定類型的注解弃锐,如果該類型注解不存在,則返回null殿托。
l Annotation[] getAnnotations():返回該程序元素上存在的所有注解霹菊。
l boolean is AnnotationPresent(Class<?extends Annotation>annotationClass):判斷該程序元素上是否包含指定類型的注解,存在則返回true,否則返回false.
l Annotation[] getDeclaredAnnotations():返回直接存在于此元素上的所有注釋旋廷。與此接口中的其他方法不同鸠按,該方法將忽略繼承的注釋。(如果沒有注釋直接存在于此元素上饶碘,則返回長度為零的一個(gè)數(shù)組)目尖。該方法的調(diào)用者可以隨意修改返回的數(shù)組;這不會(huì)對(duì)其他調(diào)用者返回的數(shù)組產(chǎn)生任何影響扎运。
使用注解定義網(wǎng)絡(luò)框架
我們?cè)诎l(fā)送網(wǎng)絡(luò)請(qǐng)求的時(shí)候瑟曲,需要知道請(qǐng)求是GET請(qǐng)求還是POST請(qǐng)求,請(qǐng)求的URL是什么豪治。這里我們通過一個(gè)簡單的自定義網(wǎng)絡(luò)框架來看一下注解是如何被應(yīng)用的洞拨。
定義注解,用于標(biāo)識(shí)網(wǎng)絡(luò)訪問的請(qǐng)求方式:RequestMethod.java
/**
* 網(wǎng)絡(luò)請(qǐng)求方法: GET or POST
*/
@Target(ElementType.METHOD) // 限制使用位置:方法體
@Retention(RetentionPolicy.RUNTIME) // 生命周期:運(yùn)行時(shí)
@Documented // 顯示在文檔中
public @interface RequestMethod {
/** 定義枚舉類负拟,限制網(wǎng)絡(luò)請(qǐng)求的方法僅POST和GET這2種 */
enum Method{GET, POST}
/** 默認(rèn)請(qǐng)求方式為GET請(qǐng)求 */
Method value() default Method.GET;
}
定義注解烦衣,用于標(biāo)識(shí)網(wǎng)絡(luò)訪問的URL地址:RequestURL.java
/**
* 網(wǎng)絡(luò)請(qǐng)求要訪問的URL地址
*/
@Target(ElementType.METHOD) // 限制使用位置:方法體
@Retention(RetentionPolicy.RUNTIME) // 生命周期:運(yùn)行時(shí)
@Documented // 顯示在文檔中
public @interface RequestURL {
/** 默認(rèn)的訪問網(wǎng)絡(luò)的URL地址為"" */
String value() default "";
}
寫一個(gè)簡單的網(wǎng)絡(luò)框架,用于解析注解掩浙,并發(fā)送網(wǎng)絡(luò)請(qǐng)求花吟,并返回請(qǐng)求的結(jié)果:HttpUtil.java
/**
* 網(wǎng)絡(luò)請(qǐng)求的工具類
*/
public class HttpUtil {
/**
* 解析并執(zhí)行網(wǎng)絡(luò)請(qǐng)求
* @param object 發(fā)起網(wǎng)絡(luò)請(qǐng)求的類
*/
public static String parseRequest(Object object) throws IOException {
// 網(wǎng)絡(luò)請(qǐng)求返回的結(jié)果
String result = "";
// 1. 獲取Class
Class clazz = object.getClass();
// 2. 獲取全部的公有方法
Method[] methods = clazz.getMethods();
// 3. 遍歷所有的方法,尋找哪個(gè)方法身上有@RequestURL和@RequestMethod
for (Method method : methods) {
// 4. 先判斷是否有@RequestURL厨姚,獲取要訪問的URL地址衅澈;
// 若獲取不到URL地址,不需要再往后解析了
if (method.isAnnotationPresent(RequestURL.class)) {
// 獲取URL地址
RequestURL annotationUrl = method.getAnnotation(RequestURL.class);
String url = annotationUrl.value();
// 如果URL地址為空谬墙,那么也不需要再往后解析了
if (!TextUtils.isEmpty(url)) {
// 5. 判斷是否有@RequestMethod今布,獲取請(qǐng)求方法
if (method.isAnnotationPresent(RequestMethod.class)) {
// 獲取訪問的方法
RequestMethod annotationMethod = method.getAnnotation(RequestMethod.class);
RequestMethod.Method requestMethod = annotationMethod.value();
if (requestMethod.equals(RequestMethod.Method.GET)) {
// 發(fā)送GET請(qǐng)求
result = get(url);
} else {
// 發(fā)送POST請(qǐng)求
result = post(url);
}
}
}
}
}
return result;
}
/**
* 發(fā)送GET請(qǐng)求
* @param url 要請(qǐng)求的URL地址
*/
public static String get(String url) throws IOException {
// 借助okHttp發(fā)送網(wǎng)絡(luò)請(qǐng)求
Request request = new Request.Builder().url(url).build();
Call call = new OkHttpClient().newCall(request);
// 發(fā)送同步請(qǐng)求
Response response = call.execute();
return response.body().string();
}
/**
* 發(fā)送POST請(qǐng)求
* @param url 要請(qǐng)求的URL地址
*/
public static String post(String url) throws IOException {
RequestBody requestBody = new FormEncodingBuilder().add("key", "value").build();
// 借助okHttp發(fā)送網(wǎng)絡(luò)請(qǐng)求
Request request = new Request.Builder().post(requestBody).url(url).build();
Call call = new OkHttpClient().newCall(request);
// 發(fā)送同步請(qǐng)求
Response response = call.execute();
return response.body().string();
}
}
寫一個(gè)測(cè)試Activity測(cè)試我們的框架是否好用:MainActivity.java
public class MainActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
loadNet();
}
@RequestMethod(RequestMethod.Method.GET)
@RequestURL("http://192.168.191.1:8080/testjson.json")
public void loadNet() {
// 開啟線程訪問網(wǎng)絡(luò)
new Thread(){
@Override
public void run() {
try {
String request = HttpUtil.parseRequest(MainActivity.this);
Log.e("MainActivity", "========網(wǎng)絡(luò)請(qǐng)求返回結(jié)果========:" + request);
} catch (IOException e) {
e.printStackTrace();
}
}
}.start();
}
}
執(zhí)行結(jié)果:
Xutils injectView實(shí)現(xiàn)原理
使用場(chǎng)景:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
@ViewInject(id = R.id.button1, clickable = true)
private Button button1;
@ViewInject(id = R.id.button2)
private Button button2;
……
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AnnotateUtils.injectView(this);
}
……
}
public static void injectViews(Object object, View sourceView){
Field[] fields = object.getClass().getDeclaredFields();
for (Field field : fields){
ViewInject viewInject = field.getAnnotation(ViewInject.class);
if(viewInject != null){
int viewId = viewInject.id();
boolean clickable = viewInject.clickable();
if(viewId != -1){
try {
field.setAccessible(true);
field.set(object, sourceView.findViewById(viewId));
if(clickable == true){
sourceView.findViewById(viewId).setOnClickListener((View.OnClickListener) (object));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
動(dòng)態(tài)代理
代理根據(jù)運(yùn)行和編譯時(shí)期,分為靜態(tài)代理和動(dòng)態(tài)代理芭梯。
如果編譯時(shí)存在的話是靜態(tài)代理险耀,所謂靜態(tài)也就是在程序運(yùn)行前就已經(jīng)存在代理類的字節(jié)碼文件弄喘,代理類和委托類的關(guān)系在運(yùn)行前就確定了玖喘。
如果運(yùn)行時(shí)存在的則是動(dòng)態(tài)代理,動(dòng)態(tài)代理之所以稱為動(dòng)態(tài)蘑志,是因?yàn)榇眍愂窃谶\(yùn)行時(shí)由Proxy類產(chǎn)生的累奈,這就大大減少了需要我們手工設(shè)計(jì)代理類的數(shù)量。
動(dòng)態(tài)代理的應(yīng)用場(chǎng)景
AOP即Aspect orientedprogram急但,面向切面的編程
系統(tǒng)中存在交叉業(yè)務(wù)澎媒,一個(gè)交叉業(yè)務(wù)就是要切入到系統(tǒng)中的一個(gè)方面
例如:
StudentService類用于處理學(xué)生信息
CourseService類用于處理課程信息
MiscService類用于處理教室信息
這三個(gè)類都有安全、事務(wù)波桩、日志的功能戒努,這三個(gè)功能貫穿到好多個(gè)模塊中,所以镐躲,它們就是交叉業(yè)務(wù)储玫。
交叉業(yè)務(wù)的編程問題即為面向方面的編程侍筛,AOP的目標(biāo)就是要使交叉業(yè)務(wù)模塊化。
所謂模塊化撒穷,就是將這些交叉業(yè)務(wù)只寫一份匣椰,應(yīng)用到所有需要的地方,而不是每個(gè)需要的地方都寫一份端礼。這就需要使用代理技術(shù)禽笑,代理技術(shù)是實(shí)現(xiàn)AOP功能的核心和關(guān)鍵。
要為系統(tǒng)中的各種接口的類增加代理功能蛤奥,如果全部采用靜態(tài)代理方式佳镜,寫成百上千的代理類,不符合實(shí)際喻括。
JVM可以在運(yùn)行期動(dòng)態(tài)的生成出類的字節(jié)碼邀杏,這種動(dòng)態(tài)生成的類往往被用作代理類,即動(dòng)態(tài)代理類唬血。
JVM生成的動(dòng)態(tài)類必須實(shí)現(xiàn)了一個(gè)或者多個(gè)接口望蜡,以便讓JVM知道他都實(shí)現(xiàn)了什么方法,需要為這些方法來生成代理拷恨。所以脖律,JVM生成的動(dòng)態(tài)類只能用于具有相同接口的目標(biāo)類的代理。
如何實(shí)現(xiàn)動(dòng)態(tài)代理
步驟:
定義接口
定義委托類腕侄,委托類需要實(shí)現(xiàn)接口
實(shí)現(xiàn)InvocationHandler接口小泉,定義委托類和代理類的橋梁
生成代理類Proxy.newProxyInstance();
調(diào)用代理類的方法
定義接口StudentInterface.java
public interface StudentInterface {
public void study();
public void play();
public void sleep();
}
定義委托類Student,委托類需要實(shí)現(xiàn)接口StudentInterface:
/**
* 委托類
*/
public class Student implements StudentInterface {
@Override
public void study() {
System.out.println("==== Student ==== study ====");
}
@Override
public void play() {
System.out.println("==== Student ==== play ====");
}
@Override
public void sleep() {
System.out.println("==== Student ==== sleep ====");
}
}
實(shí)現(xiàn)InvocationHandler接口冕杠,定義委托類和代理類的橋梁:StudentProxyHandler.java
/**
* 委托類Student和代理類的橋梁
*/
public class StudentProxyHandler implements InvocationHandler {
/** 委托類對(duì)象 */
private Object targetObj;
public StudentProxyHandler(Object targetObj) {
this.targetObj = targetObj;
}
/**
* @param proxy 代理類的對(duì)象
* @param method 要執(zhí)行的方法
* @param args 要執(zhí)行的方法的參數(shù)
* @return 方法執(zhí)行后的返回值
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 代理類可以過濾方法微姊,必須我們控制play方法不被執(zhí)行
if (method.getName().equals("play")) {
System.out.println("====StudentProxy 攔截了 play方法 ==========");
return null;
}
// 代理類可以增強(qiáng)方法仓技,必須在執(zhí)行方法前打印Log信息
System.out.println("====打印了一條log揍异,代理類有方法被執(zhí)行了 ==========");
// 執(zhí)行委托類的對(duì)應(yīng)方法
Object result = method.invoke(targetObj, args);
return result;
}
}
通過Proxy.newProxyInstance();生成代理類對(duì)象:
// 創(chuàng)建委托類對(duì)象
Student student = new Student();
// 生成代理類對(duì)象
StudentInterface proxyInstance = (StudentInterface) Proxy.newProxyInstance(StudentInterface.class.getClassLoader(),
new Class[]{StudentInterface.class}, new StudentProxyHandler(student));
調(diào)用代理類的方法:
// 調(diào)用代理類的方法
proxyInstance.study();
proxyInstance.sleep();
proxyInstance.play();
執(zhí)行結(jié)果:
線程池
線程池的優(yōu)點(diǎn)
l 避免線程的創(chuàng)建和銷毀帶來的性能開銷
l 避免大量的線程間因互相搶占系統(tǒng)資源導(dǎo)致的阻塞現(xiàn)象
l 能夠?qū)€程進(jìn)行簡單的管理并提供定時(shí)執(zhí)行扬跋、間隔執(zhí)行等功能
注意:
線程池在使用的時(shí)候最好搞成靜態(tài)的黑毅,因?yàn)榫€程池比較消耗資源危融。
線程池的概念
Java里面線程池的頂級(jí)接口是Executor瓤檐,不過真正的線程池接口是 ExecutorService憔狞, ExecutorService 的默認(rèn)實(shí)現(xiàn)是 ThreadPoolExecutor族淮;普通類 Executors 里面調(diào)用的就是 ThreadPoolExecutor凳干。
Executors提供四種線程池:
l newCachedThreadPool
newCachedThreadPool 得到的是一個(gè)可根據(jù)需要?jiǎng)?chuàng)建新線程的線程池晴裹。
特點(diǎn):
? 如果有緩存的線程可用,優(yōu)先用緩存的救赐;如果沒現(xiàn)有線程可用涧团,則創(chuàng)建一個(gè)新的線程加入到池中。
? 終止并從緩存中移除那些已有 60 秒鐘未被使用的線程。因此泌绣,長時(shí)間保持空閑的線程池不會(huì)使用任何資源喳瓣。
l newSingleThreadExecutor
newSingleThreadExecutor 創(chuàng)建是一個(gè)單線程池,也就是該線程池只有一個(gè)線程在工作赞别,所有的任務(wù)是串行執(zhí)行的畏陕,如果這個(gè)唯一的線程因?yàn)楫惓=Y(jié)束,那么會(huì)有一個(gè)新的線程來替代它仿滔,此線程池保證所有任務(wù)的執(zhí)行順序按照任務(wù)的提交順序執(zhí)行惠毁。
l newFixedThreadPool
newFixedThreadPool創(chuàng)建固定大小的線程池,每次提交一個(gè)任務(wù)就創(chuàng)建一個(gè)線程崎页,直到線程達(dá)到線程池的最大大小鞠绰,線程池的大小一旦達(dá)到最大值就會(huì)保持不變,如果某個(gè)線程因?yàn)閳?zhí)行異常而結(jié)束飒焦,那么線程池會(huì)補(bǔ)充一個(gè)新線程蜈膨。
l newScheduledThreadPool
newScheduledThreadPool 創(chuàng)建一個(gè)大小無限的線程池,此線程池支持定時(shí)以及周期性執(zhí)行任務(wù)的需求牺荠。
線程池相關(guān)構(gòu)造參數(shù)含義
/**
* corePoolSize:
* 線程池的核心線程數(shù)翁巍,一般情況下不管有沒有任務(wù)都會(huì)一直在線程池中一直存活,
* 只有在 ThreadPoolExecutor 中的方法 allowCoreThreadTimeOut(boolean value) 設(shè)置為 true 時(shí),
* 閑置的核心線程會(huì)存在超時(shí)機(jī)制休雌,如果在指定時(shí)間沒有新任務(wù)來時(shí)灶壶,核心線程也會(huì)被終止,
* 而這個(gè)時(shí)間間隔由 keepAliveTime 屬性指定杈曲。
*
* maximumPoolSize:
* 線程池所能容納的最大線程數(shù)驰凛,當(dāng)活動(dòng)的線程數(shù)達(dá)到這個(gè)值后,后續(xù)的新任務(wù)將會(huì)被阻塞担扑。
*
* keepAliveTime:
* 控制線程閑置時(shí)的超時(shí)時(shí)長恰响,超過則終止該線程。
*
* unit:
* 用于指定 keepAliveTime 參數(shù)的時(shí)間單位涌献。
*
* workQueue:
* 線程池的任務(wù)隊(duì)列胚宦,通過線程池的 execute(Runnable command) 方法會(huì)將任務(wù) Runnable 存儲(chǔ)在隊(duì)列中。
*
* threadFactory:
* 線程工廠洁奈,它是一個(gè)接口间唉,用來為線程池創(chuàng)建新線程的
*/
ThreadPoolExecutor executor = new ThreadPoolExecutor(3,//核心線程數(shù)
5, //最大線程數(shù)
5, //線程空閑時(shí)間存活時(shí)間
TimeUnit.SECONDS, //存活時(shí)間的單位
new LinkedBlockingQueue<Runnable>(), //任務(wù)隊(duì)列
Executors.defaultThreadFactory());//線程產(chǎn)生的工廠
線程池關(guān)閉的API
ThreadPoolExecutor 提供了兩個(gè)方法绞灼,用于線程池的關(guān)閉:
l shutdown()
不會(huì)立即的終止線程池利术,而是要等所有任務(wù)緩存隊(duì)列中的任務(wù)都執(zhí)行完后才終止,但再也不會(huì)接受新的任務(wù)低矮。
l shutdownNow()
立即終止線程池印叁,并嘗試打斷正在執(zhí)行的任務(wù),并且清空任務(wù)緩存隊(duì)列,返回尚未執(zhí)行的任務(wù)轮蜕。
線程池的工作原理
當(dāng)任務(wù)來了之后昨悼,如果核心線程數(shù)沒有滿,那么就使用核心線程數(shù)跃洛,如果核心線程數(shù)滿了率触,那么就將任務(wù)放入任務(wù)隊(duì)列中,如果任務(wù)隊(duì)列也滿了汇竭,就開始使用最大線程數(shù)葱蝗,如果最大線程數(shù)也使用滿了,會(huì)拋出異常细燎,拒絕任務(wù)两曼。
依賴注入
什么是依賴
如果在 Class A 中,有Class B 的實(shí)例玻驻,則稱 Class A 對(duì) ClassB 有一個(gè)依賴悼凑。
存在依賴的例子:
/**
* Human對(duì)Father有一個(gè)依賴
*/
public class Human {
// Human中使用了Father的實(shí)例,產(chǎn)生了對(duì)Father依賴
private Father father;
/**
* 主動(dòng)初始化依賴璧瞬,耦合嚴(yán)重
*/
public Human() {
father = new Father();
}
}
存在的問題:
l 如果現(xiàn)在要改變 father 生成方式户辫,如需要用new Father(String name)初始化 father,需要修改 Human 代碼嗤锉;
l 如果想測(cè)試不同 Father 對(duì)象對(duì) Human 的影響會(huì)變得很困難寸莫,因?yàn)?father 的初始化被寫死在了 Human 的構(gòu)造函數(shù)中;
l 如果new Father()過程非常緩慢档冬,單測(cè)時(shí)我們希望用已經(jīng)初始化好的 father 對(duì)象也很困難膘茎。
問題產(chǎn)生原因:
兩個(gè)類不夠獨(dú)立,耦合嚴(yán)重酷誓。
解決方案:由外界來提供依賴的實(shí)例
/**
* Human對(duì)Father有一個(gè)依賴
*/
public class Human {
private Father father;
/**
* 由外界來注入來傳入依賴披坏,解耦
*/
public Human(Father father) {
this.father = father;
}
}
類似這種非自己主動(dòng)初始化依賴,而通過外部來傳入依賴的方式盐数,我們就稱為依賴注入棒拂。
什么是依賴注入
依賴注入的目的是為了使類與類之間解耦合,提高系統(tǒng)的可擴(kuò)展性和可維護(hù)性玫氢。
Java中一般都是通過注解 + 反射的方式來實(shí)現(xiàn)依賴注入的帚屉。
依賴注入示例:
通過依賴注入,為類的成員變量賦值漾峡。
定義注解類攻旦,用于生命成員變量的值:StringAnnotation.java
/**
* 自定義注解,用于為成員變量賦值
*/
@Target(ElementType.FIELD) // 作用范圍:成員變量
@Retention(RetentionPolicy.RUNTIME) // 生命周期:運(yùn)行時(shí)
@Documented // 在文檔中顯示
public @interface StringAnnotation {
String value();
定義解析注解并為成員變量賦值的注入工具類:ParseAnnocation.java
/**
* 注入工具類
*/
public class ParseAnnocation {
public static void parseAnnocation(Object object) throws Exception{
//1.獲取字節(jié)碼文件
Class c=object.getClass();
//2.獲取成員變量
Field[] fields = c.getFields();
//3.遍歷成員變量
for (Field field : fields) {
// 4. 找到帶有StringAnnotation注解的成員變量
if(field.isAnnotationPresent(StringAnnotation.class)){
StringAnnotation annotation = field.getAnnotation(StringAnnotation.class);
//5. 獲取注解中的值
String value=annotation.value();
// 6. 為成員變量賦值
field.set(object, value);
}
}
}
}
使用注解 + 注入工具類生逸,測(cè)試注入結(jié)果:
/**
* 測(cè)試依賴注入
*/
public class Dependency {
@StringAnnotation("張三")
public String name = "猜我是誰";
public static void main(String[] args) throws Exception {
Dependency dependency = new Dependency();
// 解析注解牢屋,為成員變量賦值
ParseAnnocation.parseAnnocation(dependency);
// 打印注入后的值
System.out.println("======Dependency name =======" + dependency.name);
}
}
運(yùn)行結(jié)果: