Java的枚舉,注解和反射(二)
反射
什么是反射?
反射是指在程序運行期間,可以通過Reflection Api提供方法可以獲取任何類的內部的信息,并能直接操作任意類的方法和屬性。反射被視為動態(tài)語言的關鍵。
//在反射之前可以做的事情
@Test
public void Test1() {
//創(chuàng)建Person類的對象
Person person = new Person("name", 78);
//通過對象調用其內部的方法和屬性
person.setAge(20);
System.out.println(person.toString());
person.show();
//在Person類的外部,不能通過對象調用其內部的私有的結構
}
//在反射之后可以做的事情
@Test
public void Test2() throws Exception {
//通過反射創(chuàng)建Person類的對象
Class classPerson = Person.class;
Constructor constructor = classPerson.getConstructor(String.class, int.class);
Object object = constructor.newInstance("Tom", 13);
Person person = (Person) object;
System.out.println(person.toString());
//通過反射獲取Person內部的屬性和方法
Field name = classPerson.getField("name");
name.set(person, "Jack");
System.out.println(person.toString());
//調方法
Method show = classPerson.getDeclaredMethod("show");
show.invoke(person);
//調用私有的構造方法
Constructor constructor1 = classPerson.getDeclaredConstructor(String.class);
constructor1.setAccessible(true);
Person person1 = (Person) constructor1.newInstance("Marry");
System.out.println(person1);
//調用私有的方法
Method showNation = classPerson.getDeclaredMethod("showNation", String.class);
showNation.setAccessible(true);
showNation.invoke(person1, "中國");
}
結果:
未使用反射

使用反射:

Person類
package reflection;
/**
* user:ypc;
* date:2021-06-20;
* time: 13:55;
*/
public class Person {
public String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Person() {
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Person(String name, int age) {
this.age = age;
this.name = name;
}
private Person(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public void show() {
System.out.println("I am a person");
}
private String showNation(String nation) {
return nation;
}
}
反射的用途
1、在日常的第三方應用開發(fā)過程中,經常會遇到某個類的某個成員變量、方法或是屬性是私有的或是只對系統(tǒng)應用開放,這時候就可以利用Java的反射機制通過反射來獲取所需的私有成員或是方法 。
2、反射最重要的用途就是開發(fā)各種通用框架,比如在spring中,我們將所有的類Bean交給spring容器管理,無論是XML配置Bean還是注解配置,當我們從容器中獲取Bean來依賴注入時,容器會讀取配置,而配置中給的就是類的信息,spring根據這些信息,需要創(chuàng)建那些Bean,spring就動態(tài)的創(chuàng)建這些類。
Java程序中許多對象在運行時會出現(xiàn)兩種類型:運行時類型(RTTI)和編譯時類型,例如Person p = new Student();這句代碼中p在編譯時類型為Person,運行時類型為Student。程序需要在運行時發(fā)現(xiàn)對象和類的真實信息。而通過使用反射程序就能判斷出該對象和類屬于哪些類。
在類被加載完成之后,就會在堆區(qū),產生一個Class對象,這個對象就包含了存這個類的全部的結構信息。我們就可以通過這個對象看到這個類的全部的信息。
這個對象就像一面鏡子,通過這個鏡子可以看到這個類的全部的信息結構。所以稱之為反射。
正常的方式:通過引入需要導入的包類的名稱---->通過new來實例化---->得到實例化的對象
反射的方式:實例化對象---->通過getClass()方法---->得到完整的包類的名稱
反射的具體作用
- 在運行的時候判斷任意的一個對象所屬的類
- 在運行的時候構造任意一個類的對象
- 在運行 的時候判斷任意一個類的成員變量和方法
- 在運行的時獲取泛型的信息
- 在運行的時候調用任意一個類的成員變量和方法
- 在運行的時候處理注解
- 生成動態(tài)代理
反射的主要API
類名 用途
Class類 代表類的實體,在運行的Java應用程序中表示類和接口
Field類 代表類的成員變量/類的屬性
Method類 代表類的方法
Constructor類 代表類的構造方法
通過直接new 的方式和反射 都可以直接調用公共的結構,在開發(fā)的時候應該使用哪一個呢?
建議:使用new 的方式來創(chuàng)建對象。
什么時候使用反射呢?
反射的特性:動態(tài)性。就是在編譯的時候不知道要創(chuàng)建什么樣的對象的時候,可以使用反射方式來創(chuàng)建對象。比如在后端部署的服務器,前端傳來的時登錄的請求的話,就創(chuàng)建登錄對應的對象。前端傳來的是注冊所對應的請求的話,就創(chuàng)建登錄所對應的對象,這就是反射的動態(tài)特性。
反射的機制和封裝是不矛盾的呢?
封裝是告訴你不要調,反射可以調。
Class類
在Object類中定義了以下的方法,此方法將被所有子類繼承:
public final Class getClass()
以上的方法返回值的類型是一個Class類,此類是Java反射的源頭,實際上所謂反射從程序的運行結果來看也很好理解,即可以通過對象反射求出類的名稱。
對象使用反射后可以得到的信息:某個類的屬性、方法和構造器、某個類到底實現(xiàn)了哪些接口。對于每個類而言,JRE都為其保留一個不變的Class類型的對象。一個Class對象包含了特定某個結構( class/interface/enum/annotation/primitive type/void/[])的有關信息。也就是這些類型可以有Class對象:class 成員內部類、 靜態(tài)內部類、 局部內部類 、 匿名內部類、接口、數組、枚舉、注解、基本的數據類型、void等。
注意:
Class本身也是一個類
Class對象只能由系統(tǒng)建立對象
一個加載的類在JVM中只會有一個Class實例
一個Class對象對應的是一個加載到JVM中的一個.class文件
每個類的實例都會記得自己是由哪個Class實例所生成
通過Class可以完整地得到一個類中的所有被加載的結構
Class類是Reflection的根源,針對任何你想動態(tài)加載、運行的類,只有先獲得相應的Class對象,才能繼續(xù)下去。
關于java.lang.Class的理解:
類的加載過程:
程序通過javac.exe命令,生成一個或多個字節(jié)碼文件。接著使用java.exe命令來對某個字節(jié)碼文件來解釋運行。將字節(jié)碼文件加載到內存中,這個過程稱為類的加載。加載到內存中的類,就被稱為運行時類,此運行使類就稱為Class 的一個實例。
Class 實例就對應著一個運行時類,加載到內存中的運行時類,會緩存一段時間。在此時間之內,可以通過不同的方式來獲取運行時類。
獲取Class實例的四種方式
// Class 實例就對應著運行時類
@Test
public void test3() throws ClassNotFoundException {
//方式1 調用運行時類的屬性:.class
Class clazz1 = Person.class;
System.out.println(clazz1);
//方式2 通過運行時類的對象來調用
Person person = new Person();
Class clazz2 = person.getClass();
System.out.println(clazz2);
//方式3 通過Class的靜態(tài)方法 forName(類的全路徑名稱)
Class clazz3 = Class.forName("reflection.Person");
System.out.println(clazz3);
//方式4 通過類加載器:ClassLoader
ClassLoader classLoader = ReflectionDemo1.class.getClassLoader();
Class clazz4 = classLoader.loadClass("reflection.Person");
System.out.println(clazz4);
System.out.println(clazz1 == clazz2);
System.out.println(clazz1 == clazz3);
System.out.println(clazz1 == clazz4);
}

通過反射創(chuàng)建運行時類的對象
package reflection;
import org.junit.Test;
import java.util.Random;
/**
* user:ypc;
* date:2021-06-21;
* time: 20:36;
*/
public class NewInstanceDemo {
@Test
public void test1() throws IllegalAccessException, InstantiationException {
Class<Person> personClass = Person.class;
/*
newInstance()方法可以創(chuàng)建運行時類的實列,其內部也時調用內的無參的構造方法來創(chuàng)建對象
*/
Person person = personClass.newInstance();
System.out.println(person);
}
@Test
public void test2() throws IllegalAccessException, InstantiationException, ClassNotFoundException {
//在編譯的時候不知道要創(chuàng)建的對象。只有運行的時候才知道要創(chuàng)建的對象
for (int i = 0; i < 100; i++) {
int num = new Random().nextInt(3);
String classPath = "";
switch (num) {
case 0:
classPath = "java.util.Date";
break;
case 1:
classPath = "java.lang.Object";
break;
case 2:
classPath = "reflection.Person";
break;
}
Object object = getInstance(classPath);
System.out.println(object);
}
}
//創(chuàng)建一個指定類的對象
public Object getInstance(String classPath) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
Class clazz = Class.forName(classPath);
return clazz.newInstance();
}
}
通過反射獲取運行類的屬性及權限修飾符、變量名 、數據的類型
@Test
public void test1(){
//getFields()獲取的是運行時類及其父類中public的屬性
Class clazz = Person.class;
Field[] fields = clazz.getFields();
for (Field field: fields) {
System.out.println(field);
}
System.out.println();
//getDeclaredFields():獲取當前運行類的所有屬性
Field[] declaredFields = clazz.getDeclaredFields();
for (Field field:declaredFields) {
System.out.println(field);
}
}

//權限修飾符 變量名 數據的類型
@Test
public void test2(){
Class clazz = Person.class;
Field[] declaredFields = clazz.getDeclaredFields();
for (Field field:declaredFields) {
//權限修飾符
System.out.println(Modifier.toString(field.getModifiers())+"\t");
//變量名
System.out.println(field.getType()+"\t");
//數據的類型
System.out.println(field.getName()+"\t");
}
}

通過反射獲取運行時類的方法結構及其內部結構
package reflection2;
import org.junit.Test;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
/**
* user:ypc;
* date:2021-06-22;
* time: 13:32;
*/
public class MethodDemo {
@Test
public void test1() {
//獲取當前運行時類及其父類中所有聲明為public的方法
Class clazz = Person.class;
Method[] methods = clazz.getMethods();
for (Method method : methods) {
System.out.println(method);
}
System.out.println();
//獲取當前運行時類所有的方法
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method method : declaredMethods) {
System.out.println(method);
}
}
//@xxx注解
//權限修飾符 方法結構 返回值的類型
@Test
public void test2() {
Class clazz = Person.class;
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method method : declaredMethods) {
//獲取方法的注解
Annotation[] annotations = method.getDeclaredAnnotations();
for (Annotation annotation : annotations) {
System.out.print(annotation);
}
//獲取權限的修飾符
System.out.print(Modifier.toString(method.getModifiers()) + "\t");
//獲取返回值的類型
System.out.print(method.getReturnType().getName() + "\t");
//方法名
System.out.print(method.getName());
System.out.print("(");
//獲取形參的列表
Class[] parameterTypes = method.getParameterTypes();
if (!(parameterTypes == null && parameterTypes.length == 0)) {
for (int i = 0; i < parameterTypes.length; i++) {
if (i == parameterTypes.length - 1) {
System.out.print(parameterTypes[i].getName() + " args__" + i);
} else {
System.out.print(parameterTypes[i].getName() + " args__" + i + ",");
}
}
}
System.out.print(")");
//獲取方法的異常
Class[] exceptionTypes = method.getExceptionTypes();
if (exceptionTypes.length != 0) {
System.out.print("throws");
for (int i = 0; i < exceptionTypes.length; i++) {
if (i == exceptionTypes.length - 1) {
System.out.print(exceptionTypes[i].getName());
} else {
System.out.print(exceptionTypes[i].getName() + ",");
}
}
}
System.out.println();
}
}
}
test1():

