框架:半成品軟件。能夠在框架的基礎上進行軟件開發,簡化編碼 反射:將類的各個組成部分封裝爲其餘對象,這就是反射機制 好處: 1. 能夠在程序運行過程當中,操做這些對象。 2. 能夠解耦,提升程序的可擴展性。
Java代碼的三個階段java
1. Class.forName("全類名"):將字節碼文件加載進內存,返回Class對象 多用於配置文件,將類名定義在配置文件中。讀取文件,加載類 2. 類名.class:經過類名的屬性class獲取 多用於參數的傳遞 3. 對象.getClass():getClass()方法在Object類中定義着。 多用於對象的獲取字節碼的方式 結論: 同一個字節碼文件(*.class)在一次程序運行過程當中,只會被加載一次,不論經過哪種方式獲取的Class對象都是同一個。
public class ReflectDemo01 { public static void main(String[] args) throws ClassNotFoundException { //1.Class.forName("全類名") Class cls1 = Class.forName("domain.Person"); System.out.println(cls1); //2.類名.class Class<Person> cls2 = Person.class; System.out.println(cls2); //3.對象.getClass() Person person = new Person(); Class<? extends Person> cls3 = person.getClass(); //== 比較三個對象 System.out.println(cls1 == cls2);//true System.out.println(cls1 == cls3);//true } }
1. 獲取成員變量們 Field[] getFields() :獲取全部public修飾的成員變量 Field getField(String name) 獲取指定名稱的 public修飾的成員變量 Field[] getDeclaredFields() 獲取全部的成員變量,不考慮修飾符 Field getDeclaredField(String name) 2. 獲取構造方法們 Constructor<?>[] getConstructors() Constructor<T> getConstructor(類<?>... parameterTypes) Constructor<T> getDeclaredConstructor(類<?>... parameterTypes) Constructor<?>[] getDeclaredConstructors() 3. 獲取成員方法們: Method[] getMethods() Method getMethod(String name, 類<?>... parameterTypes) Method[] getDeclaredMethods() Method getDeclaredMethod(String name, 類<?>... parameterTypes) 4. 獲取全類名 String getName()
public class ReflectDemo02 { public static void main(String[] args) throws Exception { //0.獲取Person的Class對象 Class personClass = Person.class; //1.Field[] getFields()獲取全部public修飾的成員變量 Field[] fields = personClass.getFields(); for (Field field : fields) { System.out.println(field); } System.out.println("------------"); //2.Field getField(String name) Field a = personClass.getField("a"); //獲取成員變量a 的值 Person p = new Person(); Object value = a.get(p); System.out.println(value); //設置a的值 a.set(p,"張三"); System.out.println(p); System.out.println("==================="); //Field[] getDeclaredFields():獲取全部的成員變量,不考慮修飾符 Field[] declaredFields = personClass.getDeclaredFields(); for (Field declaredField : declaredFields) { System.out.println(declaredField); } //Field getDeclaredField(String name) Field d = personClass.getDeclaredField("d"); //忽略訪問權限修飾符的安全檢查 d.setAccessible(true);//暴力反射 Object value2 = d.get(p); System.out.println(value2); } } public class ReflectDemo04 { public static void main(String[] args) throws Exception { //0.獲取Person的Class對象 Class personClass = Person.class; //獲取指定名稱的方法 Method eat_method = personClass.getMethod("eat"); Person p = new Person(); //執行方法 eat_method.invoke(p); Method eat_method2 = personClass.getMethod("eat", String.class); //執行方法 eat_method2.invoke(p,"飯"); System.out.println("-----------------"); //獲取全部public修飾的方法 Method[] methods = personClass.getMethods(); for (Method method : methods) { System.out.println(method); String name = method.getName(); System.out.println(name); //method.setAccessible(true); } //獲取類名 String className = personClass.getName(); System.out.println(className);//cn.itcast.domain.Person } }
Field:成員變量 操做: 1. 設置值 void set(Object obj, Object value) 2. 獲取值 get(Object obj) 3. 忽略訪問權限修飾符的安全檢查 setAccessible(true):暴力反射
Constructor:構造方法 建立對象: T newInstance(Object... initargs) Class對象的newInstance方法;若是使用空參數構造方法建立對象,操做能夠簡化:
Method:方法對象 執行方法: Object invoke(Object obj, Object... args) 獲取方法名稱: String getName:獲取方法名
需求:寫一個"框架",不能改變該類的任何代碼的前提下,能夠幫咱們建立任意類的對象,而且執行其中任意方法 實現: 1. 配置文件 2. 反射 步驟: 1. 將須要建立的對象的全類名和須要執行的方法定義在配置文件中 2. 在程序中加載讀取配置文件 3. 使用反射技術來加載類文件進內存 4. 建立對象 5. 執行方法
package reflect; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Method; import java.util.Properties; /** * @Author wangjiefang * @Date 16:47 2020/5/11 * @Description **/ public class Test { public static void main(String[] args) throws Exception { //能夠建立任意類的對象,能夠執行任意方法 /* 前提:不能改變該類的任何代碼。能夠建立任意類的對象,能夠執行任意方法 */ /* Person p = new Person(); p.eat();*/ /* Student stu = new Student(); stu.sleep();*/ //1.加載配置文件 //1.1建立Properties對象 Properties pro = new Properties(); //1.2加載配置文件,轉換爲一個集合 //1.2.1獲取class目錄下的配置文件 ClassLoader classLoader = Test.class.getClassLoader(); InputStream is = classLoader.getResourceAsStream("pro.properties"); pro.load(is); //2.獲取配置文件中定義的數據 String className = pro.getProperty("className"); String methodName = pro.getProperty("methodName"); //3.加載該類進內存 Class cls = Class.forName(className); //4.建立對象 Object obj = cls.newInstance(); //5.獲取方法對象 Method method = cls.getMethod(methodName); //6.執行方法 method.invoke(obj); } }
概念:說明程序的。給計算機看的 註釋:用文字描述程序的。給程序員看的 定義:註解(Annotation),也叫元數據。一種代碼級別的說明。它是JDK1.5及之後版本引入的一個特性,與類、接口、枚舉是在同一個層次。它能夠聲明在包、類、字段、方法、局部變量、方法參數等的前面,用來對這些元素進行說明,註釋。 概念描述: JDK1.5以後的新特性 說明程序的 使用註解:@註解名稱
①編寫文檔:經過代碼裏標識的註解生成文檔【生成文檔doc文檔】 ②代碼分析:經過代碼裏標識的註解對代碼進行分析【使用反射】 ③編譯檢查:經過代碼裏標識的註解讓編譯器可以實現基本的編譯檢查【Override】
JDK中預約義的一些註解 @Override :檢測被該註解標註的方法是不是繼承自父類(接口)的 @Deprecated:該註解標註的內容,表示已過期 @SuppressWarnings:壓制警告 通常傳遞參數all @SuppressWarnings("all")
格式: 元註解 public @interface 註解名稱{ 屬性列表; } 本質:註解本質上就是一個接口,該接口默認繼承Annotation接口 public interface MyAnno extends java.lang.annotation.Annotation {} 屬性:接口中的抽象方法 要求: 1. 屬性的返回值類型有下列取值 基本數據類型、String、枚舉、註解、以上類型的數組 2. 定義了屬性,在使用時須要給屬性賦值 1. 若是定義屬性時,使用default關鍵字給屬性默認初始化值,則使用註解時,能夠不進行屬性的賦值。 2. 若是隻有一個屬性須要賦值,而且屬性的名稱是value,則value能夠省略,直接定義值便可。 3. 數組賦值時,值使用{}包裹。若是數組中只有一個值,則{}能夠省略 元註解:用於描述註解的註解 @Target:描述註解可以做用的位置 ElementType取值: TYPE:能夠做用於類上 METHOD:能夠做用於方法上 FIELD:能夠做用於成員變量上 @Retention:描述註解被保留的階段 @Retention(RetentionPolicy.RUNTIME):當前被描述的註解,會保留到class字節碼文件中,並被JVM讀取到 @Documented:描述註解是否被抽取到api文檔中 @Inherited:描述註解是否被子類繼承
在程序使用(解析)註解:獲取註解中定義的屬性值 1. 獲取註解定義的位置的對象 (Class,Method,Field) 2. 獲取指定的註解 getAnnotation(Class) //其實就是在內存中生成了一個該註解接口的子類實現對象 3. 調用註解中的抽象方法獲取配置的屬性值
簡單的測試框架 小結: 1. 之後大多數時候,咱們會使用註解,而不是自定義註解 2. 註解給誰用? 1. 編譯器 2. 給解析程序用 3. 註解不是程序的一部分,能夠理解爲註解就是一個標籤
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Check { } public class Calculator { //加法 @Check public void add(){ String str = null; str.toString(); System.out.println("1 + 0 =" + (1 + 0)); } //減法 @Check public void sub(){ System.out.println("1 - 0 =" + (1 - 0)); } //乘法 @Check public void mul(){ System.out.println("1 * 0 =" + (1 * 0)); } //除法 @Check public void div(){ System.out.println("1 / 0 =" + (1 / 0)); } public void show(){ System.out.println("永無bug..."); } } public class TestCheck { public static void main(String[] args) throws IOException { //1.建立計算器對象 Calculator c = new Calculator(); //2.獲取字節碼文件對象 Class cls = c.getClass(); //3.獲取全部方法 Method[] methods = cls.getMethods(); int number = 0;//出現異常的次數 BufferedWriter bw = new BufferedWriter(new FileWriter("bug.txt")); for (Method method : methods) { //4.判斷方法上是否有Check註解 if(method.isAnnotationPresent(Check.class)){ //5.有,執行 try { method.invoke(c); } catch (Exception e) { //6.捕獲異常 //記錄到文件中 number ++; bw.write(method.getName()+ " 方法出異常了"); bw.newLine(); bw.write("異常的名稱:" + e.getCause().getClass().getSimpleName()); bw.newLine(); bw.write("異常的緣由:"+e.getCause().getMessage()); bw.newLine(); bw.write("--------------------------"); bw.newLine(); } } } bw.write("本次測試一共出現 "+number+" 次異常"); bw.flush(); bw.close(); } }