深度思考rpc框架面經系列之二

此篇總結是接上一篇做的,當然,獨立看也是可以的,這幾個系列講解的是rpc框架的不同方面

4 序列化和反序列化(阿里一面)

4.1 為什么要序列化呢,解決什么問題呢?序列化和反序列化也是要消耗一定性能的

序列化是一個將對象的狀態信息轉換為可以存儲或傳輸的形式的過程。反序列化則是將這種格式的數據再次轉化回對象的過程。序列化和反序列化確實會消耗一定的性能,但是它們存在的理由是為了解決以下問題:

  1. 持久化: 為了將對象的狀態信息永久地保存到磁盤上(例如,數據庫、文件),通常需要將對象序列化。之后,可以從磁盤上讀取這些信息,并通過反序列化將其恢復為對象。

  2. 遠程調用 (RPC): 當在分布式系統中進行遠程方法調用時,通常需要將對象序列化成一種格式,使其可以在網絡上進行傳輸。接收方收到數據后再進行反序列化,恢復成原始的對象,然后進行處理。

  3. 數據交換: 當兩個不同的應用或系統需要交換數據時,序列化可以提供一種共同的、結構化的格式(如JSON、XML)以方便數據交換。

  4. 緩存: 當使用像Redis這樣的外部緩存時,通常需要序列化對象,以便將其存儲在緩存中。當從緩存中檢索數據時,再進行反序列化。

  5. 深拷貝: 通過序列化和隨后的反序列化,可以創建對象的一個完全獨立的拷貝。

雖然序列化和反序列化會帶來一定的性能開銷,但是為了滿足上述需求,這些開銷通常是可以接受的。然而,為了減少這些開銷,通常有一些策略和最佳實踐,例如選擇高效的序列化格式、使用專門為速度優化的庫(如Protocol Buffers、FlatBuffers等)等。

4.1.1 如果沒有序列化就不能完成持久化,rpc,數據交換,緩存以及深拷貝這些操作嗎,為什么

不完全是這樣**。序列化是為這些操作提供了一種通用、結構化、和高效的方式。沒有序列化,你仍然可以完成上述操作,但過程可能會變得復雜、低效或不可靠**。我們來詳細解析:

  1. 持久化: 在不使用序列化的情況下,你可能需要為每個對象設計專門的保存和加載邏輯。例如,為每個對象屬性定義數據庫表的列。這樣做是可行的,但會變得非常復雜,尤其是當對象結構經常變化時。

  2. 遠程調用 (RPC): 不使用序列化,你需要為每個對象定義專門的編碼和解碼邏輯,以便在網絡上傳輸。這不僅增加了復雜性,還可能引入錯誤,并導致效率低下的網絡通信。

  3. 數據交換: 在沒有序列化的情況下,兩個不同語言系統之間的數據交換可能需要人為地定義和遵循特定格式。這會增加兩個系統之間的緊耦合,從而降低靈活性和可維護性。

  4. 緩存: 外部緩存系統(如Redis)通常只能存儲基本的數據類型,如字符串或字節數組。因此,要將一個對象存儲到Redis中,你需要將其轉換為這些基本數據類型,這本質上就是序列化。

  5. 深拷貝: 不使用序列化,你可能需要手動為每個對象實現深拷貝邏輯,這不僅費時,而且容易出錯。

總之,雖然在沒有序列化的情況下仍然可以完成這些操作,但序列化為我們提供了一種結構化、高效、可靠的方法來處理這些常見的任務。

(1)簡單根本原因:對象、文件、數據,有許多不同的格式,很難統一網絡傳輸和持久化保存。序列化相當于提供了一種統一的保存和傳輸格式,使得分布式系統的各個節點能以統一的方法訪問序列化中存儲的數據。

假設你有一個包含姓名和年齡的人物(Person)對象,如果沒有序列化,你可能需要寫代碼來分別獲取這個人物的姓名和年齡,然后以某種特定的格式(例如CSV或JSON)將它們存儲或發送。然后,在需要的時候,你需要寫代碼來解析這個格式,然后使用解析的結果來創建一個新的人物對象。這種過程不僅需要寫大量的代碼,而且如果對象的結構發生改變(例如添加了新的字段),你可能需要修改你的代碼,如果對象結構變化頻繁,則代碼修改也是很頻繁的。

