JAVA 反射篇 看這一篇就夠了

一. 反射:框架設計的靈魂

1. JAVA代碼在計算機中經歷的三個階段

5
框架:半成品軟件。可以在框架的基礎上進行軟件開發,簡化編碼
反射:將類的各個組成部分封裝爲其他對象,這就是反射機制
好處

  1. 可以在程序運行過程中,操作這些對象。
  2. 可以解耦,提高程序的可擴展性。

二. 獲取Class對象的方式:

  1. Class.forName(“全類名”):將字節碼文件加載進內存,返回Class對象
    多用於配置文件,將類名定義在配置文件中。讀取文件,加載類
   Class aClass = Class.forName("SencondDay.Car");
  1. 類名.class:通過類名的屬性class獲取
    多用於參數的傳遞
   Class carClass = Car.class;
  1. 對象.getClass():getClass()方法在Object類中定義着。
    多用於對象的獲取字節碼的方式
  Car car=new Car();
  Class aClass1 = car.getClass();

我們把這些對象比較一下

		System.out.println(aClass);
		System.out.println(carClass);
        System.out.println(car.getClass());
        System.out.println(aClass==carClass);
        System.out.println(aClass==aClass1);

打印結果

class SencondDay.Car
class SencondDay.Car
class SencondDay.Car
true
true

結論
同一個字節碼文件(*.class)在一次程序運行過程中,只會被加載一次,不論通過哪一種方式獲取的Class對象都是同一個。

三. Class對功能:

獲取功能

1. 獲取成員變量們

這裏我定義四個變量,被不同的修飾符修飾

    public String a;
    protected String b;
    String c;
    private String d;

( 1 ). Field[] getFields() :獲取所有public修飾的成員變量

   		Class carClass = Car.class;
        Field[] fields=carClass.getFields();
        for (Field field : fields) {
            System.out.println(field);
        }

打印結果

public java.lang.String SencondDay.Car.a

( 2 ). Field getField(String name) 獲取指定名稱的 public修飾的成員變量

 Field field=carClass.getField("a");
        System.out.println(field);

打印結果

public java.lang.String SencondDay.Car.a

( 3 ). Field[] getDeclaredFields() 獲取所有的成員變量,不考慮修飾符

 Field[] fields1=carClass.getDeclaredFields();
        for (Field field1 : fields1) {
            System.out.println(field1);
        }

打印結果

private java.lang.String SencondDay.Car.name
private java.math.BigDecimal SencondDay.Car.price
private java.lang.String SencondDay.Car.color
public java.lang.String SencondDay.Car.a
protected java.lang.String SencondDay.Car.b
java.lang.String SencondDay.Car.c
private java.lang.String SencondDay.Car.d
private java.lang.Integer SencondDay.Car.busload
private java.lang.String SencondDay.Car.weight

( 4 ). Field getDeclaredField(String name)

Field d=carClass.getDeclaredField("d");
        Car car=new Car();
        d.set(car,"D");
        d.setAccessible(true);//暴力反射
        Object value = d.get(car);
        System.out.println(value);

因爲d是私有屬性,訪問設置值的時候需要設置一個暴力反射纔可設置值與訪問

打印結果

D

2. 獲取構造方法們

( 1 ). Constructor getConstructor(類<?>… parameterTypes)

Constructor constructor = carClass.getConstructor(String.class, BigDecimal.class,   String.class, Integer.class, String.class);//有參構造方法
System.out.println(constructor);
Object carName = constructor.newInstance("凱凱",new BigDecimal(123),"red",1500,"1T");
System.out.println(carName);

Object newCar = carClass.newInstance();//無參構造方法
System.out.println(newCar);

結果

public SencondDay.Car(java.lang.String,java.math.BigDecimal,java.lang.String,java.lang.Integer,java.lang.String)
Car{name='凱凱', price=123, color='red', busload=1500, weight='1T'}
Car{name='null', price=null, color='null', busload=null, weight='null'}

( 2 ). Constructor<?>[] getConstructors()
( 3 ). Constructor<?>[] getDeclaredConstructors()
以上方法類似,不一一演示

3. 獲取成員方法們:

