JAVA反射概念及使用詳解(超詳細)

来源:https://www.cnblogs.com/yZzcXQ/archive/2020/03/18/JavaSE-reflect.html
-Advertisement-
Play Games

JAVA反射概念及使用詳解 一、什麼是反射? 反射:框架設計的靈魂 框架:半成品軟體。可以在框架的基礎上進行軟體開發,簡化編碼 反射:將類的各個組成部分封裝為其他對象,這就是反射機制 ​ 好處: ​ 可以在程式運行過程中,操作這些對象。 ​ 可以解耦,提高程式的可擴展性。 圖片來源https://b ...


JAVA反射概念及使用詳解

一、什麼是反射?

反射:框架設計的靈魂

框架:半成品軟體。可以在框架的基礎上進行軟體開發,簡化編碼

反射:將類的各個組成部分封裝為其他對象,這就是反射機制

​ 好處:

​ 可以在程式運行過程中,操作這些對象。

​ 可以解耦,提高程式的可擴展性。

圖片來源https://blog.csdn.net/qsbbl/article/details/85801571

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

反射是通過Class對象(位元組碼文件),來知道某個類的所有屬性和方法。也就是說通過反射我們可以獲取構造器,對象,屬性,方法(原本不知道)

不像現在這個類我們能看見,之後在JAVA框架中,很多類我們是看不見的,不能直接用類名去獲取對象,只能通過反射去獲取。

圖片來源https://blog.csdn.net/sinat_38259539/article/details/71799078

二、獲取Class對象的三種方式:

要想使用反射,必須先得到代表的位元組碼的Class對象,Class類用於表示.class文件(位元組碼)

1.通過該類的對象去獲取到對應的Class對象(基本不用它)

//第一種方式: student--->Class對象  通過getClass()方法
//Student是一個空類
Student student = new Student();
//這裡我們就省去泛型了,Class<?>
Class stuClass = student.getClass();  //獲取到了對應的Class對象

System.out.println(stuClass);
System.out.println(stuClass.getName());  //獲取Class對象的名字
輸出:
class fanshe.Student
fanshe.Student

但是需要註意的是,第一種我們基本不用,這裡顯然和反射機制相悖(你有類對象 student 還去用反射獲取Class類對象幹嘛,多此一舉)

2.通過類名.class靜態屬性獲取(比較簡單)

//第二種方式:  每個類創建後 都會有一個預設的靜態的class屬性 用於返回該類的class對象
//需要註意的是:  任何數據類型(包括基本數據類型)都有“靜態”的class屬性
Class stuClass2 = Student.class;
System.out.println("是否為同一個class對象?"+(stuClass==stuClass2));

結果:true

這裡需要註意的是,這種方式雖然比較簡單,但是需要導包,不然會編譯錯誤(對比第三種,全限定類名方式)

3.通過Class類中的靜態方法 forName()方法獲取(最為常見)

//第三種方式:  Class.forName("fanshe.Student");  註意參數一定為該類的全限定類名
try {
    Class stuClass3 = Class.forName("fanshe.Student");
    //System.out.println(stuClass3); 輸出仍然是class fanshe.Student
    System.out.println("是否為同一個class對象?"+(stuClass3==stuClass2));
} catch (ClassNotFoundException e) {
    e.printStackTrace();
}

結果:true

結論:同一個位元組碼文件(*.class)在一次程式運行過程中,只會被載入一次,不論通過哪一種方式獲取的Class對象都是同一個。

二、通過Class對象獲取到該類的構造器:

獲取到Class對象後,我們就可以通過Class對象獲取到該類的構造器了。有很多方法,以下逐一介紹:

首先創建一個Class對象

Class stuClass = Student.class;//我們採用了第二種方式

這個是我的Student類,為了演示構造了多個構造器

public class Student {
    
    //通過多個構造器不同的修飾符  不同的形參列表
    
