get新技能,Java的Class與反射機制原理,讓你寫出更靈活的代碼

2020-12-20 程序猿的內心獨白

前言

最近一些朋友怎麼是說自己的代碼太複雜、太臃腫、靈活性太差,也不知道問題出在哪裡。

首先表揚一下你的精神,可以時刻關注著自己代碼的問題。

作為一個優秀的碼農,總是希望用最少的代碼來實現某一項功能,我也會經常翻看自己寫的舊代碼,看看有沒有可以提升的空間。

代碼太複雜要考慮的是有沒有「殺雞用牛刀」,架構過度的設計,代碼層級過度設計等等。

代碼臃腫一般都是為了一些簡單的功能快速上線,一直往裡面堆功能性或兼容性的代碼,這就是一直在做加法,但優秀的程式設計師應該學會做減法,時不時檢查一下自己的代碼是否存在冗餘和無用的功能性代碼。

而關於代碼的靈活性那就更有的說了,今天就來帶大家來聊聊關於這方面的技術點:反射機制。

反射機制

反射是框架設計的靈魂,java的反射機制就是增加程序的靈活性,避免將代碼寫死在程序裡

反射的概述

JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱為java語言的反射機制。

要想解剖一個類,必須先要獲取到該類的字節碼文件對象,而解剖使用的就是Class類中的方法。

所以先要獲取到每一個字節碼文件對應的Class類型的對象。

Class

在Java編譯運行過程中,當程序new一個新對象或者引用靜態成員變量時,Java虛擬機(JVM)中的類加載器子系統會將對應Class對象加載到JVM中,然後JVM再根據這個類型信息相關的Class對象創建我們需要實例對象或者提供靜態變量的引用值。

需要特別注意的是,手動編寫的每個class類,無論創建多少個實例對象,在JVM中都只有一個Class對象,即在內存中每個類有且只有一個相對應的Class對象。

類加載

實際上所有的類都是在對其第一次使用時動態加載到JVM中的,當程序創建第一個對類的靜態成員引用時,就會加載這個被使用的類。下面通過一個簡單例子來說明Class對象被加載的時間節點:

package first.test;class Student {static { System.out.println("study"); }}class Teacher{static { System.out.println("education"); }}public class watch{public static void print(Object obj) {System.out.println(obj);}public static void main(String[] args) {print("1");new Student();print("2");new Teacher();print("3");}}

在上述代碼中,每個類Student、Teacher都存在一個static語句:

運行結果:

1study2education3

Java程序在它們開始運行之前並非都被加載到內存的,其各個部分是按需加載,所以在程序使用該類時,類加載器首先會檢查這個類的Class對象是否已被加載,如果還沒有加載,默認的類加載器就會先根據類名查找.class文件(編譯後Class對象被保存在同名的.class文件中),在這個類的字節碼文件被加載時,它們必須接受相關驗證,以確保其沒有被破壞並且不包含不良Java代碼(這是java的安全機制檢測),完全沒有問題後就會被動態加載到內存中,此時相當於Class對象也就被載入內存了(畢竟.class字節碼文件保存的就是Class對象),同時也就可以被用來創建這個類的所有實例對象。

Class.forName方法

Class clazz=Class.forName("com.wk.new.Student");

forName方法是Class類的一個static成員方法,程序中所創建的所有的Class對象都源於這個Class類,因此Class類中定義的方法將適應所有Class對象。通過forName方法,我們可以獲取到類對應的Class對象引用。因此如果我們想獲取一個類的運行時類型信息並加以使用時,可以調用Class.forName()方法獲取Class對象的引用,這樣做的好處是無需通過持有該類的實例對象引用而去獲取Class對象。

Class字面常量

在Java中存在另一種方式來生成Class對象的引用,它就是Class字面常量,如下:

Class clazz = Student.class;

這種方式相對前面兩種方法更加簡單,更安全。因為它在編譯器就會受到編譯器的檢查同時由於無需調用forName方法效率也會更高,因為通過字面量的方法獲取Class對象的引用不會自動初始化該類。

理解Java的反射技術

