Java的反射機(jī)制之類加載詳解
類加載
反射機(jī)制是java實現(xiàn)動態(tài)語言的關(guān)鍵,也就是通過反射實現(xiàn)類動態(tài)加載
動態(tài)加載和靜態(tài)加載
1.靜態(tài)加載:編譯時加載相關(guān)的類,如果沒有則報錯,依賴性太強(qiáng)
2.動態(tài)加載:運行時加載需要的類,如果運行時不用該類,即使不存在該類,則不報錯,降低了依賴性
動態(tài)加載和靜態(tài)加載的區(qū)別
靜態(tài)加載是指在編譯時期確定要加載的類的類型,即通過 class 關(guān)鍵字和類名來獲取對應(yīng)類的類型,
動態(tài)加載則是在運行時期根據(jù)需要動態(tài)獲取需要加載的類的類型,例如使用 Class.forName() 方法
類加載時機(jī)
1.當(dāng)創(chuàng)建對象時 (new) //靜態(tài)加載
2.當(dāng)子類被加載時,父類也加載 //靜態(tài)加載
3調(diào)用類中的靜態(tài)成員時 //靜態(tài)加載
4.通過反射 //動態(tài)加載
類加載的五個階段
類加載器是負(fù)責(zé)把類加載到Java虛擬機(jī)中的系統(tǒng)模塊,Java中的類加載過程可以分為三個階段:
那么為什么我們有說是三個階段呢,因為在第二個階段中鏈接階段,又分為了三個部分,分別是驗證準(zhǔn)備和解析
1.加載階段:通過類的全限定名獲取其二進(jìn)制字節(jié)流,將字節(jié)流轉(zhuǎn)換成方法區(qū)內(nèi)部數(shù)據(jù)結(jié)構(gòu),在內(nèi)存中生成一個代表類的 Class 對象并返回。
2.鏈接階段:包括驗證、準(zhǔn)備和解析的過程。
- 驗證:檢查類的二進(jìn)制字節(jié)流是否符合Java虛擬機(jī)規(guī)范,并保證不會危害虛擬機(jī)自身的安全。
- 準(zhǔn)備:為類的靜態(tài)變量分配內(nèi)存,并設(shè)置默認(rèn)初始值。
代碼演示:
package com.reflection.classload_;
/**
* 我們說明一個類加載的鏈接階段-準(zhǔn)備
*/
public class ClassLoad02 {
public static void main(String[] args) {
}
}
class A {
//屬性-成員變量-字段
//分析類加載的鏈接階段-準(zhǔn)備 屬性是如何處理
//1. n1 是實例屬性, 不是靜態(tài)變量,因此在準(zhǔn)備階段,是不會分配內(nèi)存
//2. n2 是靜態(tài)變量,分配內(nèi)存 n2 是默認(rèn)初始化 0 ,而不是20
//3. n3 是static final 是常量, 他和靜態(tài)變量不一樣, 因為一旦賦值就不變 n3 = 30
public int n1 = 10;
public static int n2 = 20;
public static final int n3 = 30;
}- 解析:將類中的符號引用轉(zhuǎn)換成直接引用,這個過程包括方法和字段的解析。
3.初始化階段:
到初始化階段,才真正開始執(zhí)行類中定義的 Java 程序代碼,此階段是執(zhí)行<clinit>() 方法的過程。
<clinit>() 方法是由編譯器按語句在源文件中出現(xiàn)的順序,依次自動收集類中的所有靜態(tài)變量的賦值動作和靜態(tài)代碼塊中的語句,并進(jìn)行合并。
虛擬機(jī)會保證一個類的 <clinit>()方法在多線程環(huán)境中被正確地加鎖、同步,如果多個線程同時去初始化一個類,那么只會有一個線程去執(zhí)行這個類的<clinit>()方法,其他線程都需要阻塞等待,直到活動線程執(zhí)行 <clinit>() 方法完畢 [debug源碼]
代碼演示:
package com.reflection.classload_;
/**
* 演示類加載-初始化階段
*/
public class ClassLoad03 {
public static void main(String[] args) throws ClassNotFoundException {
//分析
//1. 加載B類,并生成 B的class對象
//2. 鏈接 num = 0
//3. 初始化階段
// 依次自動收集類中的所有靜態(tài)變量的賦值動作和靜態(tài)代碼塊中的語句,并合并
/*
clinit() {
System.out.println("B 靜態(tài)代碼塊被執(zhí)行");
//num = 300;
num = 100;
}
合并: num = 100
*/
//new B();//類加載 如果是使用的是new B()一個對象,那么還會導(dǎo)致構(gòu)造器被執(zhí)行,如果使用的是類的靜態(tài)屬性,那么也會導(dǎo)致類的加載,但是不會導(dǎo)致構(gòu)造器被執(zhí)行
//System.out.println(B.num);//100, 如果直接使用類的靜態(tài)屬性,也會導(dǎo)致類的加載
//看看加載類的時候,是有同步機(jī)制控制
/*
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
//正因為有這個機(jī)制,才能保證某個類在內(nèi)存中, 只有一份Class對象
synchronized (getClassLoadingLock(name)) {
//....
}
}
*/
B b = new B();
}
}
class B {
static {
System.out.println("B 靜態(tài)代碼塊被執(zhí)行");
num = 300;
}
static int num = 100;
public B() {//構(gòu)造器
System.out.println("B() 構(gòu)造器被執(zhí)行");
}
}獲取類的結(jié)構(gòu)信息
Java反射中可以使用以下方法來獲取類的結(jié)構(gòu)信息:
- getDeclaredFields() :獲取類的所有屬性(Field),包括私有屬性。
- getFields() :獲取類的所有公有屬性。
- getDeclaredMethods() :獲取類的所有方法(Method),包括私有方法。
- getMethods() :獲取類的所有公有方法。
- getDeclaredConstructors() :獲取類的所有構(gòu)造器(Constructor),包括私有構(gòu)造器。
- getConstructors() :獲取類的所有公有構(gòu)造器。
- getDeclaredClasses() :獲取類的所有內(nèi)部類。
- getClasses() :獲取類的所有公有內(nèi)部類。
- getDeclaredAnnotations() :獲取類的所有注解。
- getSuperclass() :獲取類的父類。
- getInterfaces() :獲取類實現(xiàn)的接口。
除了上述方法之外,還有一些其他方法可以用于獲取類的結(jié)構(gòu)信息,如 getDeclaredField() 、 getMethod() 、 getConstructor() 等,這些方法可以用于獲取指定名稱的屬性、方法或者構(gòu)造器的信息。
代碼演示;
package com.reflection;
import org.testng.annotations.Test;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* 演示如何通過反射獲取類的結(jié)構(gòu)信息
*/
public class ReflectionUtils {
public static void main(String[] args) {
}
@Test
public void api_02() throws ClassNotFoundException, NoSuchMethodException {
//得到Class對象
Class<?> personCls = Class.forName("com.reflection.Person");
//getDeclaredFields:獲取本類中所有屬性
//規(guī)定 說明: 默認(rèn)修飾符 是0 , public 是1 ,private 是 2 ,protected 是 4 , static 是 8 ,final 是 16
Field[] declaredFields = personCls.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println("本類中所有屬性=" + declaredField.getName()
+ " 該屬性的修飾符值=" + declaredField.getModifiers()
+ " 該屬性的類型=" + declaredField.getType());
}
//getDeclaredMethods:獲取本類中所有方法
Method[] declaredMethods = personCls.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println("本類中所有方法=" + declaredMethod.getName()
+ " 該方法的訪問修飾符值=" + declaredMethod.getModifiers()
+ " 該方法返回類型" + declaredMethod.getReturnType());
//輸出當(dāng)前這個方法的形參數(shù)組情況
Class<?>[] parameterTypes = declaredMethod.getParameterTypes();
for (Class<?> parameterType : parameterTypes) {
System.out.println("該方法的形參類型=" + parameterType);
}
}
//getDeclaredConstructors:獲取本類中所有構(gòu)造器
Constructor<?>[] declaredConstructors = personCls.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println("====================");
System.out.println("本類中所有構(gòu)造器=" + declaredConstructor.getName());//這里老師只是輸出名
Class<?>[] parameterTypes = declaredConstructor.getParameterTypes();
for (Class<?> parameterType : parameterTypes) {
System.out.println("該構(gòu)造器的形參類型=" + parameterType);
}
}
}
//第一組方法API
@Test
public void api_01() throws ClassNotFoundException, NoSuchMethodException {
//得到Class對象
Class<?> personCls = Class.forName("com.reflection.Person");
//getName:獲取全類名
System.out.println(personCls.getName());//com.reflection.Person
//getSimpleName:獲取簡單類名
System.out.println(personCls.getSimpleName());//Person
//getFields:獲取所有public修飾的屬性,包含本類以及父類的
Field[] fields = personCls.getFields();
for (Field field : fields) {//增強(qiáng)for
System.out.println("本類以及父類的屬性=" + field.getName());
}
//getDeclaredFields:獲取本類中所有屬性
Field[] declaredFields = personCls.getDeclaredFields();
for (Field declaredField : declaredFields) {
System.out.println("本類中所有屬性=" + declaredField.getName());
}
//getMethods:獲取所有public修飾的方法,包含本類以及父類的
Method[] methods = personCls.getMethods();
for (Method method : methods) {
System.out.println("本類以及父類的方法=" + method.getName());
}
//getDeclaredMethods:獲取本類中所有方法
Method[] declaredMethods = personCls.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
System.out.println("本類中所有方法=" + declaredMethod.getName());
}
//getConstructors: 獲取所有public修飾的構(gòu)造器,包含本類
Constructor<?>[] constructors = personCls.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println("本類的構(gòu)造器=" + constructor.getName());
}
//getDeclaredConstructors:獲取本類中所有構(gòu)造器
Constructor<?>[] declaredConstructors = personCls.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println("本類中所有構(gòu)造器=" + declaredConstructor.getName());//這里老師只是輸出名
}
//getPackage:以Package形式返回 包信息
System.out.println(personCls.getPackage());//com.reflection
//getSuperClass:以Class形式返回父類信息
Class<?> superclass = personCls.getSuperclass();
System.out.println("父類的class對象=" + superclass);//
//getInterfaces:以Class[]形式返回接口信息
Class<?>[] interfaces = personCls.getInterfaces();
for (Class<?> anInterface : interfaces) {
System.out.println("接口信息=" + anInterface);
}
//getAnnotations:以Annotation[] 形式返回注解信息
Annotation[] annotations = personCls.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println("注解信息=" + annotation);//注解
}
}
}
class A {
public String hobby;
public void hi() {
}
public A() {
}
public A(String name) {
}
}
interface IA {
}
interface IB {
}
@Deprecated
class Person extends A implements IA, IB {
//屬性
public String name;
protected static int age; // 4 + 8 = 12
String job;
private double sal;
//構(gòu)造器
public Person() {
}
public Person(String name) {
}
//私有的
private Person(String name, int age) {
}
//方法
public void m1(String name, int age, double sal) {
}
protected String m2() {
return null;
}
void m3() {
}
private void m4() {
}
}通過反射創(chuàng)建對象
1.方式一:調(diào)用類中的public修飾的無參構(gòu)造器
2.方式二:調(diào)用類中的指定構(gòu)造器
3.Class類相關(guān)方法
- newlnstance : 調(diào)用類中的無參構(gòu)造器,獲取對應(yīng)類的對象
- getConstructor(Class...clazz):根據(jù)參數(shù)列表,獲取對應(yīng)的public構(gòu)造器對象
- getDecalaredConstructor(Class...clazz):根據(jù)參數(shù)列表,獲取對應(yīng)的所有構(gòu)造器對象
4.Constructor類相關(guān)方法
- setAccessible:暴破
- newlnstance(Object...obj):調(diào)用構(gòu)造器
應(yīng)用案例1
代碼演示:
package com.reflection;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
/**
* 演示通過反射機(jī)制創(chuàng)建實例
*/
public class ReflecCreateInstance {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
//1. 先獲取到User類的Class對象
Class<?> userClass = Class.forName("com.reflection.User");
//2. 通過public的無參構(gòu)造器創(chuàng)建實例
Object o = userClass.newInstance();
System.out.println(o);
//3. 通過public的有參構(gòu)造器創(chuàng)建實例
/*
constructor 對象就是
public User(String name) {//public的有參構(gòu)造器
this.name = name;
}
*/
//3.1 先得到對應(yīng)構(gòu)造器
Constructor<?> constructor = userClass.getConstructor(String.class);
//3.2 創(chuàng)建實例,并傳入實參
Object hsp = constructor.newInstance("hsp");
System.out.println("hsp=" + hsp);
//4. 通過非public的有參構(gòu)造器創(chuàng)建實例
//4.1 得到private的構(gòu)造器對象
Constructor<?> constructor1 = userClass.getDeclaredConstructor(int.class, String.class);
//4.2 創(chuàng)建實例
//暴破【暴力破解】 , 使用反射可以訪問private構(gòu)造器/方法/屬性, 反射面前,都是紙老虎
constructor1.setAccessible(true);
Object user2 = constructor1.newInstance(100, "張三豐");
System.out.println("user2=" + user2);
}
}
class User { //User類
private int age = 10;
private String name = "明天吃火鍋";
public User() {//無參 public
}
public User(String name) {//public的有參構(gòu)造器
this.name = name;
}
private User(int age, String name) {//private 有參構(gòu)造器
this.age = age;
this.name = name;
}
public String toString() {
return "User [age=" + age + ", name=" + name + "]";
}
}訪問屬性
1.根據(jù)屬性名獲取Field對象
Field f = clazz對象.getDeclaredField(屬性名);
2.暴破 : fsetAccessible(true); //f 是Field
3.訪問 f.set(o,值); // o 表示對象
syso(f.get(o));//o 表示對象
4.注意: 如果是靜態(tài)屬性,則set和get中的參數(shù)o,可以寫成null
代碼演示:
注意訪問私有的屬性需要進(jìn)行爆破設(shè)置這個屬性 name.setAccessible(true);
package com.reflection;
import java.lang.reflect.Field;
/**
* 演示反射操作屬性
*/
public class ReflecAccessProperty {
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchFieldException {
//1. 得到Student類對應(yīng)的 Class對象
Class<?> stuClass = Class.forName("com.reflection.Student");
//2. 創(chuàng)建對象
Object o = stuClass.newInstance();//o 的運行類型就是Student
System.out.println(o.getClass());//Student
//3. 使用反射得到age 屬性對象
Field age = stuClass.getField("age");
age.set(o, 88);//通過反射來操作屬性
System.out.println(o);//
System.out.println(age.get(o));//返回age屬性的值
//4. 使用反射操作name 屬性
Field name = stuClass.getDeclaredField("name");
//對name 進(jìn)行暴破, 可以操作private 屬性
name.setAccessible(true);
//name.set(o, "Jack");
name.set(null, "jack~");//因為name是static屬性,因此 o 也可以寫出null
System.out.println(o);
System.out.println(name.get(o)); //獲取屬性值
System.out.println(name.get(null));//獲取屬性值, 要求name是static
}
}
class Student {//類
public int age;
private static String name;
public Student() {//構(gòu)造器
}
public String toString() {
return "Student [age=" + age + ", name=" + name + "]";
}
}訪問方法
1.根據(jù)方法名和參數(shù)列表獲取Method方法對象:
- Method m = clazz.getDeclaredMethod(方法名,XX.class);//得到本類的所有方法
2.獲取對象 : Object o=clazz.newlnstance():
3暴破: m.setAccessible(true);
4訪問 : Object returnValue = m.invoke(o,實參列表)://o 就是對象
5注意:如果是靜態(tài)方法,則invoke的參數(shù)o,可以寫成null!
本章習(xí)題
第一題
代碼演示:
要求:
1.定義PrivateTest類,有私有name屬性,并且屬性值為hellokitty 2.提供getName的公有方法 3創(chuàng)建PrivateTest的類,利用Class類得到私有的name屬性,修改私有的name屬性值并調(diào)用getName()的方法打印name屬性值
package com.homework;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/*
1.定義PrivateTest類,有私有name屬性,并且屬性值為hellokitty
2.提供getName的公有方法
3創(chuàng)建PrivateTest的類,利用Class類得到私有的name屬性,修改私有的name屬性值并調(diào)用getName()的方法打印name屬性值
*/
public class Homework01 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException {
//得到class類對象
Class<?> aClass = Class.forName("com.homework.PrivateTest");
//創(chuàng)建對象實例
Object o = aClass.newInstance();
//得到所有的字段
Field name = aClass.getDeclaredField("name");
//因為name是private私有的 因此需要進(jìn)行爆破
name.setAccessible(true);
//修改name的值
name.set(o, "你好");
//得到所有方法,因為這里方法是public的因此不需要使用爆破
Method getName = aClass.getMethod("getName");
Object invoke = getName.invoke(o);
System.out.println("name的屬性值是=" + invoke);
}
}
class PrivateTest {
private String name = "helloKitty";
public String getName() {
return name;
}
}第二題
代碼演示:
要求:
利用Class類的forName方法得到File類的class 對象 在控制臺打印File類的所有構(gòu)造器 通過newlnstance的方法創(chuàng)建File對象,并創(chuàng)建E:\ mynew.txt文件 提示:創(chuàng)建文件的正常寫法如下:File file = new File("d: aa.txt");file.createNewFile();
package com.homework;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/*
利用Class類的forName方法得到File類的class 對象
在控制臺打印File類的所有構(gòu)造器
通過newlnstance的方法創(chuàng)建File對象,并創(chuàng)建E:\ mynew.txt文件
提示:創(chuàng)建文件的正常寫法如下:File file = new File("d: aa.txt");file.createNewFile();
*/
public class Homework02 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
//得到File類 的class對象
Class<?> aClass = Class.forName("java.io.File");
//得到所有的構(gòu)造器,并輸出
Constructor<?>[] declaredConstructors = aClass.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}
//得到一個指定的構(gòu)造器
Constructor<?> constructor = aClass.getConstructor(String.class);
//指定文件創(chuàng)建的路徑
String filePath = "d:\\mynew.txt";
//創(chuàng)建文件對象
/*
此時o相當(dāng)于執(zhí)行了這么一個步驟 File file = new File(filePath);
此時o就是File對象的實例
*/
Object o = constructor.newInstance(filePath);
//得到createNewFile方法的對象
/*
通過反射,得到file類中的一個方法createNewFIle()方法的對象
*/
Method createNewFile = aClass.getMethod("createNewFile");
//最后使用反射調(diào)用方法,相當(dāng)于執(zhí)行了file.createNewFIle();
//只不過一個是使用反射的方式創(chuàng)建,一個是使用new 出來的File對象的實例進(jìn)行創(chuàng)建,在反射中,萬物皆對象,因此可以使用這種方法創(chuàng)建
createNewFile.invoke(o);
//此時o的運行類型是Java.io.File 因為在反射中所有的編譯類型都是Object
System.out.println(o.getClass());
System.out.println("文件創(chuàng)建成功");
}
}到此這篇關(guān)于Java的反射機(jī)制之類加載詳解的文章就介紹到這了,更多相關(guān)Java的反射機(jī)制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
從Myeclipse 導(dǎo)入到eclipse中無法識別為 web項目 問題的解決步驟
這篇文章主要介紹了從Myeclipse 導(dǎo)入到eclipse中無法識別為 web項目 問題的解決步驟,需要的朋友可以參考下2018-05-05
spring AOP定義AfterThrowing增加處理實例分析
這篇文章主要介紹了spring AOP定義AfterThrowing增加處理,結(jié)合實例形式分析了spring面向切面AOP定義AfterThrowing相關(guān)實現(xiàn)步驟與操作技巧,需要的朋友可以參考下2020-01-01
Spring Boot應(yīng)用監(jiān)控的實戰(zhàn)教程
Spring Boot 提供運行時的應(yīng)用監(jiān)控和管理功能,下面這篇文章主要給大家介紹了關(guān)于Spring Boot應(yīng)用監(jiān)控的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-05-05
PostgreSQL Docker部署+SpringBoot集成方式
本文介紹了如何在Docker中部署PostgreSQL和pgadmin,并通過SpringBoot集成PostgreSQL,主要步驟包括安裝PostgreSQL和pgadmin,配置防火墻,創(chuàng)建數(shù)據(jù)庫和表,以及在SpringBoot中配置數(shù)據(jù)源和實體類2024-12-12
Java利用poi實現(xiàn)word表格轉(zhuǎn)excel
這篇文章主要為大家詳細(xì)介紹了Java如何利用poi實現(xiàn)word表格轉(zhuǎn)excel,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2025-03-03