( 1 ). Method[] getMethods()

		Method[] methods = carClass.getMethods();
        for (Method method : methods) {
            System.out.println(method);
            String name = method.getName();//獲取方法名
            System.out.println(name);
        }

打印結果
把我們這個類的所有方法都打印出來了,包括繼承Object類的方法

public static void SencondDay.Car.main(java.lang.String[]) throws java.lang.Exception
main
public java.lang.String SencondDay.Car.toString()
toString
public java.lang.String SencondDay.Car.getName()
getName
public void SencondDay.Car.setName(java.lang.String)
setName
public void SencondDay.Car.setColor(java.lang.String)
setColor
public static void SencondDay.Car.CarLife()
CarLife
public java.lang.String SencondDay.Car.getColor()
getColor
public static void SencondDay.Car.CarPrice()
CarPrice
public static void SencondDay.Car.CarRun()
CarRun
public void SencondDay.Car.setPrice(java.math.BigDecimal)
setPrice
public static void SencondDay.Car.CarFly()
CarFly
public static void SencondDay.Car.AllCar()
AllCar
public java.math.BigDecimal SencondDay.Car.getPrice()
getPrice
public static void SencondDay.Car.CarPeople()
CarPeople
public java.lang.Integer SencondDay.Car.getBusload()
getBusload
public java.lang.String SencondDay.Car.getWeight()
getWeight
public void SencondDay.Car.setWeight(java.lang.String)
setWeight
public void SencondDay.Car.setBusload(java.lang.Integer)
setBusload
public final void java.lang.Object.wait() throws java.lang.InterruptedException
wait
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
wait
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
wait
public boolean java.lang.Object.equals(java.lang.Object)
equals
public native int java.lang.Object.hashCode()
hashCode
public final native java.lang.Class java.lang.Object.getClass()
getClass
public final native void java.lang.Object.notify()
notify
public final native void java.lang.Object.notifyAll()
notifyAll

( 2 ). Method getMethod(String name, 類<?>… parameterTypes)

		public static void CarFly(){
    	    System.out.println("這車不能飛!");
    	}
    	
 		Car car=new Car();
        Method canFly = carClass.getMethod("CarFly");
        canFly.invoke(car);//調用invoke方法實現

打印結果

這車不能飛!

( 3 ). Method[] getDeclaredMethods()
( 4 ). Method getDeclaredMethod(String name, 類<?>… parameterTypes)
以上這兩個方法針對所有方法,不限於修飾符,所以要訪問私有方法可以直接 method.setAccessible(true)即可訪問

4. 獲取全類名

 String name = carClass.getName();
 System.out.println(name);

打印結果

SencondDay.Car//輸出全類名

四. 寫一個反射的案例

public class ReflectTest  {
    public static void main(String[] args)throws Exception  {

		//創建propert對象
        Properties properties=new Properties();
        //類加載器,知道類的位置,也能知道配置文件的位置,獲取配置文件文件位置
        ClassLoader classLoader = ReflectTest.class.getClassLoader();
        InputStream resourceAsStream = classLoader.getResourceAsStream("pro.properties");
        //加載配置文件
        properties.load(resourceAsStream);

		//獲取配置文件信息
        String className = properties.getProperty("className");
        String methodName = properties.getProperty("methodName");
		
		//加載該類進內存
        Class people = Class.forName(className);
        //通過反射創建對象
        Object obj = people.newInstance();
        //獲取方法對象
        Method method = people.getMethod(methodName);
        //執行方法
        method.invoke(obj);
    }
}

配置文件 pro.properties

className=SencondDay.People
methodName=speak

實體類

package SencondDay;

@Data
public class People {
    private String name;
    private String  title;
    private Integer age;
    
    public void speak(){
        System.out.println("人會說話");
    }
}

打印結果

人會說話

五. 總結

通過這種方法,我們可以通過配置文件來幫我們創建任意的類,去執行任意的方法,類似於一個框架一樣,只需要我們去改配置文件就可以實現相同的功能,也類似於Spring框架的IOC,工廠模式,通過這一點我們可以使程序之間解耦,並且不讓程序在編譯期間報錯

製作不易,轉載請標註~~~

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