    Student(String name) {
        System.out.println("用default修飾的Student的含有一個String參數的構造器:"+name);
    }
    public Student() {
        System.out.println("用public修飾的Student的無參構造器");
    }
    public Student(String name,int age) {
        System.out.println("用public修飾的Student的含有兩個參數的構造器:"+name+age);
    }
    public Student(boolean sex) {
        System.out.println("用public修飾的Student的含有一個參數的構造器:"+sex);
    }
    protected Student(int age) {
        System.out.println("用protected修飾的Student的含有一個參數的構造器:"+age);
    }
    private Student(String name,int age,boolean sex) {
        System.out.println("用private修飾的Student的含有三個參數的構造器:"+name+age+sex);
    }

}

1.getDeclaredConstructors()
返回 Constructor 對象的一個數組,這些對象反映此 Class 對象表示的類聲明的所有構造方法

Constructor[] conArray01 = stuClass.getDeclaredConstructors();
for (Constructor constructor : conArray01) {
    System.out.println(constructor);
}
//輸出結果:輸出了所有的構造器
private fanshe.Student(java.lang.String,int,boolean)
protected fanshe.Student(int)
public fanshe.Student(boolean)
public fanshe.Student(java.lang.String,int)
public fanshe.Student()
fanshe.Student(java.lang.String)

2.getConstructors()
返回一個包含某些 Constructor 對象的數組,這些對象反映此 Class 對象所表示的類的所有公共(public)構造方法。

Constructor[] consArray02 = stuClass.getConstructors();
for (Constructor constructor : consArray02) {
    System.out.println(constructor);
}

//輸出的是所有的public修飾的構造方法:
//public fanshe.Student(boolean)
//public fanshe.Student(java.lang.String,int)
//public fanshe.Student()

3.getConstructor(Class<?>... parameterTypes)
返回一個 Constructor 對象,它反映此 Class 對象所表示的類的指定公共(public)構造方法。

//參數為長度可變的形參:獲取指定的構造器  參數為指定構造器的形參類型對應的class對象
//用public修飾的構造器
Constructor con0 = stuClass.getConstructor(null);  //無參的  無參構造器參數就是null或者空,等同於stuClass.getConstructor()
Constructor con = stuClass.getConstructor(boolean.class);  //boolean的
System.out.println(con0);
System.out.println(con);

//輸出:
//public fanshe.Student()
//public fanshe.Student(boolean)

註意,輸出的都是public類型的,也只能是public類型的。

4.getDeclaredConstructor(Class<?>... parameterTypes)
返回一個 Constructor 對象,該對象反映此 Class 對象所表示的類或介面的指定構造方法。

Constructor con1 = stuClass.getDeclaredConstructor(int.class);
System.out.println(con1);
Constructor con2 = stuClass.getDeclaredConstructor(String.class,int.class,boolean.class);
System.out.println(con2);

//輸出:
//protected fanshe.Student(int)
//private fanshe.Student(java.lang.String,int,boolean)

總結:

①有參數的3和4,返回的都是一個構造器(好理解,構造器重載要求參數列表不一樣)。沒參數的1和2,返回的都是Constructor類型的數組。你可以這樣記:方法名加了s,例getConstructors(), 的返回都是多個構造器,沒有s的都是一個。

②方法名包含了Declared返回的構造器訪問許可權修飾符沒有限制,而沒有包含Declared例getConstructors(),返回的都是public修飾的構造器。

​ 簡而言之:加s的比沒加s的返回的構造方法多,加Declared比沒加的多,全加的(getDeclaredConstructors())最多

那麼獲取了構造器如何去創建對象呢?

三、通過獲取到的構造器創建對象:

//這裡我們需要捕獲一下異常
//因為我們的構造器是私有的,不能在其他類去創建,所以我們用了setAccessible()這個方法,從而可以創建
/*
private Student(String name,int age,boolean sex){
        System.out.println("用private修飾的Student的含有三個參數的構造器:"+name+age+sex);
}*/
try {
    con2.setAccessible(true);   //忽略構造器的訪問修飾符,解除私有限定
    Object object = con2.newInstance("張三",10,true);  //這句話就相當於new Student("張三",10,true);
    Student student = (Student) object;  //指向子類對象的父類引用重新轉為子類引用
} catch (Exception e) {
    e.printStackTrace();
} 

//輸出:用private修飾的Student的含有三個參數的構造器:張三10true

註意:

xxx.setAccessible(true) 是為瞭解除私有限定,以後會經常出現,之後就不多加解釋了

四、 通過Class對象獲取成員變數

以下方法有很多異常需要拋出,我就省略了

先定義一個Teacher類:

public class Teacher {
    
    public Teacher() {
    }
    
    public String name;
    
    protected int age;
    
    boolean sex;
    
    private String address;

    @Override
    public String toString() {
        return "Teacher [name=" + name + ", age=" + age + ", sex=" + sex + ", address=" + address + "]";
    }
    
}
Class teaClass = Class.forName("fanshe.Teacher");   //創建Class對象(第三種方式)

//獲取Teacher類中的所有的public欄位
Field[] f = teaClass.getFields();
for (Field field : f) {
    System.out.println(field);
}

//獲取Teacher類中的所有的欄位(包含各種訪問修飾符)
System.out.println("===========所有的欄位=======================>");
Field[] df = teaClass.getDeclaredFields();
for (Field field : df) {
    System.out.println(field);
}
System.out.println("===========公共的特定的欄位=======================>");
// 獲取公共的特定的欄位
Field field = teaClass.getField("name");
System.out.println(field);
System.out.println("===========特定的欄位=======================>");
// 獲取特定的欄位
Field field2 = teaClass.getDeclaredField("age");
System.out.println(field2);

/**
 * 為欄位設置具體的值
 * 參數一:該類的對象
 * 參數二:為特定的屬性賦值
 */
Object object = teaClass.getConstructor().newInstance();//相當於Object object = new Teacher();
field.set(object, "張三豐");

System.out.println("名稱為:"+((Teacher)object).name);

輸出結果:

public java.lang.String fanshe.Teacher.name  //所有public欄位
===========所有的欄位=======================>
public java.lang.String fanshe.Teacher.name
protected int fanshe.Teacher.age
boolean fanshe.Teacher.sex
private java.lang.String fanshe.Teacher.address
===========公共的特定的欄位=======================>
public java.lang.String fanshe.Teacher.name
===========特定的欄位=======================>
protected int fanshe.Teacher.age
名稱為:張三豐

記憶方法同上,這裡就不贅述了。

五、通過Class對象獲取到該類的方法

首先創建了一個Employee類:

public class Employee {
    
    public Employee() {}
    
    public String name;
    
    protected double money;
    
    String address;
    
    private long number;
    
    
    protected void getDayMoney(String name) {
        System.out.println("我是Employee受保護的獲取日薪的方法,有一個參數為:"+name);
    }
    
    public void getweekMoney(String name,double money) {
        System.out.println("我是Employee公有的獲取周薪的方法,沒有參數..");
    }
    
    void getMonthMoney() {
        System.out.println("我是Employee預設的獲取月薪的方法,沒有參數..");
    }
    
    private void getYearMoney(int age) {
        System.out.println("我是Employee私有的的獲年薪月薪的方法,有一個參數為:"+age);
    }
    
    public static void main(String[] args) {
        System.out.println("Employee中的main()方法執行了...");
        System.out.println(Arrays.toString(args));
    }
    
    @Override
    public String toString() {
        return "Employee [name=" + name + ", money=" + money + ", address=" + address + ", number=" + number + "]";
    }
    
}

通過Class對象去獲取該類的方法:

Class<?> emplClass = Class.forName("fanshe.Employee");
//1.獲取所有的公共的方法  (包含父類的方法)
Method[] methods = emplClass.getMethods();
for (Method method : methods) {
    System.out.println(method);
}
//輸出:
public java.lang.String fanshe.Employee.toString()
public void fanshe.Employee.getweekMoney(java.lang.String,double)
public final void java.lang.Object.wait() throws java.lang.InterruptedException
public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
public boolean java.lang.Object.equals(java.lang.Object)
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.getClass()
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()

註意:

1.上面的方法都是public公共的;

2.會將父類(Object)類中的公共方法也輸出。

//2.獲取該類中的所有方法,以數組形式返回:
Method[] methods = emplClass.getDeclaredMethods();
for (Method method : methods) {
    System.out.println(method);
}

//輸出:
public java.lang.String fanshe.Employee.toString()
public void fanshe.Employee.getweekMoney(java.lang.String,double)
private void fanshe.Employee.getYearMoney(int)
void fanshe.Employee.getMonthMoney()
protected void fanshe.Employee.getDayMoney(java.lang.String)
//3.獲取特定的公有的方法:  只能是公共的
//參數一:要獲取的方法的名稱,參數二:方法對應的形參列表的類型
Method method = emplClass.getMethod("getweekMoney",new Class[]{String.class,double.class});
System.out.println(method);     //輸出:public void fanshe.Employee.getweekMoney(java.lang.String,double)
    
//4.獲取特定的方法:    可以是私有的
Method method2 = emplClass.getDeclaredMethod("getYearMoney", int.class);
System.out.println(method2);    //輸出:private void fanshe.Employee.getYearMoney(int)

接下來我準備獲取private void getYearMoney(int age) {}方法,然後去執行這個私有方法。

//emplClass對應的類的對象
Object obj = emplClass.getConstructor().newInstance();
//可以通過Employee類對象去直接調用非私有方法,我這裡將測試類和Employee類放在了同一個包下,所有可以訪問預設方法(包限定方法)
Employee employee = (Employee)obj;
employee.getMonthMoney();

//參數一:要調用的對象 參數二:方法具體要求傳遞的值   返回值:為調用該方法後的返回值所對應的對象,如果沒有返回值(void),則對象為null
method2.setAccessible(true);
Object result = method2.invoke(obj, 20);   //執行method2方法對應的代碼
System.out.println(result);

//輸出:
/*
我是Employee預設的獲取月薪的方法,沒有參數..
我是Employee私有的的獲年薪月薪的方法,有一個參數為:20
null
*/

調用Employee類的主方法(main):

Class<?> class1 = Class.forName("fanshe.Employee");

Method methodMain = class1.getMethod("main", String[].class);

//參數一:對象類型 null 當調用的方法為靜態時,此時第一個參數可以為null
//方式一:
Object object = methodMain.invoke(null, (Object)new String[]{"a","b","c","d"});
//方式二:
Object object = methodMain.invoke(null, new Object[] {new String[]{"a","b","c","d"}});

System.out.println(object);
//輸出:
Employee中的main()方法執行了...        //這裡是main方法里的輸出 
[a, b, c, d]                        //main方法里的輸出調用了toString
Employee中的main()方法執行了...
[a, b, c, d]
null                                //main方法返回類型為void,所以這裡返回值為null

六、通過Method對象調用指定方法

下麵我們對Method中的invoke()方法進行詳解。

public Object invoke(Object obj, Object... args)

對帶有指定參數的指定對象調用由此 Method 對象表示的底層方法。
如果底層方法是靜態的,那麼可以忽略指定的 obj 參數。該參數可以為 null。

如果底層方法所需的形參數為 0,則所提供的 args 數組長度可以為 0 或 null。

如果底層方法是靜態的,並且尚未初始化聲明此方法的類,則會將其初始化。

