1. 程式人生 > 實用技巧 >JAVA學習之-反射

JAVA學習之-反射

一,類物件

  • 類的物件:基於某個類new出來的物件,也叫例項物件
  • 類物件:類載入的產物,封裝了一個類所有的資訊(類名,父類,介面,屬性,方法,構造方法)Java原始碼編譯後的.class檔案儲存在硬碟中,從硬碟載入進記憶體。就是一個類物件
  • 將類的各個組成部分封裝為其他物件,就是反射機制

反射的好處:

1.可以在程式執行過程中,操作這些物件。
2.可以解耦,提高程式的可擴充套件性。

1.1Java程式碼在計算機中的三個階段

1.2 獲取Class物件的方式:

  • Class.forName("包名.類名"):將位元組碼檔案載入進記憶體,返回Class物件 * 多用於配置檔案,將類名定義在配置檔案中。讀取檔案,載入類 推薦寫法
  • 類名.class:通過類名的屬性class獲取,返回Class物件 * 多用於引數的傳遞
  • 物件.getClass():getClass()方法在Object類中定義著。* 多用於物件的獲取位元組碼的方式
  1. 同一個位元組碼檔案(*.class)在一次程式執行過程中,只會被載入一次,不論通過哪一種方式獲取的Class物件都是同一個。

通用操作:

  • 使用反射機制獲取類物件,並使用Class物件的方法獲取表示類成員的各種物件(Constructor,Method,Field),實現反射各種應用

案例,

package com.qf;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Properties;

