java使用反射創(chuàng)建并操作對(duì)象的方法
Class 對(duì)象可以獲得該類里的方法(由 Method 對(duì)象表示)、構(gòu)造器(由 Constructor 對(duì)象表示)、成員變量(由 Field 對(duì)象表示),這三個(gè)類都位于 java.lang.reflect 包下,并實(shí)現(xiàn)了 java.lang.reflect.Member 接口。程序可以通過(guò)對(duì)象來(lái)執(zhí)行對(duì)應(yīng)的方法,通過(guò) Constructor 對(duì)象來(lái)調(diào)用對(duì)應(yīng)的構(gòu)造器創(chuàng)建實(shí)例,能通過(guò) Field 對(duì)象直接訪問(wèn)并修改對(duì)象的成員變量值。
創(chuàng)建對(duì)象
通過(guò)反射來(lái)生成對(duì)象需要先使用 Class 對(duì)象獲取指定的 Constructor 對(duì)象,再調(diào)用 Constructor 對(duì)象的 newInstance() 方法來(lái)創(chuàng)建該 Class 對(duì)象對(duì)應(yīng)類的實(shí)例。通過(guò)這種方式可以選擇使用指定的構(gòu)造器來(lái)創(chuàng)建實(shí)例。
在很多 Java EE 框架中都需要根據(jù)配置文件信息來(lái)創(chuàng)建 Java 對(duì)象,從配置文件讀取的只是某個(gè)類的字符串類名,程序需要根據(jù)該字符串來(lái)創(chuàng)建對(duì)應(yīng)的實(shí)例,就必須使用反射。
下面程序就實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的對(duì)象池,該對(duì)象池會(huì)根據(jù)配置文件讀取 key-value 對(duì),然后創(chuàng)建這些對(duì)象,并將這些對(duì)象放入一個(gè) HashMap 中。
public class ObjectPoolFactory { // 定義一個(gè)對(duì)象池,前面是對(duì)象名,后面是實(shí)際對(duì)象 private Map<String, Object> objectPool = new HashMap<>(); // 定義一個(gè)創(chuàng)建對(duì)象的方法 // 該方法只要傳入一個(gè)字符串類名,程序可以根據(jù)該類名生成Java對(duì)象 private Object createObject(String clazzName) throws Exception, IllegalAccessException, ClassNotFoundException { // 根據(jù)字符串來(lái)獲取對(duì)應(yīng)的Class對(duì)象 Class<?> clazz = Class.forName(clazzName); // 使用clazz對(duì)應(yīng)類的默認(rèn)構(gòu)造器創(chuàng)建實(shí)例 return clazz.getConstructor().newInstance(); } // 該方法根據(jù)指定文件來(lái)初始化對(duì)象池 // 它會(huì)根據(jù)配置文件來(lái)創(chuàng)建對(duì)象 public void initPool(String fileName) throws InstantiationException, IllegalAccessException, ClassNotFoundException { try (FileInputStream fis = new FileInputStream(fileName)) { Properties props = new Properties(); props.load(fis); for (String name : props.stringPropertyNames()) { // 每取出一對(duì)key-value對(duì),就根據(jù)value創(chuàng)建一個(gè)對(duì)象 // 調(diào)用createObject()創(chuàng)建對(duì)象,并將對(duì)象添加到對(duì)象池中 objectPool.put(name, createObject(props.getProperty(name))); } } catch (Exception ex) { System.out.println("讀取" + fileName + "異常"); } } public Object getObject(String name) { // 從objectPool中取出指定name對(duì)應(yīng)的對(duì)象 return objectPool.get(name); } public static void main(String[] args) throws Exception { ObjectPoolFactory pf = new ObjectPoolFactory(); pf.initPool("obj.txt"); System.out.println(pf.getObject("a")); // ① System.out.println(pf.getObject("b")); // ② } }
上面程序中 createObject() 方法里的兩行粗體字代碼就是根據(jù)字符串來(lái)創(chuàng)建 Java 對(duì)象的關(guān)鍵代碼,程序調(diào)用 Class 對(duì)象的 newInstance() 方法即可創(chuàng)建一個(gè) Java 對(duì)象。程序中的 initPool() 方法會(huì)讀取屬性文件,對(duì)屬性文件中每個(gè) key-value 對(duì)創(chuàng)建一個(gè) Java 對(duì)象,其中 value 是該 Java 對(duì)象的實(shí)現(xiàn)類,而 key 是該 Java 對(duì)象放入對(duì)象池中的名字。為該程序提供如下屬性配置文件。
a=java.util.Date
b=javax.swing.JFrame
編譯、運(yùn)行上面的 ObjectPoolFactory 程序,執(zhí)行到 main 方法中的①號(hào)代碼處,將看到輸出系統(tǒng)當(dāng)前時(shí)間——這表明對(duì)象池中已經(jīng)有了一個(gè)名為a的對(duì)象,該對(duì)象是一個(gè) java.util.Date 對(duì)象。執(zhí)行到②號(hào)代碼處,將看到輸出一個(gè) JFrame 對(duì)象。
提示:這種使用配置文件來(lái)配置對(duì)象,然后由程序根據(jù)配置文件來(lái)創(chuàng)建對(duì)象的方式非常有用,大名鼎鼎的 Spring 框架就采用這種方式大大簡(jiǎn)化了 Java EE 應(yīng)用的開(kāi)發(fā)。當(dāng)然,Spring 采用的是 XML 配置文件——畢竟屬性文件能配置的信息太有限了,而 XML 配置文件能配置的信息就豐富多。
如果不想利用默認(rèn)構(gòu)造器來(lái)創(chuàng)建 Java 對(duì)象,而想利用指定的構(gòu)造器來(lái)創(chuàng)建 Java 對(duì)象,則需要利用 Constructor 對(duì)象,每個(gè) Constructor 對(duì)應(yīng)一個(gè)構(gòu)造器。為了利用指定的構(gòu)造器來(lái)創(chuàng)建 Java 對(duì)象,需要如下三個(gè)步驟。
- 獲取該類的 Class 對(duì)象。
- 利用 Class 對(duì)象的 getConstructor() 方法來(lái)獲取指定的構(gòu)造器。
- 調(diào)用 Constructor 的 newInstance() 方法來(lái)創(chuàng)建 Java 對(duì)象。
下面程序利用反射來(lái)創(chuàng)建一個(gè) JFrame 對(duì)象,而且使用指定的構(gòu)造器。
public class CreateJFrame { public static void main(String[] args) throws Exception { // 獲取JFrame對(duì)應(yīng)的Class對(duì)象 Class<?> jframeClazz = Class.forName("javax.swing.JFrame"); // 獲取JFrame中帶一個(gè)字符串參數(shù)的構(gòu)造器 Constructor ctor = jframeClazz.getConstructor(String.class); // 調(diào)用Constructor的newInstance方法創(chuàng)建對(duì)象 Object obj = ctor.newInstance("測(cè)試窗口"); // 輸出JFrame對(duì)象 System.out.println(obj); } }
上面程序中第一行粗休字代碼用于獲取 JFrame 類的指定構(gòu)造器,前面已經(jīng)提到:如果要唯一地確定某類中的構(gòu)造器,只要指定構(gòu)造器的形參列表即可。第一行粗體字代碼獲取構(gòu)造器時(shí)傳入了一個(gè) String 類型,即表明想獲取只有一個(gè)字符串參數(shù)的構(gòu)造器。
程序中第二行粗體字代碼使用指定構(gòu)造器的 newInstance() 方法來(lái)創(chuàng)建一個(gè) Java 對(duì)象,當(dāng)調(diào)用 Constructor 對(duì)象的 newInstance() 方法時(shí)通常需要傳入?yún)?shù),因?yàn)檎{(diào)用 Constructor 的 newInstance() 方法實(shí)際上等于調(diào)用它對(duì)應(yīng)的構(gòu)造器,傳給 newInstance() 方法的參數(shù)將作為對(duì)應(yīng)構(gòu)造器的參數(shù)。
對(duì)于上面的 CreateFrame.java 中已知 java.swing.JFrame 類的情形,通常沒(méi)有必要使用反射來(lái)創(chuàng)建該對(duì)象,畢竟通過(guò)反射創(chuàng)建對(duì)象時(shí)性能要稍低一些。實(shí)際上,只有當(dāng)程序需要?jiǎng)討B(tài)創(chuàng)建某個(gè)類的對(duì)象時(shí)才會(huì)考慮使用反射,通常在開(kāi)發(fā)通用性比較廣的框架、基礎(chǔ)平臺(tái)時(shí)可能會(huì)大量使用反射。
調(diào)用方法
當(dāng)獲得某個(gè)類對(duì)應(yīng)的 Class 對(duì)象后,就可以通過(guò)該 Class 對(duì)象的 getMethods() 方法或者 getMethod()方法來(lái)獲取全部方法或指定方法——這兩個(gè)方法的返回值是 Method 數(shù)組,或者 Method 對(duì)象。
每個(gè) Method 對(duì)象對(duì)應(yīng)一個(gè)方法,獲得 Method 對(duì)象后,程序就可通過(guò)該 Method 來(lái)調(diào)用它對(duì)應(yīng)的方法。在 Method 里包含一個(gè) Invoke() 方法,該方法的簽名如下。
- Object invoke(Object obj, Object...args):該方法中的 obj 是執(zhí)行該方法的主調(diào),后面的 args 是執(zhí)行該方法時(shí)傳入該方法的實(shí)參。
下面程序?qū)η懊娴膶?duì)象池工廠進(jìn)行加強(qiáng),允許在配置文件中增加配置對(duì)象的成員變量的值,對(duì)象池工廠會(huì)讀取為該對(duì)象配置的成員變量值,并利用該對(duì)象對(duì)應(yīng)的 setter 方法設(shè)置成員變量的值。
public class ExtendedObjectPoolFactory { // 定義一個(gè)對(duì)象池,前面是對(duì)象名,后面是實(shí)際對(duì)象 private Map<String, Object> objectPool = new HashMap<>(); private Properties config = new Properties(); // 從指定屬性文件中初始化Properties對(duì)象 public void init(String fileName) { try (FileInputStream fis = new FileInputStream(fileName)) { config.load(fis); } catch (IOException ex) { System.out.println("讀取" + fileName + "異常"); } } // 定義一個(gè)創(chuàng)建對(duì)象的方法 // 該方法只要傳入一個(gè)字符串類名,程序可以根據(jù)該類名生成Java對(duì)象 private Object createObject(String clazzName) throws Exception { // 根據(jù)字符串來(lái)獲取對(duì)應(yīng)的Class對(duì)象 Class<?> clazz = Class.forName(clazzName); // 使用clazz對(duì)應(yīng)類的默認(rèn)構(gòu)造器創(chuàng)建實(shí)例 return clazz.getConstructor().newInstance(); } // 該方法根據(jù)指定文件來(lái)初始化對(duì)象池 // 它會(huì)根據(jù)配置文件來(lái)創(chuàng)建對(duì)象 public void initPool() throws Exception { for (String name : config.stringPropertyNames()) { // 每取出一個(gè)key-value對(duì),如果key中不包含百分號(hào)(%) // 這就表明是根據(jù)value來(lái)創(chuàng)建一個(gè)對(duì)象 // 調(diào)用createObject創(chuàng)建對(duì)象,并將對(duì)象添加到對(duì)象池中 if (!name.contains("%")) { objectPool.put(name, createObject(config.getProperty(name))); } } } // 該方法將會(huì)根據(jù)屬性文件來(lái)調(diào)用指定對(duì)象的setter方法 public void initProperty() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException { for (String name : config.stringPropertyNames()) { // 每取出一對(duì)key-value對(duì),如果key中包含百分號(hào)(%) // 即可認(rèn)為該key用于控制調(diào)用對(duì)象的setter方法設(shè)置值 // %前半為對(duì)象名字,后半控制setter方法名 if (name.contains("%")) { // 將配置文件中的key按%分割 String[] objAndProp = name.split("%"); // 取出調(diào)用setter方法的參數(shù)值 Object target = getObject(objAndProp[0]); // 獲取setter方法名:set + "首字母大寫" + 剩下部分 String mtdName = "set" + objAndProp[1].substring(0, 1).toUpperCase() + objAndProp[1].substring(1); // 通過(guò)target的getClass()獲取它的實(shí)現(xiàn)類所對(duì)應(yīng)的Class對(duì)象 Class<?> targetClass = target.getClass(); // 獲取希望調(diào)用的setter方法 Method mtd = targetClass.getMethod(mtdName, String.class); // 通過(guò)Method的invoke方法執(zhí)行setter方法 // 將config.getProperty(name)的值作為調(diào)用setter方法的參數(shù) mtd.invoke(target, config.getProperty(name)); } } } public Object getObject(String name) { // 從objectPool中取出指定name對(duì)應(yīng)的對(duì)象 return objectPool.get(name); } public static void main(String[] args) throws Exception { ExtendedObjectPoolFactory epf = new ExtendedObjectPoolFactory(); epf.init("extObj.txt"); epf.initPool(); epf.initProperty(); System.out.println(epf.getObject("a")); } }
上面程序中 initProperty() 方法里的第一行粗體字代碼獲取目標(biāo)類中包含一個(gè) String 參數(shù)的 setter 方法,第二行粗體字代碼通過(guò)調(diào)用 Method 的 invoke() 方法來(lái)執(zhí)行該 setter 方法,該方法執(zhí)行完成后,就相當(dāng)于執(zhí)行了目標(biāo)對(duì)象的 setter 方法。為上面程序提供如下配置文件。
a=javax.swing.JFrame
b=javax.swing.JLabel
#set the title of a
a%title=Test Title
上面配置文件中的 a%title 行表明希望調(diào)用a對(duì)象的 setTitle() 方法,調(diào)用該方法的參數(shù)值為 Test Title。編譯、運(yùn)行上面的 ExtendedObjectPoolFactory.java 程序,可以看到輸出一個(gè) JFrame 窗口,該窗口的標(biāo)題為 Test Title。
提示:Spring 框架就是通過(guò)這種方式將成員變量值以及依賴對(duì)象等都放在配置文件中進(jìn)行管理的,從而實(shí)現(xiàn)了較好的解耦。這也是 Spring 框架的 IOC 的秘密。
當(dāng)通過(guò) Method 的 invoke() 方法來(lái)調(diào)用對(duì)應(yīng)的方法時(shí),Java 會(huì)要求程序必須有調(diào)用該方法的權(quán)限。如果程序確實(shí)需要調(diào)用某個(gè)對(duì)象的 private 方法,則可以先調(diào)用 Method 對(duì)象的如下方法。
- setAccessible(boolean flag):將 Method 對(duì)象的 accessible 設(shè)置為指定的布爾值。值為true,指示該 Method 在使用時(shí)應(yīng)該取消 Java 語(yǔ)言的訪問(wèn)權(quán)限檢查:值為false,則指示該 Method 在使用時(shí)要實(shí)施 Java 語(yǔ)言的訪問(wèn)權(quán)限檢查。
注意:實(shí)際上,setAccessible() 方法并不屬于 Method,而是屬于它的父類 AccessibleObject。因此 Method、Constructor、Field 都可調(diào)用該方法,從而實(shí)現(xiàn)通過(guò)反射來(lái)調(diào)用 private 方法、private 構(gòu)造器和成員變量,下一節(jié)將會(huì)讓讀者看到這種示例。也就是說(shuō),它們可以通過(guò)調(diào)用該方法來(lái)取消訪問(wèn)權(quán)限檢查,通過(guò)反射即可訪問(wèn) private 成員。
訪問(wèn)成員變量值
通過(guò) Class 對(duì)象的 getFields() 或 getField() 方法可以獲取該類所包括的全部成員變量或指定成員變量。Field 提供了如下兩組方法來(lái)讀取或設(shè)置成員變量值。
- getXxx(Object obj):獲取 obj 對(duì)象的該成員變量的值。此處的 Xxx 對(duì)應(yīng)8種基本類型,如果該成員變量的類型是引用類型,則取消 get 后面的Xxx。
- setXxx(Object obj, Xxx val):將 obj 對(duì)象的該成員變量設(shè)置成值。此處的 Xxx 對(duì)應(yīng)8種基本類型,如果該成員變量的類型是引用類型,則取消 set 后面的Xxx。
使用這兩個(gè)方法可以隨意地訪問(wèn)指定對(duì)象的所有成員變量,包括 private 修飾的成員變量。
class Person { private String name; private int age; public String toString() { return "Person[name:" + name + " , age:" + age + " ]"; } } public class FieldTest { public static void main(String[] args) throws Exception { // 創(chuàng)建一個(gè)Person對(duì)象 Person p = new Person(); // 獲取Person類對(duì)應(yīng)的Class對(duì)象 Class<Person> personClazz = Person.class; // 獲取Person的名為name的成員變量 // 使用getDeclaredField()方法表明可獲取各種訪問(wèn)控制符的成員變量 Field nameField = personClazz.getDeclaredField("name"); // 設(shè)置通過(guò)反射訪問(wèn)該成員變量時(shí)取消訪問(wèn)權(quán)限檢查 nameField.setAccessible(true); // 調(diào)用set()方法為p對(duì)象的name成員變量設(shè)置值 nameField.set(p, "Yeeku.H.Lee"); // 獲取Person類名為age的成員變量 Field ageField = personClazz.getDeclaredField("age"); // 設(shè)置通過(guò)反射訪問(wèn)該成員變量時(shí)取消訪問(wèn)權(quán)限檢查 ageField.setAccessible(true); // 調(diào)用setInt()方法為p對(duì)象的age成員變量設(shè)置值 ageField.setInt(p, 30); System.out.println(p); } }
上面程序中先定義了一個(gè) Person 類,該類里包含兩個(gè) private 成員變量:name 和 age,在通常情況下,這兩個(gè)成員變量只能在 Person 類里訪問(wèn)。但本程序 FieldTest 的 main() 方法中6行粗體字代碼通過(guò)反射修改了 Person 對(duì)象的 name、age 兩個(gè)成員變量的值。
第一行粗體字代碼使用 getDeclaredField() 方法獲取了名為 name 的成員變量,注意此處不是使用 getField()方法,因?yàn)?getField() 方法只能獲取 public 訪問(wèn)控制的成員變量,而 getDeclaredField() 方法則可以獲取所有的成員變量;第二行粗體字代碼則通過(guò)反射訪問(wèn)該成員變量時(shí)不受訪問(wèn)權(quán)限的控制;第三行粗體字代碼修改了 Person 對(duì)象的 name 成員變量的值。修改 Person 對(duì)象的 age 成員變量的值的方式與此完全相同。
編譯、運(yùn)行上面程序,會(huì)看到如下輸出:
Person[name:Yeeku.H.Lee , age:30 ]
操作數(shù)組
在 java.lang.reflect 包下還提供了一個(gè) Array 類,Array 對(duì)象可以代表所有的數(shù)組。程序可以通過(guò)使用 Array 來(lái)動(dòng)態(tài)地創(chuàng)建數(shù)組,操作數(shù)組元素等。
Array 提供了如下幾類方法。
- static Object newInstance(Class<?> componentType,int...length):創(chuàng)建一個(gè)具有指定的元素類型、指定維度的新數(shù)組。
- static xxx getXxx(Object array, int index):返回 array 數(shù)組中第 index 個(gè)元素。其中是各種基本數(shù)據(jù)類型,如果數(shù)組元素是引用類型,則該方法變?yōu)?get(Object array, int index)。
- static void setXxx(Object array, int index, xxx val):將 array 數(shù)組中第 index 個(gè)元素的值設(shè)為 val。其中 xxx 是各種基本數(shù)據(jù)類型,如果數(shù)組元素是引用類型,則該方法變成 set(Object array, int index, Object val)。
下面程序示范了如何使用 Array 來(lái)生成數(shù)組,為指定數(shù)組元素賦值,并獲取指定數(shù)組元素的方式。
public class ArrayTest1 { public static void main(String args[]) { try { // 創(chuàng)建一個(gè)元素類型為String ,長(zhǎng)度為10的數(shù)組 Object arr = Array.newInstance(String.class, 10); // 依次為arr數(shù)組中index為5、6的元素賦值 Array.set(arr, 5, "瘋狂Java講義"); Array.set(arr, 6, "輕量級(jí)Java EE企業(yè)應(yīng)用實(shí)戰(zhàn)"); // 依次取出arr數(shù)組中index為5、6的元素的值 Object book1 = Array.get(arr, 5); Object book2 = Array.get(arr, 6); // 輸出arr數(shù)組中index為5、6的元素 System.out.println(book1); System.out.println(book2); } catch (Throwable e) { System.err.println(e); } } }
上面程序中三行粗體字代碼分別是通過(guò) Array 創(chuàng)建數(shù)組,為數(shù)組元素設(shè)置值,訪問(wèn)數(shù)組元素的值的示例代碼,程序通過(guò)使用 Array 就可以動(dòng)態(tài)地創(chuàng)建并操作數(shù)組。
下面程序比上面程序稍微復(fù)雜一點(diǎn),下面程序使用 Array 類創(chuàng)建了一個(gè)三維數(shù)組。
public class ArrayTest2 { public static void main(String args[]) { /* * 創(chuàng)建一個(gè)三維數(shù)組。 根據(jù)前面介紹數(shù)組時(shí)講的:三維數(shù)組也是一維數(shù)組, 是數(shù)組元素是二維數(shù)組的一維數(shù)組, * 因此可以認(rèn)為arr是長(zhǎng)度為3的一維數(shù)組 */ Object arr = Array.newInstance(String.class, 3, 4, 10); // 獲取arr數(shù)組中index為2的元素,該元素應(yīng)該是二維數(shù)組 Object arrObj = Array.get(arr, 2); // 使用Array為二維數(shù)組的數(shù)組元素賦值。二維數(shù)組的數(shù)組元素是一維數(shù)組, // 所以傳入Array的set()方法的第三個(gè)參數(shù)是一維數(shù)組。 Array.set(arrObj, 2, new String[] { "瘋狂Java講義", "輕量級(jí)Java EE企業(yè)應(yīng)用實(shí)戰(zhàn)" }); // 獲取arrObj數(shù)組中index為3的元素,該元素應(yīng)該是一維數(shù)組。 Object anArr = Array.get(arrObj, 3); Array.set(anArr, 8, "瘋狂Android講義"); // 將arr強(qiáng)制類型轉(zhuǎn)換為三維數(shù)組 String[][][] cast = (String[][][]) arr; // 獲取cast三維數(shù)組中指定元素的值 System.out.println(cast[2][3][8]); System.out.println(cast[2][2][0]); System.out.println(cast[2][2][1]); } }
上面程序的第一行粗體字代碼使用 Array 創(chuàng)建了一個(gè)三維數(shù)組,程序中較難理解的地方是第二段粗體字代碼部分,使用 Array 為 arrObj 的指定元素賦值,相當(dāng)于為二維數(shù)組的元素賦值。由于二維數(shù)組的元素是一維數(shù)組,所以程序傳入的參數(shù)是一個(gè)一維數(shù)組對(duì)象。
運(yùn)行上面程序,將看到 cast[2][3][8]、cast[2][2][0]、cast[2][2][1] 元素都有值,這些值就是剛才程序通過(guò)反射傳入的數(shù)組元素值。
以上就是java使用反射創(chuàng)建并操作對(duì)象的方法的詳細(xì)內(nèi)容,更多關(guān)于JAVA 反射創(chuàng)建并操作對(duì)象的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java中HTTP接口請(qǐng)求重試的實(shí)現(xiàn)方式
HTTP接口請(qǐng)求重試是指在請(qǐng)求失敗時(shí),再次發(fā)起請(qǐng)求的機(jī)制,在實(shí)際應(yīng)用中,由于網(wǎng)絡(luò)波動(dòng)、服務(wù)器故障等原因,HTTP接口請(qǐng)求可能會(huì)失敗,為了保證系統(tǒng)的可用性和穩(wěn)定性,需要對(duì)HTTP接口請(qǐng)求進(jìn)行重試,所以本文給大家介紹了HTTP接口請(qǐng)求重試的實(shí)現(xiàn)方式,需要的朋友可以參考下2024-01-01Can''t use Subversion command line client:svn 報(bào)錯(cuò)處理
這篇文章主要介紹了Can't use Subversion command line client:svn 報(bào)錯(cuò)處理的相關(guān)資料,需要的朋友可以參考下2016-09-09使用spring框架ResponseEntity實(shí)現(xiàn)文件下載
這篇文章主要介紹了使用spring框架ResponseEntity實(shí)現(xiàn)文件下載,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02Java Socket+多線程實(shí)現(xiàn)多人聊天室功能
這篇文章主要為大家詳細(xì)介紹了Java Socket+多線程實(shí)現(xiàn)多人聊天室功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-07-07Spring框架實(shí)現(xiàn)滑動(dòng)驗(yàn)證碼功能的代碼示例
之前項(xiàng)目需要在驗(yàn)證碼模塊,增加滑動(dòng)驗(yàn)證碼,用來(lái)給手機(jī)端使用的,大概看了下,主要方法就是將圖片切割,然后記住偏移量,進(jìn)行滑動(dòng),所以本文給大家介紹了Spring框架實(shí)現(xiàn)滑動(dòng)驗(yàn)證碼功能的方法示例,需要的朋友可以參考下2024-07-07詳解SpringBoot中添加@ResponseBody注解會(huì)發(fā)生什么
這篇文章主要介紹了詳解SpringBoot中添加@ResponseBody注解會(huì)發(fā)生什么,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11