如果方法正常完成,則將該方法返回的值返回給調用者;如果該值為基本類型,則首先適當地將其包裝在對象中。但是,如果該值的類型為一組基本類型,則數組元素不 被包裝在對象中;換句話說,將返回基本類型的數組。如果底層方法返回類型為 void,則該調用返回 null。

參數:
obj - 從中調用底層方法的對象
args - 用於方法調用的參數
返回:
使用參數 args 在 obj 上指派該對象所表示方法的結果

拋出:拋出的異常就不列舉了,具體可以自己查API文檔

下麵我單獨創建了一個InvokeTest類去解釋說明invoke方法的使用:

public class InvokeTest {
    
    public static void main(String[] args) throws Exception {
        
        Class classType = InvokeTest.class;
        Object invoketest = classType.getConstructor().newInstance();
        
        //獲取Method類對象
        Method m = classType.getMethod("add", new Class[] {int.class,int.class});
        
        /**
         * Method的invoke(Object obj,Object[] args) 該方法接收的參數必須為對象
         * 如果參數為基本數據類型,使用相應的包裝類對象 返回的結果總是一個對象(其結果代表調用該方法後的返回值)
         */
        //如果add方法是靜態方法,那麼可以這樣寫:Object object = m.invoke(null, new Integer(10), new Integer(20));
        Object object = m.invoke(invoketest, new Integer(10), new Integer(20));
        System.out.println(object);
    }
    
    public int add(int param1,int param2) {
        return param1+param2;
    }
}
//輸出:30

七、反射的使用

1.通過反射運行配置文件內容:

通過這種方式,當我們需要訪問其他類的時候,不需要改動源碼,利用反射,直接修改配置文件即可。

public class FileDemo {
    
    public static void main(String[] args) throws Exception {
        
        Class aClass = Class.forName(getValue("className"));
        
        Method m = aClass.getMethod(getValue("methodName"));
        
        m.invoke(aClass.getConstructor().newInstance());
        
    }
    
    
    //這個方法用於接收配置文件中與key所對應的value值
    public static String getValue(String key) throws Exception {
        
        Properties pro = new Properties();   //獲取配置文件的對象
        
        FileReader reader = new FileReader("prop.txt");
        pro.load(reader);  //將流載入到配置文件對象中
        //從輸入流中讀取屬性列表(鍵和元素對)。通過對指定的文件(比如說上面的 prop.txt 文件)進行裝載來獲取該文件中的所有鍵 - 值對。以供 getProperty ( String key) 來搜索。
        
        reader.close();
        
        return pro.getProperty(key);//用指定的鍵在此屬性列表中搜索屬性。也就是通過參數 key ,得到 key 所對應的 value。
    }

}
//prop.txt文件中的內容
className=fanshe.file.Apple
methodName=taste           
//Apple類
public class Apple {
    
    public void taste() {
        System.out.println("蘋果真好吃啊...");
    }

}
//輸出:
蘋果真好吃啊...

總結一下步驟:

1.通過Class類中的forName靜態方法獲取Apple類的Class類對象;

2.通過Class類對象獲取Method類對象,參數傳入要調用方法的方法名;

3.通過Method類的invoke非靜態方法,調用這個方法,參數傳入類對象和調用的方法的參數對象(我這裡的test方法沒有參數,所有invoke參數只有Apple類的對象)。

Java中有個比較重要的類Properties(Java.util.Properties),主要用於讀取Java的配置文件。

上面的例子是獲取text文件中的,而配置文件(.properties文件)讀取方式略有不同:

public class ReflectTest {
    