(2)其他:有的序列化協議可以在解碼時保持較好的可讀性,同時也能壓縮數據,這樣的情況下還能節省網絡帶寬

4.2 序列化和通信協議之間的關系

序列化和通信協議之間的關系主要在于序列化提供了在網絡通信中發送和接收復雜對象的方法。在網絡通信中,所有的數據最終都要被轉換為字節流,然后才能通過網絡發送。序列化就是這種轉換的過程,它將對象的狀態轉換為字節流。通信協議則定義了如何發送和接收這些字節流。所以在很多網絡通信的情況下,序列化是通信協議的一部分。例如,在HTTP協議中,我們經常使用JSON或XML作為序列化的方式來發送和接收數據。

4.3 假設有一個服務,它的入參是一個接口,這個接口下面有四個實現類,每個實現類有不同的字段,它們的特點是都是繼承了同一個接口,基于這個場景,你的rpc框架需要用哪一種序列化方式,原因是什么?

我:能告訴我這個為什么涉及到序列化?

面試官:你覺得這個場景用json能work嗎?因為你序列化的是一個接口,而不是具體的實現類

我:是不是可以在json中加一個字段呢,表示期望用的是哪一種實現類?

面試官:但是你加了字段之后,序列化和反序列化怎么進行,比如我剛開始序列化的對象中只有兩個字段,后面又新增了幾個字段,接收端怎么知道這變化的字段呢?

我:但是你用protocol buffer的話,就支持你自定義字段,然后可以這樣順利解析啊

面試官:原因是什么呢?為什么protocol buffer可以感知到新增或者減少的字段呢?

我:是因為protocol buffer的序列化是支持元數據自描述的,proto buffer中的一個字段的存儲格由(type,length,value)決定的,這樣的話,我總是能合理的切分每一個字段。比如說原來只有一個字段"k1":“v1”,現在新增一個字段"k10’":“v10”,那么第一個字段的存儲格式是(string,2,k1),(string,2,v1);新增一個字段,數據的存儲格式就是(string,2,k1),(string,2,v1),(string,3,k10),(string,3v10);;

gpt4正確答案:前向/后向兼容性:這意味著舊版本的序列化代碼可以解析由新版本的代碼生成的數據(前向),反之亦然(后向)。在Protocol Buffers中,這是通過為每個字段分配一個唯一的數字標識符并保持這些標識符的一致性來實現的;此外就涉及到前面提到的存儲格式的問題了,通過長度字段可以知道這個新增字段id的值,key和value;這也是為什么Protocol Buffers可以感知到新增或者減少的字段的原因。只要標識符不變,字段可以被重命名、添加或刪除,而不破壞兼容性。

面試官:json里面也是支持元數據描述的,只是需要特殊設置一下;如果你沒開啟的話,那你每個字段就變成了字符串了,然后單獨去json了對吧。在有類的情況下,json有一個字符表名這個類的全稱是什么,反序列化的時候會根據類的名稱去找特定的實現類。 你剛剛說的那種是序列化的時候本身會一用個描述元數據的文檔,在整個二進制里面就不需要重組二進制信息

4.4 序列化實現的深拷貝和我們重寫java的clone方法實現的深拷貝有什么不同?

使用序列化來實現深拷貝與使用clone方法有幾個關鍵的區別:

  1. 實現方式:

    • 序列化:通過將對象轉化為字節流,然后再從字節流中重新構建一個新對象,從而實現深拷貝。
    • clone方法:需要為對象實現Cloneable接口,并重寫clone方法。當你調用clone方法時,會創建一個新對象,并手動復制原始對象的屬性到新對象。
  2. 深度:

    • 序列化:自動地為對象及其所有嵌套對象實現深拷貝,不需要額外的工作。
    • clone方法:默認的clone方法是淺拷貝。要實現深拷貝,你需要手動為每個嵌套對象調用clone方法。這可能變得非常復雜,特別是對于具有多個層次的對象。
  3. 性能:

    • 序列化:由于涉及到對象與字節流之間的轉換,使用序列化實現深拷貝的性能開銷比較大。
    • clone方法:通常來說,使用clone方法實現的深拷貝性能更好,因為它直接在內存中操作。
  4. 靈活性與安全性:

    • 序列化:不需要為每個對象都寫拷貝邏輯,但所有涉及的對象都必須實現Serializable接口。此外,使用序列化進行深拷貝可能會暴露對象的私有字段,從而帶來安全風險。
    • clone方法:允許你為每個對象定制深拷貝的邏輯,但需要為每個對象都寫拷貝邏輯,這增加了實現的復雜性
  5. 異常處理:

    • 序列化:序列化和反序列化過程中可能會拋出異常,如IOExceptionClassNotFoundException
    • clone方法clone方法可能會拋出CloneNotSupportedException,但只有在對象沒有實現Cloneable接口時才會這樣。
  6. 外部資源:

    • 序列化:序列化并不會拷貝對象的外部資源,如文件句柄、數據庫連接等。
    • clone方法:同樣,clone方法也不會拷貝外部資源。

