1. 程式人生 > >高新技術---> 註解、類載入器

高新技術---> 註解、類載入器

第一  註解

一、概述:

1、註解是JDK1.5出現的新特性

2、註解相當於一種標記,在程式中加了註解就等於為程式打上了某種標記,沒加,則沒有某種標記。

3、加了註解以後,java編譯器、開發工具和其他應用程式就可以用反射來了解自己的類及各種元素上有無何種標記,有什麼標記,就會做出相應的處理。

4、註解可以加在包、類、欄位、方法、方法引數,以及區域性變數上等等。

5、在java.lang包下的annotation包中提供了最基本的註解。

6、格式:@註解類名()。如果有屬性,則在括號中加上屬性名(可省略)和屬性值。

二、java中三種最基本的註解:

1、@SuppressWarning(”deprecation”)--->壓制警告

SupressWarning是告知編譯器或開發工具等提示指定的編譯器警告;

”deprecation”是告知具體的資訊即方法已過時。

2、@Deprecated--->提示成員等已經過時,不再推薦使用。

原始碼標記@Deprecated是在JDK1.5中作為內建的annotation引入的,用於表明類(class)、方法(method)、欄位(field)已經不再推薦使用。

3、@Override--->提示覆蓋(父類方法)

加上此註解,,可對自己類中的方法判斷是否是要覆蓋的父類的方法,典型的例子即在集合中覆蓋equals(Object obj)方法,其中的引數型別必須是Object,才能被覆蓋,若不是,加上此註解就會提示警告。

三、註解類:

1、定義格式:@interface 名稱{statement}

2、元註解(註解的註解)

一個註解有其生命週期(Retetion)和存放的位置(Taget),這就可以通過元註解說明。

1)Retetion:用於說明註解保留在哪個時期,載入定義的註解之上。

①一個註解的宣告週期包含:

java源程式--(javac)-->class檔案--(類載入器)-->記憶體中的位元組碼

第一、當再源程式上加了註解,javac將java源程式編譯為class檔案,可能會把源程式中的一些註解去掉,進行相應的處理操作,當我們拿到源程式的時候,就看不到這些註解了。

第二、假設javac把這些註解留在了源程式中(或者說留在了class檔案中),當執行此class檔案的時候,用類載入器將class檔案調入記憶體中,此時有轉換的過程,即把class檔案中的註解是否保留下來也不一定。

注意:class檔案中不是位元組碼,只有把class檔案中的內部載入進記憶體,用類載入器載入處理後(進行完整的檢查等處理),最終得到的二進位制內容才是位元組碼。

②Reteton(列舉類)取值:

Retetion.Policy.SOURSE:java原始檔時期,如@Overried和@SuppressWarning

Retetion.Policy.CLASS: class檔案時期(預設階段)

Retetion.Policy.RUNTIME:執行時期,如@Deprecated

2)Taget:用於說明註解存放在哪些成分上,預設值是任何元素

其值可設定為列舉類ElementType類中的任何一個,包括:包、欄位、方法、方法引數、構造器、類等值。取值為:

PACKAGE(包宣告)

FIELD(欄位宣告)

ANNOTATION_TYPE(註釋型別宣告)

CONSIRUCTOR(構造器宣告)

METHOD(方法宣告)

PARAMETER(引數宣告)

TYPE(類、介面(包含註釋型別)或列舉宣告)

LOCAL_VARIABLE(區域性變數宣告)

注意:其中代表類的值是TYPE。因為class、enum、interface和@interface等都是屬於Type的。不可用CLASS表示。

四、為註解增加基本屬性

1、屬性:

一個註解相當於一個胸牌,但僅通過胸牌還不足以區別帶胸牌的兩個人,這時就需要給胸牌增加一個屬性來區分,如顏色等。

2、定義格式:public abstract String color();//為註解新增屬性,也可定義為String color();預設補齊public abstract

定義預設格式:String value() default ”ignal”;也就是給其屬性一個預設值

3、應用:直接在註解的括號中新增自身的屬性,如:

@MyAnnotation(color=”red”)

