JavaDay27 反射

JavaDay27 反射

一、反射總結

  • 反射:
    當一個Java文件編譯之後,編譯成一個.class文件,也就是字節碼文件,當這個字節碼文件【加載】到內存的方法區/代碼區,JVM會根據【加載】的字節碼文件內容,創建一個Class的類對象。這個Class類對象裏面包含了當前字節碼文件裏面的所有內容

    只要我們獲取到這個Class對象,就可以爲所欲爲!!!
    這個Class對象中包含了對應字節碼文件的所有成員變量(Field) 所有成員方法(Method)構造方法(Constructor)。

  • 1.獲取Class對象的三種方式:
    1> Class.forName(“完整的類名字符串”);
    完整類名字符串是指 包名.類名
    2> 類名.class;
    3> 指定類對象.getClass();

    最常用的是Class.forName(“完整的類名字符串”);
    後期大量使用

  • 2.Constructor, Method, Field
    都是Java反射這個包(java.lang.reflect)裏面的類
    Constructor 是構造方法類
    Method 是成員方法類
    Field 是成員變量類

  • 3.Constructor常用方法:

返回值 方法名 作用
Constructor[] getConstructors(); 獲取所有public修飾的構造方法
Constructor[] getDeclaredConstructors(); 獲取當前類裏面所有的構造方法,包含用private修飾的構造方法
Constructor getConstructor(Object… paramterTyeps); 根據所需參數不同,獲取指定的構造方法對象
Constructor getDeclaredConstructor(Object… paramterTyeps); 根據所需參數不同,獲取指定的構造方法對象,包括私有化的方法
Object newInstance(Object… initargs); 給予確定的參數,通過反射調用構造方法,這裏的參數列表是一個不定參數列表
  • 4.Method常用方法
返回值 方法名 作用
Method[] getMethods(); 獲取當前類裏面所有的public修飾的成員方法,這裏或顯示父類繼承而來的public方法
Method[] getDeclaredMethods(); 獲取當前類裏面的所有方法,包括private修飾的方法,但是會過濾父類繼承而來的方法
Method getMethod(String methodName, Object… args); 根據方法的名字和對應的參數列表,獲取指定方法
Method getDeclaredMethod(String methodName, Object… args); 根據方法的名字和對應的參數列表,獲取指定方法,可以獲取private修飾的方法
invoke(Object obj, Object… args); 執行成員方法的函數,第一個參數是執行該方法的類對象,第二個參數是執行該方法需要的參數列表
  • 5.Field常用方法
返回值 方法名 作用
Field[] getFields(); 獲取所有的用public修飾的成員變量
Field[] getDeclaredFields(); 獲取所用成員變量,包括用private 修飾的成員變量
Field getField(String fieldName); 根據成員變量的名字獲取對應的成員變量
Field getDeclaredField(String fieldName); 根據成員變量的名字獲取包括private修飾在內的成員變量
set(Object obj, Object value); 設置成員變量的數值,第一個參數是調用該成員變量的對象,第二個參數是賦予數值
  • 6.暴力反射賦予權限的函數
    setAccessible(boolean )

二、獲取Class 類 對象的三種方式

1.首先新建一個 Person 類,作爲示例

package com.qfedu.a_reflect;

public class Person {
	private int id;
	private String name;
	
	public int test;
	public static int testStatic = 10;
	
	private Person() {}
	
	public Person(int id, String name) {
		this.id = id;
		this.name = name;
	}
	
	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public static void eat() {
		System.out.println("黃燜雞米飯~~~");
	}
	
	public void sleep(int num) {
		System.out.println(name + "每天睡" + num + "個小時");
	}
	
	public void game() {
		System.out.println("大吉大利,今晚喫雞~~~");
	}
	
	private void testPrivate() {
		System.out.println("這是一個Private修飾的私有化方法");
	}

	@Override
	public String toString() {
		return "Person [id=" + id + ", name=" + name + "]";
	}
}

2.通過反射獲取類對象

package com.qfedu.a_reflect;

public class GetClassObject {
	public static void main(String[] args) throws ClassNotFoundException {
		//如果想要爲所欲爲,首先獲取到Class類對象
		
		/*方式1:Class.forName("完整的類名字符串");
		完整類名是包括    包名.類名
		*/
		Class cls1 = Class.forName("com.qfedu.a_reflect.Person");
		System.out.println(cls1);
		
		//方式2:類名.class
		Class cls2 = Person.class;
		System.out.println(cls2);
		
		//方式3:通過對象獲取到對應的Class類對象
		Class cls3 = new Person(1, "逗比").getClass();
		System.out.println(cls3);
		
		System.out.println(cls1 == cls2);
		System.out.println(cls2 == cls3);
		System.out.println(cls1 == cls3);
	}
}

三、Constructor常用方法

通過Class類對象獲取當前類的構造方法並且調用

