日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java学习笔记7-2——注解与反射

發布時間:2023/12/10 java 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java学习笔记7-2——注解与反射 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

目錄

  • 理解 Class 類并獲取 Class 實例
    • Class類
    • 獲取 Class 類的實例
    • 哪些類型可以有Class對象
    • 所有類型的Class對象
  • 從內存角度分析類加載【重點】
    • 類加載的過程
    • 什么時候會發生類的初始化
    • 類加載器
  • 獲取運行時類的完整結構
    • 有了Class對象能做什么
  • 性能對比分析
  • 通過反射操作泛型
  • 通過反射操作注解

理解 Class 類并獲取 Class 實例

1.先定義一個實體類

package com.cheng.reflection;public class User {// 屬性// getter setter// ... }

2.主程序

public class Test01 {public static void main(String[] args) throws Exception {// 通過反射獲取類的 Class 對象Class<?> aClass = Class.forName("com.cheng.reflection.User");System.out.println(aClass);} }

輸出:

class com.cheng.reflection.User

一個類在內存中只有一個 Class 對象,一個類被加載之后,類的整個結構都會被封裝在 Class 對象中

Class類

在 Object 類中定義了以下方法,此方法將被所有子類繼承

public final native Class<?> getClass();

上面的方法返回值的類型是一個 Class 類,此類是 Java 反射的源頭,實際上所謂反射從程序的運行結果來看也很好理解,即:可以通過對象反射求出類的名稱

對于每個類而言,JRE 都為其保留了一個不變的 Class 類型的對象。一個 Class 對象包含了特定某個結構(class / interface / enum / annotation / primitive type/void / [])

  • Class 本身也是一個類
  • Class 對象只能由系統建立對象
  • 一個加載的類在 JVM 中只會有一個 Class 實例
  • 一個 Class 對象對應的是一個加載到 JVM 中的一個 .class 文件
  • 每個類的實例都會記得自己是由哪個 Class 實例所生成
  • 通過 Class 可以完整地得到一個類中的所有被加載的結構
  • Class 類是 Reflection 的根源,針對任何你想動態加載、運行的類、唯有先獲得響應的 Class 對象

Class 類的常用方法:

方法說明
static Class forName(String name)返回指定類名 name 的 Class 對象
Object newInstance()調用缺省構造函數,返回 Class 對象的一個實例
getName()返回此 Class 對象所表示的實體(類、接口、數組類、或 void)的名稱
Class getSuperClass()返回當前 Class 對象的父類的 Class 對象
Class[] getInterfaces()獲取當前 Class 對象的接口
ClassLoader getClassLoader()返回該類的類加載器
Constructor[] getConstructors()返回一個包含某些 Constructor 對象的數組
Method getMethod(String name, Class… T)返回一個 Method 對象,此對象的形參類型為 paramType
Field[] getDeclaredFields()返回 Field 對象的一個數組

獲取 Class 類的實例

public class Test01 {public static void main(String[] args) throws Exception {// 方式一:forName 獲得Class aClass = Class.forName("com.java.demo.reflect.User");System.out.println(aClass);// 方式二:通過對象獲得Class aClass1 = new User().getClass();System.out.println(aClass1);// 方式三:通過類名.class 獲得Class<User> aClass2 = User.class;System.out.println(aClass2);} }

輸出:

class com.cheng.reflection.User
class com.cheng.reflection.User
class com.cheng.reflection.User

哪些類型可以有Class對象

  • class:外部類,成員(成員內部類、靜態內部類),局部內部類,匿名內部類
  • interface:接口
  • []:數組
  • enum:枚舉
  • annotation:注解@inerface
  • primitive type:基本數據類型
  • void

所有類型的Class對象

public class Test02 {public static void main(String[] args) {Class c1 = Object.class; // 類Class c2 = Comparable.class; // 接口Class c3 = String[].class; // 一維數組Class c4 = int[][].class; // 二維數組Class c5 = Override.class; // 注解Class c6 = ElementType.class; // 枚舉Class c7 = Integer.class; // 基本數據類型Class c8 = void.class; // voidClass c9 = Class.class; // ClassSystem.out.println(c1);System.out.println(c2);System.out.println(c3);System.out.println(c4);System.out.println(c5);System.out.println(c6);System.out.println(c7);System.out.println(c8);System.out.println(c9);// 只要元素類型與維度一樣,就是同一個Classint[] a = new int[10];int[] b = new int[100];System.out.println(a.getClass().hashCode());System.out.println(b.getClass().hashCode());} }

輸出:

class java.lang.Object
interface java.lang.Comparable
class [Ljava.lang.String;
class [[I
interface java.lang.Override
class java.lang.annotation.ElementType
class java.lang.Integer
void
class java.lang.Class
2133927002
2133927002

從內存角度分析類加載【重點】

【重點】可參考B站視頻:【狂神說Java】注解和反射


(方法區可以看作特殊的堆內存)

類加載的過程


加載:

  • 將 class 文件字節碼內容加載到內存中,并將這些靜態數據轉換成方法區的運行時數據結構,然后生成一個代表這個類的java.lang.Class 對象

鏈接:
(將 Java 類的二進制代碼合并到 JVM 的運行狀態之中的過程)

  • 驗證:確保加載的類信息符合 JVM 規范,沒有安全方面的問題
  • 準備:正式為類變量(static)分配內存并設置類變量默認初始值的階段,這些內存都將在方法區中進行分配
  • 解析:虛擬機常量池內的符號引用(常量名)替換為直接引用(地址)的過程

初始化:

  • (JVM)執行類構造器< clint>() 方法的過程,類構造器< clint>()方法是由編譯期自動收集類中所有類變量的賦值動作和靜態代碼塊中的語句合并產生的(類構造器是構造類信息的,不是構造該類對象的構造器)
  • 當初始化一個類的時候,如果發現其父類還沒有進行初始化,則需要先觸發其父類的初始化
  • 虛擬機會保證一個類的< clint>()方法在多線程環境中被正確加鎖和同步
public class Test03 {public static void main(String[] args) {/*** 1、加載到內存,會產生一個類對應的 Class 對象* 2、鏈接,鏈接結束后 m = 0* 3、初始化<clint>() {System.out.println("A 類靜態代碼塊初始化");m = 300;m = 100;}m = 100;*/A a = new A();System.out.println(A.m);} }class A {static {System.out.println("A 類靜態代碼塊初始化");m = 300;}static int m = 100;public A() {System.out.println("A 類的無參構造函數初始化");} }

輸出:

A 類靜態代碼塊初始化
A 類的無參構造函數初始化
100

什么時候會發生類的初始化

類的主動引用(一定會發生類的初始化):

  • 當虛擬機啟動,先初始化main方法所在的類
  • new一個類的對象
  • 調用類的靜態成員(除了final常量)和靜態方法
  • 使用java.lang.reflect包的方法對類進行反射調用
  • 當初始化一個類,如果父類沒有被初始化,則先會初始化它的父類

類的被動引用(不會發生類的初始化):

  • 當訪問一個靜態域時,只有真正聲明這個域的類才會被初始化。如:當通過子類引用父類的靜態變量,不會導致子類初始化
  • 通過數組定義類引用,不會觸發此類的初始化
  • 引用常量不會觸發此類的初始化(常量在鏈接階段就存入調用類的常量池中了)
// 測試類什么時候會被初始化 public class Test04 {static {System.out.println("Main被加載");}public static void main(String[] args) throws ClassNotFoundException {// 1. 主動引用//Son son = new Son(); //輸出:Main被加載 父類被加載 子類被加載// 2. 反射也會產生主動引用//Class.forName("com.cheng.reflection.Son"); //輸出:Main被加載 父類被加載 子類被加載// 3.不會產生類的引用方法://System.out.println(Son.b); //輸出:Main被加載 父類被加載 2// 數組只是賦予一個名字,開拓了一個空間,并沒有加載類//Son[] array = new Son[100]; //輸出:Main被加載//System.out.println(Son.M); 輸出:Main被加載 1} }class Father{static int b = 2;static {System.out.println("父類被加載");} } class Son extends Father{static {System.out.println("子類被加載");m = 300;}static int m = 100;static final int M = 1; }

類加載器

類加載器作用:

package com.cheng.reflection;public class Test05 {public static void main(String[] args) throws ClassNotFoundException {// 獲取系統類加載器ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();System.out.println(systemClassLoader);// 獲取系統類加載器的父類加載器——>擴展類加載器ClassLoader parent = systemClassLoader.getParent();System.out.println(parent);// 獲取擴展類加載器的父類加載器——>根加載器(用C/C++編寫,Java讀取不到,返回null)ClassLoader parent1 = parent.getParent();System.out.println(parent1);System.out.println("===============================");// 測試當前類是哪個加載器加載的ClassLoader classLoader = Class.forName("com.cheng.reflection.Test05").getClassLoader();System.out.println(classLoader);// 測試JDK內置類(如java.lang.Object)是誰加載的classLoader = Class.forName("java.lang.Object").getClassLoader();System.out.println(classLoader);System.out.println("===============================");// 獲得系統類加載器可以加載的路徑System.out.println(System.getProperty("java.class.path"));} }

輸出:

sun.misc.Launcher$AppClassLoader@18b4aac2 sun.misc.Launcher$ExtClassLoader@7f31245a null =============================== sun.misc.Launcher$AppClassLoader@18b4aac2 null =============================== C:\Program Files\Java\jdk1.8.0_201\jre\lib\charsets.jar; C:\Program Files\Java\jdk1.8.0_201\jre\lib\deploy.jar; C:\Program Files\Java\jdk1.8.0_201\jre\lib\ext\access-bridge-64.jar; ...(內容過多,不予展示)

獲取運行時類的完整結構

通過反射獲取運行時類的完整結構:

  • 實現的全部接口
  • 所繼承的父類
  • 全部的構造器
  • 全部的方法
  • 全部的 Field
  • 注解
package com.cheng.reflection;import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method;// 獲取類的信息 public class Test06 {public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {// 獲得Class對象Class c1 = Class.forName("com.cheng.reflection.User");// 獲得類的名字System.out.println(c1.getName()); // 獲得包名 + 類名System.out.println(c1.getSimpleName());// 獲得類的屬性System.out.println("=============================");Field[] fields = c1.getFields(); // 只能找到public屬性fields = c1.getDeclaredFields(); // 找到全部的屬性for (Field field : fields){System.out.println(field);}// 獲得指定屬性的值Field name = c1.getDeclaredField("name");System.out.println(name);// 獲得類的方法System.out.println("=============================");Method[] methods = c1.getMethods();// 獲得本類及其父類的全部public方法for (Method method : methods){System.out.println("正常的:" + method);}methods = c1.getDeclaredMethods(); // 獲得本類的全部方法for (Method method : methods){System.out.println("getDeclaredMethods:" + method);}// 獲得指定方法// 重載Method getName = c1.getMethod("getName", null);Method setName = c1.getMethod("setName", String.class);System.out.println(getName);System.out.println(setName);// 獲得指定的構造器System.out.println("=============================");Constructor[] constructors = c1.getConstructors();for (Constructor constructor : constructors){System.out.println(constructor);}constructors = c1.getDeclaredConstructors();for (Constructor constructor : constructors){System.out.println(constructor);}// 獲得指定的構造器(例如某個有參構造)Constructor declaredConstructor = c1.getDeclaredConstructor(int.class, String.class, int.class);System.out.println("指定的:" + declaredConstructor);} }

有了Class對象能做什么

**創建類的對象:**調用Class對象的newInstance()方法

  • 類必須有一個無參構造器
  • 類的構造器的訪問權限需要足夠
  • 沒有無參構造器也能創建對象。只要在操作的時候明確調用類中的構造器,并將參數傳遞進去之后,才可以實例化操作。
    步驟如下:

  • 通過Class類的getDeclaredConstructor(Class … parameterTypes)取得本類的指定形參類型的構造器
  • 向構造器的形參中傳遞一個對象數組進去,里面包含了構造器中所需的各個參數
  • 通過Constructor實例化對象
  • 調用指定的方法:
    通過反射,調用類中的方法,通過Method類完成

    • 通過Class類的getMethod(String name,Class…parameterTypes)方法取得一個Method對象,并設置此方法操作時所需要的參數類型
    • 之后使用Object invoke(Object obj, Object[] args)進行調用,并向方法中傳遞要設置的obj對象的參數信息

      Object invoke(Object obj, Object… args)
    • Object對應原方法的返回值,若原方法無返回值,此時返回null
    • 若原方法為靜態方法,此時形參Object obj可為null
    • 若原方法形參列表為空,則Object[] args可為null
    • 若原方法聲明為private,則需要在調用此invo()方法前,顯式調用對象的setAccessible(true)方法,即可訪問private方法

    setAccessible

    • Method、Field、Constructor對象都有setAccessible()方法
    • setAccessible作用是啟動和禁用訪問安全檢查的開關
    • 參數值為true則指示反射的對象在使用時應該取消Java語言訪問檢查,使得原本無法訪問的私有成員也可以訪問??梢蕴岣叻瓷涞男?。如果代碼中必須使用反射,而該句代碼需要頻繁地被調用,請設置為true
    package com.cheng.reflection;import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method;public class Test07 {public static void main(String[] args) throws Exception {// 獲得class對象Class c1 = Class.forName("com.cheng.reflection.User");// 構造一個對象User user = (User)c1.newInstance(); // 本質是調用了類的無參構造器 如果類沒有無參構造器就會有異常System.out.println(user);// 通過構造器創建對象Constructor constructor = c1.getDeclaredConstructor(int.class, String.class, int.class);User user2 = (User)constructor.newInstance(001, "張三", 20);System.out.println(user2);// 通過反射調用普通方法User user3 = (User)c1.newInstance();// 通過反射獲取一個方法Method setName = c1.getDeclaredMethod("setName", String.class);// invoke,激活的意思// (對象, “方法的值”)setName.invoke(user3, "李四");System.out.println(user3.getName());// 通過反射操作屬性System.out.println("==================================");User user4 = (User)c1.newInstance();Field name = c1.getDeclaredField("name");//不能直接操作私有屬性,我們需要關閉程序的安全監測,// 開啟訪問權限name.setAccessible(true);name.set(user4, "王五");System.out.println(user4.getName());} }

    性能對比分析

    上面說到了setAccessible(true)可以提高反射效率,下面開始對比普通方式調用和反射方式調用(包括反射方式的setAccessible()開啟和禁用)的效率

    //分析性能問題 public class Test08 {// 普通方式調用public static void test01() {User user = new User();long startTime = System.currentTimeMillis();for (int i = 0; i < 1000000000; i++) {user.getName();}long endTime = System.currentTimeMillis();System.out.println("普通方式執行 10 億次:" + (endTime - startTime) + "ms");}// 反射方式調用public static void test02() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {User user = new User();Class c = user.getClass();Method getName = c.getDeclaredMethod("getName", null);long startTime = System.currentTimeMillis();for (int i = 0; i < 1000000000; i++) {getName.invoke(user, null);}long endTime = System.currentTimeMillis();System.out.println("反射方式執行 10 億次:" + (endTime - startTime) + "ms");}// 反射方式調用,關閉檢測public static void test03() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {User user = new User();Class c = user.getClass();Method getName = c.getDeclaredMethod("getName", null);getName.setAccessible(true);long startTime = System.currentTimeMillis();for (int i = 0; i < 1000000000; i++) {getName.invoke(user, null);}long endTime = System.currentTimeMillis();System.out.println("關閉檢測方式執行 10 億次:" + (endTime - startTime) + "ms");}public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {test01();test02();test03();} }

    輸出:

    普通方式執行 10 億次:5ms
    反射方式執行 10 億次:1692ms
    關閉檢測方式執行 10 億次:1331ms

    通過反射操作泛型

    • Java 采用泛型擦除的機制來引入泛型,Java 中的泛型僅僅是給編譯器 javac使用的,確保數據的安全性和免去強制類型轉換問題,但是,一旦編譯完成,所有和泛型有關的類型全部擦除
    • 為了通過反射操作這些類型,Java 新增了 ParameterizedType,GenericArrayType,TypeVariable和 WildcardType 幾種類型來代表不能被歸一到 Class 類中的類型但是又和原始類型齊名的類型。
      ParameterizedType:表示一種參數化類型,比如 Collection< String>
      GenericArrayType:表示一種元素類型時參數化類型或者類型變量的數組類型
      TypeVariable :是各種類型變量的公共父接口
      WildcardType :代表一種通配符類型表達式
    package com.cheng.reflection;import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.List; import java.util.Map;public class Test09 {public void test01(Map<String, User> map, List<User> list) {System.out.println("test01");}public Map<String, User> test02() {System.out.println("test02");return null;}// 通過反射獲取泛型public static void main(String[] args) throws NoSuchMethodException {Method test01 = Test09.class.getMethod("test01", Map.class, List.class);// 獲取通用參數類型Type[] genericParameterTypes = test01.getGenericParameterTypes();// 迭代遍歷for (Type genericParameterType : genericParameterTypes) {System.out.println("# " + genericParameterType);if (genericParameterType instanceof ParameterizedType) {// 獲取實際參數類型Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments();for (Type actualTypeArgument : actualTypeArguments) {System.out.println(actualTypeArgument);}}}Method test02 = Test09.class.getMethod("test02", null);Type genericReturnType = test02.getGenericReturnType();if (genericReturnType instanceof ParameterizedType) {Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();for (Type actualTypeArgument : actualTypeArguments) {System.out.println(actualTypeArgument);}}} }

    通過反射操作注解

    package com.cheng.reflection;import java.lang.annotation.*; import java.lang.reflect.Field;public class Test10 {public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {Class c1 = Class.forName("com.cheng.reflection.Stu");// 通過反射獲得注解Annotation[] annotations = c1.getAnnotations();for (Annotation annotation : annotations) {System.out.println("---->"+annotation);}// 獲得注解的value的值TableStu tableStu = (TableStu)c1.getAnnotation(TableStu.class);String value = tableStu.value();System.out.println(value);// 獲得類指定注解Field f = c1.getDeclaredField("id");FieldStu annotation = f.getAnnotation(FieldStu.class);System.out.println(annotation.columnName());System.out.println(annotation.type());System.out.println(annotation.length());} }@TableStu("db_stu") class Stu{@FieldStu(columnName = "db_id", type = "int", length = 10)private int id;@FieldStu(columnName = "db_age", type = "int", length = 10)private int age;@FieldStu(columnName = "db_name", type = "varchar", length = 3)private String name;public Stu() {}public Stu(int id, int age, String name) {this.id = id;this.age = age;this.name = name;}public int getId() {return id;}public void setId(int id) {this.id = id;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "Stu{" +"id=" + id +", age=" + age +", name='" + name + '\'' +'}';} }// 類名的注解 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @interface TableStu{String value(); }// 屬性的注解 @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) @interface FieldStu{String columnName();String type();int length(); }

    總結

    以上是生活随笔為你收集整理的Java学习笔记7-2——注解与反射的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。