當屬性只有一個value這時可以不用寫value=“..”,可以直接寫成“..”。如:@SuppressWarnings("deprecation")

五、為註解增加高階屬性

1、可以為註解增加的高階屬性的返回值型別有:

1)八種基本資料型別   

2)String型別 

 3)Class型別

4)列舉型別   

5)註解型別   

6)前五種型別的陣列

2、陣列型別的屬性:

定義:int[]arrayArr()

應用:@MyAnnotation(arrayArr={2,3,4})

注:若陣列屬性中只有一個元素(或重新賦值為一個元素),這時屬性值部分可省略大括號。

3、列舉型別的屬性:

假設定義了一個列舉類TraffLamp,它是EnumTest的內部類,其值是交通燈的三色。

定義:EnumTest.TrafficLamplamp();

應用:@MyAnnotation(lamp=EnumTestTrafficLamp.GREEN)

4、註解型別的屬性:

假定有個註解類:MetaAnnotation,其中定義了一個屬性:String value()

定義:MetaAnnotationannotation() default @MetaAnnotation(”xxx”);

應用:@MyAnnotation([email protected](”yyy”))  --> 可重新賦值

可認為上面的@MetaAnnotation是MyAnnotation類的一個例項物件,同樣可以認為上面的@MetaAnnotation是MetaAnnotation類的一個例項物件,呼叫:

MetaAnnotation ma =MyAnnotation.annotation();

System.out.println(ma.value());

5、Class型別的屬性:

定義:Class cls();

應用:@MyAnnotation(cls=ItcastAnnotion.class)

注:這裡的.class必須是已定義的類,或是已有的位元組碼物件

示例:

/*
 * 註解相當於一種標記,在程式上加了註解就等於給程式打上了某種標記,然後編譯器或開發工具就會採取相應的動作
 * 註解可以加在包、欄位、方法、方法的引數以及區域性變數上
 * 註解相當於一個你源程式中要呼叫的類,要在遠端中呼叫某個類,得先準備好註解類
 * 註解的返回值型別可以是基本資料型別(byte,short,int,long,float,double,char,boolean),String,Class,註解型別,還可以是以上型別的陣列型別
 */
@MyAnnotation([email protected]("hah"),color="red",value="abc",arrayAttr={1,2,3},lamp=TrafficLamp.GREEN)// 為註解設定屬性值
public class AnnotationTest {
	@MyAnnotation("abc")//只有一個value需要設定時可以省略value=
	public static void main(String[] args) {
		if (AnnotationTest.class.isAnnotationPresent(MyAnnotation.class)) {
			MyAnnotation annotation = AnnotationTest.class
					.getAnnotation(MyAnnotation.class);
			System.out.println(annotation.color());
			System.out.println(annotation.value());
			System.out.println(annotation.arrayAttr());
			System.out.println(annotation.lamp().nextLamp().name());
			System.out.println(annotation.annotationAttr().value());
		}
	}

	@SuppressWarnings("deprecation")
	// 註解,告訴編譯器,知道使用的方法已過時,不用提醒
	public void test() {
		System.runFinalizersOnExit(true);
	}

	@Deprecated
	// 註解方法已過時
	public void method() {
		System.out.println("hahah");
	}

	@Override
	public String toString() {
		return super.toString();
	}

}


@Retention(RetentionPolicy.RUNTIME)//元註解,儲存在整個執行時期。@Retention表示註解的生命週期
@Target({ElementType.TYPE,ElementType.METHOD})//指定此註解被用的地方,type是指類上
public @interface MyAnnotation {
	//設定預設屬性,也就是在呼叫註解時,可以不對其進行設定
	public abstract String color() default "blue";//為註解新增屬性,也可定義為String color();預設補齊public abstract
	String value();
	int[] arrayAttr() default 1;//當陣列中只有一個元素時,可以省略大括號
	EnumTest.TrafficLamp lamp() default EnumTest.TrafficLamp.YELLOW;
	MetaAnnotation annotationAttr() default @MetaAnnotation("metaannotation");
}


