google protobuf 反射機制學習筆記
持續更新中。。。
何為反射機制
基本概念
指程式可以訪問、檢測和修改它本身狀態或行為的一種能力
程式集包含模組,而模組包含型別,型別又包含成員。反射則提供了封裝程式集、模組和型別的物件。您可以使用反射動態地建立型別的例項,將型別繫結到現有物件,或從現有物件中獲取型別。然後,可以呼叫型別的方法或訪問其欄位和屬性。
我(c++程式設計師)關注的問題
- 如何在程式執行過程中通過型別名字(一個字串,合法但是內容在編譯期間未知,比如是在配置檔案中獲取的)創建出型別物件.
- 如果在程式執行過程中通過物件和物件的屬性的名字(一個字串,合法但是內容在編譯期間未知,比如是通過通訊包獲取的)獲取,修改對應屬性.
- 如果在程式執行過程中通過物件和物件方法的名字(一個字串,合法但是內容在編譯期間未知,比如是從使用者輸入獲取的)呼叫對應的方法.
protobuf 反射使用簡介
舉個例子 :
當你有一個test.proto 比如 :
package T;
message Test
{
optional int32 id = 1;
}
通過型別名字創建出型別物件.
- 預先編譯好proto模式
//! 利用型別名字構造物件.
/*!
* @Param type_name 型別名字,比如 "Test.TestMessage".
* @Return 物件指標,new 出來的,使用者負責釋放.
*/
#include <google/protobuf/descriptor.h>
#include <google/protobuf/message.h>
#include "cpp/test.pb.h" // 這是protoc給你生成的檔案
int main()
{
// 先獲得型別的Descriptor .
auto descriptor = google::protobuf::DescriptorPool::generated_pool()->FindMessageTypeByName("T.Test");
if (nullptr == descriptor)
{
return 0 ;
}
// 利用Descriptor拿到型別註冊的instance. 這個是不可修改的.
auto prototype = google::protobuf::MessageFactory::generated_factory()->GetPrototype(descriptor);
if ( nullptr == descriptor)
{
return 0 ;
}
// 構造一個可用的訊息.
auto message = prototype->New();
// 只有當我們預先編譯了test訊息並且正確連結才能這麼幹.
auto test = dynamic_cast<T::Test*>(message);
// 直接呼叫message的具體介面
// 其實這些介面是語法糖介面.所以並沒有對應的反射機制來對應呼叫.
// 反射機制實現了的Set/Get XXX系列介面,是屬於Reflection的介面,接收Message作為引數.
test->set_id(1);
std::cout<<test->Utf8DebugString()<<std::endl;
delete message ;
return 0 ;
}
- 直接解析proto 檔案模式
#include <iostream>
#include <google/protobuf/compiler/importer.h>
#include <google/protobuf/dynamic_message.h>
int main()
{
// 準備配置好檔案系統
google::protobuf::compiler::DiskSourceTree sourceTree;
// 將當前路徑對映為專案根目錄 , project_root 僅僅是個名字,你可以你想要的合法名字.
sourceTree.MapPath("project_root","./");
// 配置動態編譯器.
google::protobuf::compiler::Importer importer(&sourceTree, NULL);
// 動態編譯proto原始檔。 原始檔在./source/proto/test.proto .
importer.Import("project_root/source_proto/test.proto");
// 現在可以從編譯器中提取型別的描述資訊.
auto descriptor1 = importer.pool()->FindMessageTypeByName("T.Test");
// 建立一個動態的訊息工廠.
google::protobuf::DynamicMessageFactory factory;
// 從訊息工廠中創建出一個型別原型.
auto proto1 = factory.GetPrototype(descriptor1);
// 構造一個可用的訊息.
auto message1= proto1->New();
// 下面是通過反射介面給欄位賦值.
auto reflection1 = message1->GetReflection();
auto filed1 = descriptor1->FindFieldByName("id");
reflection1->SetInt32(message1,filed1,1);
// 列印看看
std::cout << message1->DebugString();
// 刪除訊息.
delete message1 ;
return 0 ;
}
通過物件和物件的屬性的名字獲取,修改對應屬性.
首先定義mesage :
對於上文提到的 test.proto
#include "cpp/test.pb.h"
#include <iostream>
int main()
{
// 拿到一個物件,不在乎怎麼拿到,可以是通過反射拿到。
// 這裡簡單直接的建立一個.
T::Test p_test ;
// 拿到物件的描述包.
auto descriptor = p_test.GetDescriptor() ;
// 拿到物件的反射配置.
auto reflecter = p_test.GetReflection() ;
// 拿到屬性的描述包.
auto field = descriptor->FindFieldByName("id");
// 設定屬性的值.
reflecter->SetInt32(&p_test , field , 5 ) ;
// 獲取屬性的值.
std::cout<<reflecter->GetInt32(p_test , field)<< std::endl ;
return 0 ;
}
通過物件和物件方法的名字呼叫對應的方法.
//TODO
protobuf 反射實現解析.
基本概念
Descriptor系列.
::google::protobuf::Descriptor
系列包括:
- Descriptor – 用來描述 訊息
- FieldDescriptor – 用來描述 欄位
- OneofDescriptor – 用來描述 聯合體
- EnumDescriptor – 用來描述 列舉
- EnumValueDescriptor – 用來描述 列舉值
- ServiceDescriptor – 用來描述 伺服器
- MethodDescriptor – 用來描述 伺服器方法
- FileDescriptor – 用來描述 檔案
DescriptorProto
系列是一些用protobuf定義的,用來描述所有由protbuf產生的型別的型別資訊包.
對應的proto檔案在 : https://github.com/google/protobuf/blob/master/src/google/protobuf/descriptor.proto
Descriptor
系列最大的message是 FileDescriptor
. 每個檔案會生成一個包含本檔案所有資訊的FileDescriptor
包.
舉個例子 :
對於上文提到的 test.proto , protoc 會給就會自動填裝一個描述包,類似於:
::google::protobuf::FileDescriptorProto file;
file.set_name("test.proto");
file.set_packet("T")
auto desc = file.add_message_type() ;
desc->set_name("T.Test");
auto id_desc = desc->mutable_field();
id_desc->set_name("id");
id_desc->set_type(::google::protobuf::FieldDescriptorProto::TYPE_INT32);
id_desc->set_number(1);
//...
然後儲存起來.
如果你讀protoc生成的 test.pb.cc檔案 你會看到這樣的程式碼 :
::google::protobuf::DescriptorPool::InternalAddGeneratedFile(
"\n\013test.proto\022\001T\"\022\n\004Test\022\n\n\002id\030\001 \001(\005", 36);
其實就是在protoc生成程式碼中hard code 了對應proto檔案的FileDescriptor
包序列化之後的資料. 作為引數直接使用.
offset
任何一個物件最終都對應一段記憶體,有記憶體起始(start_addr
)和結束地址,
而物件的每一個屬性,都位於 start_addr+$offset
,所以當物件和對應屬性的offset已知的時候,
屬性的記憶體地址也就是可以獲取的。
//! 獲取某個屬性在對應型別物件的記憶體偏移.
#define GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(TYPE, FIELD) \
static_cast<int>( \
reinterpret_cast<const char*>( \
&reinterpret_cast<const TYPE*>(16)->FIELD) - \
reinterpret_cast<const char*>(16))
DescriptorDatabase
DescriptorDatabase
是一個純虛基類,描述了一系列符合通過名字(檔名,符號名。。。) 來獲取FileDescriptorProto
的介面 :
// 這裡我幹掉了裡面的英文註釋.
class LIBPROTOBUF_EXPORT DescriptorDatabase {
public:
inline DescriptorDatabase() {}
virtual ~DescriptorDatabase();
virtual ~DescriptorDatabase();
// 通過檔名字找.
virtual bool FindFileByName(const string& filename,
FileDescriptorProto* output) = 0;
// 通過符號名字找.
virtual bool FindFileContainingSymbol(const string& symbol_name,
FileDescriptorProto* output) = 0;
// 通過擴充套件資訊找.
virtual bool FindFileContainingExtension(const string& containing_type,
int field_number,
FileDescriptorProto* output) = 0;
// 通過擴充套件資訊的tag數字找...
virtual bool FindAllExtensionNumbers(const string& /* extendee_type */,
vector<int>* /* output */) {
return false;
}
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(DescriptorDatabase);
};
核心的兩個派生類是 :
EncodedDescriptorDatabase
- 支援
DescriptorDatabase
的全部介面 - 接收序列化之後的
FileDescriptorProto
, 儲存在map
中備查. - 這個類對應著預先編譯連結好的那些型別的反射機制。
- 支援
SourceTreeDescriptorDatabase
- 僅支援
DescriptorDatabase
的FindFileByName
介面。其餘直接返回false. - 每次查詢某個檔案都是從磁碟讀入proto的原始檔,編譯解析後返回對應的
FileDescriptorProto
. - 這個類對應著動態編譯proto原始檔的時候的反射機制.
- 僅支援
這裡我不探究protobuf 是如何執行時編譯proto原始檔.
DescriptorPool
任何時候想要查詢一個Descriptor
, 都是去DescriptorPool
裡面查詢。
DescriptorPool
實現了這樣的機制 :
- 快取所有查詢的檔案的
Descriptor
。 - 查詢
Descriptor
的時候,如果自身快取查到就直接返回結果,
否則去自帶的DescriptorDatabase
中查FileDescriptorProto
,
查到就轉化成Descriptor
, 返回結果並且快取.
class LIBPROTOBUF_EXPORT DescriptorPool{
public:
// Create a normal, empty DescriptorPool.
DescriptorPool();
// 幹掉一個灰常長的註釋,核心是下面兩條加一些注意事項.
// 構造一個帶著DescriptorDatabase的Pool 。
// 這樣查詢的時候,優先從Pool中查詢,找不到就到fallback_database中找.
class ErrorCollector;
explicit DescriptorPool(DescriptorDatabase* fallback_database,
ErrorCollector* error_collector = NULL);
~DescriptorPool();
// 這個獲取編譯進入二進位制的那些訊息的pool 。這個介面就是我們獲取預先編譯連結好
// 的訊息的入口。
// Get a pointer to the generated pool. Generated protocol message classes
// which are compiled into the binary will allocate their descriptors in
// this pool. Do not add your own descriptors to this pool.
static const DescriptorPool* generated_pool();
// Find a FileDescriptor in the pool by file name. Returns NULL if not
// found.
const FileDescriptor* FindFileByName(const string& name) const;
// .... 一系列Find XXX By XXX 介面 ... , 不全部複製了.
// Building descriptors --------------------------------------------
class LIBPROTOBUF_EXPORT ErrorCollector {
// 不關心這個錯誤收集類...
};
// 這個是用FileDescriptorProto 填充FileDescriptor的介面.
const FileDescriptor* BuildFile(const FileDescriptorProto& proto);
// Same as BuildFile() except errors are sent to the given ErrorCollector.
const FileDescriptor* BuildFileCollectingErrors(
const FileDescriptorProto& proto,
ErrorCollector* error_collector);
// 依賴相關介面.
void AllowUnknownDependencies() { allow_unknown_ = true; }
void EnforceWeakDependencies(bool enforce) { enforce_weak_ = enforce; }
// Internal stuff --------------------------------------------------
// 一系列實現細節的介面。。 不復制。。。
private:
// 一系列實現細節的介面。。 不復制。。。
// 當從pool本身的table找不到的時候,試圖從database中查詢的介面。
bool TryFindFileInFallbackDatabase(const string& name) const;
bool TryFindSymbolInFallbackDatabase(const string& name) const;
bool TryFindExtensionInFallbackDatabase(const Descriptor* containing_type,
int field_number) const;
// 一系列實現細節的介面。。 不復制。。。
// See constructor.
DescriptorDatabase* fallback_database_; // 持有的datebase
scoped_ptr<Tables> tables_; // Pool自身的table。 會快取所有查過的檔案內容.
// 一系列實現細節的介面。。 不復制。。。
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(DescriptorPool);
}
核心的查詢介面
Symbol DescriptorPool::Tables::FindByNameHelper(
const DescriptorPool* pool, const string& name) {
MutexLockMaybe lock(pool->mutex_);
known_bad_symbols_.clear();
known_bad_files_.clear();
//先從快取中查詢.
Symbol result = FindSymbol(name);
// 這個是內部實現的細節 不要在意
if (result.IsNull() && pool->underlay_ != NULL) {
// Symbol not found; check the underlay.
result =
pool->underlay_->tables_->FindByNameHelper(pool->underlay_, name);
}
if (result.IsNull()) {
// 這裡去資料庫嘗試獲取資料.
// Symbol still not found, so check fallback database.
if (pool->TryFindSymbolInFallbackDatabase(name)) {
// 再次剛剛資料庫更新資料之後的快取中獲取資料.
result = FindSymbol(name);
}
}
return result;
}
MessageFactory
任何時候想要獲取一個型別的instance , 都要去MessageFactory
裡面獲取。
MessageFactory
是一個純虛的基類,定義了通過Descripor來獲取對應型別instance的介面.
{
public:
inline MessageFactory() {}
virtual ~MessageFactory();
// 了通過Descripor來獲取對應型別instance 的介面
virtual const Message* GetPrototype(const Descriptor* type) = 0;
// 這個是獲取編譯連結好的那些型別的factory單例的入口.
static MessageFactory* generated_factory();
// 這個是對應的像上面哪個單例內填裝資料的介面,protoc自動生成的檔案都會有呼叫.
static void InternalRegisterGeneratedMessage(const Descriptor* descriptor,
const Message* prototype);
private:
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(MessageFactory);
}
同樣有兩個核心的派生類
GeneratedMessageFactory
- 一個map , 儲存著
Descriptor
和Message *
- 這個類對應著預先編譯連結好的那些型別的反射機制。
- 一個map , 儲存著
DynamicMessageFactory
- 有簡單的快取,儲存自己解析過的
Descriptor`` </li>
Descriptor“,動態的基於記憶體構造出一個
<li>可以通過Message
!!!
- 有簡單的快取,儲存自己解析過的
解決問題的辦法
通過型別名字創建出型別物件 — 預編譯proto並且連結進入二進位制.
查表!!
是的,你沒猜錯,就是查表!!!
資料儲存在哪裡
所有的Descriptor
儲存在單例的DescriptorPool
中。google::protobuf::DescriptorPool::generated_pool()
來獲取他的指標。
所有的instance
儲存在單例的MessageFactory
中。google::protobuf::MessageFactory::generated_factory()
來獲取他的指標。將所有的Descriptor & instance 提前維護到表中備查
在protoc 生成的每個cc檔案中, 都會有下面的程式碼(protobuf V2 版本) :
// xxx 應該替換為檔名,比如test.proto的test.
namespace {
//! 將本檔案內的全部型別的instance註冊進入MessageFactory的介面.
void protobuf_RegisterTypes(const ::std::string&) {
// 初始化本檔案的reflection資料.
protobuf_AssignDescriptorsOnce();
::google::protobuf::MessageFactory::InternalRegisterGeneratedMessage(
Test_descriptor_, &Test::default_instance());
}
//! 本檔案的初始介面.
void protobuf_AddDesc_xxx_2eproto() {
static bool already_here = false;
if (already_here) return;
already_here = true;
GOOGLE_PROTOBUF_VERIFY_VERSION;
// 註冊本檔案的Descriptor包. 這樣就可以用名字通過generated_pool獲取對應的Descriptor。
::google::protobuf::DescriptorPool::InternalAddGeneratedFile(
"\n\013xxx.proto\022\001T\"\022\n\004Test\022\n\n\002id\030\001 \001(\005", 36);
// 將本檔案的型別instance註冊介面註冊給MessageFactory.
// 這裡註冊介面是為了實現型別的lazy註冊。如果沒有使用請求某個檔案的型別,就不註冊對應檔案的型別。
::google::protobuf::MessageFactory::InternalRegisterGeneratedFile(
"xxx.proto", &protobuf_RegisterTypes);
// 構造並且初始化全部instance.
Test::default_instance_ = new Test();
Test::default_instance_->InitAsDefaultInstance();
// 註冊清理介面.
::google::protobuf::internal::OnShutdown(&protobuf_ShutdownFile_xxx_2eproto);
}
//! 下面利用全域性變數的建構函式確保main函式執行之前資料已經進行註冊.
struct StaticDescriptorInitializer_xxx_2eproto {
StaticDescriptorInitializer_xxx_2eproto() {
protobuf_AddDesc_xxx_2eproto();
}
} static_descriptor_initializer_xxx_2eproto_;
}
通過型別名字創建出型別物件 — 執行時態編譯proto
這裡要引入 Importer
類.
class LIBPROTOBUF_EXPORT Importer {
public:
// 需要使用SourceTree來構造,
// 不過SourceTree最終是用來構造SourceTreeDescriptorDatabase的。
Importer(SourceTree* source_tree,
MultiFileErrorCollector* error_collector);
~Importer();
// 這個就是執行時態載入proto原始檔的介面.
// 多次呼叫同一個檔案只有第一次有效。
const FileDescriptor* Import(const string& filename);
// 拿到DescriptorPool 的介面.
// 每個Importer都有自己的DescriptorPool。
inline const DescriptorPool* pool() const {
return &pool_;
}
// 下面是咱不在意的介面.
void AddUnusedImportTrackFile(const string& file_name);
void ClearUnusedImportTrackFiles();
private:
// 這兩個資料成員很好的解釋了Importer如何工作 :
// 有 SourceTreeDescriptorDatabase 構造一個DescriptorPool,這樣
// 每當有檔案被查詢的時候,如果快取中有,直接返回,如果沒有,
// SourceTreeDescriptorDatabase 自然會去載入解析原始檔.
// Import介面則是提前將proto解析載入進入快取的途徑.
SourceTreeDescriptorDatabase database_;
DescriptorPool pool_;
GOOGLE_DISALLOW_EVIL_CONSTRUCTORS(Importer);
}
通過物件和物件的屬性的名字獲取,修改對應屬性.
- GeneratedMessageReflection 的填裝和獲取
對於每一個message , 都有一個對應的GeneratedMessageReflection 物件.
這個物件儲存了對應message反射操作需要的資訊.
//!初始化本檔案的所有GeneratedMessageReflection物件.
void protobuf_AssignDesc_xxx_2eproto() {
protobuf_AddDesc_xxx_2eproto();
const ::google::protobuf::FileDescriptor* file =
::google::protobuf::DescriptorPool::generated_pool()->FindFileByName(
"xxx.proto");
GOOGLE_CHECK(file != NULL);
Test_descriptor_ = file->message_type(0);
static const int Test_offsets_[1] = {
//這裡在計算屬性的記憶體偏移.
GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Test, id_),
};
// 這裡是個test包填裝的GeneratedMessageReflection物件.
Test_reflection_ =
new ::google::protobuf::internal::GeneratedMessageReflection(
Test_descriptor_,
Test::default_instance_,
Test_offsets_,
GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Test, _has_bits_[0]),
GOOGLE_PROTOBUF_GENERATED_MESSAGE_FIELD_OFFSET(Test, _unknown_fields_),
-1,
::google::protobuf::DescriptorPool::generated_pool(),
::google::protobuf::MessageFactory::generated_factory(),
sizeof(Test));
}
inline void protobuf_AssignDescriptorsOnce() {
::google::protobuf::GoogleOnceInit(&protobuf_AssignDescriptors_once_,
&protobuf_AssignDesc_xxx_2eproto);
}
// message.h 中 message的基本介面.
virtual const Reflection* GetReflection() const {
return GetMetadata().reflection;
}
// 每個message獲取自己基本資訊的介面.
::google::protobuf::Metadata Test::GetMetadata() const {
protobuf_AssignDescriptorsOnce();
::google::protobuf::Metadata metadata;
metadata.descriptor = Test_descriptor_;
metadata.reflection = Test_reflection_;
return metadata;
}
- GeneratedMessageReflection 操作具體物件的屬性
按照offset陣列的提示,註解獲取操作對應記憶體,這裡以int32欄位的SetInt32介面為例子.
- 介面定義
#undef DEFINE_PRIMITIVE_ACCESSORS
#define DEFINE_PRIMITIVE_ACCESSORS(TYPENAME, TYPE, PASSTYPE, CPPTYPE)
void GeneratedMessageReflection::Set##TYPENAME( \
Message* message, const FieldDescriptor* field, \
PASSTYPE value) const { \
USAGE_CHECK_ALL(Set##TYPENAME, SINGULAR, CPPTYPE); \
if (field->is_extension()) { /*先不要在意這個*/ \
return MutableExtensionSet(message)->Set##TYPENAME( \
field->number(), field->type(), value, field); \
} else {
/*一般的欄位走這裡*/\
SetField<TYPE>(message, field, value); \
} \
}
DEFINE_PRIMITIVE_ACCESSORS(Int32 , int32 , int32 , INT32 )
#undef DEFINE_PRIMITIVE_ACCESSORS
- 記憶體賦值.
// 找到對應的記憶體地址,返回合適型別的指標.
template <typename Type>
inline Type* GeneratedMessageReflection::MutableRaw(
Message* message, const FieldDescriptor* field) const {
int index = field->containing_oneof() ?
descriptor_->field_count() + field->containing_oneof()->index() :
field->index();
void* ptr = reinterpret_cast<uint8*>(message) + offsets_[index];
return reinterpret_cast<Type*>(ptr);
}
// 設定protobuf的標誌bit.
inline void GeneratedMessageReflection::SetBit(
Message* message, const FieldDescriptor* field) const {
if (has_bits_offset_ == -1) {
return;
}
MutableHasBits(message)[field->index() / 32] |= (1 << (field->index() % 32));
}
// 設定某個欄位的值
template <typename Type>
inline void GeneratedMessageReflection::SetField(
Message* message, const FieldDescriptor* field, const Type& value) const {
if (field->containing_oneof() && !HasOneofField(*message, field)) {
ClearOneof(message, field->containing_oneof()); // V3 oneof 型別的清理。
}
*MutableRaw<Type>(message, field) = value; // 先直接覆蓋
field->containing_oneof() ?
SetOneofCase(message, field) : SetBit(message, field); // 新增標記bit
}
通過物件和物件方法的名字呼叫對應的方法.
//TODO
相關推薦
google protobuf 反射機制學習筆記
持續更新中。。。 何為反射機制 基本概念 指程式可以訪問、檢測和修改它本身狀態或行為的一種能力 程式集包含模組,而模組包含型別,型別又包含成員。反射則提供了封裝程式集、模組和型別的物件。您可以使用反射動態地建立型別的例項,將型別繫結到現有物件,或從
Java Class類與反射機制學習筆記(一)
類的載入 當一個程式使用某一個類時,如果該類還未被載入到記憶體中,則系統會通過載入、連線、初始化三個步驟來實現對這個類的初始化。 載入 就是指將該類的 .class檔案讀入記憶體中,併為之建立一個
Java反射機制學習過程——筆記
Class類 在Java中所有的反射操作都是圍繞Class類來展開的,Class這個類是在JDK1.0的時候出現的,可以說這個類是Java類中的元老李,那麼這個類究竟有什麼特別之處呢?檢視文件發現Class類是一個final類,也就是我們所說的太監類,不可能有子類,但是雖說
java反射機制學習:初始反射機制
pro sta 加載 初始 完整 tac demo 獲得 sys 本人小白一枚,想和大家一起分享我學習java的筆記和心得。 反射機制: 指的是可以於運行時加載、探知、使用編譯期間完全未知的類。 程序在運行狀態中,可以動態加載一個只有名稱的類,對於任意一個已加載的
Java反射篇學習筆記
nsh boolean 案例 即使 框架 ade 原理 劉德華 種類型 今天重新學習了java中的反射,寫一篇學習筆記總結一下。代碼基本都是照著兩篇博客敲的: 參考一: https://blog.csdn.net/sinat
201711671103《JAVA程式設計》第十二章多執行緒機制學習筆記
教材學習內容總結 1.執行緒是依附於程序的,程序是分配資源的最小單位,執行緒是比程序更小的執行單位。一個程序可以產生多個執行緒,形成多條執行線索。每條線索,即每個執行緒也有它自身的產生,存在和消亡過程,也是一個動態的概念。 2.JAVA 中的多執行緒機制:多執行緒是指一個應用程式同時存在好幾
JAVA 類載入機制學習筆記
JAVA 類生命週期 如上圖所示,Java類的生命週期如圖所示,分別為載入、驗證、準備、解析、初始化、使用、解除安裝。其中驗證、準備、解析這三個步驟統稱為連結。 載入:JVM根據全限定名來獲取一段二進位制位元組流,將二進位制流轉化為方法區的執行時資料結構,在記憶體中生成一個代表
作業系統的記憶體對齊機制學習筆記
資料對齊並不是作業系統的記憶體結構的一部分,而是C P U結構的一部分 是這麼理解嗎?cpu要讀取記憶體中的資料,以多少為單位進行讀取呢?以4個位元組,還是8個位元組。還是16個位元組為單位來讀取記憶體資料? 目前主要以2個位元組為單位嗎?是的。2個位元組作為對齊單位
Android觸控事件傳遞機制學習筆記
1、Android 觸控事件傳遞機制 http://blog.csdn.net/awangyunke/article/details/22047987 2、Android-onInterceptTouchEvent()和onTouchEvent()總結 h
Spark內部機制學習筆記
1 總體框架結構圖 由上圖我們可以看到Spark應用程式架構主要由Driver Program和Executor構成,Driver負責執行main()和建立SparkContext,Executor主要負責執行Task任務。 2 各大重要元件和概念
Linux下驅動:分層、分離機制學習筆記
一、分層、分離的概念 分層即把硬體相關和相對穩定的東西給抽出來,並向上提供統一的介面,每一層專注於自己的事情,比如輸入子系統。後續文章中會繼續介紹相關例子; 分離即把硬體相關和相對穩定的東西給分離開來,實際上即是bus-dev-drv模型(平
Google Earth Engine(GEE)學習筆記 二
GEE工作空間簡介 工作空間預覽 各個按鈕模組簡介 執行簡單例子 訪問GEE要科學上網!!! 可以看到整個工作臺可以分為四大部分:指令碼檔案存放區、程式碼編輯區、輸出控制檯、結果地圖展
Google Earth Engine(GEE)學習筆記 零
初步列了一個大綱 1. GEE初識 2. GEE示例以及學習資源介紹 3. GEE工作臺介紹 4. GEE資源查詢 5. GEE資源上傳 6. GEE資源匯出 7. GEE控制元件介紹(1
google protobuf 反射的例子
反射就是可以知道一個物件自身的資料屬性,例如有哪些欄位,欄位的值,函式等等。 下面的程式碼展示了protobuf 物件反射的例子。將一個物件按照反射的欄位順序序列化到string,然後反序列化到物件。最後呼叫反射列印其欄位值,可以看到物件能夠還原。 proto檔案為(暫時不
Android非同步訊息處理機制學習筆記
(一)Handler 什麼是Handler Android訊息機制的上層介面,Handler通過傳送和處理Message和Runnable物件來關聯相對應執行緒的MessageQueeu. 可
Android Activity和Intent機制學習筆記
Activity Android中,Activity是所有程式的根本,所有程式的流程都執行在Activity之中,Activity具有自己的生命週期(見http://www.cnblogs.com/feisky/archive/2010/01/01/1637427.ht
Java中的鎖機制學習筆記
今天回顧了下java中的鎖機制,和object類中中的wait(),notify(),notifyAll();每次敲程式碼的時候老看見這幾個方法,總讓人將它們和Android中的notifyDataSetChanged()的方法想到一塊去,其實這些東西在Java
Java反射機制複習筆記
1.瞭解反射的基本原理 2.掌握class類的使用 3.使用Class類並結合java.lang.reflect包取得一個類的完整結構 4.通過反射機制動態的呼叫類中指定的方法,並能向這些方法中傳遞引數 核心概念:一切的操作都將使用object進行完成,包括類,陣列的應用都
JAVA 學習筆記 - 反射機制
-s .html upload 通過 tostring for catch span pack 1. JAVA反射機制的概念 2. 怎樣實例化一個 Class對象 Class.forName(包名.類名); 對象.getClass();
《瘋狂Java講義》學習筆記(十四)類載入機制與反射
1、類的載入、連線和初始化 JVM和類 同一個JVM的所有執行緒、所有變數都處於同一個程序裡,它們都使用該JVM程序的記憶體區 當系統出現一下集中情況時,JVM程序將被終止: 程式執行到最後正常結束 程式執行到使用Sytem.exit()或Runti