如前言的反射機制所說,反射即是在程序動態運行的時候,對於任意一個類,可以獲得其所有的方法以及變量。

在反射包中,我們常用的類主要有:

Constructor類:表示的是Class 對象所表示的類的構造方法,利用它可以在運行時動態創建對象;Field類:Field表示Class對象所表示的類的成員變量,通過它可以在運行時動態修改成員變量的屬性值(包含private);Method類:Method表示Class對象所表示的類的成員方法,通過它可以動態調用對象的方法(包含private)。下面將對這幾個重要類進行分別說明(類中的方法其實很好區分,每個方法後面+s,表示獲取到相應類的所有對象;每個方法前面+declared,表示獲取到不僅僅是public對象,還包括private對象)。

Constructor類

獲取構造方法們//返回指定參數類型、具有public訪問權限的構造函數對象 * Constructor<?>[] getConstructors() * Constructor<T> getConstructor(類<?>... parameterTypes) //返回指定參數類型、所有聲明的(包括private)構造函數對象 * Constructor<T> getDeclaredConstructor(類<?>... parameterTypes) * Constructor<?>[] getDeclaredConstructors()

示例

Class<?> clazz = null;//獲取Class對象的引用clazz = Class.forName("com.wk.new.Student");//此時必須無參構造函數,否則將拋異常Student student = (Student) clazz.newInstance();student.setAge(3);student.setName("hali");

獲取帶String參數的public構造函數

Constructor cs =clazz.getConstructor(String.class);//創建StudentStudent student= (Student) cs.newInstance("lixiang");student.setAge(18);

取得指定帶int和String參數構造函數,該方法是私有構造

Constructor cs=clazz.getDeclaredConstructor(int.class,String.class);//由於是private必須設置可訪問,否則創建示例沒有權限(底層)cs.setAccessible(true);//創建Dog對象Dog dog= (Dog) cs.newInstance(3,"hali");

Method類

獲取成員方法們:* Method[] getMethods() * Method getMethod(String name, 類<?>... parameterTypes)* Method[] getDeclaredMethods() * Method getDeclaredMethod(String name, 類<?>... parameterTypes)

示例

Class clazz = Class.forName("com.wk.new.Student");//根據參數獲取public的Method,包含繼承自父類的方法Method method = clazz.getMethod("study",int.class,String.class);//獲取所有public的方法:Method[] methods =clazz.getMethods();//獲取當前類的方法包含private,該方法無法獲取繼承自父類的methodMethod methodSelf = clazz.getDeclaredMethod("studySelf");//獲取當前類的所有方法包含private,該方法無法獲取繼承自父類的methodMethod[] methodsSelf=clazz.getDeclaredMethods();

Field類

獲取成員變量們

* Field[] getFields() :獲取所有public修飾的成員變量 * Field getField(String name) 獲取指定名稱的 public修飾的成員變量 * Field[] getDeclaredFields() 獲取所有的成員變量,不考慮修飾符 * Field getDeclaredField(String name)

示例

Class<?> clazz = Class.forName("com.wk.new.Student");//獲取指定欄位名稱的Field類,注意欄位修飾符必須為public而且存在該欄位Field field = clazz.getField("age");//獲取所有修飾符為public的欄位,包含父類欄位Field fields[] = clazz.getFields();//獲取當前類所欄位(包含private欄位),不包含父類的欄位Field fields2[] = clazz.getDeclaredFields();

注意:Field其中的set(Object obj, Object value)方法是Field類本身的方法,用於設置欄位的值,而get(Object obj)則是獲取欄位的值,相信這個不難理解。另外,final關鍵字修飾的Field欄位是安全的,在運行時可以接收任何修改,但最終其實際值是不會發生改變的。

簡單的框架實現

一直說反射是框架的核心,接下來就簡單寫一個框架,來幫助我們創建任何類的對象(以Student為例)。編寫一個Student類

package com.wk.new;public class Student {public void study(){System.out.println("study");}}

編寫配置文件 pro.properties

className=com.wk.new.StudentmethodName=study

編寫框架類

