1. 程式人生 > >Java--(二)反射之在執行時使用反射分析物件

Java--(二)反射之在執行時使用反射分析物件

對於我這樣一個菜鳥級別的人來說,學習反射真心有點鬱悶,今天繼續分享一下學習心得。

在執行時反射分析物件,可以利用反射機制,檢視資料域的實際內容,檢視物件域的關鍵方法是Field類中的get方法,例如f是一個Field型別的物件,(通過getDeclarerFields得到的物件),obj是某個包含f域的類的物件,f.get(obj),將返回一個物件,其值為obj域當前值,直接看一下程式碼:

public class One {
	public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
	Employee harry = new Employee("Alice Adams", 75000, 1987, 12, 15);
	Class cl = harry.getClass();
	System.out.println(cl);
	Field f = cl.getDeclaredField("name");
	System.out.println(f);
	Object v = f.get(harry);
	//System.out.println(v);//不能訪問類的成員,因為name是私有的
	
	//需要呼叫Field,Method或Constructor物件的setAccessible方法
	f.setAccessible(true);

其中存在一個問題,name是私有的,所以將會丟擲一個異常,除非擁有訪問許可權,否則Java安全機制只允許檢視域還不能讀取他們的值。

為了達到目的,需要呼叫Field、Method或者Constructor物件的setAccessible方法。

下面就是讓我感到更加傷心的事情,用了一個半小時,按照書上的例子打了一個:可以供任意類使用的通用toString方法,裡面有許多方法不明白,通過看原始碼加翻譯,用了一個半小時,才有所感悟,直接看程式碼(分為兩部分):

ObjectAnalyzer:

public class ObjectAnalyzer {
	private ArrayList<Object> visited = new ArrayList<>();
	/**
	 * converts an object to a string representation than lists all fields
	 * 將一個物件轉換為字串表示比列出所有類的域
	 * @return a string with object's class name and all fields names and values 
	 * 返回一個帶有物件的類名和所有域名和值的字串。
	 */
	public String toString(Object obj) {
		if (obj==null) return "null";
			
		
		if (visited.contains(obj) ) return "...";
			//contains();方法解釋:
			//Returns {@code true} if this list contains the specified element.
			
		
		visited.add(obj);
		Class cl = obj.getClass();
		if (cl == String.class) return (String) obj;
		if (cl.isArray()) 
		{
			String r = cl.getComponentType() + "[]{";
			//getComponentType():返回一個數組的元件型別
			for (int i = 0; i < Array.getLength(obj); i++) {
				if (i>0) r += ",";
				Object val = Array.get(obj, i);
				//Array.get(obj,1);方法:返回指定陣列物件中索引元件的值。該值將自動封裝在一個物件中。如果它有原始型別。
				if (cl.getComponentType().isPrimitive())  r += val;
				else r += toString(val);
					
			}
			return r + "}";
		}
		
		String r = cl.getName();
		do {
			r += "[";
			Field[] fields =cl.getDeclaredFields();//獲取所有資料域
			AccessibleObject.setAccessible(fields, true);//將所有的域設定成可訪問的
			
			//get the name and values of all field
			for (Field f : fields) 
			{
				//  Return {@code true} if the integer argument (整形引數)includes the
			     //{@code final} modifier, {@code false} otherwise(否則).
				if (!Modifier.isStatic(f.getModifiers())) 
				{
					if(!r.endsWith("["))  r += ",";
					r +=f.getName()+ "=";
					
					
					try {
						Class t = f.getType();
						Object val = f.get(obj);//返回一個物件。其值為Obj域的當前值
						if(t.isPrimitive()) r += val;
						/**
						 * Determines if the specified {@code Class} object represents
						 * a primitive type.
						 * 確定指定的物件是否表示原始型別。
						 */
					    
						else  r += toString(val);
					
					} catch (Exception e) 
					{
						e.printStackTrace();
					}
 				}
			}
			r += "]";
			cl = cl.getSuperclass();
		}
		while (cl != null);
		
		return r;
	}	
}

ObjectAnalyzerTest:

public class ObjectAnalyzerTest {
	public static void main(String[] args) {
		ArrayList<Integer> squares = new ArrayList<>();
		for (int i = 1; i <= 5; i++) 
			squares.add(i * i);
			
			System.out.println(new ObjectAnalyzer().toString(squares));
			
			
	}

}

最終的測試結果(整理之後):

java.util.ArrayList[elementData=class java.lang.Object[]
{java.lang.Integer[value=1][][],java.lang.Integer[value=4][][],
java.lang.Integer[value=9][][],java.lang.Integer[value=16][][],
java.lang.Integer[value=25][][],null,null,null,null,null},
size=5][modCount=5][][]

打完之後腦子只有空白,然後又看原始碼,然後又上網看了一些相關部落格,才有一點點思路

這個是我認為理解透徹的 下面核心思想從此摘選:點選開啟連結

這個程式會把把執行時的非靜態域都打印出來,並且將其繼承的超類的域都打印出來。 執行進入程式的是一個ArrayList類。這個類自身有六個域,以及一個從java.util.AbstractList繼承的類modCount

