反射機制大揭秘-進階Java技巧,直擊核心!


反射在Java中扮演著重要的角色,掌握了反射,就等于掌握了框架設計的鑰匙。本文將為您逐步講解反射的基本概念、獲取Class對象的三種方式、使用反射實例化對象并操作屬性和方法,還有解析包的相關內容。跟隨我一起探索反射的奧秘,提升編碼技能!


一、反射基本概念


Java 反射是一個強大的特性,它允許程序在運行時查詢、訪問和修改類、接口、字段和方法的信息,以及創建和操作對象。通過反射,我們可以在運行時動態地創建對象,調用方法,修改字段值,這些在傳統的面向對象編程中是難以實現的。
1、運行時類型識別 RTTI(Run-Time Type Identification)

RTTI 即運行時類型識別,是許多編程語言中用于在程序執行期間確定對象類型的一種機制。在Java這種強類型語言中,RTTI提供了一種方式來獲取對象的實際類型,這在處理多態性、動態類型轉換和反射時尤為重要。


Java中RTTI的主要組成部分:

(1)、instanceof運算符

instanceof關鍵字用于在運行時檢查對象是否是特定類的實例,或者是否實現了特定的接口。它返回一個布爾值,如果對象是指定類型的實例,則返回true,否則返回false

Object obj = new MyClass();
boolean isMyClassInstance = obj instanceof MyClass; // true

(2)、Class類

java.lang.Class類是反射機制的核心類,它代表類的元數據。每個Java類在加載時都會創建一個Class對象。Class對象包含了類的名稱、字段、方法、構造函數等信息。

Class<?> clazz = obj.getClass(); // 獲取obj的Class對象
String className = clazz.getName(); // 獲取類名

(3)、反射API

Java的反射API允許程序在運行時查詢和操作類的結構。通過反射,你可以獲取類的信息,創建對象實例,調用方法,訪問字段等。

Class<?> clazz = Class.forName("MyClass");
Constructor<?> constructor = clazz.getConstructor();
Object obj = constructor.newInstance();

(4)、類型轉換

在Java中,類型轉換分為自動類型轉換(向上轉型)和強制類型轉換(向下轉型)。向上轉型不需要顯式操作,因為子類可以自動轉換為父類類型。向下轉型需要顯式操作,并且通常需要instanceof檢查以確保轉換的安全性。

// 向上轉型
MyClass myClass = new SubClass();// 向下轉型,需要先檢查類型
if (myClass instanceof SubClass) {SubClass subClass = (SubClass) myClass;
}

(5)、動態方法分派

在Java中,方法調用是基于對象的實際類型進行分派的,這稱為動態綁定或晚期綁定。這意味著即使方法調用在編譯時是未知的,JVM在運行時也能確定調用哪個方法。

class Base {void show() { System.out.println("Base"); }
}
class Derived extends Base {void show() { System.out.println("Derived"); }
}Base base = new Derived();
base.show(); // 輸出 "Derived",因為base實際上是Derived類型

RTTI的優點:

  • 靈活性:RTTI提供了在運行時處理不同類型的靈活性,使得代碼更加通用。
  • 多態性:RTTI支持多態性,允許將子類對象視為父類類型,而JVM在運行時確定正確的方法實現。
  • 動態行為:通過反射,RTTI允許程序在運行時動態地改變其行為。

RTTI的缺點:

  • 性能開銷:使用RTTI,特別是反射,可能會引入額外的性能開銷。
  • 安全問題:RTTI可能會破壞封裝性,允許訪問私有成員,這可能導致安全問題。
  • 復雜性:過度依賴RTTI可能會使代碼難以理解和維護。

RTTI是Java中一個強大的特性,它使得程序能夠在運行時識別對象的實際類型,并執行相應的操作。正確使用RTTI可以提高程序的靈活性和動態性,但開發者需要權衡其性能和安全性的影響。


2、Class類

Java中的每個類都隱式地繼承自java.lang.Object類,而java.lang.ClassObject的一個子類。

Class類是反射的核心,它代表了一個類或接口的靜態類型信息。