/*** 框架類*/public class ReflectTest {public static void main(String[] args) throws Exception {//可以創建任意類的對象,可以執行任意方法//Properties對象Properties pro = new Properties();//獲取class目錄下的配置文件ClassLoader classLoader = ReflectTest.class.getClassLoader();InputStream is = classLoader.getResourceAsStream("pro.properties");pro.load(is);//獲取配置文件中定義的數據String className = pro.getProperty("className");String methodName = pro.getProperty("methodName");Class cls = Class.forName(className);//創建對象Object obj = cls.newInstance();Method method = cls.getMethod(methodName);//執行獲取的方法method.invoke(obj);}}

好了,寫到這裡一個簡單的框架就實現了

相關焦點

  • Java基礎增強篇一,Java核心功能反射機制
    Java反射機制1. java代碼在 java 語言中最核心的就是代碼的運行, 按照面向對象的思想,在調用java代碼時往往需要先創建對象,再調用方法, 而寫在方法中的即所謂的java 代碼先分析Class對象的圖解:
  • 程式設計師:什麼是Java反射機制,有什麼作用
    什麼是Java反射機制?Java反射機制是 Java 語言的一個重要特性,它在伺服器程序和中間件程序中得到了廣泛運用。在伺服器端,往往需要根據客戶的請求,動態調用某一個對象的特定方法。此外,在 ORM 中間件的實現中,運用 Java 反射機制可以讀取任意一個 JavaBean 的所有屬性,或者給這些屬性賦值通過反射機制,可以在運行的時候訪問到對象的屬性、方法、構造方法等等哪些地方用到反射機制?其實我們都用過反射機制,只是並不知道它是反射機制而已。
  • Java面試高頻考點:反射機制使用大全
    作為一個Java開發工程師,在面試的過程中,反射機制也是經常會被問到的一個問題。例如Spring的IOC實現機制,其底層都是依賴於java的反射機制,因此,這是一個非常重要的知識點。對於初學java的同學來說,掌握其使用方法很有必要。
  • 阿里P8教你Java註解與反射
    例如新的函數,對象,甚至代碼可以被引進,已有的函數可以被刪除或者結構上的一些變化。簡單說即是在運行時代碼可以根據某些條件改變自身結構。動態語言主要C#,Object-C,JavaScript,PHP,Python等。靜態語言是運行時結構不可改變的,例如Java,C,C++等。
  • 說說Java的反射機制
    一、什麼是反射動態獲取類的信息以及動態調用對象的方法稱為Java的反射(Reflection)機制。反射提供了封裝程序集、模塊和類型的對象。在Java運行時環境中,對於任意一個類的對象,可以通過反射獲取這個類的信息。
  • Java高級特性:反射機制
    概述JAVA反射機制:在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意方法和屬性;這種動態獲取信息以及動態調用對象方法的功能稱為java語言的反射機制。
  • Java反射:框架設計的靈魂
    ,就是 Java 反射機制。 = clz.getMethod("get",int.class);  System.out.println(method_get.invoke(object, 0)); 兩段代碼執行的結果是一樣的
  • 你知道java反射機制中class.forName和classloader的區別嗎?
    前兩天頭條有朋友留言說使用class.forName找不到類,可以使用classloader加載。趁此機會總結一下,正好看到面試中還經常問到。一、類加載機制上面兩種加載類的方式說到底還是為了加載一個java類,因此需要先對類加載的過程進行一個簡單的了解。
  • 如何使用JAVA反射/JAVA反射實例
    2、反射如何操作對象:package com.test.instancedemo ;class Person{ private String name ; // name屬性 private int age ;  //
  • Java編程中基礎反射詳細解析
    反射機制允許程序在運行時取得任何一個已知名稱的class的內部信息,包括包括其modifiers(修飾符),fields(屬性),methods(方法)等,並可於運行時改變fields內容或調用methods。那麼我們便可以更靈活的編寫代碼,代碼可以在運行時裝配,無需在組件之間進行原始碼連結,降低代碼的耦合度;還有動態代理的實現等等。
  • 一起學JAVA——反射技術
    反射技術是java動態特性的基石,java之所以有很多開發框架就是因為反射技術的存在。什麼是反射JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱為java語言的反射機制。
  • JAVA反序列化 - commons-collections - 1
    No.2前言這是個人學習java反序列化的第一篇利用鏈的文章,就好像P牛說的不知道為什麼網上講到java反序列化學習,上來就是cc鏈,你知道這個鏈它有多複雜麼.jpg。萌新也是理所當然的踩了這個坑,然後…..在一路質疑自己智商和"我不服"的情況下趟了過去。
  • Java中反射學習系列教程三-常用API方法
    本文是《Java中反射學習系列教程》中的第三篇文章。本文我們接著之前的講解。先來看看今天學習主要內容:4.2.2 實例化class對象的幾種方式Class對象獲取共有三種方式。> clazz1 = null;clazz1 = Class.forName("com.kaigejava.domain.Person");//2:通過new關鍵之創建的類調用其getClass方法Class<?
  • 課程設計指導——如何應用Java反射技術靈活地創建程序類對象實例
    利用JDK系統庫中的java.util.Properties類中的load方法可以加載屬性文件和利用Properties類中的getProperty方法獲得屬性文件中的指定屬性項目。但要注意getResourceAsStream方法是相對於」/」根路徑下的位置,這樣的路徑在J2EE Web應用系統的環境下是指定為絕對路徑,如果不以」/「開頭, 則路徑是相對於這個類所在的包——也就是表示從當前類的classPath路徑找指定的文件。
  • 兩分鐘學會Java反射
    什麼是反射程序在運行中,對任意一個類都能獲取其所有的屬性和方法,並且對任意一個對象都能調用其任意一個方法,這種機制被稱為Java的反射機制。反射的作用我們自己寫代碼的時候,當對一個對象的類型不確定的時候,我們就用反射的機制,來獲取這個對象的信息,我們看下面這個例子。
  • 強烈推薦:Java的反射機制和使用你們都知道嗎?
    Java中的反射機制和使用想必大家都不是很了解,今天我就和大家一起來認識和學習這些知識點吧,發射這個詞相信大家平時學習時用的不多但聽的很多,特別是在各種開源框架中,很多框架都用到反射機制,hibernate,struts ,spring等框架都是用反射機制實現的。
  • Java反射,泛型在Json中的運用
    最近項目中遇到了Json數據自動獲取的功能,不然令人想起java的反射,已經很長時間沒複習java了正好一塊連java的這一塊內容一起過一遍。java中的反射無疑就相當於java開發者的春天,在眾多的框架中也能看到它的身影,可以在運行時檢查類,接口、變量和方法等信息,可以實例化調用方法以及設置變量值等。本文主要以代碼的形式直接將反射,泛型的運用展現出來。java中的反射首先新建一個基礎類Author。
  • Java中的回調與事件機制
    代碼 Callback.javainterface Operation{void operate();}class在這個例子中,我們沒有使用內部類,而且廢了好大力氣就是為了得到一個簡單的結果,這看起來沒有什麼意義,實際上,我們只是通過這個例子來說明Java中回調機制的工作模式。接下來我們使用一個比較複雜的例子來說明在回調中使用內部類的優勢,如以下代碼所示。
  • JAVA的反射,你需要了解
    JAVA的反射,是JAVA的核心技術要點。有很多我們耳熟能詳的框架、api,其底層就是使用了java反射功能來實現的。比如jdbc加載不同的資料庫驅動,spring容器加載對象,idea的提示等等。那麼,究竟什麼是JAVA 的反射呢?JAVA反射又是怎樣實現上述功能的呢?一、什麼是JAVA反射?
  • 反射——Java高級開發必須懂得
    如果是類形式的,名稱為類的全稱,例如:c1的類類型名稱為int,c2的類類型名稱為java.lang.String。getSimpleName():不包含包名的類的名稱。提供了getReturnType()得到方法返回值類類型(比如返回值為String,則函數返回的是String.class,如果是int,函數返回的是int.class),可以通過Class的getName返回返回值的名字而不帶.class。