    public static void main(String[] args) throws Exception {   
        
        //1.載入配置文件
        //1.1創建Properties對象
        Properties pro = new Properties();
        //1.2獲取到class目錄下的配置文件
        ClassLoader classLoader = ReflectTest.class.getClassLoader();
        InputStream is = classLoader.getResourceAsStream("pro.properties");
        
        //1.3載入配置文件
        pro.load(is);
        
        //2.獲取到配置文件中定義的數據
        String className = pro.getProperty("className");
        String methodName = pro.getProperty("methodName");
        
        //3.有了全限定類名 有了方法名(參數) 完全可以通過反射調用該方法
        
        //3.1:獲取到該類的Class對象
        Class cls = Class.forName(className);
        
        //3.2:獲取到該類的實例對象
        Object object = cls.getConstructor().newInstance();
        
        //3.3:獲取到該類特定的方法對象
        Method method = cls.getMethod(methodName);
        
        //4.執行方法
        method.invoke(object);
        
    }

}
//pro.properties文件內容,這個配置文件可以包->右鍵->New->File->File name:pro.properties
className=bean.Student
methodName=study

Student類和運行結果這裡就不列舉了,和上面的差不多

2.通過反射越過泛型檢查:

我們的目的是:向String泛型集合中添加一個Integer對象

List<String> list = new ArrayList<>();
list.add("我愛Java");
list.add("成功上岸");
//list.add(100);    如果直接add直接會編譯報錯

//1.獲取到List類的Class對象
Class listClass = list.getClass();

Method method = listClass.getMethod("add", Object.class);

method.invoke(list, 100);

for (Object object : list) {
    System.out.println(object);
}

/*輸出:
我愛Java
成功上岸
100
*/

如有錯誤,歡迎指點


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 100個不同類型的python語言趣味編程題 在求解的過程中培養編程興趣,拓展編程思維,提高編程能力。 第一部分:趣味演算法入門;第七題:最佳存款方案 假設銀行一年整存零取得月息為0.63%,現某人手中有一筆錢,他打算在今後的5年中的每年年底取出1000 元,到第五年剛好取完,請算出他存錢時應該存入多 ...
  • 文件的操作 + 打開文件 2.對文件句柄進行操作 3.關閉文件。 open() 有三個參數:1. 文件路徑 (文件夾路徑+文件名+文件類型) 2. 編碼方式(encoding)3. 模式(mode) open: 內置函數,open底層調用的是操作系統的介面。 fl: 變數,一般在文件操作時設置的約定 ...
  • 1.使用虛擬環境 創建虛擬環境 conda create -n tfpy3 python=3.5 激活tfpy3虛擬環境 activate tfpy3 安裝ipykernel 插件 pip/conda install ipykernel 將環境添加到Jyputer中(Name是此環境顯示在Jyput ...
  • 接上一篇: "adb命令_一鍵截取logcat日誌" , 有一天, 系統穩定性開發負責人找到我,希望我能在跑android 系統monkey的時候, 實時監控logcat的輸出,如果一旦發現“java.lang.NullPointerException"空指針異常, 則立刻用adb bugrepor ...
  • Java多線程的wait(),notify(),notifyAll()、sleep()和yield()方法使用詳解 ...
  • 什麼是Requests Requests是用python語言基於urllib編寫的,採用的是Apache2 Licensed開源協議的HTTP庫如果你看過上篇文章關於urllib庫的使用,你會發現,其實urllib還是非常不方便的,而Requests它會比urllib更加方便,可以節約我們大量的工作 ...
  • JVM的概念 JVM是Java Virtual Machine的簡稱。意為Java虛擬機。 JVM使用軟體模擬Java位元組碼的指令集。 規範 java語言規範 語法 if else else if、函數等語法 詞法 行終結符、空白符、註釋、標識符、關鍵字等 java記憶體模型 類載入連接過程 異常 . ...
  • 第一次作業 類圖 類複雜度 程式結構分析 第一次作業得程式按功能分為了三塊:輸入輸出,解析表達式和求導。整個程式的主體就在於Poly類中的解析字元串方法parsePoly,Term類和termHanlder方法實質上是當時為了應付CheckStyle被逼無奈才創建的。這樣面向過程的做法很符合以前編程 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...