public class TestStudent { public static void main(String[] args) throws Exception { //getClazz(); //getClazzInfo(); // getConstructors(); // getMethods(); // Properties properties=new Properties(); // ArrayList<String> arrayList=new ArrayList<String>();
// //properties.setProperty(key, value) // properties.setProperty("username", "張三"); // invokeAny(properties, "setProperty", new Class<?>[] {String.class,String.class}, new Object[] {"username","張三"}); // invokeAny(arrayList, "add", new Class<?>[] {Object.class}, new Object[] {"梓源"}); // System.out.println(properties); // System.out.println(arrayList); //getFields(); testIntrospector(); } //獲取類物件 public static void getClazz() throws Exception { //(1)通過類的物件,呼叫getClass()方法 Student xiangrui=new Student(); Class<?> class1= xiangrui.getClass(); System.out.println(class1.hashCode()); //(2)通過類名.class Class<?> class2=Student.class; System.out.println(class2.hashCode()); //(3)通過Class.forName()靜態方法獲取,引數是類的全名稱, 推薦寫法:(1)靈活 (2)耦合性低 Class<?> class3=Class.forName("com.qf.Student"); System.out.println(class3.hashCode()); } //獲取類名、包、父類、實現介面 public static void getClazzInfo() throws Exception { //1獲取類物件 Class<?> class1=Class.forName("com.qf.Student"); //2獲取類的全名稱 System.out.println(class1.getName()); //3獲取包資訊 Package package1 = class1.getPackage(); System.out.println(package1.getName()); //4獲取父類 Class<?> superclass = class1.getSuperclass(); System.out.println(superclass.getName()); System.out.println("-----------------"); //5獲取實現介面 Class<?>[] interfaceClasses=class1.getInterfaces(); for (Class<?> class2 : interfaceClasses) { System.out.println(class2); } } //獲取類的構造方法【重點】 public static void getConstructors() throws Exception { //1 獲取類物件 Class<?> class1=Class.forName("com.qf.Student"); //2獲取構造方法 //2.1getConstructors()獲取所有的構造方法 // Constructor<?>[] constructors=class1.getConstructors(); // for (Constructor<?> constructor : constructors) { // System.out.println(constructor); // } //2.2獲取無參構造方法 Constructor<?> constructor=class1.getConstructor(); //使用無參構造方法建立物件 Student student=(Student)constructor.newInstance(); System.out.println(student); //2.3獲取帶參構造方法 Constructor<?> constructor2=class1.getConstructor(String.class,int.class,String.class,String.class); //使用帶參構造方法建立物件 Student xiangrui=(Student)constructor2.newInstance("祥睿",20,"1001",""); System.out.println(xiangrui); //3建立物件的簡便方法(預設是無參構造方法) Student student2=(Student)class1.newInstance(); System.out.println(student2); } //獲取類的普通方法【重點】 public static void getMethods() throws Exception{ //獲取類物件 Class<?> class1=Class.forName("com.qf.Student"); //1獲取所有方法 getMethods()獲取所有公開的方法,包括繼承的公開方法 //Method[] methods=class1.getMethods(); //2獲取所有方法 getDeclaredMethods(); 獲取類中所有的方法,包括私有、保護、預設方法、不包括繼承的方法。 // Method[] methods=class1.getDeclaredMethods(); // for (Method method : methods) { // System.out.println(method); // } //3獲取show方法 Method methodShow=class1.getMethod("show"); //呼叫方法 // Student student=new Student(); // student.show(); Student student=(Student)class1.newInstance(); methodShow.invoke(student);//student.show(); //獲取帶參show方法 Method methodShow2=class1.getMethod("show",String.class); methodShow2.invoke(student, "110"); //獲取getName()方法 Method methodGetName=class1.getMethod("getName"); String name=(String)methodGetName.invoke(student); System.out.println(name); //獲取靜態方法 Method methodPrintInfo=class1.getMethod("printInfo"); methodPrintInfo.invoke(null);//Student.printInfo(); //獲取私有方法 Method methodPrintInfo2=class1.getDeclaredMethod("printInfo2"); //設定訪問許可權無效(不要用,破壞封裝性) methodPrintInfo2.setAccessible(true); methodPrintInfo2.invoke(student); } //通用方法 public static Object invokeAny(Object obj,String methodName,Class<?>[] paramsType,Object... paramsValue) throws Exception { Class<?> class1=obj.getClass(); Method method=class1.getMethod(methodName, paramsType); return method.invoke(obj, paramsValue); } //使用反射操作屬性 public static void getFields() throws Exception { //獲取類物件 Class<?> class1 = Class.forName("com.qf.Student"); //獲取屬性 //getFields() 獲取公開的屬性、從父類繼承的屬性 //class1.getFields() // Field[] fields=class1.getDeclaredFields(); // for (Field field : fields) { // System.out.println(field); // } //獲取name Field fieldName=class1.getDeclaredField("name"); Student ziyuan = (Student)class1.newInstance(); //呼叫 fieldName.setAccessible(true); fieldName.set(ziyuan, "梓源"); System.out.println(fieldName.get(ziyuan)); Field fieldAge=class1.getDeclaredField("age"); fieldAge.setAccessible(true); fieldAge.set(ziyuan, 20); System.out.println(fieldAge.get(ziyuan)); } //內省機制:使用反射技術操作屬性的一種機制。 //Introspector 內省工具類 //BeanInfo 類資訊 //PropertyDescriptor 屬性描述符(代表一個屬性) //以getXxx setXxx isXxx開頭的方法叫屬性 public static void testIntrospector() throws Exception { Class<?> class1=Class.forName("com.qf.Teacher"); //BeanInfo 類資訊 //BeanInfo beanInfo = Introspector.getBeanInfo(class1); //PropertyDescriptor //以getXxx setXxx isXxx開頭的方法叫屬性 // PropertyDescriptor[] propertyDescriptors=beanInfo.getPropertyDescriptors(); // for (PropertyDescriptor pd : propertyDescriptors) { // System.out.println(pd.getName()); // } Teacher lilaoshi=(Teacher)class1.newInstance(); //獲取屬性描述符 PropertyDescriptor pdName=new PropertyDescriptor("name", class1); PropertyDescriptor pdAge=new PropertyDescriptor("age", class1); PropertyDescriptor pdSalary=new PropertyDescriptor("salary", class1); //賦值 pdName.getWriteMethod().invoke(lilaoshi, "李老師");//setName(); pdAge.getWriteMethod().invoke(lilaoshi, 40); pdSalary.getWriteMethod().invoke(lilaoshi, 50000); System.out.println(lilaoshi); System.out.println(pdName.getReadMethod().invoke(lilaoshi)); } }

1.2 java建立物件的四種方式:

  • 使用new關鍵字,呼叫構造方法
  • 使用反射技術,呼叫構造方法
  • 使用Cloneable介面克隆物件,不會呼叫構造方法,必須實現介面重寫clone方法
  • 使用反序列化,不會呼叫構造方法,實現serializable介面

1.3 設計模式

