A. 求助,如何判斷一個string類型的字元串
首先樓主肯定知道"=="和equals的區別,我這里再說下:
==比的是兩個對象的地址。
equals是java.lang.Object類的一個方法,默認跟"=="一樣也是比的地址。如果判斷對象相等有其他的標准(例如只要值相等就代表相等),那麼就需要重寫java.lang.Object的equals方法。String類就重寫了此方法,只要對象值相等即可。
Java運行時會維護一個String Pool(String池),String Pool與JAVA堆棧是各自獨立的。
String池用來存放運行時中產生的各種字元串,並且池中的字元串的內容不重復。
String對象的創建遵循以下幾個原則。
原則1:只要使用new關鍵字來創建對象(String str = new String("123")),則一定會(在堆區或棧區)創建一個新的對象。 我們知道,只要一個對象是New出來的,那JVM肯定會在內存中為此對象分配一個唯一的地址,因此New出的對象跟任意一個對象用「==」判斷肯定為false。
原則2:使用直接指定來創建String對象(如String str="123"),則僅僅會檢查維護String池中的字元串,池中沒有就在池中創建一個,有則罷了!但絕不會在堆棧區再去創建該String對象。
原則3:使用包含變數的表達式來創建對象String(String str = "123"+"456"),則不僅會檢查維護String池,而且還會在堆棧區創建一個String對象。
B. java.lang.String的常用的方法
public boolean equals(Object obj)
判斷當前字元串與obj的內容是否相同
public boolean equalsIgnoreCase(String str)
判斷當前字元串與str的內容是否相同,這個方法不會區分大小寫字母的區別
public int length()
返回字元串的長度,即字元的總個數
public String trim()
去掉字元串兩端的空白,包括「空格, ,
,
等控制符」
public String substring(int start,int end)
根據開始和結束的位置,返回當前String的子字元串
public String substring(int start)
從開始位置開始到字元串結束,返回子字元串
public char charAt(int index)
返回指定位置的字元
public int indexOf(String str)
返回子字元串在當前字元串的位置,如果當前字元串不包含子字元串就返回-1
public String concat(String str)
返回一個字元串,內容是當前字元串與str連接而成的。
字元串連接可以簡化寫為String str = str1 + str2;結果與concat方法相同
public boolean startsWith(String str)
判斷當前字元串,是否以str開頭
public boolean endsWith(String str)
判斷當前字元串,是否以str結尾
========================================================
String str = I am + Lingo!;
這樣可以獲得一個內容為I am Lingo!的字元串,在java里可以通過這種簡單的方式實現字元串的連接
。這里需要注意的是,這個過程實際上生成了三個String對象,I am 和Lingo!先被生成,然後用他
們再創建一個String對象str,str的內容是兩者的總和。所以,使用+進行字元串連接的時候會很耗費資
源,這個時候就需要使用另一個類StringBuffer,它的內容是可以修改的,實際上jvm內部編譯之後,「
用+進行字元串連接」也是用StringBuffer實現的。
String str = I am + Lingo!;
String str = new StringBuffer(I am ).append(Lingo!).toString();
上邊兩個是等價的。
StringBuffer類還提供了許多便利的方法,對字元串進行操作
public void reverse()
反轉字元串
public void append(...)
在字元串最後添加信息
public void insert(int start,...)
在索引位置插入信息
public void delete(int start,int end)
刪除指定范圍的內容
split與replaceAll方法
public String[] split(String regex)
根據分隔符,把字元串切割成字元串數組
public String replace(String regex,String str)
把字元串中所有與regex匹配的部分都替換成str
regex代表「正則表達式」,如果你並不清楚它的原理,很可能會出現問題。
1,3,4.split(,)返回的結果是{1,3,4}這三個字元串組成的數組
1|3|4.split(|)返回的結果卻是{1,|,3,|,4}五個字元串組成的數組
這個問題的原因是由於在「正則表達式」中,「|」是一個有特殊含義的字元,表示「或」,直接使用
split(|)就會把每個字元分開了。如果希望使用|作為分隔符,就需要使用轉義字元。
1|3|4.split(\|)返回的結果就是{1,3,4}三個字元串組成的數組了
「|」是正則表達式中代表|的專一字元,但因為在String中「」不能單獨出現,還需要進行一次轉義
,就變成了「\|」這種形式。
replaceAll(String regex,String str)也是這種情況
C. 已經編譯好的jvm指令集如何生成class文件
語言表達的.....說的通俗易懂點,感覺你的問題也不能算問題吧
D. java如果判斷一個字元串中是否有亂碼
據我所知, 貌似沒有這樣的解決辦法...
從編譯器角度來說 , 因為java在得到一個字元串變數的時候, JVM就已經認定它是合法對象了
從業務邏輯上來說 , 什麼叫亂碼? 有可能是一堆中國人無法正常解讀的字元串, 如果是硬性要求的話, 需要參考一些中文的分詞器來做了, 例如庖丁解牛.
最後勸您一句, 如果沒有硬性要求, 不要繼續研究這個了, 沒有太大的實際意義.
E. java編譯器和JVM有什麼區別
java編譯器把java源碼編譯成位元組碼 (.class文件).
jvm是在運行期將class文件編譯成機器碼文件.供程序運行.
F. 編譯原理中如何用c語言來編寫程序判斷輸入的字元串是否符合文法規則
scanf()有返回值,若返回值是0,則不符合文法規則
一般情況下,scanf()返回值是輸入的字元數
G. Java:為什麼說這里的字元串值不能在編譯時就確定下來
應該是這個意思,編譯完成之後:
String s4 = "瘋狂Java";
String s5 = s2 + s3;
H. 前的Java的switch語句能判斷String 類型嗎
在Java7之前,switch只能支持 byte、short、char、int或者其對應的封裝類以及Enum類型。在Java7中,呼籲很久的String支持也終於被加上了。
例如,下面是一段switch中使用String的示例代碼。
public class Test {
public void test(String str) {
switch(str) {
case "abc":
System.out.println("abc");
break;
case "def":
System.out.println("def");
break;
default:
System.out.println("default");
}
}
}
在switch語句中,String的比較用的是String.equals,因此大家可以放心的使用。
需要注意的是,傳給switch的String變數不能為null,同時switch的case子句中使用的字元串也不能為null。
為什麼要有這些非null的限制呢?其實,我們只要將這段代碼反匯編出來,看一下底層到底是如何實現的,就可以明白了。下面是匯編出來的代碼。
Compiled from "Test.java"
public class Test extends java.lang.Object{
public Test();
Code:
0: aload_0
1: invokespecial #1; //Method java/lang/Object."":()V
4: return
public void test(java.lang.String);
Code:
0: aload_1
1: astore_2
2: iconst_m1
3: istore_3
4: aload_2
5: invokevirtual #2; //Method java/lang/String.hashCode:()I
8: lookupswitch{ //2
96354: 36;
99333: 50;
default: 61 }
36: aload_2
37: ldc #3; //String abc
39: invokevirtual #4; //Method java/lang/String.equals:(Ljava/lang/Object;)Z
42: ifeq 61
45: iconst_0
46: istore_3
47: goto 61
50: aload_2
51: ldc #5; //String def
53: invokevirtual #4; //Method java/lang/String.equals:(Ljava/lang/Object;)Z
56: ifeq 61
59: iconst_1
60: istore_3
61: iload_3
62: lookupswitch{ //2
0: 88;
1: 99;
default: 110 }
88: getstatic #6; //Field java/lang/System.out:Ljava/io/PrintStream;
91: ldc #3; //String abc
93: invokevirtual #7; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
96: goto 118
99: getstatic #6; //Field java/lang/System.out:Ljava/io/PrintStream;
102: ldc #5; //String def
104: invokevirtual #7; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
107: goto 118
110: getstatic #6; //Field java/lang/System.out:Ljava/io/PrintStream;
113: ldc #8; //String default
115: invokevirtual #7; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
118: return
}
估計有些同學懶得看這些匯編,其實把上面的匯編代碼用Java寫出來就是下面的樣子了。
寫到這里,大家應該能明白為什麼不能用null了吧。
public class Test {
public void test(String str) {
int i = -1;
switch(str.hashCode()) {
case 96354: // "abc".hashCode()
if (str.equals("abc")) {
i = 0;
}
break;
case 99333: // "def".hashCode()
if (str.equals("def")) {
i = 1;
}
break;
default:
break;
}
switch(i) {
case 0:
System.out.println("abc");
break;
case 1:
System.out.println("def");
break;
default:
System.out.println("default");
}
}
}
如果switch傳入的null,那麼在運行時對一個null對象調用hashCode方法會出現NullPointerException。
如果switch的case寫的是null,那麼在編譯時無法求出hashCode,因此在編譯時就會報錯了。
switch支持String只是一個語法糖,由javac來負責生成相應的代碼。底層的JVM在switch上並沒有進行修改。
I. Java:字元串在JVM常量池中是如何存儲的呢
首先你要知道jvm常量池也是對象池,它和在堆中的存儲沒有區別(底層存儲都是一樣的,只是對象之間的引用有差別)。那為什麼要有常量池呢?因為它可以節省時間和空間,當需要一個對象的時候,可以直接從常量池中獲取,而不需要重新創建,這樣也就節省了時間和空間(常量池判斷對象是否存在應該是equals方法)。
除了String外,Java的8種基本類型(Byte, Short, Integer, Long, Character, Boolean, Float, Double)除Float和Double以外,其它六種都實現了常量池。
樓主這么好學,我出個題目給樓主:
Integer i = 127;
Integer j = 127;
System.out.println(i == j);
提示:對象存在常量池
Integer m = 128;
Integer n = 128;
System.out.println(m == n);
提示:對象存在堆內存
J. java高手來啊,字元串相等問題
這個其實很簡單,但是實際中這么寫代碼的人會被人鄙視死的。
先說第一點
String對象是不可變的。String類中每一個看起來會修改String值的方法,實際上的都是創建了一個全
新的String對象。而用於String的+和+=是java中僅有的兩個重載過的操作符。而java(1.6)編譯器是
通過引入StringBuilder(你可以打開你所寫的class文件看位元組碼,里邊肯定會有invokespecial和
invokevirtual這就是初始化init方法了一個StringBuilder對象和調用其append方法,如果不想了解這
么深,括弧里的內容可以忽略)來實現的+或者+=的重載,最後通過toString返回其產生的新對象(源代碼里是new的)。
然而要注意一點(只針對這個題目),對於一個class來說當其成員變數是String的靜態final域時,在類被編譯時編譯器會進行優化和改進。這里的優化就是String替換.舉個例子
public static final String c="c";
public static final String d="d";
public static final String dddccc=d+d+d+c+c+c;
public static void main(String[] args){
String ccd = c+c+d;
}
會優化變成
public static final String c="c";
public static final String d="d";
public static final String dddccc="dddccc";
public static void main(String[] args){
String ccd = "ccd";
}
原因很簡單編譯器為了提高效率和無效代碼。而如果將上述變數(a和b)的初始化話放在構造方法(雖然這里的a,b必須在static域中初始化,但非static在構造方法里)或者static域中進行的話就不能進行替換啦。static域是在類的init方法後執行(至於神馬是init方法和有什麼特點,你可以看看JVM相關的資料,還有至於為什麼編譯器可以這么優化,如果樓主對final域比較了解的話就能理解了)。
2。字元串池
這個可以把它理解成是java對字元串的一種緩存機制(雖然實際並非如此),由於字元串操作頻繁,
如果沒有字元串池的話,會造成大量String對象被頻繁的創建和銷毀。這對於java這種語言的效率來說
是不可接受的。因此java在內存中會單獨開辟一塊內存用來存放字元串對象(只能由String類來維護)。程序中出現的字元串常量都是在池中的。比如String a = 「ab」,String c =」cd「。而new操作符產生的String對象將不會放在字元串池中。如String cd = new String("cd");是在堆中的(java中所有的對象的存放地)至於為什麼,還是那句話:說來話長。
好我可以開始解釋了
String ab=a+b;//通過+運算符返回新的String對象
System.out.println(ab=="ab");//"ab"是池中對象,ab沒有在池中的對象不一樣
結果false 不解釋。
String cd=c+d;
System.out.println(cd=="cd");
編譯器優化後
String cd="cd';//池中對象
System.out.println(cd=="cd");//跟自身相比肯定相等 true 不解釋
String tmp=new String(new char[]{'c','d'});
這里解釋一下intern的實現邏輯
如果池中已經有相同的字元串了,直接返回池中字元串,否則將tmp拷貝一份放入池中並返回池中對象
注意這里是拷貝而不是將引用指向池中對象(String是final的)
String t=tmp.intern();// 返回池中的"cd",因為已經存在了cd所以這個返回的對象跟cd沒有半毛錢的關系。
System.out.println(cd==t);// 肯定是true不解釋
我寫了這么多,只是覺得大家對於String這個對象特別的執著,不論是面試還是考試 ,其實你只要不再循環里+或者+=字元串的話其實是沒有什麼問題。你平時寫個 Stirng aaa = "a'+"b"的話編譯器是會幫你優化的。無關痛癢。