Java中的char、String、StringBuilder與StringBuffer全方面解析
在Java編程中,char、String、StringBuilder和StringBuffer是處理字符和字符串的四個(gè)基石。理解它們的設(shè)計(jì)哲學(xué)、底層實(shí)現(xiàn)和性能差異,對(duì)于編寫高效、健壯的代碼至關(guān)重要。本文將深入淺出地為你剖析這四大金剛的方方面面。
第一章:一切的基礎(chǔ)——char原始類型
在探討復(fù)雜的字符串類之前,我們首先需要了解構(gòu)成字符串的最基本單元:char。
1.1 定義與本質(zhì)
char是Java中的一種原始數(shù)據(jù)類型(Primitive Type),用于表示一個(gè)單一的16位Unicode字符。在Java誕生之初,設(shè)計(jì)者就采用了Unicode字符集,這使得Java天生具有良好的國(guó)際化支持。
- 大小:16位(2個(gè)字節(jié)),范圍從
0到65,535(\u0000到\uffff)。 - 無(wú)符號(hào)性:
char是一個(gè)無(wú)符號(hào)類型,這意味著它不能表示負(fù)數(shù)。
1.2 字符編碼的演變:從char到byte
在JDK 9之前,String類的內(nèi)部實(shí)現(xiàn)也是采用 char[] 數(shù)組來(lái)存儲(chǔ)字符。然而,一個(gè)深刻的洞察是,大多數(shù)應(yīng)用程序使用的字符串主要由Latin-1字符集(如英文、數(shù)字)構(gòu)成,這些字符僅需一個(gè)字節(jié)(8位)即可表示,用兩個(gè)字節(jié)的char來(lái)存儲(chǔ)會(huì)造成一半的內(nèi)存浪費(fèi)。
因此,從JDK 9開(kāi)始,為了優(yōu)化內(nèi)存占用,String(以及StringBuilder和StringBuffer的底層)不再使用 char[],而是改用了 byte[] 數(shù)組,并引入一個(gè) coder(編碼器)字段來(lái)標(biāo)識(shí)使用的是LATIN1(每個(gè)字符1字節(jié))還是UTF16(每個(gè)字符2字節(jié))編碼。這是一個(gè)非常重要的底層變化,但對(duì)開(kāi)發(fā)者來(lái)說(shuō)是透明的,我們?cè)谶壿嬌弦廊豢梢詫⑺鼈円暈樽址蛄小?/p>
1.3 char的初始化與賦值
char的賦值方式非常靈活,可以通過(guò)以下幾種方式:
- 字符字面量:用單引號(hào)括起來(lái)的單個(gè)字符??梢允怯⑽淖帜福部梢允侵形淖址?。
char c1 = 'A'; char c2 = '中';
- 整數(shù)編碼值:直接賦值為字符在Unicode表中的碼點(diǎn)(整數(shù))。
char c3 = 65; // 十進(jìn)制,對(duì)應(yīng) 'A' char c4 = 0101; // 八進(jìn)制,對(duì)應(yīng) 'A' char c5 = 0x41; // 十六進(jìn)制,對(duì)應(yīng) 'A'
- Unicode轉(zhuǎn)義序列:使用
\u前綴加上4位十六進(jìn)制數(shù)。char c6 = '\u0041'; // 對(duì)應(yīng) 'A'
- 轉(zhuǎn)義字符:表示一些特殊功能字符。
char c7 = '\n'; // 換行符 char c8 = '\''; // 單引號(hào)字符本身 char c9 = '\\'; // 反斜杠字符本身
1.4 char的運(yùn)算
由于char底層存儲(chǔ)的是整數(shù)值,因此它可以進(jìn)行算術(shù)運(yùn)算和比較。
char ch = 'A';
System.out.println("ch is " + ch); // 輸出: ch is A
ch = (char)(ch + 1); // 將 'A' 的碼點(diǎn) (65) 加 1,得到 66,再?gòu)?qiáng)轉(zhuǎn)為 char
System.out.println("ch is now " + ch); // 輸出: ch is now Y
char ch2 = 'a' + 'b'; // 'a'(97) + 'b'(98) = 195,結(jié)果在int范圍內(nèi)
System.out.println(ch2); // 輸出:195對(duì)應(yīng)的字符?這里實(shí)際上輸出的是195作為char類型的字符,需要查碼表。
int sum = 'a' + 'b';
System.out.println(sum); // 輸出: 195關(guān)鍵點(diǎn):當(dāng) char 和 char 或 char 和 int 進(jìn)行運(yùn)算時(shí),結(jié)果會(huì)被提升為 int 類型。如果需要重新賦給 char 變量,必須進(jìn)行顯式的強(qiáng)制類型轉(zhuǎn)換 (char)。
第二章:不可變的字符串——String類
String 類是Java中使用頻率最高的類之一,其“不可變性”是其最核心的特征。
2.1 類的定義與不可變性
查看 String 類的源碼(以JDK 8為例,后續(xù)版本底層數(shù)組變?yōu)閎yte[],但邏輯一致),我們可以清晰地看到其不可變性的實(shí)現(xiàn):
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[]; // JDK 9 之后變?yōu)?private final byte[] value
/** Cache the hash code for the string */
private int hash; // Default to 0
// ... 其他代碼
}- 類被
final修飾:這意味著String類不能被繼承,防止子類破壞其不可變行為。 - 存儲(chǔ)數(shù)組被
private final修飾:value數(shù)組的引用不可變,且無(wú)法從外部訪問(wèn)。final保證了數(shù)組的引用一旦指向某個(gè)地址后就不能再改變。雖然沒(méi)有直接的語(yǔ)法阻止數(shù)組內(nèi)部元素的變化,但String類沒(méi)有提供任何可以修改數(shù)組元素的方法,從而確保了內(nèi)部的字符數(shù)組也“不可變”。
2.2 不可變性的優(yōu)勢(shì)
這種精心的設(shè)計(jì)帶來(lái)了許多好處:
- 線程安全:由于對(duì)象內(nèi)容不可變,它可以在多個(gè)線程之間自由共享,無(wú)需任何同步措施。
- 字符串常量池(String Pool):這是不可變性帶來(lái)的最大性能優(yōu)化之一。當(dāng)創(chuàng)建一個(gè)字符串字面量(如
String s = "hello";)時(shí),JVM會(huì)檢查常量池中是否已存在相同內(nèi)容的字符串。如果存在,則直接返回其引用;如果不存在,則在池中創(chuàng)建新字符串并返回引用。這種機(jī)制極大地節(jié)省了內(nèi)存。String s1 = "hello"; String s2 = "hello"; System.out.println(s1 == s2); // 輸出 true,因?yàn)橹赶虺A砍刂械耐粋€(gè)對(duì)象
- 哈希值緩存:如上源碼所示,
String類中有一個(gè)hash字段。因?yàn)樽址豢勺?,其哈希值在第一次?jì)算后就可以被緩存起來(lái),之后直接返回。這使得String非常適合作為HashMap或HashTable的鍵,提高了查找效率。
2.3 創(chuàng)建String對(duì)象的兩種方式
方式一:字面量賦值
String str1 = "abc";
這種方式可能會(huì)從字符串常量池中獲取對(duì)象。方式二:new關(guān)鍵字
String str2 = new String("abc");
這種方式一定會(huì)在堆(Heap)中創(chuàng)建一個(gè)新的String對(duì)象。如果常量池中還沒(méi)有"abc"這個(gè)字符串,JVM會(huì)先在常量池中創(chuàng)建,然后再在堆中創(chuàng)建對(duì)象。因此,這種方式通常會(huì)創(chuàng)建1個(gè)或2個(gè)對(duì)象。String s3 = new String("hello"); String s4 = new String("hello"); System.out.println(s1 == s3); // 輸出 false,s1指向常量池,s3指向堆 System.out.println(s3 == s4); // 輸出 false,s3和s4指向堆中不同的對(duì)象
2.4 操作的真相:總是生成新對(duì)象
理解了不可變性,就不難明白,對(duì) String 對(duì)象的任何修改操作(如拼接、替換、截?。疾皇窃谠瓕?duì)象上進(jìn)行的,而是返回一個(gè)新的 String 對(duì)象。
String original = "Hello";
String modified = original.concat(" World");
System.out.println(original); // 輸出: Hello (原對(duì)象未變)
System.out.println(modified); // 輸出: Hello World (新對(duì)象)
String upper = original.toUpperCase();
System.out.println(original); // 輸出: Hello
System.out.println(upper); // 輸出: HELLO2.5 字符串拼接的陷阱與優(yōu)化
正是由于上述特性,在循環(huán)中使用 + 進(jìn)行字符串拼接會(huì)帶來(lái)嚴(yán)重的性能問(wèn)題。
// 低效的寫法
String result = "";
for (int i = 0; i < 1000; i++) {
result = result + i; // 每次循環(huán)都會(huì)創(chuàng)建新的String對(duì)象
}
在JDK 5之后,Java編譯器會(huì)對(duì) + 運(yùn)算符進(jìn)行優(yōu)化,自動(dòng)將其轉(zhuǎn)換為 StringBuilder 的 append 操作。例如 String c = a + b; 會(huì)被編譯為 (new StringBuilder()).append(a).append(b).toString();。但是,在循環(huán)體內(nèi),這種優(yōu)化依然會(huì)導(dǎo)致每次循環(huán)都新建一個(gè) StringBuilder 對(duì)象,反編譯后的字節(jié)碼可以清晰地證明這一點(diǎn)。因此,在循環(huán)或頻繁修改字符串的場(chǎng)景下,我們必須手動(dòng)使用 StringBuilder 或 StringBuffer。
第三章:可變的字符序列——StringBuilder與StringBuffer
為了解決 String 不可變帶來(lái)的性能問(wèn)題,Java提供了兩個(gè)可變的字符序列類:StringBuilder 和 StringBuffer。它們都繼承自 AbstractStringBuilder 類,底層使用可變的字符數(shù)組(JDK 9后為byte[])來(lái)存儲(chǔ)數(shù)據(jù)。
3.1 AbstractStringBuilder:共同的祖先
雖然我們不能直接使用 AbstractStringBuilder,但它是理解這兩個(gè)類的關(guān)鍵。
// 以JDK 8為例
abstract class AbstractStringBuilder implements Appendable, CharSequence {
char[] value; // 非final,存儲(chǔ)字符序列,JDK 9后變?yōu)?byte[]
int count; // 已使用的字符個(gè)數(shù)
// 擴(kuò)容機(jī)制
public void ensureCapacity(int minimumCapacity) {
if (minimumCapacity > value.length) {
expandCapacity(minimumCapacity);
}
}
void expandCapacity(int minimumCapacity) {
int newCapacity = value.length * 2 + 2; // 新容量通常是舊容量的2倍+2
if (newCapacity < minimumCapacity) {
newCapacity = minimumCapacity;
}
// 創(chuàng)建新數(shù)組并復(fù)制原數(shù)據(jù)
value = Arrays.copyOf(value, newCapacity);
}
// ...
}- 可變性:
value數(shù)組沒(méi)有被final修飾,可以被重新賦值指向一個(gè)新的數(shù)組地址,也可以修改數(shù)組內(nèi)部元素。 - 自動(dòng)擴(kuò)容:當(dāng)調(diào)用
append或insert方法時(shí),如果當(dāng)前字符序列的長(zhǎng)度超過(guò)了底層數(shù)組的容量,它會(huì)自動(dòng)觸發(fā)expandCapacity方法,創(chuàng)建一個(gè)更大的新數(shù)組(通常是原容量的2倍+2),并將原內(nèi)容復(fù)制過(guò)去。
3.2 StringBuilder:非線程安全的“快槍手”
- 誕生時(shí)間:JDK 1.5引入。
- 核心特點(diǎn):非線程安全。它的所有方法(如
append,insert,delete等)都沒(méi)有使用synchronized關(guān)鍵字進(jìn)行同步。 - 適用場(chǎng)景:單線程環(huán)境下操作字符串緩沖區(qū)。因?yàn)楸苊饬随i的競(jìng)爭(zhēng)和獲取開(kāi)銷,它通常擁有最好的性能,是單線程字符串操作的默認(rèn)選擇。
3.3 StringBuffer:線程安全的“老大哥”
- 誕生時(shí)間:JDK 1.0就已存在。
- 核心特點(diǎn):線程安全。它的絕大多數(shù)方法都使用了
synchronized關(guān)鍵字修飾,確保在多線程并發(fā)訪問(wèn)時(shí),不會(huì)出現(xiàn)數(shù)據(jù)錯(cuò)亂的問(wèn)題。// StringBuffer 的 append 方法 @Override public synchronized StringBuffer append(String str) { toStringCache = null; super.append(str); return this; } - 適用場(chǎng)景:多線程環(huán)境下,多個(gè)線程可能同時(shí)操作同一個(gè)
StringBuffer對(duì)象時(shí)。在這種場(chǎng)景下,為了保證數(shù)據(jù)的正確性,必須使用StringBuffer。
3.4 核心API對(duì)比
三個(gè)類都實(shí)現(xiàn)了 CharSequence 接口,因此它們的方法非常相似。StringBuilder 和 StringBuffer 的API是兼容的,可以無(wú)縫替換。
| 方法分類 | 常用方法 | 描述 |
|---|---|---|
| 構(gòu)造器 | StringBuilder() / StringBuffer() | 創(chuàng)建一個(gè)初始容量為16字符的空對(duì)象。 |
StringBuilder(int capacity) | 指定初始容量。 | |
StringBuilder(String str) | 根據(jù)字符串創(chuàng)建,初始容量為 16 + str.length()。 | |
| 追加 | append(任意類型 x) | 將參數(shù)的字符串表示形式追加到序列末尾。這是最常用的方法,支持重載。 |
| 插入 | insert(int offset, 任意類型 x) | 在指定位置插入?yún)?shù)的字符串表示形式。 |
| 刪除 | delete(int start, int end) | 刪除從 start 到 end-1 的子序列。 |
deleteCharAt(int index) | 刪除指定位置的字符。 | |
| 替換與反轉(zhuǎn) | replace(int start, int end, String str) | 用 str 替換指定范圍的字符。 |
reverse() | 將序列反轉(zhuǎn)。 | |
| 修改 | setCharAt(int index, char ch) | 修改指定位置的字符。 |
| 查詢 | charAt(int index) / length() | 獲取指定字符/長(zhǎng)度。 |
indexOf(String str) / lastIndexOf(String str) | 查找子串位置。 | |
| 轉(zhuǎn)String | toString() | 返回此序列中數(shù)據(jù)的字符串表示形式。 |
3.5 性能對(duì)比
下面通過(guò)一個(gè)簡(jiǎn)單的性能測(cè)試來(lái)直觀感受三者的差異:
public class PerformanceTest {
private static final int TIMES = 20000;
public static void main(String[] args) {
testString();
testStringBuffer();
testStringBuilder();
}
public static void testString() {
long start = System.currentTimeMillis();
String str = "";
for (int i = 0; i < TIMES; i++) {
str += "java";
}
long end = System.currentTimeMillis();
System.out.println("String 拼接耗時(shí): " + (end - start) + "ms");
}
public static void testStringBuffer() {
long start = System.currentTimeMillis();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < TIMES; i++) {
sb.append("java");
}
String str = sb.toString();
long end = System.currentTimeMillis();
System.out.println("StringBuffer 拼接耗時(shí): " + (end - start) + "ms");
}
public static void testStringBuilder() {
long start = System.currentTimeMillis();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < TIMES; i++) {
sb.append("java");
}
String str = sb.toString();
long end = System.currentTimeMillis();
System.out.println("StringBuilder 拼接耗時(shí): " + (end - start) + "ms");
}
}典型輸出結(jié)果(不同機(jī)器配置有差異,但趨勢(shì)一致):
String 拼接耗時(shí): 1500ms StringBuffer 拼接耗時(shí): 3ms StringBuilder 拼接耗時(shí): 1ms
結(jié)論:
String的+操作在大量拼接時(shí)性能最差,差了幾個(gè)數(shù)量級(jí)。StringBuilder通常比StringBuffer快(本例中快2-3倍),因?yàn)槭∪チ送介_(kāi)銷。
第四章:橫向?qū)Ρ扰c選型指南
為了讓你一目了然,這里將四個(gè)核心概念進(jìn)行橫向?qū)Ρ龋?/p>
| 特性 | char | String | StringBuilder | StringBuffer |
|---|---|---|---|---|
| 類型 | 原始數(shù)據(jù)類型 | 類 | 類 | 類 |
| 不可變性 | / | 不可變 (Immutable) | 可變 (Mutable) | 可變 (Mutable) |
| 線程安全 | / | 線程安全 (通過(guò)不可變性) | 非線程安全 | 線程安全 (通過(guò)synchronized) |
| 底層存儲(chǔ) | 16位Unicode值 | byte[] (JDK 9+) | byte[] (JDK 9+) | byte[] (JDK 9+) |
| 性能(修改操作) | N/A | 極差(創(chuàng)建大量對(duì)象) | 最高 | 中等(有同步開(kāi)銷) |
| 適用場(chǎng)景 | 存儲(chǔ)單個(gè)字符 | 操作少的字符串、常量、作為鍵的HashMap | 單線程下大量字符串操作(如循環(huán)拼接) | 多線程下共享的字符串緩沖區(qū) |
4.1 選型指南:到底該用誰(shuí)?
- 處理單個(gè)字符:毫無(wú)疑問(wèn),使用
char。 - 操作少量、不變的字符串:使用
String。例如配置項(xiàng)、常量、不經(jīng)常變化的文本。 - 單線程環(huán)境下,需要大量操作字符串內(nèi)容(拼接、刪除、修改):首選
StringBuilder。例如,在方法內(nèi)部構(gòu)建復(fù)雜的SQL語(yǔ)句、處理JSON字符串、日志組裝等。這是最常見(jiàn)的場(chǎng)景。 - 多線程環(huán)境下,多個(gè)線程需要操作同一個(gè)字符串緩沖區(qū):必須使用
StringBuffer來(lái)保證數(shù)據(jù)同步和安全。例如,一個(gè)全局的日志緩沖區(qū),多個(gè)線程都要向其追加內(nèi)容。 - 字符串作為HashMap的鍵:必須使用
String,因?yàn)槠洳豢勺冃院驼_的hashCode()實(shí)現(xiàn)。
第五章:常見(jiàn)面試題深度剖析
1. 談?wù)勀銓?duì)String的理解,它為什么是不可變的?
答:String的不可變性體現(xiàn)在:
- 類本身被
final修飾,不可繼承,防止子類破壞。 - 底層存儲(chǔ)字符的
byte[](或char[]) 數(shù)組被private final修飾,引用不可變,且String類沒(méi)有提供任何可以修改該數(shù)組內(nèi)部元素的方法(如setCharAt())。所有看似修改的操作,如concat(),substring(),replace()等,都是返回一個(gè)新的String對(duì)象。
這種設(shè)計(jì)帶來(lái)了線程安全、字符串常量池的內(nèi)存復(fù)用、哈希值緩存等巨大優(yōu)勢(shì)。
2. String、StringBuilder、StringBuffer的區(qū)別?
答:三者都是用來(lái)處理字符串的,主要區(qū)別如下:
- 可變性:
String是不可變類,操作后產(chǎn)生新對(duì)象。StringBuilder和StringBuffer是可變類,可以在原對(duì)象上修改。 - 線程安全:
String是線程安全的。StringBuffer的方法使用了synchronized修飾,是線程安全的。StringBuilder的方法沒(méi)有同步,是線程不安全的。 - 性能:在單線程環(huán)境下,
StringBuilder的性能最好,因?yàn)樗鼪](méi)有同步開(kāi)銷。StringBuffer次之。String在進(jìn)行大量修改時(shí)性能最差。
3. String s = new String(“xyz”); 創(chuàng)建了幾個(gè)對(duì)象?
答:這取決于“xyz”這個(gè)字符串常量是否已經(jīng)存在于字符串常量池中。
- 如果常量池中已經(jīng)存在"xyz":那么只在堆中創(chuàng)建一個(gè)
String對(duì)象。答案是 1個(gè)。 - 如果常量池中還沒(méi)有"xyz":JVM會(huì)先在常量池中創(chuàng)建字面量"xyz",然后再在堆中創(chuàng)建一個(gè)
String對(duì)象。答案是 2個(gè)。
4. 為什么用StringBuilder要好于用String的“+”拼接?
答:雖然編譯器會(huì)將 + 優(yōu)化為 StringBuilder,但在循環(huán)等場(chǎng)景中,優(yōu)化是局部的。例如 for 循環(huán)內(nèi)的 +,每次循環(huán)都會(huì)在循環(huán)體內(nèi)生成一個(gè)新的 StringBuilder 對(duì)象,然后調(diào)用 append(),最后調(diào)用 toString() 返回。這會(huì)產(chǎn)生大量的中間對(duì)象,給垃圾回收帶來(lái)巨大壓力,嚴(yán)重影響性能。而手動(dòng)使用 StringBuilder 可以在循環(huán)外部創(chuàng)建一個(gè)對(duì)象,在整個(gè)循環(huán)中復(fù)用這一個(gè)對(duì)象進(jìn)行 append,極大地減少了對(duì)象創(chuàng)建,提升了效率。
總結(jié)
本文從微觀的 char 原始類型講起,深入剖析了其在Java中的Unicode特性和運(yùn)算規(guī)則。隨后,我們?cè)敿?xì)探討了 String 類的不可變性及其帶來(lái)的設(shè)計(jì)權(quán)衡,并由此引出為了解決性能問(wèn)題而誕生的 StringBuilder 和 StringBuffer。通過(guò)對(duì)它們底層實(shí)現(xiàn)(繼承自 AbstractStringBuilder 的可變數(shù)組與擴(kuò)容機(jī)制)和線程安全性(synchronized 關(guān)鍵字的有無(wú))的對(duì)比,我們明確了各自的優(yōu)缺點(diǎn)和適用場(chǎng)景。
理解這些核心概念的區(qū)別與聯(lián)系,不僅僅是應(yīng)對(duì)面試,更是為了在日常開(kāi)發(fā)中做出正確的技術(shù)選型,寫出既高效又穩(wěn)健的代碼。希望這篇長(zhǎng)達(dá)數(shù)千字的詳解能幫助你徹底掌握J(rèn)ava中字符與字符串的奧秘。
到此這篇關(guān)于Java中的char、String、StringBuilder與StringBuffer全方面解析的文章就介紹到這了,更多相關(guān)Java char、String、StringBuilder與StringBuffer內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Java字符處理之char、String、StringBuilder和StringBuffer詳解
- Java字符串四大類之String、StringBuffer、StringBuilder、StringJoiner詳解
- Java中String、StringBuffer和StringBuilder底層實(shí)現(xiàn)深入剖析
- Java中String、StringBuffer和StringBuilder的區(qū)別
- Java中StringBuilder與StringBuffer的區(qū)別
- Java中StringBuilder與StringBuffer使用及源碼解讀
- 淺析Java中StringBuffer和StringBuilder的使用
- 詳解Java中String,StringBuffer和StringBuilder的使用
- Java中String和StringBuffer及StringBuilder?有什么區(qū)別
- Java 中 String、StringBuffer、StringBuilder 的區(qū)別
相關(guān)文章
Java泛型之協(xié)變與逆變及extends與super選擇
這篇文章主要介紹了Java泛型之協(xié)變與逆變及extends與super選擇,文章圍繞主題內(nèi)容展開(kāi)詳細(xì)內(nèi)容介紹,需要的小伙伴可以參考一下2022-05-05
Java將Word文檔轉(zhuǎn)換為PDF文件的幾種常用方法總結(jié)
這篇文章主要介紹了Java將Word文檔轉(zhuǎn)換為PDF文件的四種常用方法,分別使用ApachePOI+iText、Aspose.Words?for?Java、Docx4j和JODConverter,這些庫(kù)各有優(yōu)點(diǎn),但在使用時(shí)需要注意庫(kù)與Java環(huán)境的兼容性、安裝所需依賴、轉(zhuǎn)換速度和資源消耗,需要的朋友可以參考下2024-10-10
SpringBoot異步Async使用Future與CompletableFuture區(qū)別小結(jié)
本文主要介紹了SpringBoot異步Async使用Future與CompletableFuture區(qū)別小結(jié),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06
SpringBoot集成E-mail發(fā)送各種類型郵件
這篇文章主要為大家詳細(xì)介紹了SpringBoot集成E-mail發(fā)送各種類型郵件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-04-04
java實(shí)現(xiàn)分段讀取文件并通過(guò)HTTP上傳的方法
這篇文章主要介紹了java實(shí)現(xiàn)分段讀取文件并通過(guò)HTTP上傳的方法,實(shí)例分析了java分段讀取文件及使用http實(shí)現(xiàn)文件傳輸?shù)南嚓P(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-07-07
基于Java和FFmpeg實(shí)現(xiàn)視頻壓縮和剪輯功能
在視頻處理開(kāi)發(fā)中,壓縮和剪輯是常見(jiàn)的需求,本文將介紹如何使用 Java 結(jié)合 FFmpeg 實(shí)現(xiàn)視頻壓縮和剪輯功能,同時(shí)去除數(shù)據(jù)庫(kù)操作,僅專注于視頻處理,需要的朋友可以參考下2025-08-08
Java設(shè)計(jì)模式單例模式(Singleton)用法解析
這篇文章主要介紹了Java設(shè)計(jì)模式單例模式(Singleton)用法解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11
淺析Spring的事務(wù)實(shí)現(xiàn)原理
這篇文章主要為大家詳細(xì)介紹了Spring中事務(wù)實(shí)現(xiàn)的原理,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Spring有一定的幫助,需要的可以參考一下2022-11-11

