1. 程式人生 > >Java的反射機制初步(Java基礎複習歸納系列)

Java的反射機制初步(Java基礎複習歸納系列)

目錄

一、通過反射檢視類資訊

        Java程式中的許多物件在執行時都會出現兩種型別:編譯時型別和執行時型別,如Person p = new Student(),p變數編譯時型別為Person,而執行時型別為Student。我們常常需要在程式執行時獲取物件和類的真實資訊,這就有兩種可能:

  • 一是在編譯和執行時完全知道型別的具體資訊,可以直接使用instanceof運算子進行判斷。instanceof運算子的前一個運算元通常是一個引用型變數,後一個運算元可以是一個類或介面,它用於判斷前面的物件是否是後面的類或者其子類、實現類的例項。如:

public class InstanceofTest {
	public static void main(String[] args) {
		// 宣告hello時使用Object類,則hello的編譯型別是Object,但hello變數的實際型別是String
		Object hello = "Hello";
		// 以下三條語句的返回結果均為true
		// String是Object類的子類,可以進行instanceof運算。
		System.out.println("字串是否是Object類的例項:" + (hello instanceof Object));
		System.out.println("字串是否是String類的例項:" + (hello instanceof String));
		// String實現了Comparable介面,所以返回true。
		System.out.println("字串是否是Comparable介面的例項:" + (hello instanceof Comparable));
	}
}
  • 二是在編譯時根本無法知道該物件可能屬於哪些類,程式只能依靠執行時資訊來發現該物件和類的真實資訊,這就需要用到反射。

1.獲得Class物件

        每個類在被載入之後系統都會為該類生成一個對應的Class物件,通過該Class物件就可以訪問到JVM中的這個類。獲得Class物件通常有3種方式:

  1. 使用 static Class<?> forName(String className) throws ClassNotFoundException返回與帶有給定字串名的類或介面相關聯的 Class 物件。其中,className必須是某個類的全限定類名(即需新增完整包名)。

  2. 呼叫某個類的class屬性來獲取該類對應的Class物件。如:Person.class。

  3. 呼叫某個物件getClass()方法。該方法是java.lang.Object包中的一個方法,所有物件均可呼叫,該方法會返回該物件所屬類對應的Class物件。

package reflect;

public class TestGetClass {
	public static void main(String[] args) throws ClassNotFoundException {
		Person p = new Person();
		System.out.println(Class.forName("reflect.Person").getName());
		System.out.println(Person.class.getName());
		System.out.println(p.getClass().getName());
	}
}

class Person {

}

執行結果:

其中,b.方法由於在編譯階段就可以檢查需要訪問的Class物件是否存在,且無需呼叫方法,所以更加安全,效能更好。

2.獲取Class中的資訊

        Class類中提供了大量的例項方法來獲得該Class物件的所對應類的詳細資訊,包括:獲取Class對應類所包含的構造方法、獲取Class對應類所包含的方法、獲取Class對應類所包含的Field、獲取Class對應類所包含的Annotation、獲取Class對應類所包含的內部類、獲取Class對應類所繼承的父類和所實現的介面、獲取Class對應類的修飾符、所在包以及類名等基本資訊,除此之外,還有方法可以判斷該類是否為介面、列舉、註釋型別等。

import java.lang.reflect.Constructor;

public class GetClassInfo {

	public static void main(String[] args) throws NoSuchMethodException, SecurityException {
		Class c = Student.class;
		// 返回對應形參為String型別的public構造方法
		System.out.println(c.getConstructor(String.class));
		// 返回無形參的構造方法,無論其訪問許可權為何
		System.out.println(c.getDeclaredConstructor());
		// 返回所有的構造方法,無論其訪問許可權為何
		Constructor[] csts = c.getDeclaredConstructors();
		for (Constructor cst : csts) {
			System.out.println(cst);
		}

	}
}

class Student {
	private String id;

	private Student() {

	}

	public Student(String id) {
		this.id = id;
	}
}

執行結果:

二、使用反射生成並操作物件

        java.lang.reflect包java 1.8之前及java 1.8的改變:

     

        在java.lang.reflect包中包含了Method類、Constructor類和Field類,這三個類都實現了java.lang.reflect.Member介面。程式可以通過Method物件來執行對應的方法,通過Constructor物件來呼叫對應的構造方法建立例項,通過Field物件直接訪問並修改物件的屬性值。

