尚學堂-Day018

未曾想努力的活着只爲了成爲一個普通人

  • 反射
    • 反射的介紹
    • 獲取Class
    • 獲取類的修飾符
    • 創建對象
    • 父類與接口
    • 屬性和方法
    • 數組
    • 類加載器
  • 註解
    • 註解的介紹
    • 註解的分類
    • 內置註解
      • @Override
      • @Deprecated
      • @SuppressWarnings
    • 自定義註解
      • 定義
      • 元註解
      • 定義註解格式
      • 註解參數(方法)

一、反射

1、反射的介紹

“程序運行時,允許改變程序結構或變量類型,這種語言稱爲動態語言”,如Python,Ruby是動態語言;顯然C++,Java,C#不是動態語言,但是JAVA有着一個非常突出的動態相關機制:Reflflection。JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱爲java語言的反射機制。

反射機制可以實現的功能

  • 在運行時判斷任意一個對象所屬的類
  • 在運行時構造任意一個類的對象
  • 在運行時判斷任意一個類所具有的成員變量和方法
  • 在運行時調用任意一個對象的方法
  • 生成動態代理

2、獲取Class

所有類的對象其實都是Class的實例

Class對象就是,編譯後的字節碼文件在內存中的對象

Class對象的創價有三種方式

2.1、通過對象獲取

Class clz = new String().getClass();

2.2、通過類獲取

Class clz = String.class;

2.3、Class.forName獲取

Class clz = Class.forName("java.lang.String");

獲取自定義類的Class

public class Student {
	private String naem;
	public int age;
	
	
	public Student() {
		super();
	}
	public Student(String naem, int age) {
		super();
		this.naem = naem;
		this.age = age;
	}
}
Class clz = Class.forName("cn.yanghuisen.test.Student");

這種方式會拋出一個異常,需要自己處理


3、獲取類的修飾符(getModifiers)

getModifiers方法獲取類的修飾符,返回一個int類型

public class Test {
	public static void main(String[] args) throws Exception {
		Class clz = Class.forName("cn.yanghuisen.test.Student");
		int i = clz.getModifiers();		// 獲取類的修飾符
		System.out.println(i);
	}
}
運行結果
1

1代表是public修飾

設置 修飾符
0 default
1 public
2 private
4 protected

可以通過Modifier.toString()方法獲取對象數值的修飾符

public class Test {
	public static void main(String[] args) throws Exception {
		Class clz = Class.forName("cn.yanghuisen.test.Student");
		int i = clz.getModifiers();		// 獲取類的修飾符
		System.out.println(Modifier.toString(i));
		System.out.println(Modifier.toString(0));
		System.out.println(Modifier.toString(2));
		System.out.println(Modifier.toString(4));
	}
}
運行結果
public

private
protected

Tips:第二行空行爲Default


4、創建對象

4.1、獲取構造器創建對象

4.1.1、無參構造器

public class Test2 {
	public static void main(String[] args) throws Exception {
		Class clz = Class.forName("cn.yanghuisen.test.Student");
		Constructor<Student> con = clz.getConstructor();	// 獲取一個無參構造器
		Student student = con.newInstance();			// 創建對象
		System.out.println(student);
	}
}
運行結果
Student [name=null, age=0]

4.1.2、帶參構造器

public class Test2 {
	public static void main(String[] args) throws Exception {
		Class clz = Class.forName("cn.yanghuisen.test.Student");
		Constructor<Student> con = clz.getConstructor(String.class,int.class);	// 獲取一個帶參構造器
		Student student = con.newInstance("張三",20);			// 創建對象
		System.out.println(student);
	}
}
運行結果
Student [name=張三, age=20]

4.1.3、構造器數組

public class Test2 {
	public static void main(String[] args) throws Exception {
		Class clz = Class.forName("cn.yanghuisen.test.Student");
		Constructor<Student>[]  cons = clz.getConstructors();	// 獲取構造器數組
		for(Constructor c:cons) {		// 遍歷一下,確認構造器順序
			System.out.println(c);
		}
		Student student1 = cons[0].newInstance();			// 創建對象
		Student student2 = cons[1].newInstance("張三",20);			// 創建對象
		System.out.println(student1);
		System.out.println(student2);
	}
}
運行結果
public cn.yanghuisen.test.Student()
public cn.yanghuisen.test.Student(java.lang.String,int)
Student [name=null, age=0]
Student [name=張三, age=20]

獲取構造器數組,然後遍歷以下構造器數組,確定以下構造器在數組中的排列順序

4.1.4、newInstance

