[转载] java常量池-字符串常量池、class常量池和运行时常量池
參考鏈接: 如何在Java中初始化和比較字符串
原文鏈接:http://tangxman.github.io/2015/07/27/the-difference-of-java-string-pool/?
? ? ? ? ? ? ? ? ? ? ? ??
在java的內(nèi)存分配中,經(jīng)常聽到很多關(guān)于常量池的描述,我開始看的時(shí)候也是看的很模糊,網(wǎng)上五花八門的說(shuō)法簡(jiǎn)直太多了,最后查閱各種資料,終于算是差不多理清了,很多網(wǎng)上說(shuō)法都有問題,筆者嘗試著來(lái)區(qū)分一下這幾個(gè)概念。?
1.全局字符串池(string pool也有叫做string literal pool)?
全局字符串池里的內(nèi)容是在類加載完成,經(jīng)過驗(yàn)證,準(zhǔn)備階段之后在堆中生成字符串對(duì)象實(shí)例,然后將該字符串對(duì)象實(shí)例的引用值存到string pool中(記住:string pool中存的是引用值而不是具體的實(shí)例對(duì)象,具體的實(shí)例對(duì)象是在堆中開辟的一塊空間存放的。)。 在HotSpot VM里實(shí)現(xiàn)的string pool功能的是一個(gè)StringTable類,它是一個(gè)哈希表,里面存的是駐留字符串(也就是我們常說(shuō)的用雙引號(hào)括起來(lái)的)的引用(而不是駐留字符串實(shí)例本身),也就是說(shuō)在堆中的某些字符串實(shí)例被這個(gè)StringTable引用之后就等同被賦予了”駐留字符串”的身份。這個(gè)StringTable在每個(gè)HotSpot VM的實(shí)例只有一份,被所有的類共享。?
2.class文件常量池(class constant pool)?
我們都知道,class文件中除了包含類的版本、字段、方法、接口等描述信息外,還有一項(xiàng)信息就是常量池(constant pool table),用于存放編譯器生成的各種字面量(Literal)和符號(hào)引用(Symbolic References)。 字面量就是我們所說(shuō)的常量概念,如文本字符串、被聲明為final的常量值等。 符號(hào)引用是一組符號(hào)來(lái)描述所引用的目標(biāo),符號(hào)可以是任何形式的字面量,只要使用時(shí)能無(wú)歧義地定位到目標(biāo)即可(它與直接引用區(qū)分一下,直接引用一般是指向方法區(qū)的本地指針,相對(duì)偏移量或是一個(gè)能間接定位到目標(biāo)的句柄)。一般包括下面三類常量:?
類和接口的全限定名字段的名稱和描述符方法的名稱和描述符
常量池的每一項(xiàng)常量都是一個(gè)表,一共有如下表所示的11種各不相同的表結(jié)構(gòu)數(shù)據(jù),這每個(gè)表開始的第一位都是一個(gè)字節(jié)的標(biāo)志位(取值1-12),代表當(dāng)前這個(gè)常量屬于哪種常量類型。 每種不同類型的常量類型具有不同的結(jié)構(gòu),具體的結(jié)構(gòu)本文就先不敘述了,本文著重區(qū)分這三個(gè)常量池的概念(讀者若想深入了解每種常量類型的數(shù)據(jù)結(jié)構(gòu)可以查看《深入理解java虛擬機(jī)》第六章的內(nèi)容)。?
3.運(yùn)行時(shí)常量池(runtime constant pool)?
當(dāng)java文件被編譯成class文件之后,也就是會(huì)生成我上面所說(shuō)的class常量池,那么運(yùn)行時(shí)常量池又是什么時(shí)候產(chǎn)生的呢??
jvm在執(zhí)行某個(gè)類的時(shí)候,必須經(jīng)過加載、連接、初始化,而連接又包括驗(yàn)證、準(zhǔn)備、解析三個(gè)階段。而當(dāng)類加載到內(nèi)存中后,jvm就會(huì)將class常量池中的內(nèi)容存放到運(yùn)行時(shí)常量池中,由此可知,運(yùn)行時(shí)常量池也是每個(gè)類都有一個(gè)。在上面我也說(shuō)了,class常量池中存的是字面量和符號(hào)引用,也就是說(shuō)他們存的并不是對(duì)象的實(shí)例,而是對(duì)象的符號(hào)引用值。而經(jīng)過解析(resolve)之后,也就是把符號(hào)引用替換為直接引用,解析的過程會(huì)去查詢?nèi)肿址?#xff0c;也就是我們上面所說(shuō)的StringTable,以保證運(yùn)行時(shí)常量池所引用的字符串與全局字符串池中所引用的是一致的。?
舉個(gè)實(shí)例來(lái)說(shuō)明一下:?
?
??
?public class HelloWorld {? public static void main(String []args) {? String str1 = "abc";? ?String str2 = new String("def");? ?String str3 = "abc";? ?String str4 = str2.intern();? ?String str5 = "def";? ?System.out.println(str1 == str3);//true? ?System.out.println(str2 == str4);//false? ?System.out.println(str4 == str5);//true? }? }?
回到上面的那個(gè)程序,現(xiàn)在就很容易解釋整個(gè)程序的內(nèi)存分配過程了,首先,在堆中會(huì)有一個(gè)”abc”實(shí)例,全局StringTable中存放著”abc”的一個(gè)引用值,然后在運(yùn)行第二句的時(shí)候會(huì)生成兩個(gè)實(shí)例,一個(gè)是”def”的實(shí)例對(duì)象,并且StringTable中存儲(chǔ)一個(gè)”def”的引用值,還有一個(gè)是new出來(lái)的一個(gè)”def”的實(shí)例對(duì)象,與上面那個(gè)是不同的實(shí)例,當(dāng)在解析str3的時(shí)候查找StringTable,里面有”abc”的全局駐留字符串引用,所以str3的引用地址與之前的那個(gè)已存在的相同,str4是在運(yùn)行的時(shí)候調(diào)用intern()函數(shù),返回StringTable中”def”的引用值,如果沒有就將str2的引用值添加進(jìn)去,在這里,StringTable中已經(jīng)有了”def”的引用值了,所以返回上面在new str2的時(shí)候添加到StringTable中的 “def”引用值,最后str5在解析的時(shí)候就也是指向存在于StringTable中的”def”的引用值,那么這樣一分析之后,下面三個(gè)打印的值就容易理解了。上面程序的首先經(jīng)過編譯之后,在該類的class常量池中存放一些符號(hào)引用,然后類加載之后,將class常量池中存放的符號(hào)引用轉(zhuǎn)存到運(yùn)行時(shí)常量池中,然后經(jīng)過驗(yàn)證,準(zhǔn)備階段之后,在堆中生成駐留字符串的實(shí)例對(duì)象(也就是上例中str1所指向的”abc”實(shí)例對(duì)象),然后將這個(gè)對(duì)象的引用存到全局String Pool中,也就是StringTable中,最后在解析階段,要把運(yùn)行時(shí)常量池中的符號(hào)引用替換成直接引用,那么就直接查詢StringTable,保證StringTable里的引用值與運(yùn)行時(shí)常量池中的引用值一致,大概整個(gè)過程就是這樣了。?
總結(jié)?
1.全局常量池在每個(gè)VM中只有一份,存放的是字符串常量的引用值。2.class常量池是在編譯的時(shí)候每個(gè)class都有的,在編譯階段,存放的是常量的符號(hào)引用。3.運(yùn)行時(shí)常量池是在類加載完成之后,將每個(gè)class常量池中的符號(hào)引用值轉(zhuǎn)存到運(yùn)行時(shí)常量池中,也就是說(shuō),每個(gè)class都有一個(gè)運(yùn)行時(shí)常量池,類在解析之后,將符號(hào)引用替換成直接引用,與全局常量池中的引用值保持一致。
??
================================?
??
class文件常量池和運(yùn)行時(shí)常量池?
最近一直被方法區(qū)里面存著什么東西困擾著??
? ? ? ?1.方法區(qū)里存class文件信息和class文件常量池是個(gè)什么關(guān)系。?
? ? ? ? 2.class文件常量池和運(yùn)行時(shí)常量池是什么關(guān)系。? ? ? ? ?
? ? ? ? 方法區(qū)存著類的信息,常量和靜態(tài)變量,即類被編譯后的數(shù)據(jù)。這個(gè)說(shuō)法其實(shí)是沒問題的,只是太籠統(tǒng)了。更加詳細(xì)一點(diǎn)的說(shuō)法是方法區(qū)里存放著類的版本,字段,方法,接口和常量池。常量池里存儲(chǔ)著字面量和符號(hào)引用。?
? ? ? ?符號(hào)引用包括:1.類的全限定名,2.字段名和屬性,3.方法名和屬性。?
? ? ? ?下面一張圖是我畫的方法區(qū),class文件信息,class文件常量池和運(yùn)行時(shí)常量池的關(guān)系?
? ? ? ??
? ? ? ?下面一張圖用來(lái)表示方法區(qū)class文件信息包括哪些內(nèi)容:?
? ? ? ??
? ? ? ? ?可以看到在方法區(qū)里的class文件信息包括:魔數(shù),版本號(hào),常量池,類,父類和接口數(shù)組,字段,方法等信息,其實(shí)類里面又包括字段和方法的信息。?
? ? ? ? ?下面的圖表是class文件中存儲(chǔ)的數(shù)據(jù)類型? ? ? ? ? ? ??
??
類型名稱數(shù)量u4magic1u2minor_version1u2major_version1u2constant_pool_count1cp_infoconstant_poolconstant_pool_count - 1u2access_flags1u2this_class1u2super_class1u2interfaces_count1u2interfacesinterfaces_countu2fields_count1field_infofieldsfields_countu2methods_count1method_infomethodsmethods_countu2attribute_count1attribute_infoattributesattributes_count
??
? ? ? ?下面用一張圖來(lái)表示常量池里存儲(chǔ)的內(nèi)容:?
? ? ? ? ?
??
用一個(gè)class文件實(shí)際反編譯一下?
下面是原java代碼?
??
[java] view plain copy?
public class TestInt {? ? ? ? private String str = "hello";? ? ? ? void printInt(){? ? ? ? ? ? System.out.println(65535);? ? ? ? }? ? }? ?
?經(jīng)過反編譯后獲得class文件是下面這樣的?
??
?
可以看出被反編譯的class文件中的內(nèi)容和上面所說(shuō)的是能對(duì)應(yīng)上的。這就解答了class文件和class文件常量池的關(guān)系?
class文件常量池和運(yùn)行時(shí)常量池的關(guān)系以及區(qū)別?
class文件常量池存儲(chǔ)的是當(dāng)class文件被java虛擬機(jī)加載進(jìn)來(lái)后存放在方法區(qū)的一些字面量和符號(hào)引用,字面量包括字符串,基本類型的常量。?
運(yùn)行時(shí)常量池是當(dāng)class文件被加載完成后,java虛擬機(jī)會(huì)將class文件常量池里的內(nèi)容轉(zhuǎn)移到運(yùn)行時(shí)常量池里,在class文件常量池的符號(hào)引用有一部分是會(huì)被轉(zhuǎn)變?yōu)橹苯右玫?#xff0c;比如說(shuō)類的靜態(tài)方法或私有方法,實(shí)例構(gòu)造方法,父類方法,這是因?yàn)檫@些方法不能被重寫其他版本,所以能在加載的時(shí)候就可以將符號(hào)引用轉(zhuǎn)變?yōu)橹苯右?#xff0c;而其他的一些方法是在這個(gè)方法被第一次調(diào)用的時(shí)候才會(huì)將符號(hào)引用轉(zhuǎn)變?yōu)橹苯右玫摹?
總結(jié):?
方法區(qū)里存儲(chǔ)著class文件的信息和運(yùn)行時(shí)常量池,class文件的信息包括類信息和class文件常量池。?
運(yùn)行時(shí)常量池里的內(nèi)容除了是class文件常量池里的內(nèi)容外,還將class文件常量池里的符號(hào)引用轉(zhuǎn)變?yōu)橹苯右?#xff0c;而且運(yùn)行時(shí)常量池里的內(nèi)容是能動(dòng)態(tài)添加的。例如調(diào)用String的intern方法就能將string的值添加到String常量池中,這里String常量池是包含在運(yùn)行時(shí)常量池里的,但在jdk1.8后,將String常量池放到了堆中。?
下面有一篇文章寫的是比較好的?
http://blog.csdn.net/vegetable_bird_001/article/details/51278339? ?
https://www.cnblogs.com/holos/p/6603379.html?
=====================================?
基本類型的包裝類、String類和常量池?
??
一.相關(guān)概念?
?
?什么是常量 用final修飾的成員變量表示常量,值一旦給定就無(wú)法改變! final修飾的變量有三種:靜態(tài)變量、實(shí)例變量和局部變量,分別表示三種類型的常量。? Class文件中的常量池 在Class文件結(jié)構(gòu)中,最頭的4個(gè)字節(jié)用于存儲(chǔ)魔數(shù)Magic Number,用于確定一個(gè)文件是否能被JVM接受,再接著4個(gè)字節(jié)用于存儲(chǔ)版本號(hào),前2個(gè)字節(jié)存儲(chǔ)次版本號(hào),后2個(gè)存儲(chǔ)主版本號(hào),再接著是用于存放常量的常量池,由于常量的數(shù)量是不固定的,所以常量池的入口放置一個(gè)U2類型的數(shù)據(jù)(constant_pool_count)存儲(chǔ)常量池容量計(jì)數(shù)值。 常量池主要用于存放兩大類常量:字面量(Literal)和符號(hào)引用量(Symbolic References),字面量相當(dāng)于Java語(yǔ)言層面常量的概念,如文本字符串,聲明為final的常量值等,符號(hào)引用則屬于編譯原理方面的概念,包括了如下三種類型的常量:?
類和接口的全限定名字段名稱和描述符方法名稱和描述符
?方法區(qū)中的運(yùn)行時(shí)常量池 運(yùn)行時(shí)常量池是方法區(qū)的一部分。 CLass文件中除了有類的版本、字段、方法、接口等描述信息外,還有一項(xiàng)信息是常量池,用于存放編譯期生成的各種字面量和符號(hào)引用,這部分內(nèi)容將在類加載后進(jìn)入方法區(qū)的運(yùn)行時(shí)常量池中存放。 運(yùn)行時(shí)常量池相對(duì)于CLass文件常量池的另外一個(gè)重要特征是具備動(dòng)態(tài)性,Java語(yǔ)言并不要求常量一定只有編譯期才能產(chǎn)生,也就是并非預(yù)置入CLass文件中常量池的內(nèi)容才能進(jìn)入方法區(qū)運(yùn)行時(shí)常量池,運(yùn)行期間也可能將新的常量放入池中,這種特性被開發(fā)人員利用比較多的就是String類的intern()方法。? 常量池的好處 常量池是為了避免頻繁的創(chuàng)建和銷毀對(duì)象而影響系統(tǒng)性能,其實(shí)現(xiàn)了對(duì)象的共享。 例如字符串常量池,在編譯階段就把所有的字符串文字放到一個(gè)常量池中。 (1)節(jié)省內(nèi)存空間:常量池中所有相同的字符串常量被合并,只占用一個(gè)空間。 (2)節(jié)省運(yùn)行時(shí)間:比較字符串時(shí),==比equals()快。對(duì)于兩個(gè)引用變量,只用==判斷引用是否相等,也就可以判斷實(shí)際值是否相等。? 雙等號(hào)==的含義 基本數(shù)據(jù)類型之間應(yīng)用雙等號(hào),比較的是他們的數(shù)值。 復(fù)合數(shù)據(jù)類型(類)之間應(yīng)用雙等號(hào),比較的是他們?cè)趦?nèi)存中的存放地址。?
二.8種基本類型的包裝類和常量池?
?
java中基本類型的包裝類的大部分都實(shí)現(xiàn)了常量池技術(shù), 即Byte,Short,Integer,Long,Character,Boolean;
?
??
?Integer i1 = 40;? Integer i2 = 40;? System.out.println(i1==i2);//輸出TRUE??
這5種包裝類默認(rèn)創(chuàng)建了數(shù)值[-128,127]的相應(yīng)類型的緩存數(shù)據(jù),但是超出此范圍仍然會(huì)去創(chuàng)建新的對(duì)象。?
?
??
?//Integer 緩存代碼 :? public static Integer valueOf(int i) {? assert IntegerCache.high >= 127;? if (i >= IntegerCache.low && i <= IntegerCache.high)? return IntegerCache.cache[i + (-IntegerCache.low)];? return new Integer(i);? }??
?
??
?Integer i1 = 400;? Integer i2 = 400;? System.out.println(i1==i2);//輸出false??
兩種浮點(diǎn)數(shù)類型的包裝類Float,Double并沒有實(shí)現(xiàn)常量池技術(shù)。
?
??
?Double i1=1.2;? Double i2=1.2;? System.out.println(i1==i2);//輸出false??
應(yīng)用常量池的場(chǎng)景 (1)Integer i1=40;Java在編譯的時(shí)候會(huì)直接將代碼封裝成Integer i1=Integer.valueOf(40);,從而使用常量池中的對(duì)象。 (2)Integer i1 = new Integer(40);這種情況下會(huì)創(chuàng)建新的對(duì)象。
?
??
?Integer i1 = 40;? Integer i2 = new Integer(40);? System.out.println(i1==i2);//輸出false??
Integer比較更豐富的一個(gè)例子
?
??
?Integer i1 = 40;? Integer i2 = 40;? Integer i3 = 0;? Integer i4 = new Integer(40);? Integer i5 = new Integer(40);? Integer i6 = new Integer(0);? ?System.out.println("i1=i2 " + (i1 == i2));? System.out.println("i1=i2+i3 " + (i1 == i2 + i3));? System.out.println("i1=i4 " + (i1 == i4));? System.out.println("i4=i5 " + (i4 == i5));? System.out.println("i4=i5+i6 " + (i4 == i5 + i6));? ?System.out.println("40=i5+i6 " + (40 == i5 + i6));? ?
?
??
?i1=i2 true? i1=i2+i3 true? i1=i4 false? i4=i5 false? i4=i5+i6 true? 40=i5+i6 true??
解釋:語(yǔ)句i4 == i5 + i6,因?yàn)?#43;這個(gè)操作符不適用于Integer對(duì)象,首先i5和i6進(jìn)行自動(dòng)拆箱操作,進(jìn)行數(shù)值相加,即i4 == 40。然后Integer對(duì)象無(wú)法與數(shù)值進(jìn)行直接比較,所以i4自動(dòng)拆箱轉(zhuǎn)為int值40,最終這條語(yǔ)句轉(zhuǎn)為40 == 40進(jìn)行數(shù)值比較。Java中的自動(dòng)裝箱與拆箱?
??
三.String類和常量池?
?
String對(duì)象創(chuàng)建方式
?
??
?String str1 = "abcd";? String str2 = new String("abcd");? System.out.println(str1==str2);//false??
這兩種不同的創(chuàng)建方法是有差別的,第一種方式是在常量池中拿對(duì)象,第二種方式是直接在堆內(nèi)存空間創(chuàng)建一個(gè)新的對(duì)象。只要使用new方法,便需要?jiǎng)?chuàng)建新的對(duì)象。?
連接表達(dá)式 + (1)只有使用引號(hào)包含文本的方式創(chuàng)建的String對(duì)象之間使用“+”連接產(chǎn)生的新對(duì)象才會(huì)被加入字符串池中。 (2)對(duì)于所有包含new方式新建對(duì)象(包括null)的“+”連接表達(dá)式,它所產(chǎn)生的新對(duì)象都不會(huì)被加入字符串池中。
?
??
?String str1 = "str";? String str2 = "ing";? ?String str3 = "str" + "ing";? String str4 = str1 + str2;? System.out.println(str3 == str4);//false? ?String str5 = "string";? System.out.println(str3 == str5);//true??
java基礎(chǔ):字符串的拼接?
特例1
?
??
?public static final String A = "ab"; // 常量A? public static final String B = "cd"; // 常量B? public static void main(String[] args) {? String s = A + B; // 將兩個(gè)常量用+連接對(duì)s進(jìn)行初始化? ?String t = "abcd";? ?if (s == t) {? ?System.out.println("s等于t,它們是同一個(gè)對(duì)象");? ?} else {? ?System.out.println("s不等于t,它們不是同一個(gè)對(duì)象");? ?}? ?}? ?s等于t,它們是同一個(gè)對(duì)象??
A和B都是常量,值是固定的,因此s的值也是固定的,它在類被編譯時(shí)就已經(jīng)確定了。也就是說(shuō):String s=A+B; 等同于:String s="ab"+"cd";?
特例2
?
??
?public static final String A; // 常量A? public static final String B; // 常量B? static {? ?A = "ab";? ?B = "cd";? ?}? ?public static void main(String[] args) {? ?// 將兩個(gè)常量用+連接對(duì)s進(jìn)行初始化? ?String s = A + B;? ?String t = "abcd";? ?if (s == t) {? ?System.out.println("s等于t,它們是同一個(gè)對(duì)象");? ?} else {? ?System.out.println("s不等于t,它們不是同一個(gè)對(duì)象");? ?}? ?}? ?s不等于t,它們不是同一個(gè)對(duì)象??
A和B雖然被定義為常量,但是它們都沒有馬上被賦值。在運(yùn)算出s的值之前,他們何時(shí)被賦值,以及被賦予什么樣的值,都是個(gè)變數(shù)。因此A和B在被賦值之前,性質(zhì)類似于一個(gè)變量。那么s就不能在編譯期被確定,而只能在運(yùn)行時(shí)被創(chuàng)建了。?
?String s1 = new String("xyz"); 創(chuàng)建了幾個(gè)對(duì)象?? 考慮類加載階段和實(shí)際執(zhí)行時(shí)。 (1)類加載對(duì)一個(gè)類只會(huì)進(jìn)行一次。"xyz"在類加載時(shí)就已經(jīng)創(chuàng)建并駐留了(如果該類被加載之前已經(jīng)有"xyz"字符串被駐留過則不需要重復(fù)創(chuàng)建用于駐留的"xyz"實(shí)例)。駐留的字符串是放在全局共享的字符串常量池中的。 (2)在這段代碼后續(xù)被運(yùn)行的時(shí)候,"xyz"字面量對(duì)應(yīng)的String實(shí)例已經(jīng)固定了,不會(huì)再被重復(fù)創(chuàng)建。所以這段代碼將常量池中的對(duì)象復(fù)制一份放到heap中,并且把heap中的這個(gè)對(duì)象的引用交給s1 持有。 這條語(yǔ)句創(chuàng)建了2個(gè)對(duì)象。? java.lang.String.intern() 運(yùn)行時(shí)常量池相對(duì)于CLass文件常量池的另外一個(gè)重要特征是具備動(dòng)態(tài)性,Java語(yǔ)言并不要求常量一定只有編譯期才能產(chǎn)生,也就是并非預(yù)置入CLass文件中常量池的內(nèi)容才能進(jìn)入方法區(qū)運(yùn)行時(shí)常量池,運(yùn)行期間也可能將新的常量放入池中,這種特性被開發(fā)人員利用比較多的就是String類的intern()方法。 String的intern()方法會(huì)查找在常量池中是否存在一份equal相等的字符串,如果有則返回該字符串的引用,如果沒有則添加自己的字符串進(jìn)入常量池。?
?
??
?public static void main(String[] args) {? ?String s1 = new String("計(jì)算機(jī)");? String s2 = s1.intern();? String s3 = "計(jì)算機(jī)";? System.out.println("s1 == s2? " + (s1 == s2));? System.out.println("s3 == s2? " + (s3 == s2));? }??
?
??
?s1 == s2? false? s3 == s2? true??
字符串比較更豐富的一個(gè)例子
?
??
?public class Test {? public static void main(String[] args) {? ?String hello = "Hello", lo = "lo";? System.out.println((hello == "Hello") + " ");? System.out.println((Other.hello == hello) + " ");? System.out.println((other.Other.hello == hello) + " ");? System.out.println((hello == ("Hel"+"lo")) + " ");? System.out.println((hello == ("Hel"+lo)) + " ");? System.out.println(hello == ("Hel"+lo).intern());? }? ?}? class Other { static String hello = "Hello"; }? package other;? public class Other { public static String hello = "Hello"; }??
?
??
?true true true true false true```? 在同包同類下,引用自同一String對(duì)象.? 在同包不同類下,引用自同一String對(duì)象.? 在不同包不同類下,依然引用自同一String對(duì)象.? 在編譯成.class時(shí)能夠識(shí)別為同一字符串的,自動(dòng)優(yōu)化成常量,引用自同一String對(duì)象.? 在運(yùn)行時(shí)創(chuàng)建的字符串具有獨(dú)立的內(nèi)存地址,所以不引用自同一String對(duì)象.? ? -----? [2015-08-26]
總結(jié)
以上是生活随笔為你收集整理的[转载] java常量池-字符串常量池、class常量池和运行时常量池的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: linux的 dev vdal,RAZV
- 下一篇: opencv 绘制坐标曲线_OpenCV