1.建立物件

        通過反射建立物件有兩種方式:

  1. 直接使用Class物件的newInstance()方法。該方式需要Class物件對應的類有預設的構造方法。

  2. 先使用Class物件通過getConstructor()方法獲取指定的Constructor物件,再呼叫Constructor物件的newInstance()方法建立Java物件。通過這種方法可以選擇指定的構造方法來建立例項。     

import java.util.*;
import java.io.*;
import java.lang.reflect.Constructor;

/**
 * @Title ObjectPoolFactory.java
 * @Description TODO 
 * @Author 15643
 * @Time 2018年8月28日 上午10:25:24
 * @Other obj.txt中的內容:a=java.util.Date
 */
public class ObjectPoolFactory {
	// 定義一個物件池,前面是物件名,後面是實際物件
	private Map<String, Object> objectPool = new HashMap<>();

	// 該方法只要傳入一個字串類名,程式可以根據該類名生成Java物件
	private static Object createObject(String clazzName)
			throws InstantiationException, IllegalAccessException, ClassNotFoundException {
		// 根據字串來獲取對應的Class物件
		Class<?> clazz = Class.forName(clazzName);
		// 使用clazz對應類的預設構造器建立例項
		return clazz.newInstance();
	}

	// 該方法根據指定檔案來初始化物件池,並根據配置檔案來建立物件
	public void initPool(String fileName)
			throws InstantiationException, IllegalAccessException, ClassNotFoundException {
		try (FileInputStream fis = new FileInputStream(fileName)) {
			Properties props = new Properties();
			props.load(fis);
			for (String name : props.stringPropertyNames()) {
				// 根據鍵值對的值建立一個物件,並將物件新增到物件池中
				objectPool.put(name, createObject(props.getProperty(name)));
			}
		} catch (IOException ex) {
			System.out.println("讀取" + fileName + "異常");
		}

	}

	public Object getObject(String name) {
		// 從objectPool中取出指定name對應的物件。
		return objectPool.get(name);
	}

	public static void main(String[] args) throws Exception {
		ObjectPoolFactory pf = new ObjectPoolFactory();
		pf.initPool("obj.txt");
		System.out.println(pf.getObject("a"));
		// 直接在這裡示範了,使用指定的構造方法建立物件
		Constructor<?> c = pf.getObject("a").getClass().getConstructor(long.class);
		System.out.println(c.newInstance(1000));
	}
}

         執行結果:

2.呼叫方法

        在Class類裡面有四個用來獲取方法的方法,getMethod()、getDeclaredMethod()、getDeclaredMethods()、getMethods(),前兩個返回Method物件,後兩個返回Method物件陣列。

        Method類的常用方法:  

Object  invoke(Object obj, Object... args)

對帶有指定引數的指定物件呼叫由此 Method 物件表示的底層方法。obj - 從中呼叫底層方法的物件,args - 執行方法時傳入的實參
boolean  equals(Object obj)        將此 Method 與指定物件進行比較。
String  getName()         以 String 形式返回此 Method 物件表示的方法名稱。
Class<?>[]  getParameterTypes() 按照宣告順序返回 Class 物件的陣列,這些物件描述了此 Method 物件所表示的方法的形參型別。
Class<?>  getReturnType()  返回一個 Class 物件,該物件描述了此 Method 物件所表示的方法的正式返回型別。
import java.lang.reflect.*;

public class TestMethod {

	public static void main(String[] args) throws NoSuchMethodException, SecurityException, InvocationTargetException,
			InstantiationException, IllegalAccessException {
		String field = "id";
		Class<?> cl = Employee.class;
		Object obj = cl.newInstance();
		// 使用get+首字母大寫的Field來匹配方法
		Method getMt = cl.getMethod("get" + field.substring(0, 1).toUpperCase() + field.substring(1));
		Method setMt = cl.getMethod("set" + field.substring(0, 1).toUpperCase() + field.substring(1), String.class);
        // 使用invoke()呼叫Method物件對應表示的方法
		setMt.invoke(obj, "100001");
		System.out.println("StudentID is " + getMt.invoke(obj));
	}

}