總的來說,使用序列化實現深拷貝是一種簡單而自動的方法,但可能會犧牲性能。而使用clone方法實現深拷貝雖然更高效,但可能需要更多的手工工作和維護。選擇哪種方法取決于具體的應用場景和需求。

5 jdk的序列化接口接口-Serializable(用友一面)

5.1 這個接口是用來做什么的?

Serializable 是 Java 提供的一個標記接口(即沒有任何方法的接口),它告訴 Java 虛擬機 (JVM) 一個類的對象可以被序列化。序列化是將對象的狀態信息轉化為可以存儲或傳輸的形式的過程。這個形式通常是字節流,但也可以是其他格式。當對象被序列化后,可以將其寫入到文件、發送到網絡等,然后在適當的時候再反序列化回對象。

序列化主要用于以下場合:

  1. 持久化數據:對象的狀態可以被存儲在磁盤上,之后再恢復。
  2. 遠程方法調用 (RMI):對象可以在網絡上進行傳輸,然后在另一側反序列化,以便在遠程系統上使用。
  3. 分布式計算:對象可以在集群或網格中的不同節點間傳輸。

簡而言之,Serializable 接口使得一個類的對象可以被轉換為字節流,從而可以進行存儲、傳輸,并在之后再轉換回原始對象狀態。

5.2 我們在spring中經常會將對象實例序列化成一個json哈,那這個時候并不需要實現這個接口對吧?

是的,你是對的。在 Spring 或許多其他框架中,當我們將對象實例序列化為 JSON 時,我們通常不需要讓該對象實現 Serializable 接口。這是因為 JSON 序列化通常是通過反射來訪問對象的字段并將它們轉化為 JSON 字符串的,而不是通過 Java 的內置序列化機制。

Serializable 接口主要用于 Java 原生的序列化機制,特別是當你需要將對象狀態保存到文件或通過網絡在 Java 應用之間發送對象時。這個機制涉及到將對象轉化為一個特定的字節流格式,該格式可以隨后再反序列化回對象。

但在現代應用中,JSON 已經成為了一種非常流行的數據交換格式,特別是在 web 應用和 RESTful 服務中。為了序列化和反序列化 JSON,我們通常會使用如 Jackson、Gson 等庫,這些庫并不要求對象實現 Serializable 接口。

5.3 如何使用這個序列化接口

當你想通過Java原生的序列化機制序列化一個對象時,該對象的類必須實現Serializable接口。這是一個標記接口,也就是說它沒有任何方法需要實現,但是它告訴JVM這個對象是可以序列化的。

以下是一個簡單的Java示例,展示了如何使用Serializable接口進行序列化和反序列化:

import java.io.*;class Person implements Serializable {private static final long serialVersionUID = 1L;String name;int age;Person(String name, int age) {this.name = name;this.age = age;}@Overridepublic String toString() {return "Person [name=" + name + ", age=" + age + "]";}
}public class SerializationDemo {public static void main(String[] args) {// 對象序列化try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.ser"))) {Person person = new Person("John", 25);oos.writeObject(person);System.out.println("Person object has been serialized.");} catch (IOException e) {e.printStackTrace();}// 對象反序列化try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.ser"))) {Person deserializedPerson = (Person) ois.readObject();System.out.println("Deserialized Person: " + deserializedPerson);} catch (IOException | ClassNotFoundException e) {e.printStackTrace();}}
}

5.4 這個接口里有一個id,你知道這個id是干什么的嘛?

它是serialVersionUID, 是一個私有的靜態常量,用于表示序列化版本。這是可選的,但建議總是包含它,以確保序列化兼容性。

注意事項:

  1. serialVersionUID

是一個私有的靜態常量,用于表示序列化版本。這是可選的,但建議總是包含它,以確保序列化兼容性。

  1. 如果類的字段發生改變(例如添加新字段),可能需要更改

serialVersionUID。如果你沒有設置serialVersionUID并且更改了類的結構,那么在嘗試反序列化舊的對象時,可能會收到InvalidClassException。

  1. 不是所有的Java對象都可以被序列化。對象必須是可序列化的,并且它引用的所有對象也都必須是可序列化的。如果對象包含不能序列化的字段,你可以將該字段標記為transient,這樣它就不會被序列化。使用ObjectOutputStream來序列化對象,并將其寫入文件。使用ObjectInputStream從文件讀取并反序列化對象。

5.4.1 為什么需要這么一個serialVersionUID字段?

答:因為接收方反序列化發送方發送的字節流時,需要有一個對象來對接,從字節流中解析出的所有字段必須在對接的對象在全部存在,否則會造成數據不一致。這里也就是說接收發送雙方的序列化和反序列化的對象的版本必須一致。比如發送方在第一次發送Person對象的字節流時,這個對象只有name字段,接收方的對接對象也只有name字段,接收和發送雙方使用的對象版本號都是1,所以第一次接收方反序列化成功,隨后發送方往Person中新增了一個age字段,并且版本號置為2,但是接收方的對接Person并沒有新增這個字段并且版本號還是1,那么發送方第二次發送的字節流,接收方會拋異常。

5.4.2 所以如果發送方新增了一個字段,將serialVersionUID自增,同時也需要將接收端的這個對應接收類添加相應的字段并且自增相應的版本嘛,所以在雙方的版本和字段都同步更新的情況下,一定能反序列化成功,對嘛?

是的,你理解得很準確。當你手動管理serialVersionUID時,當發送方的類結構發生變化(例如,添加或刪除字段),你需要確保:更新serialVersionUID。保證發送方和接收方使用的類定義是同步的。只要雙方的類定義(包括字段和serialVersionUID)保持一致,就可以成功進行序列化和反序列化。但需要注意的是,僅僅同步serialVersionUID是不夠的。例如,如果發送方增加了一個字段并更新了serialVersionUID,而接收方僅更新了serialVersionUID而沒有添加相應的字段,那么在反序列化時仍然會出錯。
因此,手動管理serialVersionUID和確保類的同步都是為了保證序列化的兼容性。當你有完全的控制權,并可以確保兩邊都同步更新時,這種方式很有效。但在大型分布式系統中,可能需要更多的策略和工具來管理這種兼容性。

5.4.3 如果接收方和發送方的序列化的原始類能做到同步更新保持一致,這個serialVersionUID應該能夠允許去掉把?

理論上,如果你確保兩端的類結構始終是一致的,那么serialVersionUID可以省略,因為JVM會為沒有定義serialVersionUID的類生成一個默認的版本號。這個默認的版本號是基于類的結構(包括成員變量、方法等)計算出來的。
但實際上,這樣做存在風險:
隱式行為:省略serialVersionUID意味著JVM會為你的類生成默認的版本號。當類的任何細節發生變化時,這個版本號也會發生變化。這種隱式的行為可能會導致難以預見的反序列化問題。
跨JVM版本的不確定性:不同的JVM實現(或版本)可能會產生不同的默認serialVersionUID值,這可能會導致在不同的JVM版本之間進行序列化/反序列化時出現問題。
減少出錯的機會:手動設置serialVersionUID意味著你已經考慮過版本兼容性問題。當類的結構發生改變時,有意識地更改或保持serialVersionUID值可以減少意外的序列化問題。
所以,即使你能確保發送方和接收方的類結構始終保持一致,為了減少潛在的序列化問題,最佳實踐還是推薦為你的可序列化類顯式地定義serialVersionUID。

5.5 分布式環境下,一般是如何做到管理這個對象的版本的呢?

在分布式環境下,對象的版本管理變得尤為重要,因為服務間通信、數據交換和狀態共享是分布式系統的基礎。下面是一些建議和最佳實踐,以確保對象版本在分布式環境中得到適當的管理:

  1. 中央化倉庫:使用中央化的倉庫(如 Maven Central、Nexus、Artifactory 等)來存儲和管理所有的JAR包和庫。這確保了在分布式環境中,所有服務都引用的是相同的庫版本。

  2. 契約驅動的設計 (Contract-Driven Design):在微服務環境中,你可以使用工具(如Spring Cloud Contract)來定義并驗證服務間的交互。這確保了服務間的接口和數據格式的一致性,而不需要每個服務都更新到最新版本。

  3. 使用數據模式管理:對于如 Apache Kafka、Apache Avro 這樣的系統,你可以使用 Confluent Schema Registry 或 Apache Avro 的內置模式版本控制來管理數據結構的變化。

  4. 向后兼容:盡量使新版本的對象向后兼容,這樣即使服務版本不一致,它們仍然可以正常交互。

  5. 版本命名約定:遵循一致的版本命名約定,例如語義版本控制(Semantic Versioning),這樣你可以通過版本號輕松地了解更改的性質。

  6. 棄用策略:如果你需要移除或更改對象的某個部分,提供一個過渡期,并在此期間支持舊版本。這給予其他服務足夠的時間來進行必要的調整。

  7. 服務發現與注冊:使用服務注冊與發現機制(如Eureka、Consul等),這樣服務可以知道其他服務的版本,并據此做出決策。

  8. 監控與警告:使用監控工具來跟蹤分布式環境中的版本變化。如果檢測到不一致的版本,立即發出警告。

  9. 灰度部署與金絲雀發布:在引入新版本的服務或對象時,不要立即在所有實例上部署。先在一小部分實例上部署,確保其與其他服務的兼容性,然后再逐漸擴大部署范圍。

  10. 維護文檔:持續更新文檔,記錄每個版本的更改和不同版本之間的差異。

在分布式環境中,版本管理是一個持續的、需要多方面關注的過程。與團隊合作,制定策略,并使用工具來自動化流程,是確保成功的關鍵。

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

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

相關文章

VScode如何設置中文教程

前言:打開VSCode軟件,可以看到剛剛安裝的VSCode軟件默認使用的是英文語言環境,但網上都是vscode中文界面教你怎么設置中文,可能不利于小白閱讀,所以重裝vscode,手摸手從英文變成中文。 設置為中文 打開VS…

CDH6.3.2搭建HIVE ON TEZ

參考 https://blog.csdn.net/ly8951677/article/details/124152987 ----配置hive運行引擎 在/etc/hive/conf/hive-site.xml中修改如下: hive.execution.engine mr–>tez hive.execution.engine 設為tez或者運行代碼的時候: set hive.execution.eng…

android app控制ros機器人五(百度地圖)

半吊子改安卓,新增了標簽頁,此標簽頁需要顯示百度地圖 按照官方教程注冊信息,得到訪問應用AK,步驟也可以參照下面csdn Android地圖SDK | 百度地圖API SDK 【Android】實現百度地圖顯示_賓有為的博客-CSDN博客 本人使用的是aar開…

區分多個鼠標的滾輪滾動消息

實現功能:電腦插了兩個或多個鼠標,程序中需要區分不同鼠標的滾輪滾動消息。 實現方式:直接上代碼,復制粘貼可用,具體處理邏輯可以根據實際需求進行修改和擴展,請注意,這段代碼是在Windows操作系…

Python 圖形界面框架TkInter(第八篇:理解pack布局)

前言 tkinter圖形用戶界面框架提供了3種布局方式,分別是 1、pack 2、grid 3、place 介紹下pack布局方式,這是我們最常用的布局方式,理解了pack布局,絕大多數需求都能滿足。 第一次使用pack() import …

學習筆記整理-DOM-02-事件監聽

一、什么是"事件監聽" DOM允許書寫JavaScript代碼以讓HTML元素對事件作出反應什么是"事件": 用戶與網頁的交互動作當用戶點擊元素時當鼠標移動到元素上時當文本框的內容被改變時當鍵盤在文本框中被按下時當網頁已加載完畢時… “監聽”,顧名思義…

開學季電容筆怎么選?iPad第三方電容筆了解下

不少的學生黨開學必備清單里都少不了電容筆,可見其的重要性。自從蘋果發布了ipad的原裝電容筆以來,這款電容筆在目前市面上就一直很受歡迎,不過由于Apple Pencil的售價實在是太貴了,使得大部分人都買不起。于是,市面上…

leetcode做題筆記78子集

給你一個整數數組 nums ,數組中的元素 互不相同 。返回該數組所有可能的子集(冪集)。 解集 不能 包含重復的子集。你可以按 任意順序 返回解集。 思路一:回溯 void backtracking(int* nums, int numsSize, int** res, int* ret…

在 Linux 虛擬機上使用 Azure 自定義腳本擴展版本

參考 azure創建虛擬機,創建虛擬機注意入站端口規則開放80端口、 2.轉到資源,點擊擴展應用程序,創建存儲賬戶,創建容器,上傳文件,選擇文件,會自動執行部署。 apt-get update -y && apt-get insta…

ROS訂閱相機圖像消息,并將圖像保存為視頻幀

需求 需要編寫一個Python程序,訂閱電腦外接的深度相機發出的視頻消息,錄制視頻并逐幀保存為圖片到本地,用于采集制作數據集的圖片信息 運行環境 Ubuntu18.04 ROS Melodic Python2.7 Python程序 #!/usr/bin/env python # -*- coding: u…

Vue-day03 組件

1.組件機制 組件 (Component) 是 Vue.js 最強大的功能之一。組件可以擴展HTML元素,封裝可重用的代碼。在較高層面上,組件是自定義元素,Vue的編譯器為它添加特殊功能。在有些情況下,組件也可以表現為用is特性進行了擴展的原生 HTML…

chatgpt和xmind結合起來幫你制作精美的思維導圖

介紹 chatgpt和xmind結合起來幫你制作精美的思維導圖。 1.輸出Markdown格式 2.xmind導入.md文件

web集群學習:nginx+keepalived實現負載均衡高可用性

目錄 項目架構 一,環境介紹 二,項目部署 在Web服務器上配置Web測試頁面 nginx負載均衡配置 配置Nginx_Master 通過vrrp_script實現對集群資源的監控(1>通過killall命令探測服務運行狀態) 通過vrrp_script實現對集群資源…

div輸入框的文字超過指定行數用省略號表示css

實現效果:超過四行用省略號表示 實現方法: .text{overflow: hidden;text-overflow: ellipsis;display: -webkit-box;-webkit-line-clamp: 4; // 自定義行數-webkit-box-orient: vertical; }

Go和Java實現外觀模式

Go和Java實現外觀模式 下面我們通過一個構造各種形狀的案例來說明外觀模式的使用。 1、外觀模式 外觀模式隱藏系統的復雜性,并向客戶端提供了一個客戶端可以訪問系統的接口。這種類型的設計模式屬于結構型 模式,它向現有的系統添加一個接口&#xff…

【設計模式】代理模式

在代理模式(Proxy Pattern)中,一個類代表另一個類的功能。這種類型的設計模式屬于結構型模式。 在代理模式中,我們創建具有現有對象的對象,以便向外界提供功能接口。 介紹 意圖:為其他對象提供一種代理以…

【面試問題】事務中執行了異步任務分發數據,由于事務未提交,導致異步任務無法執行

文章目錄 問題描述:解決辦法: 問題描述: OverrideTransactional(rollbackFor Exception.class)public ServiceResponse ctsqCallbackProcess(OaFlowRecord params) {// 查詢任務單數據// 更新任務單信息// 異步分發數據到CRMS系統}客戶數據分…

TX Text Control .NET Server for ASP.NET Crack

TX Text Control .NET Server for ASP.NET Crack TX Text Control.NET Server for ASP.NET是用于Web應用程序或服務的服務器端組件。它是一個完全可編程的ASP.NET文字處理引擎,提供了廣泛的文字處理功能。使用TX Text Control.NET Server,程序員可以開發…

react組件化開發詳解

React是一個流行的JavaScript庫,用于構建用戶界面,并且以組件化的方式進行開發。下面將詳解React組件化開發的概念和步驟: 組件化思維: 組件化開發是將復雜的用戶界面劃分為獨立、可重用的小部件(組件)。…

【833. 字符串中的查找與替換】

來源:力扣(LeetCode) 描述: 你會得到一個字符串 s (索引從 0 開始),你必須對它執行 k 個替換操作。替換操作以三個長度均為 k 的并行數組給出:indices, sources, targets。 要完成第 i 個替換操作: 檢查…