package com.qfedu.a_reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class GetClassConstructor {
	public static void main(String[] args) throws 
	ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
/*
 【加載】指定類的字節碼文件,獲取對應的Class對象
	下面的語句做了兩件事情:
	1. 讓JVM根據類名,加載Person.java對應的字節碼文件Person.class 到內存的代碼區
	2. 把加載到內存代碼區Person.class 字節碼文件生成一個Class類對象返回
*/
		Class cls = Class.forName("com.qfedu.a_reflect.Person");
		
		
	//Constructor 這是構造方法的類
	//第一種:可以通過Class獲取所有的【非私有化構造方法】,方法是 getConstuctors();
		Constructor[] constructors = cls.getConstructors();
		
		for (Constructor constructor : constructors) {
			System.out.println(constructor);
		}
		System.out.println("---------------------------");


	//第二種:暴力反射(獲取所有構造方法,不管是不是私有化)
		Constructor[] allConstructors = cls.getDeclaredConstructors();
		for (Constructor constructor : allConstructors) {
			System.out.println(constructor);
		}
		
	//第三種方法:
		/*
		 根據參數獲取具體的構造方法 
		 getConstructor(Class... parameterTypes);
		 上面括號裏面是不定參數列表,表示參數的類型要求是Class類型,但是數量不限制
		 在確定數據類型的情況下,可以直接通過數據類型.class獲取對應Class數據類型
		 例如:
		 	int.class String.class 這裏會自動包裝爲Integer.class和String.class
		 */
		System.out.println("----------------------------------");
		Constructor aConstructor = cls.getConstructor(int.class, String.class);
		System.out.println(aConstructor);
		
		

	//☆☆☆☆根據Constructor對象創建類對象,即調用構造函數

		/*
		 構造方法Constructor對象獲取完畢,怎麼利用Constructor對象創建一個Person類對象
		 newInstance(Object... initargs) 
		 也是一個不定參的方法,需要的參數都是Object類型的,參數個數不確定
		 */
		Person p = (Person) aConstructor.newInstance(1, "海洋");//默認產生是Object對象,因此需要強轉
		System.out.println(p.getId() + ":" + p.getName());
		p.sleep(5);
		p.game();
		p.eat(); //如何通過反射機制,調用static修飾的成員方法,並且不報警告
		

		/*
		 通過暴力反射,藉助於指定的參數,獲取private修飾的無參構造方法 
		 */
		System.out.println("---------------------------------------");
		Constructor privateConstructor = cls.getDeclaredConstructor(null);
		System.out.println(privateConstructor);
		
		//這裏需要通過setAccessible(boolean ) 給予操作Private修飾方法的權限
		privateConstructor.setAccessible(true);
		Person p2 = (Person) privateConstructor.newInstance(null);
		p2.setId(2);
		p2.setName("劉德華");
		System.out.println(p2);
		
		/*
		 單例和反射的共存:
		  	在實際開發中,如果一個類是一個單例類,那麼一般不會有程序猿通過反射的方式來使用這個類裏面
		  	私有化的構造方法,這違背了單例的原則
		 */
	}
}

至此:上面除了 Person 類以及暴力反射之外所有代碼的整理精簡版如下

/**
 * @author GJXAIOU
 * @create 2019-08-10-13:56
 */
public class ReflectPractice {
    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        // 1.獲取class類對象
        Class person = Class.forName("entity.Person");

        // 2.獲取具體的構造方法
        // 獲取無參構造方法
        Constructor constructor = person.getConstructor();
        // 獲取帶有參數的構造方法
        Constructor constructor1 = person.getConstructor(int.class,String.class);
        // 獲取全部構造方法(非私有的)
        Constructor[] constructors = person.getConstructors();
        for (Constructor constructor2 : constructors) {
            System.out.println(constructor);
        }

        // 3.使用構造方法構建類對象,默認是Object類型,需要強轉
        Person person1 = (Person) constructor.newInstance();
        Person person2 = (Person) constructor1.newInstance(2, "張三");

        // 4.使用成員變量和成員方法
        System.out.println(person1.getName());
        System.out.println(person2.getName());
    }

補充:單例

package com.qfedu.a_reflect;

public class SingleDemo {
	
	int id;
	
	//爲了判定數據,定義一個靜態私有化成員變量,數據類型爲該類的變量,保存上次創建的數據的類對象的首地址
	private static SingleDemo s = null;
	
	//1.私有化構造方法
	private SingleDemo(int id) {  
		this.id = id;
	}
	
	//2.提供一個類外可以通過類名直接調用的,返回值爲當前類對象類型的,參數爲對應需要的構造方法參數,
	//方法名通常爲getInstance,作爲類外獲取方式
	public static SingleDemo getInstance(int id) {
		//但我們調用這個公開的,靜態方法修飾的獲取類對象的這種方法時,對這個保存地址的變量進行判定
		if (null == s) {
			s = new SingleDemo(id);
		}	
		return s; //返回上次創建對象
	}
}

四、Method常用方法

4.通過反射藉助於Class類對象,獲取這個類裏面所有的成員方法,並且進行調用

package com.qfedu.a_reflect;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

