• 【UE4 反射系统】 UCLAS UFUNCTION UPROPERTY 宏简单解析 持续更新


    请添加图片描述

    • 🙋‍♂️ 作者:海码007
    • 📜 专栏:UE虚幻引擎专栏
    • 💥 标题:【UE4 反射系统】 UCLAS USTRUCT UFUNCTION UPROPERTY 宏详解
    • ❣️ 寄语:加油,一次专注一件事!
    • 🎈 最后:文章作者技术和水平有限,如果文中出现错误,希望大家能指正,同时有问题的话,欢迎大家留言讨论。

    0 引言

    • 反射系统是一种允许程序在运行时获取类型信息动态操作类和对象的机制。在C++语言中,没有内置的反射系统,但可以使用不同的库和技术实现类似的功能。(Java和C#语言都是有内置的反射机制的)
    • UE4在C++的基础上搭建了自己的一套反射机制,使用自定义的反射系统来支持蓝图编程和编辑器功能。UE4的反射系统允许在运行时访问类和对象的属性、函数和元数据,并提供了一些宏和关键字来标识和定义可被反射系统处理的内容。其中,“UCLASS”、"UFUNCTION"和"UPROPERTY"是UE4中用于反射的一些重要宏。
    • UE4的反射系统是选择加入的,只有主动标记的类型、属性、方法会被反射系统追踪,UnrealHeaderTool(UHT)会收集这些信息,生成用于支持反射机制的C++代码,然后再编译工程。

    1 C++如何实现反射机制

    实现C++的反射机制有多种方式。下面详细介绍其中几种常见的方法:

    1. 手动定义转换函数:这是最基础的反射实现方式。在类的定义中,手动定义转换函数,用于将类的数据成员或方法映射到字符串或其他类型。例如,可以为每个类定义一个静态成员函数,接受类的实例作为参数,并返回包含类信息的结构体。这种方法需要手动编写大量的重复代码,不够灵活。
    2. 使用宏:宏是C++中实现代码生成的一种方式。通过定义一系列宏来模拟反射机制。可以使用宏定义类的元信息,例如类名、成员变量名、函数名等。通过宏展开,可以在运行时获取类的元信息,并执行对应的操作。这种方法可以减少手动编写的代码量,但仍然需要繁琐的宏定义。
    3. 使用代码生成工具:可以编写一个代码生成工具,根据特定的标记或注释解析源代码,并生成反射所需的代码。工具可以根据类定义自动生成访问元信息的代码,避免手动编写大量重复的代码。通过将生成工具作为构建系统的一部分,可以在每次构建时自动更新反射代码。这种方法相对灵活且易于维护,但需要编写自定义的代码生成工具。(QT和UE4就是采用的该方法实现反射机制
    4. 使用第三方库:也可以使用现有的第三方库来实现C++反射。例如,"Boost.Reflection"库提供了一套用于C++的反射功能。它可以动态地获取和操作类的成员变量、函数和类型信息。这样,开发者可以在运行时获得类的元信息,从而实现动态加载和操作类的功能。

    需要注意的是,C++作为静态类型语言,没有内置的反射机制。因此,以上这些方法只是模拟了反射的部分功能,无法像动态类型语言一样灵活。
    总结起来,实现C++的反射机制可以通过手动定义转换函数、使用宏、编写代码生成工具或使用第三方库等方式。每种方式都有其优缺点,具体选择取决于项目需求和开发者的偏好。

    1.1 使用代码生成工具实现反射机制

    • 代码生成工具通常是在构建过程中的一个独立阶段,通过解析源代码来生成反射所需的代码

    简单的实现步骤:

    1. 定义注释或标记:在源代码中定义一些注释或特定的标记,用于标识需要反射的类、成员变量和方法
      例如,可以使用REFLECT_CLASS、REFLECT_MEMBER和REFLECT_METHOD等注释类、成员变量和方法
    2. 编写代码生成工具:编写一个代码生成工具,它可以读取源代码文件,并解析这些注释或标记
      例如,可以使用编译器提供的编译器前端库,如Clang的LibTooling,来辅助解析源代码。
    3. 解析源代码:使用代码生成工具解析源代码文件,并提取类、成员变量和方法的相关信息
      例如,可以获取类名成员变量的名称类型访问修饰符,以及方法的名称、参数列表和返回类型等
    4. 生成反射代码:根据解析得到的信息,代码生成工具可以生成反射所需的代码。
      例如,可以生成用于获取类的元信息的结构体或类以及用于动态访问成员变量和调用方法的函数。生成的代码可以包括类注册、获取类信息、访问成员变量、调用方法等功能。
    5. 构建生成工具将代码生成工具集成到项目的构建过程中
      例如,使用Makefile、CMake或其他构建系统。确保代码生成工具在每次构建时自动运行,以根据最新的源代码生成反射代码。
    6. 使用反射功能在运行时可以使用生成的反射代码动态地获取和操作类的成员变量和方法
      根据类的元信息,可以在不知道类的实际名称的情况下,动态创建对象、获取和修改成员变量的值,以及调用方法。

    2 UE4的反射系统

    在了解了C++中如何使用代码生成工具实现反射机制之后,那么再来理解 UE4 反射系统就会容易许多。

    先从最简单的一个UObject子类开始分析(因为日常使用的类基本上都继承UObject),我们新建一个C++类继承UObject,UE会自动生成许多反射的代码:

    // Fill out your copyright notice in the Description page of Project Settings.
    
    #pragma once
    
    #include "CoreMinimal.h"
    #include "UObject/NoExportTypes.h"
    
    #include "MyObject1.generated.h"
    
    /**
     * 
     */
    UCLASS()
    class CPP_CLASS_10_15_API UMyObject1 : public UObject
    {
    	GENERATED_BODY()
    	
    public:
    
    	UFUNCTION()
    	void fun1()
    	{
    		UE_LOG(LogTemp, Warning, TEXT("name = %s"), *name);
    	}
    
    	UPROPERTY()
    	FString name = "007";
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    2.1 ****.generated.h头文件

    • generate.h,其实就是UE对我们自己写的.h文件产生的反射代码文件。
    • #include "MyObject1.generated.h"需要写在头文件包含的最后。不然编译时会报错。错误如下:
    error : #include found after .generated.h file - the .generated.h file should always be the last #include in a header
    错误:#include在.generated.h文件之后找到-.generated.h文件应该始终是头中的最后一个#include
    
    • 1
    • 2

    2.2 GENERATED_BODY()

    使用GENERATED_BODY()宏自动展开为通用的成员函数接口和数据成员

    2.3 反射宏 UCLASS 等

    可以看到上面出现了三个宏,这些都是反射宏,下图中是UE源码中对宏的定义
    在这里插入图片描述

    反射宏名称作用
    UCLASS告诉UE这个类是个反射类。此类必须派生自UObject(如果不用这个宏,那么成员函数和成员变量加了宏也反射不了)
    UFUNCTION定义一个反射的函数
    UPROPERTY定义一个反射的变量
    USTRUCT可以不用派生自UObject。不支持GC,也不能包含函数
    UENUM告诉UE这是一个反射的枚举类。支持enum, enum class, enum namespace
    UINTERFACE定义一个反射接口类,只能包含函数
    UMETA反射的一些元数据定义,可以通过标签定义一些该变量的属性
    UPARAM定义函数的参数属性。主要就是显示名字和Ref属性
    UDELEGATE告诉UE这是一个可反射的委托(很少用到)

    2.4 UHT和UBT

    由于作者目前水平有限,理解还不透彻。等学习领悟之后再来填坑。
    这里放一个 参考文章 帮助大家理解

    3 基本宏的使用

    3.1 UCLASS

    • 如果想要使用蓝图生成C++类,则需要给UCLASS宏中添加说明符,UCLASS宏的所有说明符如下所示:
    • 最常用的就是 BlueprintType(将此类公开为可用于蓝图中变量的类型)
    // These are used for syntax highlighting and to allow autocomplete hints
    
    namespace UC
    {
    	// valid keywords for the UCLASS macro
    	enum 
    	{
    		/// This keyword is used to set the actor group that the class is show in, in the editor.
    		classGroup,
    
    		/// Declares that instances of this class should always have an outer of the specified class.  This is inherited by subclasses unless overridden.
    		Within, /* =OuterClassName */
    
    		/// Exposes this class as a type that can be used for variables in blueprints
    		BlueprintType,
    
    		/// Prevents this class from being used for variables in blueprints
    		NotBlueprintType,
    
    		/// Exposes this class as an acceptable base class for creating blueprints. The default is NotBlueprintable, unless inherited otherwise. This is inherited by subclasses.
    		Blueprintable,
    
    		/// Specifies that this class is *NOT* an acceptable base class for creating blueprints. The default is NotBlueprintable, unless inherited otherwise. This is inherited by subclasses.
    		NotBlueprintable,
    
    		/// This keyword indicates that the class should be accessible outside of it's module, but does not need all methods exported.
    		/// It exports only the autogenerated methods required for dynamic_cast<>, etc... to work.
    		MinimalAPI,
    
    		/// Prevents automatic generation of the constructor declaration.
    		customConstructor,
    
    		/// Class was declared directly in C++ and has no boilerplate generated by UnrealHeaderTool.
    		/// DO NOT USE THIS FLAG ON NEW CLASSES.
    		Intrinsic,
    
    		/// No autogenerated code will be created for this class; the header is only provided to parse metadata from.
    		/// DO NOT USE THIS FLAG ON NEW CLASSES.
    		noexport,
    
    		/// Allow users to create and place this class in the editor.  This flag is inherited by subclasses.
    		placeable,
    
    		/// This class cannot be placed in the editor (it cancels out an inherited placeable flag).
    		notplaceable,
    
    		/// All instances of this class are considered "instanced". Instanced classes (components) are duplicated upon construction. This flag is inherited by subclasses. 
    		DefaultToInstanced,
    
    		/// All properties and functions in this class are const and should be exported as const.  This flag is inherited by subclasses.
    		Const,
    
    		/// Class is abstract and can't be instantiated directly.
    		Abstract,
    
    		/// This class is deprecated and objects of this class won't be saved when serializing.  This flag is inherited by subclasses.
    		deprecated,
    
    		/// This class can't be saved; null it out at save time.  This flag is inherited by subclasses.
    		Transient,
    
    		/// This class should be saved normally (it cancels out an inherited transient flag).
    		nonTransient,
    
    		/// This class is optional and might not be available in certain context. reference from non optional data type is not allowed.
    		Optional,
    
    		/// Load object configuration at construction time.  These flags are inherited by subclasses.
    		/// Class containing config properties. Usage config=ConfigName or config=inherit (inherits config name from base class).
    		config,
    		/// Handle object configuration on a per-object basis, rather than per-class. 
    		perObjectConfig,
    		/// Determine whether on serialize to configs a check should be done on the base/defaults ini's
    		configdonotcheckdefaults,
    
    		/// Save object config only to Default INIs, never to local INIs.
    		defaultconfig,
    
    		/// Mark the editor config file to load from if loading into this object.
    		EditorConfig,
    
    		/// These affect the behavior of the property editor.
    		/// Class can be constructed from editinline New button.
    		editinlinenew,
    		/// Class can't be constructed from editinline New button.
    		noteditinlinenew,
    		/// Class not shown in editor drop down for class selection.
    		hidedropdown,
    
    		/// Shows the specified categories in a property viewer. Usage: showCategories=CategoryName or showCategories=(category0, category1, ...)
    		showCategories,
    		/// Hides the specified categories in a property viewer. Usage: hideCategories=CategoryName or hideCategories=(category0, category1, ...)
    		hideCategories,
    		/// Indicates that this class is a wrapper class for a component with little intrinsic functionality (this causes things like hideCategories and showCategories to be ignored if the class is subclassed in a Blueprint)
    		ComponentWrapperClass,
    		/// Shows the specified function in a property viewer. Usage: showFunctions=FunctionName or showFunctions=(category0, category1, ...)
    		showFunctions,
    		/// Hides the specified function in a property viewer. Usage: hideFunctions=FunctionName or hideFunctions=(category0, category1, ...)
    		hideFunctions,
    		/// Specifies which categories should be automatically expanded in a property viewer.
    		autoExpandCategories,
    		/// Specifies which categories should be automatically collapsed in a property viewer.
    		autoCollapseCategories,
    		/// Clears the list of auto collapse categories.
    		dontAutoCollapseCategories,
    		/// Display properties in the editor without using categories.
    		collapseCategories,
    		/// Display properties in the editor using categories (default behaviour).
    		dontCollapseCategories,
    		/// Specifies category display order, unspecified will follow default display order.
    		prioritizeCategories,
    
    		/// All the properties of the class are hidden in the main display by default, and are only shown in the advanced details section.
    		AdvancedClassDisplay,
    
    		/// A root convert limits a sub-class to only be able to convert to child classes of the first root class going up the hierarchy.
    		ConversionRoot,
    
    		/// Marks this class as 'experimental' (a totally unsupported and undocumented prototype)
    		Experimental,
    
    		/// Marks this class as an 'early access' preview (while not considered production-ready, it's a step beyond 'experimental' and is being provided as a preview of things to come)
    		EarlyAccessPreview,
    
    		/// Some properties are stored once per class in a sidecar structure and not on instances of the class
    		SparseClassDataType,
    
    		/// Specifies the struct that contains the CustomThunk implementations
    		CustomThunkTemplates
    	};
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131

    3.2 UFUNCTION

    • 最常用的函数说明符就是 BlueprintCallable(此函数可在蓝图或关卡蓝图图表中执行)
    namespace UF
    {
    	// valid keywords for the UFUNCTION and UDELEGATE macros
    	enum 
    	{
    		/// This function is designed to be overridden by a blueprint.  Do not provide a body for this function;
    		/// the autogenerated code will include a thunk that calls ProcessEvent to execute the overridden body.
    		BlueprintImplementableEvent,
    
    		/// This function is designed to be overridden by a blueprint, but also has a native implementation.
    		/// Provide a body named [FunctionName]_Implementation instead of [FunctionName]; the autogenerated
    		/// code will include a thunk that calls the implementation method when necessary.
    		BlueprintNativeEvent,
    
    		/// This function is sealed and cannot be overridden in subclasses.
    		/// It is only a valid keyword for events; declare other methods as static or final to indicate that they are sealed.
    		SealedEvent,
    
    		/// This function is executable from the command line.
    		Exec,
    
    		/// This function is replicated, and executed on servers.  Provide a body named [FunctionName]_Implementation instead of [FunctionName];
    		/// the autogenerated code will include a thunk that calls the implementation method when necessary.
    		Server,
    
    		/// This function is replicated, and executed on clients.  Provide a body named [FunctionName]_Implementation instead of [FunctionName];
    		/// the autogenerated code will include a thunk that calls the implementation method when necessary.
    		Client,
    
    		/// This function is both executed locally on the server and replicated to all clients, regardless of the Actor's NetOwner
    		NetMulticast,
    
    		/// Replication of calls to this function should be done on a reliable channel.
    		/// Only valid when used in conjunction with Client or Server
    		Reliable,
    
    		/// Replication of calls to this function can be done on an unreliable channel.
    		/// Only valid when used in conjunction with Client or Server
    		Unreliable,
    
    		/// This function fulfills a contract of producing no side effects, and additionally implies BlueprintCallable.
    		BlueprintPure,
    
    		/// This function can be called from blueprint code and should be exposed to the user of blueprint editing tools.
    		BlueprintCallable,
    
    		/// This function is used as the get accessor for a blueprint exposed property. Implies BlueprintPure and BlueprintCallable.
    		BlueprintGetter,
    
    		/// This function is used as the set accessor for a blueprint exposed property. Implies BlueprintCallable.
    		BlueprintSetter,
    
    		/// This function will not execute from blueprint code if running on something without network authority
    		BlueprintAuthorityOnly,
    
    		/// This function is cosmetic and will not run on dedicated servers
    		BlueprintCosmetic,
    
    		/// Indicates that a Blueprint exposed function should not be exposed to the end user
    		BlueprintInternalUseOnly,
    	
    		/// This function can be called in the editor on selected instances via a button in the details panel.
    		CallInEditor,
    
    		/// The UnrealHeaderTool code generator will not produce a execFoo thunk for this function; it is up to the user to provide one.
    		CustomThunk,
    
    		/// Specifies the category of the function when displayed in blueprint editing tools.
    		/// Usage: Category=CategoryName or Category="MajorCategory,SubCategory"
    		Category,
    
    		/// This function must supply a _Validate implementation
    		WithValidation,
    
    		/// This function is RPC service request
    		ServiceRequest,
    
    		/// This function is RPC service response
    		ServiceResponse,
    		
    		/// [FunctionMetadata]	Marks a UFUNCTION as accepting variadic arguments. Variadic functions may have extra terms they need to emit after the main set of function arguments
    		///						These are all considered wildcards so no type checking will be performed on them
    		Variadic,
    
    		/// [FunctionMetadata] Indicates the display name of the return value pin
    		ReturnDisplayName, 
    
    		/// [FunctionMetadata] Indicates that a particular function parameter is for internal use only, which means it will be both hidden and not connectible.
    		InternalUseParam, 
    
    		/// [FunctionMetadata] Indicates that the function should be ignored when considered for blueprint type promotion
    		IgnoreTypePromotion,
    	};
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94

    3.3 UPROPERTY

    • BlueprintReadWrite 可从蓝图读取或写入此属性。此说明符与 BlueprintReadOnly 说明符不兼容
    namespace UP
    {
    	// valid keywords for the UPROPERTY macro
    	enum 
    	{
    		/// This property is const and should be exported as const.
    		Const,
    
    		/// Property should be loaded/saved to ini file as permanent profile.
    		Config,
    
    		/// Same as above but load config from base class, not subclass.
    		GlobalConfig,
    
    		/// Property should be loaded as localizable text. Implies ReadOnly.
    		Localized,
    
    		/// Property is transient: shouldn't be saved, zero-filled at load time.
    		Transient,
    
    		/// Property should always be reset to the default value during any type of duplication (copy/paste, binary duplication, etc.)
    		DuplicateTransient,
    
    		/// Property should always be reset to the default value unless it's being duplicated for a PIE session - deprecated, use NonPIEDuplicateTransient instead
    		NonPIETransient,
    
    		/// Property should always be reset to the default value unless it's being duplicated for a PIE session
    		NonPIEDuplicateTransient,
    
    		/// Value is copied out after function call. Only valid on function param declaration.
    		Ref,
    
    		/// Object property can be exported with it's owner.
    		Export,
    
    		/// Hide clear (and browse) button in the editor.
    		NoClear,
    
    		/// Indicates that elements of an array can be modified, but its size cannot be changed.
    		EditFixedSize,
    
    		/// Property is relevant to network replication.
    		Replicated,
    
    		/// Property is relevant to network replication. Notify actors when a property is replicated (usage: ReplicatedUsing=FunctionName).
    		ReplicatedUsing,
    
    		/// Skip replication (only for struct members and parameters in service request functions).
    		NotReplicated,
    
    		/// Interpolatable property for use with cinematics. Always user-settable in the editor.
    		Interp,
    
    		/// Property isn't transacted.
    		NonTransactional,
    
    		/// Property is a component reference. Implies EditInline and Export.
    		Instanced,
    
    		/// MC Delegates only.  Property should be exposed for assigning in blueprints.
    		BlueprintAssignable,
    
    		/// Specifies the category of the property. Usage: Category=CategoryName.
    		Category,
    
    		/// Properties appear visible by default in a details panel
    		SimpleDisplay,
    
    		/// Properties are in the advanced dropdown in a details panel
    		AdvancedDisplay,
    
    		/// Indicates that this property can be edited by property windows in the editor
    		EditAnywhere,
    
    		/// Indicates that this property can be edited by property windows, but only on instances, not on archetypes
    		EditInstanceOnly,
    
    		/// Indicates that this property can be edited by property windows, but only on archetypes
    		EditDefaultsOnly,
    
    		/// Indicates that this property is visible in property windows, but cannot be edited at all
    		VisibleAnywhere,
    		
    		/// Indicates that this property is only visible in property windows for instances, not for archetypes, and cannot be edited
    		VisibleInstanceOnly,
    
    		/// Indicates that this property is only visible in property windows for archetypes, and cannot be edited
    		VisibleDefaultsOnly,
    
    		/// This property can be read by blueprints, but not modified.
    		BlueprintReadOnly,
    
    		/// This property has an accessor to return the value. Implies BlueprintReadOnly if BlueprintSetter or BlueprintReadWrite is not specified. (usage: BlueprintGetter=FunctionName).
    		BlueprintGetter,
    
    		/// This property can be read or written from a blueprint.
    		BlueprintReadWrite,
    
    		/// This property has an accessor to set the value. Implies BlueprintReadWrite. (usage: BlueprintSetter=FunctionName).
    		BlueprintSetter,
    
    		/// The AssetRegistrySearchable keyword indicates that this property and it's value will be automatically added
    		/// to the asset registry for any asset class instances containing this as a member variable.  It is not legal
    		/// to use on struct properties or parameters.
    		AssetRegistrySearchable,
    
    		/// Property should be serialized for save games.
    		/// This is only checked for game-specific archives with ArIsSaveGame set
    		SaveGame,
    
    		/// MC Delegates only.  Property should be exposed for calling in blueprint code
    		BlueprintCallable,
    
    		/// MC Delegates only. This delegate accepts (only in blueprint) only events with BlueprintAuthorityOnly.
    		BlueprintAuthorityOnly,
    
    		/// Property shouldn't be exported to text format (e.g. copy/paste)
    		TextExportTransient,
    
    		/// Property shouldn't be serialized, can still be exported to text
    		SkipSerialization,
    
    		/// If true, the self pin should not be shown or connectable regardless of purity, const, etc. similar to InternalUseParam
    		HideSelfPin, 
    	};
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
  • 相关阅读:
    【多线程】Synchronized 用法详解
    【MQTT基础篇(一)】MQTT介绍
    K-Means(上):数据分析 | 数据挖掘 | 十大算法之一
    datax和datax-web打包成docker运行
    Mysql函数汇总
    学习JAVA的第七天(基础)
    Java常量:Java常量的定义和分类
    无人驾驶(二)---室外导航之RTK配置与接入及GPS与UTM坐标转换
    【NodeJs-5天学习】第四天存储篇③ ——基于物联网的WiFi自动打卡考勤系统,升级存储为mysql,提醒功能改为QQ
    Flink 数据目录体系:深入理解 Catalog、Database 及 Table 概念
  • 原文地址:https://blog.csdn.net/hhw_hhw/article/details/133839944