public @interface MetaAnnotation {
	String value();
}
public class EnumTest {
	
	public enum TrafficLamp {
		// RED,GREEN,YELLOW;
		// 子類實現父類的抽象方法
		RED(30) {
			@Override
			public TrafficLamp nextLamp() {
				return GREEN;
			}
		},
		GREEN(40) {
			@Override
			public TrafficLamp nextLamp() {
				return YELLOW;
			}
		},
		YELLOW(5) {
			@Override
			public TrafficLamp nextLamp() {
				return RED;
			}
		};
		
		private int time;
		//構造方法
		private TrafficLamp(int time){
			this.time = time;
		}

		public abstract TrafficLamp nextLamp();
	}

}

第二   類載入器

一、概述:

1、簡單的說,類載入器就是載入類的工具。當用到一個類是,java虛擬機器就需要類位元組碼載入進記憶體。

2、類載入器的作用:將.class檔案中的內容載入進記憶體進行處理,處理完後的結果就是位元組碼。

3、預設的類載入器:

①、Java虛擬機器中可安裝多個類載入器,系統預設的有三個主要的,每個類負責載入特定位置的類:BootStrap、ExtClassLoader、AppClassLoader

②、BootStrap--頂級類載入器:

類載入器本身也是Java類,因為它是Java類,本身也需要載入器載入,顯然必須有第一個類載入器而不是java類的,這正是BootStrap。它是巢狀在Java虛擬機器核心中的,已啟動即出現在虛擬機器中,是用c++寫的一段二進位制程式碼。所以不能通過java程式獲取其名字,獲得的只能是null。

4、Java虛擬機器中的所有類載入器採用子父關係的樹形結構進行組織。類載入是從上往下的這麼一種結構,也就是載入類時首先從父類開始查詢,父類沒有找到再交給子類,如果到了載入器的發起者位置都還沒有找到需要被載入的指定檔案,就會丟擲異常。

類載入器樹狀圖:

例項:

public static void main(String[] args) {
		System.out.println(Demo.class.getClassLoader().getClass().getName());
		// 類載入器為null,說明它是被BootStrap載入,BootStrap載入器內嵌在虛擬機器內,不需要被別的載入器載入
		System.out.println(System.class.getClassLoader());

//		System.out.println(new ClassLoaderAttachment().toString());

		ClassLoader loader = Demo.class.getClassLoader();
		while (loader != null) {
			System.out.println(loader.getClass().getName());
			loader = loader.getParent();
		}
	}

二、類載入器的委託機制:

1、載入類的方式

當Java虛擬機器要載入一個類時,到底要用哪個類載入器載入呢?

1)首先,當前執行緒的類載入器去載入執行緒中的第一個類。

2)若A引用類B(繼承或者使用了B),Java虛擬機器將使用載入類的類載入器來載入類B。

3)還可直接呼叫ClassLoader的LoaderClass()方法,來指定某個類載入器去載入某個類。

2、載入器的委託機制:每個類載入器載入類時,又先委託給上級類載入器。

每個ClassLoader本身只能分別載入特定位置和目錄中的類,但他們可以委託其他類的載入器去載入,這就是類載入器的委託模式,類載入器一級級委託到BootStrap類載入器,當BootStrap在指定目錄中沒有找到要載入的類時,無法載入當前所要載入的類,就會一級級返回子孫類載入器,進行真正的載入,每級都會先到自己相應指定的目錄中去找,有沒有當前的類;直到退回到最初的類裝載器的發起者時,如果它自身還未找到,未完成類的載入,那就報告ClassNoFoundException的異常。

簡單說,就是先由發起者將類一級級委託給BootStrap,從父級開始找,找到了直接返回,沒找到再返回給其子級找,直到發起者,再沒找到就報異常。

3、委託機制的優點:可以集中管理,不會產生多位元組碼重複的現象。

二、類載入器的委託機制:

1、載入類的方式

當Java虛擬機器要載入一個類時,到底要用哪個類載入器載入呢?

1)首先,當前執行緒的類載入器去載入執行緒中的第一個類。

