Java学习笔记三
41.
當(dāng)一個(gè)Object實(shí)例被轉(zhuǎn)化成String時(shí),Java就會(huì)自動(dòng)調(diào)用toString()方法返回一個(gè)String。
System.out.println標(biāo)準(zhǔn)輸出,只能輸出String,所以,所有需要輸出的Object都會(huì)被轉(zhuǎn)化成String。
如果你沒有覆蓋toString,那么,Java會(huì)自動(dòng)調(diào)用最上層父類Object的toString()方法。
其內(nèi)容如下:
return getClass().getName() + "@" +Integer.toHexString(hashCode());
?
?
?
所以,沒有定義toString,并用System.out.println輸出實(shí)例的時(shí)候,會(huì)看到類名和哈希碼(@)
所以才會(huì)出現(xiàn)下面的情況
public classObjectTest
{
????????????? public static void main (String[]args)
????????????? {
??????????????????????????? Child child = newChild ();
??????????????????????????? System.out.println(child);
??????????????????????????? System.out.println(child.toString ());
????????????? }
}
class Child
{
????????????? /*public String toString ()
????????????? {
????????????? String str = "child isrunning";
????????????? return str ;
????????????? }
????????????? */
?????????????
}
我已經(jīng)注釋掉了,并沒有對(duì) toString方法進(jìn)行重寫,所以Child類默認(rèn)調(diào)用父類Object方法
也是可以寫成Child extends Object
42.
關(guān)鍵字instanceof 的作用是判斷左邊的引用是不是右邊類的實(shí)例,會(huì)返回一個(gè)boolean類型的值
public classInstanceofTest
{
?????? ?????? publicstatic void main (String[] args)
????????????? {
??????????????????????????? A a = new A();
??????????????????????????? System.out.println(a instanceof A);
????????????? }
}
class? A
{
?????????????
}
43.
java中equals與== 的區(qū)別
equals 方法(是String類從它的超類Object中繼承的)被用來檢測(cè)兩個(gè)對(duì)象是否相等,即兩個(gè)對(duì)象的內(nèi)容是否相等。
==用于比較引用和比較基本數(shù)據(jù)類型時(shí)具有不同的功能:
比較基本數(shù)據(jù)類型,如果兩個(gè)值相同,則結(jié)果為true
而在比較引用時(shí),如果引用指向內(nèi)存中的同一對(duì)象,結(jié)果為true
基本用法
Eg:s1 = newString("sony"); //創(chuàng)建的是字符串對(duì)象
s1.equals("sony");//返回true
s1 =="sony" //返回false
//如果
s1 ="sony";
s1 == "sony" //返回true
(一個(gè)是引用一個(gè)是具體的值;)
44.
Java中的equals方法和==運(yùn)算值類型是存儲(chǔ)在內(nèi)存中的堆棧(以后簡(jiǎn)稱棧),而引用類型的變量在棧中僅僅是存儲(chǔ)引用類型變量的地址,而其本身則存儲(chǔ)在堆中。
???????? ==操作比較的是兩個(gè)變量的值是否相等,對(duì)于引用型變量表示的是兩個(gè)變量在堆中存儲(chǔ)的地址是否相同,即棧中的內(nèi)容是否相同。
???????? equals操作表示的兩個(gè)變量是否是對(duì)同一個(gè)對(duì)象的引用,即堆中的內(nèi)容是否相同。
==比較的是2個(gè)對(duì)象的地址,而equals比較的是2個(gè)對(duì)象的內(nèi)容。
顯然,當(dāng)equals為true時(shí),==不一定為true;
一、String中的equals和==
1、
public class TestString {
public static void main(String[] args) {
String s1 = "Monday";
String s2 = "Monday";
}
}
上面這段程序中,到底有幾個(gè)對(duì)象呢?
來檢測(cè)一下吧,稍微改動(dòng)一下程序
public class TestString {
public static void main(String[] args) {
String s1 = "Monday";
String s2 = "Monday";
if (s1 == s2)
System.out.println("s1 == s2");
else
System.out.println("s1 != s2");
}
}
編譯并運(yùn)行程序,輸出:s1 == s2
說明:s1 與 s2 引用同一個(gè) String 對(duì)象 -- "Monday"!
2.
再稍微改動(dòng)一下程序,會(huì)有更奇怪的發(fā)現(xiàn):
public class TestString {
public static void main(String[] args) {
String s1 = "Monday";
String s2 = new String("Monday");
if (s1 == s2)
System.out.println("s1 == s2");
else
System.out.println("s1 != s2");
if (s1.equals(s2))
System.out.println("s1 equals s2");
else
System.out.println("s1 not equals s2");
}
}
我們將 s2 用 new 操作符創(chuàng)建
程序輸出:
s1 != s2
s1 equals s2
說明:s1 s2分別引用了兩個(gè)"Monday"String對(duì)象
3. 字符串緩沖池
原來,程序在運(yùn)行的時(shí)候會(huì)創(chuàng)建一個(gè)字符串緩沖池
當(dāng)使用 s2 = "Monday" 這樣的表達(dá)是創(chuàng)建字符串的時(shí)候,程序首先會(huì)
在這個(gè)String緩沖池中尋找相同值的對(duì)象,在第一個(gè)程序中,s1先被
放到了池中,所以在s2被創(chuàng)建的時(shí)候,程序找到了具有相同值的s1
將 s2 引用 s1 所引用的對(duì)象"Monday"
第二段程序中,使用了 new 操作符,他明白的告訴程序:
"我要一個(gè)新的!不要舊的!"于是一個(gè)新的"Monday"Sting對(duì)象被創(chuàng)
建在內(nèi)存中。他們的值相同,但是位置不同,一個(gè)在池中游泳
一個(gè)在岸邊休息。哎呀,真是資源浪費(fèi),明明是一樣的非要分開做什么呢?
4.
再次更改程序:
public class TestString {
public static void main(String[] args) {
String s1 = "Monday";
String s2 = new String("Monday");
s2 = s2.intern();
if (s1 == s2)
System.out.println("s1 == s2");
else
System.out.println("s1 != s2");
if (s1.equals(s2))
System.out.println("s1 equals s2");
else
System.out.println("s1 not equals s2");
}
}
這次加入:s2 = s2.intern();
程序輸出:
s1 == s2
s1 equals s2
原來,(java.lang.String的intern()方法
"abc".intern()方法的返回值還是字符串"abc",表面上看起來好像這個(gè)方法沒什么用處。但實(shí)際上,它做了個(gè)小動(dòng)作:
檢查字符串池里是否存在"abc"這么一個(gè)字符串,如果存在,就返回池里的字符串;如果不存在,該方法會(huì)把"abc"添加到字符串池中,然后再返回它的引用。
)
更好的辦法:
把所有的String都intern()到緩沖池去吧
最好在用到new的時(shí)候就進(jìn)行這個(gè)操作
String s2 = new String("Monday").intern();
然后就可以用==比較兩個(gè)字符串的值了
二、簡(jiǎn)單數(shù)據(jù)類型和封裝類中的equals和==
Java為每一個(gè)簡(jiǎn)單數(shù)據(jù)類型提供了一個(gè)封裝類,每個(gè)基本數(shù)據(jù)類型可以封裝成對(duì)象類型。
除int(Integer)和char(Character),其余類型首字母大寫即成封裝類類型名。double (Double), float(Float),long(Long),short(Short),byte(Byte),boolean(Boolean).
以int和Integer為例說明
Java中int和Integer區(qū)別如下:
1.int是基本的數(shù)據(jù)類型,默認(rèn)值可以為0;
2.Integer是int的封裝類,默認(rèn)值為null;
3.int和Integer都可以表示某一個(gè)數(shù)值;
4.int和Integer不能夠互用,因?yàn)樗麄儍煞N不同的數(shù)據(jù)類型;
int a1=1;
int a2=1;
Integer b1 =new Integer (1);
Integer b2 =new Integer (1);
------------------------------
a1==a2 這個(gè)是成立的,很簡(jiǎn)單,都知道
a1==b1 這個(gè)是不成立的.表達(dá)式的值為 false ,它們是不同的數(shù)據(jù)類型
b1==b2 這個(gè)也是不成立的.表達(dá)式的值為 false,雖然是相同的數(shù)據(jù)類型,但是它們是兩個(gè)對(duì)象,==比較的是2個(gè)對(duì)象的地址,它們的地址是不相等的,內(nèi)容相等都是1;
b1.equals(b2)==true 這個(gè)是成立的,表達(dá)式的值為 true. 相同數(shù)據(jù)類型,兩個(gè)對(duì)象,地址不同,內(nèi)容相同, quals比較的是2個(gè)對(duì)象的內(nèi)容,所以成立。
(a.equals(b),因?yàn)閑quals比較的是兩個(gè)對(duì)象,所以a,b都不能為基本數(shù)據(jù)類型,否則會(huì)出編譯錯(cuò)誤。)(在jdk1.5以上版本中,b可以為基本數(shù)據(jù)類型,a不可以)
同理,其它的封裝類和基本類型也是這樣的.
java中equals和==的區(qū)別
==比較的是2個(gè)對(duì)象的地址,而equals比較的是2個(gè)對(duì)象的內(nèi)容。
在jdk1.5以上的版本中,基本類型和封裝類能自動(dòng)轉(zhuǎn)化,與String類型的對(duì)象和字符串常量類似。
Integer i1 = 123;
Integer i2 = 123;
int i =123;
Integer i3= new Integer(123);
Integer i4 = new Integer(123);
System.out.println("i1== i2 = "+(i1 == i2));
System.out.println("i1.equals(i2) = "+(i1.equals(i2)));
System.out.println();
System.out.println("i3 == i4 = "+(i3 == i4));
System.out.println("i3.equals(i4) = "+(i3.equals(i4)));
System.out.println();
System.out.println("i2 == i4 = "+(i2 == i4));
System.out.println("i2.equals(i4) = "+(i2.equals(i4)));
System.out.println();
System.out.println("i == i2 = "+(i == i2));
System.out.println("i1.equals(i) = "+(i1.equals(i)));
------------------------------
i1 == i2 = true
i1.equals(i2) = true
i3 == i4 =false
i3.equals(i4) = true
i2 == i4 =false
i2.equals(i4) = true
i == i2 =true
i1.equals(i) = true
三、其他類怎么使用equals和==
API里的類大部分都重寫了equals方法,沒有重寫的一般是自己寫的類,
如果是你自己定義的一個(gè)類,比較自定義類用equals和==是一樣的,都是比較句柄地址,
因?yàn)樽远x的類是繼承于object,而object中的equals就是用==來實(shí)現(xiàn)的,你可以看源碼。
四、java里equals和hashCode之間什么關(guān)系
只是為了維護(hù) hashCode 方法的常規(guī)協(xié)定,才要求用equals比較的兩個(gè)對(duì)象的hashCode相同.
equals()和hashCode()都來自java.lang.Object.你當(dāng)然可以重寫.
比如a.equals(b).僅當(dāng)a的內(nèi)存地址相等時(shí),才返回true.當(dāng)然如String等類已經(jīng)對(duì)這個(gè)方法進(jìn)行了重寫,比較的就不再是內(nèi)存地址了.
hashCode()的值也是與內(nèi)存地址相關(guān)的.所以僅當(dāng)內(nèi)存地址相等時(shí),hashCode才相等.
同樣很多類也重寫了這個(gè)方法,還是以String為例:
public int hashCode() {
int h = hash;
if (h == 0) {
int off = offset;
char val[] = value;
int len = count;
for (int i= 0; i < len; i++) {
h = 31*h + val[off++];
}
hash = h;
}
return h;
}
就不在與內(nèi)存地址相關(guān)了.這樣做是為了保證用equals比較返回為true的兩個(gè)對(duì)象,他們的hashCode是相同的.
所以一般重寫equals的時(shí)候都會(huì)重寫hashCode().
當(dāng)然,這個(gè)相當(dāng)于一個(gè)約定,一個(gè)協(xié)議.你不這么做并不會(huì)錯(cuò).
五、hashCode
在一般的應(yīng)用中你不需要了解hashcode的用法,但當(dāng)你用到hashmap,hashset等集合類時(shí)要注意下hashcode。
你想通過一個(gè)object的key來拿hashmap的value,hashmap的工作方法是,
通過你傳入的object的hashcode在內(nèi)存中找地址,
當(dāng)找到這個(gè)地址后再通過equals方法來比較這個(gè)地址中的內(nèi)容是否和你原來放進(jìn)去的一樣,一樣就取出value。
所以這里要匹配2部分,hashcode和equals
但假如說你new一個(gè)object作為key去拿value是永遠(yuǎn)得不到結(jié)果的,
因?yàn)槊看?/span>new一個(gè)object,這個(gè)object的hashcode是永遠(yuǎn)不同的,所以我們要重寫hashcode,
你可以令你的hashcode是object中的一個(gè)恒量,這樣永遠(yuǎn)可以通過你的object的hashcode來找到key的地址,
然后你要重寫你的equals方法,使內(nèi)存中的內(nèi)容也相等。。。
public classTest2
{
??????????? public static void main(String[]args)
??????????? {
????????????????????????? String child = newString ();
????????????????????????? String child2 = newString ();
????????????????????????? System.out.println(child);
????????????????????????? System.out.println(child2);
????????????????????????? System.out.println(child == child2);//為假是因?yàn)楸容^的是二者的引用,顯然是獨(dú)立的
????????????????????????? System.out.println(child.equals (child2));//為真是因?yàn)楸容^的是對(duì)象的內(nèi)容,String()不管加沒加
????????????????????????? //數(shù)據(jù),都一樣,
?????????????????????????
??????????? }
}
class Child
{
}
上面的child和child2用equal表示是想等的,是因?yàn)?/span>string方法的重寫,如果調(diào)用的不是String類結(jié)果就是false;
比如 Child child = new Child();
???? Child child = new Child () ;
為什么呢?因?yàn)?/span>Object類是所有類的父類,比如上面的Child,在上面生成對(duì)象的過程中并沒有對(duì)String方法進(jìn)行重寫,而調(diào)用String類顯然是對(duì)String方法的重寫!
?
? 對(duì)于String類型的數(shù)據(jù)來說判斷相等性,必須要使用equals方法,因?yàn)榕袛嘧址南嗟刃?#xff0c;肯定就是判斷字符串內(nèi)容的相等性!
總之,注意區(qū)分Object類與String類equals方法的不同;
45.
總結(jié)equals:
對(duì)于Object類來說,equals方法相當(dāng)于 == ;即判斷的是兩個(gè)對(duì)象的地址是不是相同;
對(duì)于繼承了Object類的子類來說,只有重寫了父類的Equals方法的類才是判斷兩個(gè)對(duì)象的內(nèi)容!
46.
?
String Pool(字符串池)
String s =“aaa”;(采用字面值方式賦值)
1) 查找String Pool中是否存在“aaa”這個(gè)對(duì)象,如果不存在,則在StringPool中創(chuàng)建一個(gè)“aaa”對(duì)象,然后將String Pool中的這個(gè)“aaa”對(duì)象的地址返回來,賦給引用變量s,這樣s會(huì)指向String Pool中的這個(gè)“aaa”字符串對(duì)象
也就是說內(nèi)同相同相當(dāng)于指向同一個(gè)對(duì)象,用==判斷是true;
2) 如果存在,則不創(chuàng)建任何對(duì)象,直接將String Pool中的這個(gè)“aaa”對(duì)象地址返回來,賦給s引用。
String s =new String(“aaa”);
1) 首先在String Pool中查找有沒有“aaa”這個(gè)字符串對(duì)象,如果有,則不在StringPool中再去創(chuàng)建“aaa”這個(gè)對(duì)象了,直接在堆中(heap)中創(chuàng)建一個(gè)“aaa”字符串對(duì)象,然后將堆中的這個(gè)“aaa”對(duì)象的地址返回來,賦給s引用,導(dǎo)致s指向了堆中創(chuàng)建的這個(gè)“aaa”字符串對(duì)象。
2) 如果沒有,則首先在String Pool中創(chuàng)建一個(gè)“aaa“對(duì)象,然后再在堆中(heap)創(chuàng)建一個(gè)”aaa“對(duì)象,然后將堆中的這個(gè)”aaa“對(duì)象的地址返回來,賦給s引用,導(dǎo)致s指向了堆中所創(chuàng)建的這個(gè)”aaa“對(duì)象。
47.
//java中會(huì)默認(rèn)調(diào)用java.lang.*即java.lang包中的類,所以位于java.lang包下的類都無(wú)需聲明可以直接調(diào)用
public class StringBufferTest
{
????????????? public static void main(String[] args)
????????????? {
??????????????????????????? StringBuffer str = new StringBuffer();//StringBuffer位于java.lang包下;所以可以直接調(diào)用
??????????????????????????? str.append("A").append("dog").append( " is").append(" running");
??????????????????????????? str.append("A dog isrunning");//這樣做的結(jié)果是形同的;
??????????????????????????? System.out.println(str);//System方法其實(shí)也是java.lang包下的;
???????????????????????????
???????????????????????????
????????????? ?????????????
???????????????????????????
???????????????????????????
????????????? }
}
StringBuffer的作用是為當(dāng)前對(duì)象添加新的內(nèi)容,而且不會(huì)新建一個(gè)對(duì)象,我們知道String字符串是一個(gè)定值,生成了字符串就不會(huì)改變,再次生成只會(huì)新建對(duì)象,而SringBuffer中的append方法可以增加字符串中的內(nèi)容而不會(huì)新建對(duì)象;
48、
定義數(shù)組初始化數(shù)組時(shí),不能定義數(shù)組的長(zhǎng)度,因?yàn)槌跏蓟幸呀?jīng)定義好了數(shù)組的長(zhǎng)度;
49.
length 是數(shù)組特有的一個(gè)屬性,它表示返回當(dāng)前數(shù)組的長(zhǎng)度;
如 int[] a = new int[10];
則a.length = 10;
另外length是final類型的,所以不能通過length屬性更改數(shù)組的長(zhǎng)度
如a.length = 20; 是不可以的
數(shù)組長(zhǎng)度一旦確定就無(wú)法再次改變
50.
boolean類型的初始值為false;
總結(jié)