public class Test2 {
	public static void main(String[] args) throws Exception {
		Class clz = Class.forName("cn.yanghuisen.test.Student");
		Student student = (Student) clz.newInstance();
		System.out.println(student);
	}
}
運行結果
Student [name=null, age=0]

newInstance是調用的無參構造器,如果無參空構造器不存在,則會出現異常


5、父類與接口

public class Test2 {
	public static void main(String[] args) throws Exception {
		Class clz = Class.forName("cn.yanghuisen.test.Student");
		System.out.println(clz.getSuperclass());		// 返回父類的Class
		System.out.println(Arrays.toString(clz.getInterfaces()));	// 獲取接口,返回的是一個Class數組
	}
}
結果
class java.lang.Object
[]

因爲沒有實現任何接口,所以第二行是[]


6、屬性與方法

也可以通過反射獲取類中的屬性和方法,包括父類和接口中的

6.1、屬性

6.1.1、獲取指定屬性

public class Test2 {
	public static void main(String[] args) throws Exception {
		Class clz = Class.forName("cn.yanghuisen.test.Student");
		Field field = clz.getDeclaredField("name");		// 獲取本類中的指定屬性(包含private)
		System.out.println(field);
		System.out.println(field.getName());		// 獲取屬性名
		System.out.println(field.getType());		// 獲取屬性類型
		field = clz.getField("age");		// 獲取本類或父類中的public修飾的指定的屬性
		field = clz.getField("sex");
	}
}
運行結果
private java.lang.String cn.yanghuisen.test.Student.name
name
class java.lang.String

6.1.2、獲取屬性數組

public class Test2 {
	public static void main(String[] args) throws Exception {
		Class clz = Class.forName("cn.yanghuisen.test.Student");
		Field[] fields = clz.getDeclaredFields();		// 獲取本類中的所有屬性(包含private)
		System.out.println(Arrays.toString(fields));
		fields = clz.getFields();	// 獲取本類或父類中修飾爲public的屬性
		System.out.println(Arrays.toString(fields));
	}
}
運行結果
[private java.lang.String cn.yanghuisen.test.Student.name, public int cn.yanghuisen.test.Student.age]
[public int cn.yanghuisen.test.Student.age, public boolean cn.yanghuisen.test.Person.sex]

6.1.3、修改屬性值

public class Test2 {
	public static void main(String[] args) throws Exception {
		Class clz = Class.forName("cn.yanghuisen.test.Student");
		Field field = clz.getDeclaredField("name");		// 獲取本類中的指定屬性(包含private)
		field.setAccessible(true);		// 允許反射修改private的屬性,如果指定的屬性是public的可以不用設置
		Student student = (Student) clz.newInstance();
		field.set(student, "張三");
		System.out.println(student);
	}
}
運行結果
Student [naem=張三, age=0]

6.2、方法

6.2.1、獲取指定方法

public class Test2 {
	public static void main(String[] args) throws Exception {
		Class clz = Class.forName("cn.yanghuisen.test.Student");
		Method method = clz.getDeclaredMethod("setName",String.class);		// 獲取本類中的指定的方法
		System.out.println(method.getName());		// 獲取方法名
		System.out.println(method.getParameterCount());		// 獲取方法的參數數量
		System.out.println(method.getReturnType());		// 獲取方法的返回值類型
		method = clz.getMethod("setSex",boolean.class);		// 獲取public的方法(包含父類和接口)
	}
}
運行結果
setName
1
void

6.2.2、獲取方法數組

public class Test2 {
	public static void main(String[] args) throws Exception {
		Class clz = Class.forName("cn.yanghuisen.test.Student");
		Method[] methods = clz.getDeclaredMethods();		// 獲取本類中的所有的方法
		Arrays.asList(methods).stream().forEach(System.out::println);
		methods = clz.getMethods();		// 獲取public的所有的方法(包含父類和接口)
		Arrays.asList(methods).stream().forEach(System.out::println);
	}
}
運行結果
public java.lang.String cn.yanghuisen.test.Student.toString()
public java.lang.String cn.yanghuisen.test.Student.getName()
public void cn.yanghuisen.test.Student.setName(java.lang.String)
public void cn.yanghuisen.test.Student.setAge(int)
public int cn.yanghuisen.test.Student.getAge()
public java.lang.String cn.yanghuisen.test.Student.toString()
public java.lang.String cn.yanghuisen.test.Student.getName()
public void cn.yanghuisen.test.Student.setName(java.lang.String)
public void cn.yanghuisen.test.Student.setAge(int)
public int cn.yanghuisen.test.Student.getAge()
public boolean cn.yanghuisen.test.Person.isSex()
public void cn.yanghuisen.test.Person.setSex(boolean)
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()