2)若A引用類B(繼承或者使用了B),Java虛擬機器將使用載入類的類載入器來載入類B。

3)還可直接呼叫ClassLoader的LoaderClass()方法,來制定某個類載入器去載入某個類。

2、載入器的委託機制:每個類載入器載入類時,又先委託給上級類載入器。

每個ClassLoader本身只能分別載入特定位置和目錄中的類,但他們可以委託其他類的載入器去載入,這就是類載入器的委託模式,類載入器一級級委託到BootStrap類載入器,當BootStrap在指定目錄中沒有找到要載入的類時,無法載入當前所要載入的類,就會一級級返回子孫類載入器,進行真正的載入,每級都會先到自己相應指定的目錄中去找,有沒有當前的類;直到退回到最初的類裝載器的發起者時,如果它自身還未找到,未完成類的載入,那就報告ClassNoFoundException的異常。

簡單說,就是先由發起者將類一級級委託為BootStrap,從父級開始找,找到了直接返回,沒找到再返回給其子級找,直到發起者,再沒找到就報異常。

3、委託機制的優點:可以集中管理,不會產生多位元組碼重複的現象。

三、自定義類載入器

1、自定義的類載入器必須繼承抽象類ClassLoader,要覆寫其中的findClass(String name)方法,而不用覆寫loadClass()方法。

2、覆寫findClass(String name)方法的原因:

1)是要保留loadClass()方法中的流程,因為loadClass()中呼叫了findClass(String name)這個方法,此方法返回的就是去尋找父級的類載入器。

2)在loadClass()內部是會先委託給父級,當父級找到後就會呼叫findClass(String name)方法,而找不到時就會用子級的類載入器,再找不到就報異常了,所以只需要覆寫findClass方法,那麼就具有了實現用自定義的類載入器載入類的目的。

流程:

父級-->loadClass-->findClass-->得到Class檔案後轉化成位元組碼-->defind()。

看例項:

public class Demo {
	public static void main(String[] args) {
		System.out.println(Demo.class.getClassLoader().getClass().getName());
		// 類載入器為null,說明它是被BootStrap載入,BootStrap載入器內嵌在虛擬機器內,不需要被別的載入器載入
		System.out.println(System.class.getClassLoader());

//		System.out.println(new ClassLoaderAttachment().toString());

		ClassLoader loader = Demo.class.getClassLoader();
		while (loader != null) {
			System.out.println(loader.getClass().getName());
			loader = loader.getParent();
		}
		loadClass();
	}