    private static final long serialVersionUID = 8683452581122892189L;
    private static final int DEFAULT_CAPACITY = 10;
    private static final Object[] EMPTY_ELEMENTDATA = {};
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    transient Object[] elementData; // non-private to simplify nested class access
    private int size;

這其中非靜態類只有第五個和第六個當進入這個兩個靜態類的時候會巢狀呼叫toString。 

而modCount會在cl上提為AbstractList類的適合進行toString。

整個答案的結構為: 
ArrayList[….]  [modCount]  [  ] [  ] 
這四個方括號執行時此物件分別從ArrayList,AbstractList,AbstractCollection,Object繼承的非靜態域。這個順序正好符合ArrayList的繼承關係。 
同理在java.lang.Integer[value=1] [ ] [ ]中, 
之所以有三個括號也是因為,Integer,Number,Object類中繼承的非靜態域,只不過,Number和Object中並沒有非靜態域。所以為空。

現在回頭看ArrayList的 elementData資料。當資料有值時進入isarray程式塊。分別會對陣列中每個元素進入以此toString,當然如果元素是基本型別就不用呼叫tostring了。當然這裡面還有一個size域也需要進行tostring,但原理是一樣的。

大概就是這樣,感謝引用,希望分享給像我一樣在Java學習道路上的莘莘學子 加油!

小夥伴們可以關注我的公眾號,留言必回覆哦

Java核心基礎

----------------------------------

長按關注哦(看那兩撇小鬍子)

基礎 | 心得 | 經歷 | 更重要的是開心嘛!


相關推薦

SpringMVC執行的過程

服務器 上傳 執行過程 gin 開始 throw pack 獲取 type (DispatcherServlet在Spring當中充當一個前端控制器的角色,它的核心功能是分發請求。請求會被分發給對應處理的Java類,Spring MVC中稱為Handle。) ① 用戶

Java--反射執行使用反射分析物件

對於我這樣一個菜鳥級別的人來說,學習反射真心有點鬱悶,今天繼續分享一下學習心得。在執行時反射分析物件,可以利用反射機制,檢視資料域的實際內容,檢視物件域的關鍵方法是Field類中的get方法,例如f是一個Field型別的物件,(通過getDeclarerFields得到的物件

Java基礎語法

一、第一個java程式,與註釋. 1.hello world程式書寫. public class HelloWorld { public static void main(String[] args) { System.out.println("Hel

Java核心深入理解執行緒池ThreadPool

本文你將獲得以下資訊: 執行緒池原始碼解讀 執行緒池執行流程分析 帶返回值的執行緒池實現 延遲執行緒池實現 為了方便讀者理解,本文會由淺入深,先從執行緒池的使用開始再延伸到原始碼解讀和原始碼分析等高階內容,讀者可根據自己的情況自主選擇閱讀順序和需要了解的章節。 一、執行緒池優點

java併發程式設計多個執行緒多個鎖

多個執行緒多個鎖 多個執行緒多個鎖:多個執行緒,每個執行緒都可以拿到自己制定的鎖,分別獲得鎖之後,執行synchronized方法體的內容。就是在上次那個部落格上說道的鎖競爭的問題,是因為所有的執行緒過來以後都爭搶同一個鎖。如果說每個執行緒都可以或得到自己的鎖,這樣的話我們的鎖競爭問題就沒有了

胡八一Java:多執行

多執行緒的優勢:多程序執行需要獨立的記憶體空間,而多執行緒可以共享記憶體,從而提高了執行緒的執行效率。 建立執行緒一般使用兩種方式: 1、繼承Thread類: import java.io.IOException; public class Test extends

將MySQL去重操作優化到極致三彈連發:多執行緒並行執行

        上一篇已經將單條查重語句調整到最優,但該語句是以單執行緒方式執行。能否利用多處理器,讓去重操作多執行緒並行執行,從而進一步提高速度呢?比如我的實驗環境是4處理器,如果使用4個執行緒同時執行查重sql,理論上應該接近4倍的效能提升。一、資料分片        我

Java執行緒-併發工具類等待多執行緒完成的CountDownLatch

參考:https://www.jianshu.com/p/1716ce690637http://ifeve.com/talk-concurrency-countdownlatch/CountDownLatch是什麼CountDownLatch也叫閉鎖,在JDK1.5被引入,允

承載Host通用語言執行

然而 導入 platform parameter eth obj 模塊 之間 兼容性 承載(Host)通用語言執行時(CLR) 還有一種使用COM 的方法是是把須要集成的 F# 代碼與已有的 C/C++ 應用程序集成到一起。開成自己定義的承載通用語言執行時。通用語言

【轉】JMeter學習十八JMeter測試Java

sets interval permsize int 文件 不同 時間 結果 argument 實例: 服務為:將輸入的兩個參數通過IO存入文件; 1、打開MyEclipse,編寫Java代碼 服務: package test; import java.io.F

easyUI消息提示框

onf 間隔 asc sage utf-8 quest easy tle click <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%&

Centos在VM虛擬機中安裝Centos操作系統

logs tps 用戶名 權限 就是 最好 啟動 網絡 iyu 一、下載 阿裏雲鏡像 https://mirrors.aliyun.com/centos/7/isos/x86_64/ 下載那個 DVD版本即可。 二、安裝 我們選 典型 安裝 簡單

資料新增非同步解析重新整理大資料量redis —— SpringBootCommandLineRunner介面和ApplicationRunner介面

在spring boot應用中,我們可以在程式啟動之前執行任何任務。為了達到這個目的,我們需要使用CommandLineRunner或ApplicationRunner介面建立bean,spring boot會自動監測到它們。這兩個介面都有一個run()方法,在實現介面時需要覆蓋該方法,並使用@

第一次用伺服器專案打包執行

開發環境:IDEA 2018.1 基於SpringBoot 2.0 + Maven1. 首先打包:  在pom.xml中配置: <groupId>自定義</groupId> <artifactId>自定義</artifactId> <

verilog學習語法資料基礎篇

一、關於模組 Verilog 的基本設計單元是“模組” (block)。一個模組是由兩部分組 成的 ,一部分描述接 口,另一部分描述邏輯功能 ,即定義輸入是如何影響輸出的 。          

新手初入Java資料型別、變數和常量以及拆包和

資料型別、變數和常量以及拆包和裝包 一、資料型別 Java資料型別分為基本型別(primitive types)和引用型別(reference type),其中基本型別又分為數值型、字元型、布林型。引用型別又分為類型別、介面型別、陣列型別、null型別。這兩種大的型別包含了int

JMeter學習十八JMeter測試Java

例項: 服務為:將輸入的兩個引數通過IO存入檔案;   1、開啟MyEclipse,編寫Java程式碼 服務: package test; import java.io.File; import java.io.PrintWriter; public c

Java 第一個程式HelloWorld

      我們都知道java是跨平臺的,所說的跨平臺就是指可以在不同得平臺環境上執行,win7,win8,win10,xp,mark等,在不同得平臺上執行就要安裝不同的執行工具JRE,JDK是開發工具它裡面包含了JRE。 1.安裝好開發工具JDK 2.在

Java

一:Java程式設計 編寫Java原始檔(我們也稱之為原始碼檔案),它的副檔名為.java(人寫的); 然後通過編譯器把原始檔編譯成位元組碼檔案,位元組碼副檔名為.class(機器碼); 最後使用直譯器來執行位元組碼檔案。 編譯和執行操作需要使用DOS命令,我們要學

機器學習實踐—sklearn資料集

一、可用資料集 Kaggle網址:https://www.kaggle.com/datasets UCI資料集網址: http://archive.ics.uci.edu/ml/ scikit-learn網址:http://scikit-learn.org/sta