/**
 * 通過反射藉助於Class類對象,獲取這個類裏面所有的成員方法
 * Method 就是成員方法類
 */
public class GetClassMethod {
	public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException, SecurityException {
		//1.加載對應類的字節碼文件,獲取該類的Class類對象
		Class cls = Class.forName("com.qfedu.a_reflect.Person");
		
		//獲取所有的公共的方法,這裏也會獲取一些額外Object裏面公開的方法
		Method[] allPublicMethods = cls.getMethods();
		for (Method method : allPublicMethods) {
			System.out.println(method);
		}
		System.out.println("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$");

		//暴力反射
		//能夠獲取Person裏面的private方法, 並且能夠過濾掉從父類繼承而來的方法	
		Method[] allMethods = cls.getDeclaredMethods();
		for (Method method : allMethods) {
			System.out.println(method);
		}
		
		/*
		 通過反射機制,執行類中的成員方法 
		 invoke(Object obj, Object... args);
		 Object obj 這是底層調用該方法的類對象	
		 	the object the underlying method is invoked from
		 Object... args 不定參數,是執行該放的參數列表,是Object類型
		 	args is arguments used for method call
		 */
		//1.先利用反射,創建一個當前類的對象
		System.out.println("$$$$$$$$$$$$$$$$$$$$$$$$$$$$");
		Person p = (Person) cls.getConstructor(int.class, String.class).
				newInstance(1, "狗蛋");
		
		//獲取一個指定的方法,需要的參數是方法的名字字符串和參數列表,
		Method aPublicMethod = cls.getMethod("sleep", int.class);
		System.out.println(aPublicMethod);
		
		aPublicMethod.invoke(p, 15);
		System.out.println("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$");
		
		//獲取一個靜態方法
		Method aPublicStaticMethod = cls.getMethod("eat", null);
		aPublicStaticMethod.invoke(null, null);
		
		//利用暴力反射獲取一個私有化的成員方法
		Method aPrivateMethod = cls.getDeclaredMethod("testPrivate", null);
		aPrivateMethod.setAccessible(true);
		aPrivateMethod.invoke(p, null);
		
	}
}

五、Field常用方法

5.通過反射獲取Class類對象裏面所有的成員變量

package com.qfedu.a_reflect;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

/**
 * 通過反射獲取Class類對象裏面所有的成員變量
 * Field 成員變量類
 *
 */

public class GetClassField {
	public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, NoSuchMethodException {
		//加載字節碼文件,獲取Class類對象
		Class cls = Class.forName("com.qfedu.a_reflect.Person");

		//獲取所有用public修飾的成員變量
		Field[] allPublicFields = cls.getFields();

		for (Field field : allPublicFields) {
			System.out.println(field);
		}
		System.out.println("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$");
		
		//暴力反射,獲取私有化成員變量
		Field[] allFields = cls.getDeclaredFields();
		for (Field field : allFields) {
			System.out.println(field);
		}
		System.out.println("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$");
		
		//獲取一個公開的成員變量
		Field aPublicField = cls.getField("test");
		System.out.println(aPublicField);

		
		//set方法
		//set(Oject obj, Object value);
		//第一個參數: 要操作的是哪一個對象裏面的成員變量
		//第二個參數: 需要設置的值
		
		//首先獲得對象,然後調用set方法
		Person p = (Person) cls.getConstructor(int.class, String.class).
				newInstance(1, "狗蛋");
		aPublicField.set(p, 20);
		
		System.out.println(p.test);
		System.out.println("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$");
		
		//靜態的
		Field aStaticField = cls.getField("testStatic");
		System.out.println(aStaticField);
		aStaticField.set(null, 20);
		System.out.println(Person.testStatic);
		
		//暴力反射:私有化的
		System.out.println("$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$");
		Field aPrivateField = cls.getDeclaredField("id");
		System.out.println(aPrivateField);
		aPrivateField.setAccessible(true);
		aPrivateField.set(p, 10);
		System.out.println(p.getId());
	
	}
}

總結

  • 反射:
    一個Java文件,編譯之後生成的.class文件,程序運行時加載到內存中,會保存在內存的【代碼區】,而這個.class字節碼文件,會對應生成一個Class類對象,藉助於Class類對象,可以完成一系列反射操作

    • 獲取Class類對象的方式:
      1.Class.forName(“完整的類名”); 最常用!!!
      2.已知類名.class;
      3.對應類的類對象.getClass();

    • Constructor
      getConstructors();
      getDeclaredConstructors();
      getConstructor(Class… parameterTypes);
      例如:getConstructor(int.class, String.class);
      getDeclaredConstructor(Class… parameterTypes);
      Constructor的類對象.newInstance(Object… args);

    • Method
      invoke(Object obj, Object… args);

    • Field
      set(Object obj, Object value);

    • 設置權限的方式:
      setAccessible(boolean b);

    • 反射和單例類的一個平衡
      一個類如果是單例類,一般不會採用反射的方式去暴力獲取該類的構造方法,不符合單例的機制

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