每個Java類型(類、接口、數組等)都有一個對應的Class對象。

數組同樣也被映射為class 對象的一個類,所有具有相同元素類型和維數的數組都共享該 Class 對象。

基本類型boolean,byte,char,short,int,long,float,double和關鍵字void同樣表現為 class 對象。

每個java類運行時都在JVM里表現為一個class對象,可通過類名.class、類型.getClass()、Class.forName(“類名”)等方法獲取class對象)。

public final class Class<T> implements java.io.Serializable,GenericDeclaration,Type,AnnotatedElement {private static final int ANNOTATION= 0x00002000;private static final int ENUM      = 0x00004000;private static final int SYNTHETIC = 0x00001000;private static native void registerNatives();static {registerNatives();}/** Private constructor. Only the Java Virtual Machine creates Class objects.   //私有構造器,只有JVM才能調用創建Class對象* This constructor is not used and prevents the default constructor being* generated.*/private Class(ClassLoader loader) {// Initialize final field for classLoader.  The initialization value of non-null// prevents future JIT optimizations from assuming this final field is null.classLoader = loader;}

由上可知:

  • Class類也是類的一種,與class關鍵字是不一樣的。

  • 手動編寫的類被編譯后會產生一個Class對象,其表示的是創建的類的類型信息,而且這個Class對象保存在同名.class的文件中(字節碼文件)

  • 每個通過關鍵字class標識的類,在內存中有且只有一個與之對應的Class對象來描述其類型信息,無論創建多少個實例對象,其依據的都是用一個Class對象。

  • Class類只存私有構造函數,因此對應Class對象只能有JVM創建和加載。

  • Class類的對象作用是運行時提供或獲得某個對象的類型信息,這點對于反射技術很重要。

  • 再來看看 Class類的方法

方法名說明
forName()(1)獲取Class對象的一個引用,但引用的類還沒有加載(該類的第一個對象沒有生成)就加載了這個類。
(2) 為了產生Class引用,forName()立即就進行了初始化。
Object-getClass()獲取Class對象的一個引用,返回表示該對象的實際類型的Class引用。
getName()取全限定的類名(包括包名),即類的完整名字。
getSimpleName()獲取類名(不包括包名)
getCanonicalName()獲取全限定的類名(包括包名)
isInterface()判斷Class對象是否是表示一個接口
getInterfaces()返回Class對象數組,表示Class對象所引用的類所實現的所有接口。
getSupercalss()返回Class對象,表示Class對象所引用的類所繼承的直接基類。應用該方法可在運行時發現一個對象完整的繼承結構。
newInstance()返回一個Oject對象,是實現“虛擬構造器”的一種途徑。使用該方法創建的類,必須帶有無參的構造器。
getFields()獲得某個類的所有的公共(public)的字段,包括繼承自父類的所有公共字段。 類似的還有getMethods和getConstructors。
getDeclaredFields獲得某個類的自己聲明的字段,即包括public、private和proteced,默認但是不包括父類聲明的任何字段。類似的還有getDeclaredMethods和getDeclaredConstructors。

3、類加載

類加載機制和類字節碼技術,感興趣的朋友請前往查閱。

  • 探索Java的DNA-JVM字節碼深度解析
  • Java 類加載機制解密一探到底

其中,這里我們需要回顧的是,類加載機制流程:

包括5個階段:加載、驗證、準備、解析和初始化。其中加載、驗證、準備、初始化這4個階段的順序是確定的,只有解析階段在特定情況下可以在初始化之后再開始。

在這里插入圖片描述


二、反射組件及使用方法


在Java中,Class類是反射機制的一部分,它代表了一個類或接口的靜態類型信息。使用Class類,你可以獲取類的信息,包括構造函數、方法、字段等。

以下示例代碼,展示如何獲取和使用Class類對象。


1、獲取Class對象

要獲取一個Class對象,你可以使用以下方法之一:

  • 使用.class語法。
  • 使用Class.forName()靜態方法。

(1)、使用.class語法
public class MyClass {public void myMethod() {System.out.println("Hello, World!");}
}public class Main {public static void main(String[] args) {Class<MyClass> myClassClass = MyClass.class; // 獲取MyClass的Class對象System.out.println(myClassClass.getName()); // 打印類名}
}

(2)、使用Class.forName()
public class Main {public static void main(String[] args) throws ClassNotFoundException {Class<?> myClassClass = Class.forName("com.example.MyClass"); // 獲取MyClass的Class對象System.out.println(myClassClass.getName()); // 打印類名}
}

(3)、對象實例獲取
Object obj = new Object();
Class clazz = obj.getClass();

2、獲取類的構造函數
public class Main {public static void main(String[] args) throws Exception {Class<?> myClassClass = Class.forName("com.example.MyClass");Constructor<?> constructor = myClassClass.getConstructor(); // 獲取無參構造函數System.out.println(constructor);}
}

3、創建類的實例
public class Main {public static void main(String[] args) throws Exception {Class<?> myClassClass = Class.forName("com.example.MyClass");Constructor<?> constructor = myClassClass.getConstructor();Object myClassInstance = constructor.newInstance(); // 創建MyClass的實例myClassInstance.getClass().getMethod("myMethod").invoke(myClassInstance); // 調用方法}
}

4、獲取類的方法
public class Main {public static void main(String[] args) throws Exception {Class<?> myClassClass = Class.forName("com.example.MyClass");Method method = myClassClass.getMethod("myMethod"); // 獲取myMethod方法System.out.println(method);}
}

5、調用方法
public class Main {public static void main(String[] args) throws Exception {Class<?> myClassClass = Class.forName("com.example.MyClass");Constructor<?> constructor = myClassClass.getConstructor();Object myClassInstance = constructor.newInstance();Method method = myClassClass.getMethod("myMethod");method.invoke(myClassInstance); // 調用myMethod方法}
}

6、獲取類的字段

反射可以獲取某個類的所有屬性信息,包括私有屬性。

public class Main {public static void main(String[] args) throws Exception {Class<?> myClassClass = Class.forName("com.example.MyClass");Field field = myClassClass.getField("myField"); // 獲取public字段System.out.println(field);}
}

7、訪問字段的值
public class Main {public static void main(String[] args) throws Exception {Class<?> myClassClass = Class.forName("com.example.MyClass");Constructor<?> constructor = myClassClass.getConstructor();Object myClassInstance = constructor.newInstance();Field field = myClassClass.getField("myField");field.setAccessible(true); // 如果字段是private,需要設置為可訪問Object fieldValue = field.get(myClassInstance); // 獲取字段值System.out.println(fieldValue);}
}

請注意,以上示例代碼中的com.example.MyClass需要替換為實際的類路徑。此外,如果類、方法或字段是私有的,你可能需要調用setAccessible(true)來允許反射訪問它們。使用反射時要小心,因為它可能會破壞封裝性,并帶來性能開銷。


8、反射獲取包信息

Package類提供了一些與包相關的實用方法和信息。比如獲取包名、包版本等。示例:

Package pkg = Class.class.getPackage();
System.out.println("包名: " + pkg.getName());
System.out.println("包說明: " + pkg.getSpecificationTitle());
System.out.println("包版本: " + pkg.getSpecificationVersion());

三、反射的優缺點

  • 性能開銷:反射操作通常比直接代碼調用要慢,因為它涉及到類型解析和動態調用。
  • 安全問題:反射可以破壞封裝性,允許代碼訪問私有成員,這可能導致安全問題。
  • 難以優化:由于反射操作的動態性,JVM難以對其進行優化。

四、反射的使用場景

1、框架開發

許多Java框架(如Spring、Hibernate)使用反射來實現依賴注入、ORM映射等。


(1)、依賴注入(DI)

場景:依賴注入是一種設計模式,用于實現控制反轉(IoC),允許框架在運行時自動裝配對象的依賴關系。

案例:Spring框架使用反射來實現依賴注入。Spring容器在啟動時會掃描指定的包,查找帶有特定注解(如@Component@Service等)的類,并為這些類創建實例和管理它們的生命周期。

示例代碼

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;public class Main {public static void main(String[] args) {ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);MyService myService = context.getBean(MyService.class);myService.doWork();}
}// AppConfig.java
@Configuration
public class AppConfig {@Beanpublic MyService myService() {return new MyServiceImpl();}
}// MyService.java
public interface MyService {void doWork();
}// MyServiceImpl.java
@Service
public class MyServiceImpl implements MyService {@Overridepublic void doWork() {System.out.println("Doing work...");}
}

(2)、對象關系映射(ORM)

場景:ORM框架允許開發者使用面向對象的方式來操作數據庫,而不是使用SQL語句。

案例:Hibernate是一個流行的ORM框架,它使用反射來將Java對象映射到數據庫表中。

示例代碼

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;public class Main {public static void main(String[] args) {Configuration configuration = new Configuration().configure();SessionFactory sessionFactory = configuration.buildSessionFactory();Session session = sessionFactory.openSession();try {session.beginTransaction();Employee employee = new Employee(1, "John Doe", "Developer");session.save(employee);session.getTransaction().commit();} finally {session.close();}}
}// Employee.java
@Entity
@Table(name = "employees")
public class Employee {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private int id;private String name;private String jobTitle;// Constructors, getters and setters
}

(3)、動態代理

場景:動態代理允許在運行時創建一個實現了一組接口的新類,而不需要事先編寫具體的類代碼。

案例:Java的java.lang.reflect.Proxy類和java.lang.reflect.InvocationHandler接口可以用來創建動態代理。

示例代碼

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;public class Main {public static void main(String[] args) {MyService myService = (MyService) Proxy.newProxyInstance(MyService.class.getClassLoader(),new Class<?>[]{MyService.class},new MyServiceHandler(new MyServiceImpl()));myService.doWork();}
}interface MyService {void doWork();
}class MyServiceImpl implements MyService {public void doWork() {System.out.println("Doing work...");}
}class MyServiceHandler implements InvocationHandler {private MyService myService;public MyServiceHandler(MyService myService) {this.myService = myService;}public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("Before method: " + method.getName());method.invoke(myService, args);System.out.println("After method: " + method.getName());return null;}

2、插件系統

在Java中,反射可以用來實現一個插件系統,這允許主應用程序在運行時加載和使用插件,而無需在編譯時知道插件的具體實現。這種機制非常有用,因為它提供了極大的靈活性和可擴展性。

假設我們有一個文本編輯器應用程序,我們希望允許用戶通過插件來擴展編輯器的功能,比如添加語法高亮、拼寫檢查等。


步驟 1: 定義插件接口

首先,我們定義一個插件接口,所有插件都必須實現這個接口。

public interface TextEditorPlugin {void apply(String text);
}

步驟 2: 創建具體插件

然后,我們創建具體的插件實現。

public class SpellCheckPlugin implements TextEditorPlugin {@Overridepublic void apply(String text) {// 實現拼寫檢查邏輯System.out.println("Spell check applied: " + text);}
}public class SyntaxHighlightPlugin implements TextEditorPlugin {@Overridepublic void apply(String text) {// 實現語法高亮邏輯System.out.println("Syntax highlighted: " + text);}
}

步驟 3: 加載和使用插件

最后,我們使用反射來加載和使用插件。

import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.List;public class PluginLoader {public static void loadAndApplyPlugins(String pluginPath, String text) {// 創建類加載器URL[] urls = new URL[] { new File(pluginPath).toURI().toURL() };URLClassLoader classLoader = new URLClassLoader(urls);// 獲取插件JAR中的所有類List<Class<?>> pluginClasses = new ArrayList<>();try {String[] entries = ((String) new File(pluginPath).listFiles()[0]).split(" ");for (String entry : entries) {Class<?> clazz = Class.forName(entry, true, classLoader);if (TextEditorPlugin.class.isAssignableFrom(clazz)) {pluginClasses.add(clazz);}}} catch (Exception e) {e.printStackTrace();}// 實例化插件并應用for (Class<?> pluginClass : pluginClasses) {try {TextEditorPlugin plugin = (TextEditorPlugin) pluginClass.newInstance();plugin.apply(text);} catch (InstantiationException | IllegalAccessException e) {e.printStackTrace();}}// 關閉類加載器try {classLoader.close();} catch (IOException e) {e.printStackTrace();}}public static void main(String[] args) {String text = "This is a sample text for the editor.";loadAndApplyPlugins("path/to/plugin/folder", text);}
}

在這個例子中,PluginLoader類負責加載插件。它首先創建一個URLClassLoader來加載插件JAR文件。然后,它獲取JAR文件中的所有類,并檢查這些類是否實現了TextEditorPlugin接口。如果是,它將這些類實例化并調用apply方法。


請注意,這個例子假設插件JAR文件中的類名存儲在一個文本文件中,并且這個文本文件位于插件文件夾中。這只是一個簡化的示例,實際應用中可能需要更復雜的機制來發現和加載插件。


通過這種方式,我們可以實現一個靈活的插件系統,主應用程序可以在運行時加載和使用插件,而無需在編譯時知道插件的具體實現。這為應用程序的擴展和定制提供了極大的便利。


3、運行時配置

反射在運行時配置中非常有用,它允許應用程序根據配置文件在運行時動態加載類和創建對象。這種機制使得應用程序能夠靈活地適應不同的環境和需求,而無需重新編譯或部署。

運行時配置的使用場景
  • 環境適應性:應用程序可以根據不同的環境(開發、測試、生產)加載不同的配置。
  • 模塊化:應用程序可以由多個模塊組成,每個模塊都可以在運行時動態加載。
  • 可擴展性:應用程序可以設計為可擴展的,允許在運行時添加新功能。
  • 插件支持:如前所述,插件系統可以利用運行時配置來加載和集成插件。

假設我們有一個簡單的應用程序,它需要根據配置文件來決定使用哪個服務類來處理請求。我們將使用一個配置文件來指定服務類的全限定名,并使用反射來動態加載和實例化這個類。


步驟 1: 創建服務接口

首先,我們定義一個服務接口,所有的服務類都將實現這個接口。

public interface Service {void execute();
}

步驟 2: 創建具體的服務實現

然后,我們創建具體的服務實現。

public class ServiceImplA implements Service {@Overridepublic void execute() {System.out.println("Executing Service A");}
}public class ServiceImplB implements Service {@Overridepublic void execute() {System.out.println("Executing Service B");}
}

步驟 3: 創建配置文件

接下來,我們創建一個配置文件(例如config.txt),它包含服務類的全限定名。

com.example.ServiceImplA

步驟 4: 使用反射加載和使用服務

最后,我們使用反射來根據配置文件加載和使用服務。

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;public class RuntimeConfigDemo {public static void main(String[] args) {// 從配置文件中讀取服務類的名稱String className;try {className = new String(Files.readAllBytes(new File("config.txt").toPath()));} catch (IOException e) {e.printStackTrace();return;}// 使用反射加載并實例化服務類Service service = loadService(className);if (service != null) {service.execute();} else {System.out.println("Service class could not be loaded or instantiated.");}}private static Service loadService(String className) {try {Class<?> serviceClass = Class.forName(className);return (Service) serviceClass.getDeclaredConstructor().newInstance();} catch (Exception e) {e.printStackTrace();return null;}}
}

在這個例子中,RuntimeConfigDemo類負責根據配置文件加載服務。它首先從config.txt文件中讀取服務類的全限定名,然后使用Class.forName來加載類,接著通過getDeclaredConstructor().newInstance()來創建類的實例。最后,它調用服務的execute方法。

通過這種方式,應用程序可以根據配置文件在運行時動態地加載和使用不同的服務實現,而無需事先知道具體的服務類。這為應用程序提供了極大的靈活性和可配置性。


以上就是本文的主要內容,希望您在閱讀過程中有所收獲。敬請期待下一期,我將分享更多反射的實戰技巧和使用場景,讓您的Java之旅更上一層樓!


本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/19298.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/19298.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/19298.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

使用 Ubuntu + Docker + Vaultwarden + Tailscale 自建密碼管理器

使用 Ubuntu Docker Vaultwarden Tailscale 自建密碼管理器 先決條件 一臺運行 Ubuntu 系統的服務器。可以是云提供商的 VPS、家庭網絡中的樹莓派、或者 Windows 電腦上的虛擬機等等 一個 Tailscale 賬戶。如果還沒有 Tailscale 賬戶&#xff0c;可以通過此鏈接迅速創建一個…

SelfKG論文翻譯

SelfKG: Self-Supervised Entity Alignment in Knowledge Graphs SelfKG&#xff1a;知識圖中的自監督實體對齊 ABSTRACT 實體對齊旨在識別不同知識圖譜&#xff08;KG&#xff09;中的等效實體&#xff0c;是構建網絡規模知識圖譜的基本問題。在其發展過程中&#xff0c;標…

華納云:MAC電腦怎么遠程連接Windows服務器桌面?

在Mac電腦上遠程連接Windows服務器桌面可以通過多種方式實現&#xff0c;最常用的方法是使用微軟提供的免費應用程序 "Microsoft Remote Desktop"。以下是詳細的步驟來設置和使用該工具&#xff1a; 步驟一&#xff1a;下載和安裝 Microsoft Remote Desktop 打開App …

SpringBoot的自動裝配

我們今天再來說一下關于 SpringBoot 的自動裝配&#xff0c;為什么會有這樣的問題呢&#xff1f;一般這種情況都是在面試的過程中&#xff0c;面試官有時候會問到這個問題&#xff0c;就比如從開始問SpringBoot 的一些常用注解&#xff0c;到SpringBoot的一些特性&#xff0c;然…

zynq之UART

之前嘗試UART0&#xff08;MIO50、51&#xff09;&#xff0c;串口調試助手收到發送的內容。 現在板子上EMIO端有多個串口&#xff0c;所以看看這個怎么弄。 串口是484的轉接板&#xff08;接232的串口就會輸出亂碼&#xff09; https://blog.51cto.com/u_15262460/2882973 …

【九十三】【算法分析與設計】719. 找出第 K 小的數對距離,N 臺電腦的最長時間,二分答案法

719. 找出第 K 小的數對距離 - 力扣&#xff08;LeetCode&#xff09; 數對 (a,b) 由整數 a 和 b 組成&#xff0c;其數對距離定義為 a 和 b 的絕對差值。 給你一個整數數組 nums 和一個整數 k &#xff0c;數對由 nums[i] 和 nums[j] 組成且滿足 0 < i < j < nums.le…

java調用遠程接口下載文件

在postman中這樣下載文件 有時下載文件太大postman會閃退&#xff0c;可以通過代碼下載&#xff0c;使用hutool的http包

3步操作助您輕松實現蘋果手機照片一鍵傳輸至電腦

對于很多使用蘋果手機的用戶來說&#xff0c;隨著手機中照片和視頻數量的不斷積累&#xff0c;如何將這些珍貴的回憶從手機轉移到電腦&#xff0c;以便更好地保存、整理和分享&#xff0c;成為了一個值得關注的問題。那么&#xff0c;蘋果手機怎么把照片導入電腦呢&#xff1f;…

鴻蒙課程培訓 | 訊方技術與鴻蒙生態服務公司簽約,成為鴻蒙鉆石服務商

3月15日&#xff0c;深圳市訊方技術股份有限公司與鴻蒙生態服務公司簽署合作協議&#xff0c;訊方技術成為鴻蒙鉆石服務商&#xff0c;正式進軍鴻蒙原生應用培訓開發領域。訊方技術總裁劉國鋒、副總經理劉銘皓、深圳區域總經理張松柏、深圳區域交付總監張梁出席簽約儀式。 作…

鄉村振興的鄉村產業創新發展:培育鄉村新興產業,打造鄉村產業新名片,促進鄉村經濟多元化發展

目錄 一、引言 二、鄉村產業創新發展的必要性 &#xff08;一&#xff09;適應新時代發展要求 &#xff08;二&#xff09;滿足消費升級需求 &#xff08;三&#xff09;促進農民增收致富 三、培育鄉村新興產業策略 &#xff08;一&#xff09;加強科技創新引領 &#…

在 MFC 中 UNICODE 加 _T 與 L 長字符串,有什么區別?

在MFC&#xff08;Microsoft Foundation Classes&#xff09;和更廣泛的Windows編程環境中&#xff0c;UNICODE宏用于指示程序應使用Unicode字符集&#xff08;通常是UTF-16&#xff09;來處理文本。當定義了UNICODE宏時&#xff0c;編譯器和庫函數會期待和處理寬字符&#xff…

Android下HWC以及drm_hwcomposer普法((上)

Android下HWC以及drm_hwcomposer普法((上) 引言 按摩得全套&#xff0c;錯了&#xff0c;做事情得全套&#xff0c;普法分析也是如此。drm_hwcomposer如果對Android圖形棧有一定研究的童鞋們應該知道它是Android提供的一個的圖形后端合成處理HAL模塊的實現。但是在分析這個之前…

Java復習-集合篇

集合 集合分為倆大類 單列集合 每個元素數據只包含一個值 雙列集合 每個元素包含倆個鍵值對 Conllection單列集合 單列集合常用的主要是下列幾種 List集合 List系列集合的特點&#xff1a;添加元素是有序、可重復、有索引 這里我們來試一下ArrayList ArrayList<String&g…

Spring OAuth2:開發者的安全盾牌!(上)

何利用Spring OAuth2構建堅不可摧的安全體系&#xff1f;如何使用 OAuth2 從跨域挑戰到性能優化&#xff0c;每一個環節都為你的應用保駕護航&#xff1f; 文章目錄 Spring OAuth2 詳解1. 引言簡述OAuth2協議的重要性Spring Framework對OAuth2的支持概述 2. 背景介紹2.1 OAuth2…

比較Rust和Haskel

在比較Rust和Haskell時&#xff0c;我們可以從多個維度來分析它們各自的優勢。以下是Rust相對于Haskell的優勢&#xff0c;以及Haskell相對于Rust的優勢&#xff1a; Rust比Haskell強的方面&#xff1a; 內存安全與并發性&#xff1a; Rust通過獨特的所有權系統和借用檢查器在…

智能倉儲物流系統(WMS)系列-管理查詢調整

好的應用系統應是細分簡單&#xff0c;界面簡潔易操作&#xff0c;程序代碼簡潔易懂的。

史上最全排序算法整理(2)

本篇文章我們將接著上篇繼續介紹常見的排序算法&#xff0c;有需要的小伙伴可以移步史上最全排序算法整理&#xff08;1&#xff09;查看相關內容哦 1.冒泡排序 1.1基本思想 在待排序的一組數中&#xff0c;將相鄰的兩個數進行比較&#xff0c;若前面的數比后面的數大就交換兩…

【解決npm install -g windows-build-tools的安裝問題】

解決npm install -g windows-build-tools的安裝問題 https://developer.huawei.com/consumer/cn/forum/topic/0203740461436730610?fid26

gitlab 創建 ssh 和 token

文章目錄 一、創建ssh key二、將密鑰內容復制到gitlab三、創建token 一、創建ssh key 打開控制臺cmd&#xff0c;執行命令 ssh-keygen -t rsa -C xxxxx xxxxx是你自己的郵箱 C:\Users\xx\.ssh 目錄下會創建一個名為id_rsa.pub的文件&#xff0c;用記事本打開&#xff0c;并…

基于深度學習的中文情感分析系統python flask

基于python的畢業設計 基于深度學習的中文情感分析系統(flask)(源碼說明文檔演示) 畢業設計課程設計期末大作業、課程設計、高分必看&#xff0c;下載下來&#xff0c;簡單部署&#xff0c;就可以使用。 包含&#xff1a;項目源碼、數據庫腳本、軟件工具等&#xff0c;該項目…