test2():

通過反射獲取運行時類的構造結構
@Test
public void test1() {
Class clazz = Person.class;
//獲取運行時類的public構造方法
Constructor[] constructors = clazz.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
System.out.println();
//獲取當前運行時類中的所有的構造方法
Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
for (Constructor declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}
}

通過反射獲取運行時類的父類和父類的泛型、注解、接口、所在包
反射所使用到的包、接口、類、注釋等👇

package reflection2;
/**
* user:ypc;
* date:2021-06-21;
* time: 21:38;
*/
public interface MyInterface {
void info();
}
package reflection2;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
@Target({TYPE, FIELD,CONSTRUCTOR,ANNOTATION_TYPE,PARAMETER,LOCAL_VARIABLE,METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value() default "hello";
}
package reflection2;
import java.io.Serializable;
/**
* user:ypc;
* date:2021-06-21;
* time: 21:13;
*/
public class Creature<T> implements Serializable {
private char gender;
public double weight;
private void breath(){
System.out.println("creature breath");
}
public void eat(){
System.out.println("creature eat");
}
}
package reflection2;
@MyAnnotation(value = "hi")
public class Person extends Creature<String> implements Comparable, MyInterface {
private String name;
public int age;
int id;
Person() {
}
@MyAnnotation(value = "a")
private Person(String name) {
this.name = name;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
private String show(String nation) throws NullPointerException,CloneNotSupportedException {
System.out.println("I am form" + nation);
return nation;
}
private static void showInterests(){
System.out.println("I like programmer");
}
@MyAnnotation(value = "b")
public String show2(String publicMethod) {
return publicMethod;
}
@Override
public int compareTo(Object o) {
return 0;
}
@Override
public void info() {
System.out.println("I am a Person");
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", id=" + id +
'}';
}
}
package reflection2;
import org.junit.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.ParameterizedType;
import java.util.Arrays;
/**
* user:ypc;
* date:2021-06-22;
* time: 15:11;
*/
public class ConstructorDemo {
@Test
public void test1() {
Class clazz = Person.class;
//獲取運行時類的public構造方法
Constructor[] constructors = clazz.getConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
System.out.println();
//獲取當前運行時類中的所有的構造方法
Constructor[] declaredConstructors = clazz.getDeclaredConstructors();
for (Constructor declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}
}
@Test
public void test2() {
//獲取運行時類的父類
Class clazz = Person.class;
System.out.println(clazz.getSuperclass());
//獲取運行時類帶泛型的父類
System.out.println(clazz.getGenericSuperclass());
//獲取父類所帶的泛型
System.out.println(((ParameterizedType) clazz.getGenericSuperclass()).getActualTypeArguments()[0]);
System.out.println();
//獲取運行時類的接口
Class[] interfaces = clazz.getInterfaces();
for (Class intF : interfaces) {
System.out.println(intF);
}
System.out.println();
//獲取運行類的父類的接口
Class[] supInterfaces = clazz.getSuperclass().getInterfaces();
for (Class intF : supInterfaces) {
System.out.println(intF);
}
//獲取運行時類的包
System.out.println(clazz.getPackage());
System.out.println();
//獲取運行時類的注解
System.out.println(Arrays.toString(clazz.getAnnotations()));
;
}
}

通過反射調用運行時類的指定屬性、方法、構造方法
package reflection2;
import org.junit.Test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* user:ypc;
* date:2021-06-22;
* time: 15:57;
* 調用運行時類的指定的結構:方法、屬性、構造方法
*/
public class ReflectionDemo {
@Test
public void test1() throws Exception {
Class clazz = Person.class;
//創(chuàng)建運行時類的實例
Person person = (Person) clazz.newInstance();
//獲取指定的屬性,要求運行時類的屬性為public
Field id = clazz.getField("id");
id.set(person, 10001);
int fieldId = (int) id.get(person);
System.out.println(fieldId);
}
@Test
public void test2() throws Exception {
Class clazz = Person.class;
//創(chuàng)建運行時類的實例
Person person = (Person) clazz.newInstance();
//getDeclaredField獲取運行時類指定的屬性
Field name = clazz.getDeclaredField("name");
//保證當前的屬性是可訪問的
name.setAccessible(true);
//設置指定對象的屬性值
name.set(person, "Tom");
System.out.println(name.get(person));
}
//操作運行時類的方法
@Test
public void test3() throws Exception {
Class clazz = Person.class;
//創(chuàng)建運行時類的實例
Person person = (Person) clazz.newInstance();
//1.獲取某個指定的方法 getDeclaredMethod()第一個參數是指明獲取的方法的名稱,第二個參數是指明獲取的方法的參數列表
Method show = clazz.getDeclaredMethod("show", String.class);
//2.保證當前的方法是可訪問的
show.setAccessible(true);
//3.使用invoke()方法 第一個參數是方法的調用者,第二個參數是給方法形參賦值的實參
show.invoke(person, " CHN");
//invoke 方法的返回值就是調用的方法的返回值
Object returnValue = show.invoke(person, " CHN");
System.out.println(returnValue);
//調用靜態(tài)的方法
Method showInterests = clazz.getDeclaredMethod("showInterests");
showInterests.setAccessible(true);
showInterests.invoke(Person.class);
//如果運行時類地方沒有返回值的話,那么invoke的返回值就是null
System.out.println(showInterests.invoke(Person.class));
}
/*
調用運行時類的指定的構造方法
*/
@Test
public void test4() throws Exception {
Class clazz = Person.class;
//1.參數是構造方法的參數列表
Constructor constructor = clazz.getDeclaredConstructor(String.class);
//2.保證此構造方法是可以訪問的
constructor.setAccessible(true);
//3.調用次構造方法創(chuàng)建運行時類的對象
Person person = (Person) constructor.newInstance("Tom");
System.out.println(person);
}
}
test1():

test2():

test3():

test4():

總結
本篇文章的上半部分就到這里了,希望對你有所幫助,也希望您能夠多多關注腳本之家的更多內容!
相關文章
SpringCloud Gateway 利用 Mysql 實現(xiàn)動態(tài)路由的方法
這篇文章主要介紹了SpringCloud Gateway 利用 Mysql 實現(xiàn)動態(tài)路由的方法,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-02-02
Spring?boot詳解fastjson過濾字段為null值如何解決
這篇文章主要介紹了解決Spring?boot中fastjson過濾字段為null值的問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-07-07
詳談java編碼互轉(application/x-www-form-urlencoded)
下面小編就為大家?guī)硪黄斦刯ava編碼互轉(application/x-www-form-urlencoded)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-07-07
基于springBoot配置文件properties和yml中數組的寫法
這篇文章主要介紹了springBoot配置文件properties和yml中數組的寫法,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11