class Employee {
	private String id;

	public Employee() {

	}

	public String getId() {
		return id;
	}

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

}

        若程式需要呼叫某個物件的private方法,則可以先用Method物件呼叫setAccessible(boolean flag),當flag為true時,則指示反射的物件在使用時應該取消 Java 語言訪問許可權檢查。值為 false 則指示反射的物件應該實施 Java 語言訪問許可權檢查。 (setAccessible()方法是屬於AccessibleObject類的,由於它是Method、Constructor、Field的父類,所以它們都可以呼叫該方法從而通過反射來呼叫私有的方法、屬性等。)

3.訪問Field

        Field類提供了兩組方法來訪問、設定Field的值:

getXxx(Object obj) 獲取obj物件的該Field的屬性值。Xxx對應8個基本型別,若為引用型別,則為get(Object obj)方法
setXxx(Object obj,Xxx value) 將obj物件的該Field設定成value值。Xxx對應8個基本型別,若為引用型別,則為set(Object obj,Object value)方法
import java.lang.reflect.*;

public class FieldTest {
	public static void main(String[] args) throws Exception {
		Class cl = Employee.class;
		Object p = cl.newInstance();
		// 獲取Person的名為id的Field,使用getDeclaredField無視訪問控制符獲取field
		Field idField = cl.getDeclaredField("id");
		// 設定通過反射訪問該Field時取消訪問許可權檢查,否則IllegalAccessException
		idField.setAccessible(true);
		idField.set(p, "1000001");
		System.out.println(p);
	}
}
class Employee {
	private String id;
	public Employee() {
    }
	public String getId() {
		return id;
	}
	public void setId(String id) {
		this.id = id;
	}	
	@Override
	public String toString() {
		return "StudentID is "+id;
	}
}

        參考資料:《Java瘋狂講義》

相關推薦

Java反射機制初步Java基礎複習歸納系列

目錄 一、通過反射檢視類資訊         Java程式中的許多物件在執行時都會出現兩種型別:編譯時型別和執行時型別,如Person p = new Student(),p變數編譯時型別為Person,而執行時型別為Student。我們常常需

Java的File類Java基礎複習歸納系列

參考資料:THU 諶衛軍教學PPT 目錄 一、檔案 什麼是檔案 檔案的屬性 目錄 一、檔案 什麼是檔案 檔案是一種抽象機制,它提供了一種把資訊儲存在磁碟等儲存裝置上,並且便於以後訪問的方法。 在一個作業系統中,負責處理檔案相關

Java的多執行緒Java基礎複習歸納系列

目錄 1.程序 2.執行緒 一、執行緒概述 1.程序 在一個作業系統中,每個獨立執行的程式都可稱之為一個程序,也就是“正在執行的程式”。 程序和程式 A  proce

Java的Scanner類Java基礎複習歸納系列

   參考資料:Java doc、THU 諶衛軍老師的教學PPT Scanner類         作用:一個可以使用正則表示式來解析基本型別和字串的簡單文字掃描器。可以方便地對文字檔案中的每一個不同型別的資料單元進行訪問;         Scanner 使用分隔

Java反射機制demo—獲得並調用一個類中的方法

color 擁有 oca logs over super getmethod equals() bool 這個demo在使用反射機制操作屬性之前,主要原因是因為在.class文件字節碼中,方法排在屬性的前面。 1,獲得一個類中的方法 先看一下方法和運行結果。獲取所有的方

JAVA反射機制java.lang.reflect包

instance 檢查 item 類繼承 final win 基類 cte member 一、簡介 java.lang.reflect包提供了用於獲取類和對象的反射信息的類和接口。反射API允許對程序訪問有關加載類的字段,方法和構造函數的信息進行編程訪問。它允許在安全限制

java反射機制反射的基本概念和理解

反射就是對一個類進行解剖,把一個類中的各種成分對映成一個類 java程式執行位元組碼檔案的過程 1啟動jvm程序 2把所有相關的位元組碼加入記憶體(類載入器) 3.系統為每個位元組碼生成一個class物件 4初始化(靜態程式碼塊) 5靜態成員變數 得到一個類的C