  • 特定問題的固定解決辦法,程式碼設計經驗的總結
  • Gof設計模式中,介紹了23種
  • 好處是可重用,

1.4 工廠設計模式

  • 開閉原則,對拓展開放,對修改關閉
  • 工廠模式主要負責物件的建立問題
  • 可通過反射進行工廠模式的設計,動態完成物件建立

1.5 單例設計模式

1.5.1餓漢式寫法 :缺點:宣告週期比較長,浪費空間。 優點:執行緒安全

  • 把類的構造方法變為私有的。
  • 在單例類內部類建立物件並例項化
  • 在單例類中新增一個公開的方法,返回這個物件
 1 //餓漢式
 2 public class SingleTon1{
 3     private SingleTon1(){
 4 
 5     }
 6     private static final SingleTon1 instance = new SingleTon1();
 7    
 8   public static SingleTon1 getInstance(){
 9       return instance;
10     }
11 }

1.5.2 懶漢式寫法:懶漢式寫法: 優點:宣告週期短,節省空間 缺點:執行緒不安全

  • 把類的構造方法變為私有的。
  • 在單例類內部類建立物件,沒有初始化。
  • 在單例類中新增一個公開的方法,返回這個物件
public class SingleTon2 {
    // (1)把類的構造方法變為私有的。
    private SingleTon2() {
    
    }
    //(2)在單例類內部類建立物件,沒有初始化。
    private static SingleTon2 instance=null;
    
    //(3)在單例類中新增一個公開的方法,返回這個物件
    
    public static SingleTon2 getInstance() { //100執行緒 ,只要有一個執行緒進入到synchronized中,instance一定例項化了,另外99個執行緒不需要判斷synchronized
        if(instance==null) {//提高效率 另外99判斷if
            synchronized (SingleTon2.class) {
                if(instance==null) {
                    instance=new SingleTon2();
                }
            }
        }
        return instance;
    }
}

1.5.3 靜態內部類寫法

public class SingleTon3 {
    //1私有化構造方法
    private SingleTon3() {
        // TODO Auto-generated constructor stub
    }
    //2建立物件(1 靜態內部類不使用,不會提前載入,宣告週期短,2 JVM例項化物件時解決執行緒安全問題)
    private static class Holder{
        private static final SingleTon3 instance=new SingleTon3();
    }
    //3返回這個物件
    public static SingleTon3 getInstance() {
        return Holder.instance;
    }
}

二,列舉

  • 引用型別,規定了取值範圍的資料型別
  • 列舉變數不能使用其他的資料,只能使用列舉中的常量賦值,提高程式安全性
  • 定義列舉使用enum關鍵字
  • 列舉的本質:
  1. 是一個終止類,繼承Enum抽象類
  2. 列舉中常量是當前型別的靜態常量
/**
 * 定義列舉
 * 關鍵字enum  (Enumeration)
 * 包含 
 * 1 【常量】 ;可以省略
 * 2 【私有構造方法、屬性、方法】必須在常量的後面寫
 * @author wgy
 *
 */
public enum Gender {

    //1【常量】
    MALE,FEMALE;
    
    //2 【私有構造方法、屬性、方法】必須在常量的後面寫
    private Gender() {
        
    }
    private int value;
    
    public void show() {
        System.out.println(value);
    }

    public int getValue() {
        return value;
    }

    public void setValue(int value) {
        this.value = value;
    }

}

三,註解

  • 註解(Annotation),也叫元資料。一種程式碼級別的說明。它是JDK1.5及以後版本引入的一個特性,與類、介面、列舉是在同一個層次。它可以宣告在包、類、欄位、方法、區域性變數、方法引數等的前面,用來對這些元素進行說明,註釋。
  • 程式可以讀取註解,一般用於代替配置檔案
  • 通過反射技術獲取註解,決定怎麼執行類

3.1 定義註解

@interface關鍵字,註解中只能包含屬性

3.2 註解屬性型別

1 基本型別
2 String
3 Class型別
4 列舉型別
5 註解型別
以及上述型別的一維陣列

@Retention(value=RetentionPolicy.RUNTIME)
//@Target({ElementType.METHOD,ElementType.TYPE,ElementType.FIELD})
public @interface MyAnnotation {
    //public 屬性
    String name() default "張三";
    int age();
    String address();
    
}

3.3 元註解

用來描述註解的註解