	//呼叫自定義的載入器進行載入檔案
	public static void loadClass() {
		try {
			//如果父類有就有父類載入,如果父類沒有才交給子類,子類沒有就拋異常
			Class clazz = new MyClassLoder("mycypher")
					.loadClass("enhance.test.classloader.ClassLoaderAttachment");
			Date d = (Date) clazz.newInstance();// 不能用本類,要是用本來也就被編譯器編譯,造成父類載入,不能被自定義的載入器載入
			System.out.println(d);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}



//自定義載入器
public class MyClassLoder extends ClassLoader {

	private String classDir;

	public MyClassLoder() {
	}

	public MyClassLoder(String classDir) {
		this.classDir = classDir;
	}

	/*
	 * 對檔案進行加密
	 */
	public static void main(String[] args) throws Exception {
		// 通過執行時把引數傳遞給住函式,在eclipse中Run as-->Run Configurations
		String srcPath = args[0];// 獲取原路徑
		String destDir = args[1];// 獲取目的路徑

		FileInputStream fis = new FileInputStream(srcPath);
		String destFileName = srcPath.substring(srcPath.lastIndexOf('\\') + 1);
		String destPath = destDir + "\\" + destFileName;
		FileOutputStream fos = new FileOutputStream(destPath);

		cypher(fis, fos);
	}

	/*
	 * 加密,解密檔案
	 */
	private static void cypher(InputStream ins, OutputStream ops)
			throws IOException {
		int b = -1;
		while ((b = ins.read()) != -1) {
			ops.write(b ^ 0xff);// 0變1,1變0
		}
	}

	/*
	 * 重寫ClassLoader的findClass方法,定義自己載入類的方式 
	 * 其實這裡只是定義查詢檔案和得到位元組碼陣列,具體的載入細節還是有父類去實現
	 */
	@Override
	protected Class<?> findClass(String name) throws ClassNotFoundException {
		String classFileName = classDir + "\\" + name.substring(name.lastIndexOf('.') + 1) + ".class";
		try {
			FileInputStream fis = new FileInputStream(classFileName);
			ByteArrayOutputStream bos = new ByteArrayOutputStream();
			cypher(fis, bos);
			fis.close();
			byte[] bytes = bos.toByteArray();// 得到位元組碼陣列
			return defineClass(null, bytes, 0, bytes.length);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return super.findClass(name);
	}

}

public class ClassLoaderAttachment extends Date {//被載入的類

	@Override
	public String toString() {
		return "hi";
	}

}

補充:面試題

可不可以自己寫個類載入java.lang.System呢?

回答:第一、通常是不可以的,由於類載入器的委託機制,會先將System這個類一級級委託給最頂級的BootStrap,由於BootStrap在其指定的目錄中載入的是rt.jar中的類,且其中有System這個類,那麼就會直接載入自己目錄中的,也就是Java已經定義好的System這個類,而不會載入自定義的這個System。

第二、但是還是有辦法載入這個自定義的System類的,此時就不能交給上級載入了,需要用自定義的類載入器載入,這就需要有特殊的寫法才能去載入這個自定義的System類的。



相關推薦

高新技術---> 註解載入

第一  註解 一、概述: 1、註解是JDK1.5出現的新特性 2、註解相當於一種標記,在程式中加了註解就等於為程式打上了某種標記,沒加,則沒有某種標記。 3、加了註解以後,java編譯器、開發工具和其他應用程式就可以用反射來了解自己的類及各種元素上有無何種標記,有

Java高新技術第一篇 載入詳解

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

反射(ConstructorFieldMethod載入

一:什麼是反射 在認識反射之前,首先回顧下什麼是正向處理。如果我們需要例項化一個物件,首先需要匯入這個類的包,在這個包中找這個類: package CODE.反射; import java.util.Date; public class Fan { public static

JAVA多執行緒:JVM載入(自動載入雙親委託機制載入名稱空間執行時包的解除安裝等)

  Jvm提供了三大內建的類載入器,不同的類載入器負責將不同的類載入到記憶體之中 根載入器(Bootstrap ClassLoader) 是最頂層的載入器,是由C++編寫的,主要負責虛擬機器核心類庫的載入,如整個java.lang包,根載入器是獲取不到引用的,因此

深入JVM系列(三)之類載入載入雙親委派機制與常見問題

一.概述 定義:虛擬機器把描述類的資料從Class檔案載入到記憶體,並對資料進行校驗、轉換解析和初始化,最終形成可以被虛擬機器直接使用的java型別。類載入和連線的過程都是在執行期間完成的。 二.

檔案的結構JVM 的載入過程載入機制載入雙親委派模型

# 一、類檔案的結構 我們都知道,各種不同平臺的虛擬機器,都支援 “位元組碼 Byte Code” 這種程式儲存格式,這構成了 Java 平臺無關性的基石。甚至現在平臺無關性也開始演變出 “語言無關性” ,就是其他語言也可以執行在 Java 虛擬機器之上,比如現在的 Kotlin、Scala 等。

JAVA載入註解和動態代理

一.類載入器 1.什麼是類載入器,作用是什麼 類載入器就載入位元組碼檔案(.class) 2.類載入器的種類 類載入器有三種,不同載入器載入不同 BootStrap:引導類載入器:載入都是最基礎的檔案 ExtClassLoader:擴充套

Java載入( CLassLoader ) 死磕 12: 匯入 & 分類

JAVA類載入器 死磕系列 目錄 by   瘋狂創客圈 1.匯入1.1. 從class檔案的載入開始1.2. 什麼是類載入器2. JAVA類載入器分類2.1. 作業系統的環境變數2.2. Bootstrap ClassLoader(啟動類載入器)2.3. Extention ClassL

Java原始碼分析——ClassClassLoader解析(三) 載入實現自定義載入

    在這個系列的第一篇章就講解了Class類的獲取以及載入過程,但是並沒有提及具體的載入過程,在java中,載入一個類是通過ClassLoader類來執行的,也就是類載入器完成。java中所有的類,都必須載入進jvm中才能執行,這個載入的意思是

Java進階--08--註解反射位元組碼載入

一、註解(Annotation) 1、內建註解 註解與註釋:註解除了可以看,還能被其他的程式所讀取; 常用註解:@override、@deprecated--不建議使用的、@suppressWarining("all")--警告抑制 2、自定義註解 (1)註解關鍵字:

載入 註解

一:類載入器 類載入器:載入位元組碼檔案(.class) 類載入器分類:   獲取位元組碼檔案物件的3種方式:   1.類名.class   2.物件.getclass()   3.class.forName("包名.類名") 怎麼獲得類載入器(重點)??

JavaEE複習回顧十一: (基礎加強) 載入 && 註解 && 動態代理

參考文章: 1,動態代理——從一竅不通到恍然大悟 2,UML類圖與類的關係詳解   一,類載入器 1.1 什麼是類載入器,作用是什麼? 一個自己編寫的 Java 程式碼原始檔,起執行的過程主要分為三個階段。 第一個階段: SOURCE 級別,即原始碼階段,已

載入|註解|動態代理

一、類載入器 1.什麼是類載入器,作用是什麼? 類載入器就載入位元組碼檔案(.class)   2.類載入器的種類   類載入器有三種,不同類載入器載入不同的 1)BootStrap:引導類載入器:載入都是最基礎的檔案 2)ExtClassLoader:擴充套件類載入器:載入都是基

載入 註解

一:類載入器 類載入器:載入位元組碼檔案(.class) 類載入器分類: 獲取位元組碼檔案物件的3種方式:   1.類名.class   2.物件.getclass()   3.class.forName("包名.類名") 怎麼獲得類載入器(重點)??

載入反射,反射的應用例項(泛型擦除和配置檔案)

類載入器 1.1類的載入 當程式要使用某個類時,如果該類還未被載入到記憶體中,則系統會通過載入,連線,初始化三步來實現對這個類進行初始化。   1.1.1載入 指將class檔案讀入記憶體,併為之建立一個Class物件。 任何類被使用時系統都會建立一個Class物件(位元組碼檔案物件,建

黑馬程式設計師--java高新技術 26--javaBean,泛型,載入,代理spring小框架

---------------------- ASP.Net+Android+IO開發S、.Net培訓、期待與您交流! ---------------------- /*設計模式: Singleton: 單例模式 Factorty: 工廠模式 Iterator: 迭代器模

載入 - 載入連線與初始化

類的載入、連線與初始化 概述 在Java程式碼中,型別的載入、連線與初始化過程都是在程式執行期間完成的 型別:可以理解為一個class 載入:查詢並載入類的二進位制資料,最常見的情況是將已經編譯完成的類的class檔案從磁碟載入到記憶體中 連線:確定型別與型別之間的關係,對於位元組碼的相關處理 驗證:確保

java載入——ClassLoader

web rac rgb 好的 全盤負責機制 安全 trac 字節 如何 Java的設計初衷是主要面向嵌入式領域,對於自己定義的一些類,考慮使用依需求載入原則。即在程序使用到時才載入類,節省內存消耗,這時就可以通過類載入器來動態載入。 假設你平時僅僅是做web開發,那應該

Java載入 ClassLoader的解析

index html dir obj ble body 6.4 odin 普通 //參考 : http://www.ibm.com/developerworks/cn/java/j-lo-classloader/ 類載入器基本概念 類載

finalkeyword對JVM載入的影響

public fin port args stat gpo sys tint () 眾所周知,當訪問一個類的變量或方法的時候。假設沒有初始化該類。就會先去初始化一個類 可是,當這個類的變量為final的時候,就