基於NACOS和JAVA反射機制動態更新JAVA靜態常量非@Value註解

1.前言 專案中都會使用常量類檔案, 這些值如果需要變動需要重新提交程式碼,或者基於@Value註解實現動態重新整理, 如果常量太多也是很麻煩; 那麼 能不能有更加簡便的實現方式呢? 本文講述的方式是, 一個JAVA類對應NACOS中的一個配置檔案,優先使用nacos中的配置,不配置則使用程式中的預設值; 2

JAVA反射機制JavaBean的內省與BeanUtils庫

getc ron 輸出結果 下載 比較 static 完成 自動完成 規則   內省(Introspector) 是Java 語言對JavaBean類屬性、事件的一種缺省處理方法。   JavaBean是一種特殊的類,主要用於傳遞數據信息,這種類中的方法主要用於訪問私有的

Java基礎——Java反射機制

靜態 load super actual 返回 generic ref 基本 有一個 Reflection(反射)是Java被視為動態語言的關鍵,反射機制允許程序在執行期借助於Reflection API取得任何類的內部信息,並能直接操作任意對象的內部屬性及方法 Java

Java學習總結十四——java反射機制,利用反射動態創建對象

Java反射 動態創建對象 一.Java反射機制1.什麽是反射:反射就是把Java類中的各種成份影射成一個個的Java對象。例:一個類有:成員變量,方法,構造方法等,包等等信息,利用反射技術可以對一個類進行剖析,把各個組成部分影射成一個個對象。2.Java反射常用類:(1)Class類—可獲取類和類的

Java 基礎之詳解 Java 反射機制

一行代碼 strac classname for 內部 系統資源 用戶 管理 ann 一、什麽是 Java 的反射機制? ??反射(Reflection)是Java的高級特性之一,是框架實現的基礎,定義:JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有

Java反射機制詳解由淺入深

一、什麼是Java反射機制? Java 反射機制在程式執行時,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個物件,都能夠呼叫它的任意一個方法和屬性。這種 動態的獲取資訊 以及 動態呼叫物件的方法 的功能稱為 java 的反

Java 反射機制詳解

Java 反射機制詳解(一) 主要介紹以下幾方面內容 理解 Class 類 理解 Java 的類載入機制 學會使用 ClassLoader 進行類載入 理解反射的機制 掌握 Constructor、Method、Field 類的用法 理解並掌握動態代理&n

Java 反射機制詳解

Java 反射機制詳解(四) 4. 反射與泛型  定義一個泛型類: public class DAO<T> { //根據id獲取一個物件 T get(Integer id){ return null; }

Java反射機制超詳細

文章目錄 反射機制是什麼 反射機制能做什麼 案例 通過一個物件獲得完整的包名和類名 例項化Class類物件 獲取一個物件的父類與實現的介面 通過反射機制例項化一個類的物件 獲取某個類的全部屬性 獲得某個

java反射機制複習

1、Class類是被final修飾的類不能被繼承 2、Class類的例項表示正在執行的Java應用程式中的類和介面。列舉是一種類,註釋是一種介面。每個陣列屬於被對映為Class物件的一個類,所有具有相同元素型別和維數的陣列都共享該Class物件 。基本的Java型別(bo

Java 基礎】15 Java 反射機制

反射 為什麼使用反射 需求:我公司定義了一組介面,然後第三方公司按照我公司的介面 實現了一套功能,然後交給我們,但是我們公司的專案已經結束,如何實現動態載入第三方公司提供的功能。 什麼是反射 反射就是把 Java 類中的各種成分對映成一個個的 Java 物件。例

Spring學習51:IoC容器之IoC概述,JAVA反射機制,資源訪問器

spring學習5(1)  在經過了對spring框架基本開發的瞭解以及對spring boot流程的學習,《精通spring4.x…》這本書正式開始了spring的講解,我也跟隨著這本書的腳步進行學習。 IoC概述  首先需要學習的是spring的IoC技術,

軟體測試---私有方法的呼叫Java反射機制

1.私有構造方法如何例項化類? public class MSD { private int a; private String b; //無參構造方法 private MSD() { } //有參構造方法 private MSD(int a, Strin