6.2.3、調用方法

public class Test2 {
	public static void main(String[] args) throws Exception {
		Class clz = Class.forName("cn.yanghuisen.test.Student");
		Method method = clz.getDeclaredMethod("setName",String.class);		// 獲取本類中的指定的方法
		Student student = (Student) clz.newInstance();		// 創建對象
		method.invoke(student, "張三");
		System.out.println(student);
	}
}
運行結果
Student [naem=張三, age=0]

7、數組

public class Test3 {
	public static void main(String[] args) {
		Object obj = Array.newInstance(int.class, 10);		// 創建一個int類型的數組,長度爲10
		System.out.println(Array.getLength(obj));		// 獲取數組長度
		Array.set(obj, 6, 6);				// 設置指定索引位置的值
		System.out.println(Array.get(obj, 0));			// 獲取指定索引位置的值
		System.out.println(Arrays.toString((int[])obj));
	}
}
運行結果
10
0
[0, 0, 0, 0, 0, 0, 6, 0, 0, 0]

8、類加載器

在java中有三種類類加載器:

  • Bootstrap ClassLoader 此加載器採用c++編寫,一般開發中很少見。
  • Extension ClassLoader 用來進行擴展類的加載,一般對應的是jre\lib\ext目錄中的類
  • AppClassLoader 加載classpath指定的類,是最常用的加載器。同時也是java中默認的加載器。
public static void main(String[] args) throws Exception { 
    System.out.println("類加載器"+ClassLoader.class.getClassLoader().getClass().getName());
}

類的生命週期:

在一個類編譯完成之後,下一步就需要開始使用類,如果要使用一個類,肯定離不開JVM。在程序執行中JVM通過裝載鏈接初始化這3個步驟完成。

類的裝載是通過類加載器完成的,加載器將.class文件的二進制文件裝入JVM的方法區,並且在堆區創建描述這個類的java.lang.Class對象。用來封裝數據。 但是同一個類只會被類裝載器裝載一次。

鏈接就是把二進制數據組裝爲可以運行的狀態。鏈接分爲校驗,準備,解析這3個階段

  1. 校驗一般用來確認此二進制文件是否適合當前的JVM(版本),
  2. 準備就是爲靜態成員分配內存空間。並設置默認值
  3. 解析指的是轉換常量池中的代碼作爲直接引用的過程,直到所有的符號引用都可以被運行程序使用(建立完整的對應關係)完成之後,類型也就完成了初始化,初始化之後類的對象就可以正常使用了,直到一個對象不再使用之後,將被垃圾回收。釋放空間。當沒有任何引用指向Class對象時就會被卸載,結束類的生命週期

二、註解

1、註解的介紹

註解是Java 1.5引入的,目前已被廣泛應用於各種Java框架,如Hibernate,Jersey,Spring。註解相當於是一種嵌入在程序中的元數據,可以使用註解解析工具或編譯器對其進行解析,也可以指定註解在編譯期或運行期有效。在註解誕生之前,程序的元數據存在的形式僅限於java註釋或javadoc,但註解可以提供更多功能,它不僅包含元數據,還能作用於運行期,註解解析器能夠使用註解決定處理流程。

**Annotation(****註解)**就是Java提供了一種元程序中的元素關聯任何信息和任何元數據 (metadata)的途徑和方法。

Annotation是一個接口,程序可以通過反射來獲取指定程序元素的Annotation對象,然後通過Annotation對象來獲取註解裏面的元數據。註解API非常強大,被廣泛應用於各種Java框架。

元數據:描述數據的數據


2、註解的分類

2.1、根據註解參數的個數

  • 標記註解:一個沒有成員定義的Annotation類型稱爲標記註解。
  • 單值註解:只有一個值。
  • 完整註解:擁有多個值。

2.2、根據註解使用方法和用途

  • JDK內置系統註解
  • 元註解
  • 自定義註解

3、內置註解

Java中內置了三個標準註解,定義在Java.lang中

3.1、@Override

標記重寫了父類的方法

@Override
public String toString() {
    return "Student [naem=" + name + ", age=" + age + "]";
}

3.2、@Deprecated

標記已過時的方法

public class Test4 {
	
	public static void main(String[] args) {
		a1();
	}
	
	@Deprecated
	static void a1() {
		System.out.println("啊哈哈");
	}
}

被標記@Deprecated的方法不會被停用,可以正常使用,只是不建議使用

3.3、SuppressWarnings

抑制編輯器警告信息

@SuppressWarnings("all")
public class Test4 {

	public static void main(String[] args) {
		int a = 0;
	}
	
}
  • all to suppress all warnings (抑制所有警告)
  • boxing to suppress warnings relative to boxing/unboxing operations(抑制裝箱、拆箱操作時候的警告)
  • cast to suppress warnings relative to cast operations (抑制映射相關的警告)
  • dep-ann to suppress warnings relative to deprecated annotation(抑制啓用註釋的警告)
  • deprecation to suppress warnings relative to deprecation(抑制過期方法警告)
  • fallthrough to suppress warnings relative to missing breaks in switch statements(抑制確在switch中缺失breaks的警告)
  • finally to suppress warnings relative to finally block that don’t return (抑制finally模塊沒有返回的警告)
  • hiding to suppress warnings relative to locals that hide variable()
  • incomplete-switch to suppress warnings relative to missing entries in a switch statement (enum case)(忽略沒有完整的switch語句)
  • nls to suppress warnings relative to non-nls string literals(忽略非nls格式的字符)
  • null to suppress warnings relative to null analysis(忽略對null的操作)
  • rawtypes to suppress warnings relative to un-specific types when using generics on class params(使用generics時忽略沒有指定相應的類型)
  • restriction to suppress warnings relative to usage of discouraged or forbidden references
  • serial to suppress warnings relative to missing serialVersionUID field for a serializable class(忽略在serializable類中沒有聲明serialVersionUID變量)
  • static-access to suppress warnings relative to incorrect static access(抑制不正確的靜態訪問方式警告)
  • synthetic-access to suppress warnings relative to unoptimized access from inner classes(抑制子類沒有按最優方法訪問內部類的警告)
  • unchecked to suppress warnings relative to unchecked operations(抑制沒有進行類型檢查操作的警告)
  • unqualified-field-access to suppress warnings relative to field access unqualified (抑制沒有權限訪問的域的警告)
  • unused to suppress warnings relative to unused code (抑制沒被使用過的代碼的警告)

4、自定義註解

4.1、定義

public @interface A {
	
}

**@interface:**用來聲明一個註解

4.2、元註解

元註解的作用就是負責註解其他註解。Java5.0定義了4個標準的meta-annotation類型,它們被用來提供對其它annotation類型作說明。Java5.0定義的元註解有四個,這些類型和它們所支持的類在ava.lang.annotation包中可以找到。

4.2.1、@Target

描述註解使用的範圍

@Target({ElementType.METHOD,ElementType.FIELD,ElementType.TYPE})
public @interface A {
	
}

取值範圍:

  • CONSTRUCTOR:用於描述構造器
  • FIELD:用於描述域
  • LOCAL_VARIABLE:用於描述局部變量
  • METHOD:用於描述方法
  • PACKAGE:用於描述包
  • PARAMETER:用於描述參數
  • TYPE:用於描述類、接口(包括註解類型) 或enum聲明

4.2.2、@Retention

表示需要在什麼級別保存該註解,用於描述註解的聲明週期

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.FIELD,ElementType.TYPE})
public @interface A {
	
}
  • SOURCE:在源文件中有效(即源文件保留)
  • CLASS:在class文件中有效(即class保留)
  • RUNTIME:在運行時有效(即運行時保留)

4.2.3、@Documented

該註解的元素應該被JavaDoc或類似工具文檔化

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.FIELD,ElementType.TYPE})
public @interface A {
	
}

4.2.4、Inherited

註解會被自動繼承

@Inherited
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.FIELD,ElementType.TYPE})
public @interface A {
	
}

4.3、定義註解格式

使用@interface自定義註解時,自動繼承了java.lang.annotation.Annotation接口,由編譯程序自動完成其他細節。在定義註解時,不能繼承其他的註解或接口。

public @Interface 註解名{
	定義體
}

4.4、註解參數(方法)

註解裏面的每一個方法實際上就是一個配置參數,其規則如下

  • 修飾符:只能用public或default這兩個訪問權限修飾符,默認會default。

  • 類型:註解參數只支持以下類型

    • 基本數據類型
    • String類型
    • Class類型
    • enum類型
    • Annotation類型

    TIPS:不支持自定義類型

  • 命名:對名字沒有要求,如果只有一個參數成員,最好把參數名設置爲“value”,後面加小括號

  • 參數:註解中的方法不能存在參數

  • 默認值:可以包含默認值,使用default聲明

@Inherited
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.FIELD,ElementType.TYPE})
public @interface A {
	
	// 數組
	public String[] values();
	// 單個值
	boolean flag();
	// 默認值
	public String ab() default "啊哈哈";
}
@A(values= {"張三","李四"},flag=true)
public class Test9 {

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章