我爱电脑技术论坛's Archiver

star2008 发表于 2008-5-19 11:14

关于Java初学者需要了解的几个基础问题

对于这个系列里的问题,每个学Java的人都应该搞懂。当然,如果只是学Java玩玩就无所谓了。如果你认为自己已经超越初学者了,却不很懂这些问题,请将你自己重归初学者行列。
r.\/X |i 6G7]+a]4c_

QE)SQ&J-FVi-Fd'y
k_ M4H'^D d 问题一:我声明了什么!
:oa$N h4j%v6b'P3km
xL*Os%b1IJ(dQ|"w
6_b.B$V2w'@0wW
gDcw4S piF;b
V\P l6?Gp,w)q+b,h String s = "Hello world!"; !y.P*Zv)y
lzxG8Wv*D[9kxe@
Hz2D9d5F6L/n
(g^'K9z)]!N9^L
许多人都做过这样的事情,但是,我们到底声明了什么?回答通常是:一个String,内容是“Hello world!”。这样模糊的回答通常是概念不清的根源。如果要准确的回答,一半的人大概会回答错误。 fM G"f RK
7]-T#it1hw[yu'g

EZ6\}y6o)_ew[
v&t,x'~y%Da 这个语句声明的是一个指向对象的引用,名为“s”,可以指向类型为String的任何对象,目前指向"Hello world!"这个String类型的对象。这就是真正发生的事情。我们并没有声明一个String对象,我们只是声明了一个只能指向String对象的引用变量。所以,如果在刚才那句语句后面,如果再运行一句:
f[6vd V l7C J7O{iN,B
~I n3Lo

$o Hh4iv4M(_&h F%@j T8w)lI
String string = s; *f*Psy4h3Z x])qM3f
lZrgR/T&e!c
x LAW1Q5F:W1y:F

\~fMZl 我们是声明了另外一个只能指向String对象的引用,名为string,并没有第二个对象产生,string还是指向原来那个对象,也就是,和s指向同一个对象。 F(zC*~ Q,hMT$U7Z

:U$zQ T'HX[9cE2A N g(f1p\!A+x0y_8W

n/~K3[S 问题二:"=="和equals方法究竟有什么区别?
W7r J5B!HWtCc m.VGV:l2] T J
e }3L)@AY G
Cj2[!W[Yri
==操作符专门用来比较变量的值是否相等。比较好理解的一点是:
8T'm7jX+w:so1s
Wp*\0y*G o*v.We;np
W_ I8~#P@'_P*^
k gJ#k;s/L
int a=10;
X/v2kKS8OP z
+J]GP4FF3j!x-o
-K*d0w v1I6E5[q8a int b=10;
\`9yddW9u
:T8i h~UC
d$s'Azk6u c v"h'N[[-X
则a==b将是true。
A.n.CI~;`*z
FUb D}+VA
UX.a$tj"q!K7e*rdc
8}5d f {z%p6@4W} 但不好理解的地方是:
3YxV2Zx)HS?
5c4o1C.bx^m(p
x'w-j*Vc#q0F g h ^R;m0VV,G

Jn:[x'_l&f"O String a=new String("foo"); WNV,L.B)DW['`q
@gx d'N*`vq8a
vu ?ER4qn0iP
String b=new String("foo"); 1h ]({OLmkv~ j1U

.SjP^fj
vU_Z!N3^
7l5`6^.c\j,z0m5m 则a==b将返回false。 l;prI5MEGi
5yT(v9EQh
v)eA-p-HH)W` tQ

;p^ ^l4YZJ#KQ 根据前一帖说过,对象变量其实是一个引用,它们的值是指向对象所在的内存地址,而不是对象本身。a和b都使用了new操作符,意味着将在内存中产生两个内容为"foo"的字符串,既然是“两个”,它们自然位于不同的内存地址。a和b的值其实是两个不同的内存地址的值,所以使用"=="操作符,结果会是false。诚然,a和b所指的对象,它们的内容都是"foo",应该是“相等”,但是==操作符并不涉及到对象内容的比较。 _d T:m[rp
{#e$K6iRd.ng'o

$rhS&N9n!D W%[1m
,gh/?.d8f.?3\*`pc1Y 对象内容的比较,正是equals方法做的事。
'zt;T:^/z2nX
M9u%cYO&m'V 2p1xOa!X6M%W
_7U%A:x ]
看一下Object对象的equals方法是如何实现的:
.RO3\ c#h 4tX8g sjHF F5g

&cjdt-}|
+^omZE p
|3{}0f(D t boolean equals(Object o){
;Q[;Ae#k^.x7D;r;m;l
)tSoB)qx _
u6q SB3p [
.F/m2x#^c5cD1D)Y~7Q return this==o;
d|x)L{w7]m \._w]5Cl
$}1O(A\h

0O^PPI+s }
?K(~]\a/R %luK,qs2t;D#VuJym
z%M%O@,]B ?s;|m
9Kpdx'e/~ _%x-U
Object对象默认使用了==操作符。所以如果你自创的类没有覆盖equals方法,那你的类使用equals和使用==会得到同样的结果。同样也可以看出,Object的equals方法没有达到equals方法应该达到的目标:比较两个对象内容是否相等。因为答案应该由类的创建者决定,所以Object把这个任务留给了类的创建者。
QF6p\MH.o9TV FG!YWP@:zG

k5K4p`N \ gnff^hE
看一下一个极端的类: 7k5{ V(eV|

*UkF&b0H%x4\z p(t;oB \bbY

0]V)\KK,}_
*L QoLH$[g n;V Class Monster{ k!hM9fuDa:x%D

9{3`k hlV S
)Y q uT4LT private String content;
be f&G+e)W,b'M_] T ^r ZW1vQ
Za {'T ?@$K+sf
... yN.U6F Pp

WI5Z R5J1A
C3Pc,d,~%j)?c boolean equals(Object another){ return true;} Q+Uq&SEc+F
J2qs+K@^
bX2K9]D n0Ec*o^l

T,N d9LW+^:c } 8d C%q V8S/O K5c"N+RS
6@]3? E{y;fx:N

Q*K2Ol7k6^;pP'y
~[J+\B{J 我覆盖了equals方法。这个实现会导致无论Monster实例内容如何,它们之间的比较永远返回true。
q7~l&zZ!^z$E |R SY wu0~/C
y6a kk5x8IE

!P;~C S'I1n\N 所以当你是用equals方法判断对象的内容是否相等,请不要想当然。因为可能你认为相等,而这个类的作者不这样认为,而类的equals方法的实现是由他掌握的。如果你需要使用equals方法,或者使用任何基于散列码的集合(HashSet,HashMap,HashTable),请察看一下java doc以确认这个类的equals逻辑是如何实现的。 ~2q%f],B|"P8r y
$T3U2?(z0C&A#Z-Ga

{3B:V7qq@s {
+~0X/V,D T3H9q&m 问题三:String到底变了没有?
?UF:FS^(vZ%g ~_ c#^#L;DA r

_2YR y[:u7F bPc~r?&S1w6X
没有。因为String被设计成不可变(immutable)类,所以它的所有对象都是不可变对象。请看下列代码:
!b.E2L#VJj F O0Qcb_e
{"ms0t G9d._#I

vF}a$`z_$]4vw&n c l*gWTE'q$?.P+j
String s = "Hello";
a$T*R l3c;P uCM t2jw9T5t#eCa

{\"S1Tsoay s = s + " world!";
8emX|.r ;K _ CB-K!y v%w1_

Z5UlwX8j
3bG]RL s所指向的对象是否改变了呢?从本系列第一篇的结论很容易导出这个结论。我们来看看发生了什么事情。在这段代码中,s原先指向一个String对象,内容是"Hello",然后我们对s进行了+操作,那么s所指向的那个对象是否发生了改变呢?答案是没有。这时,s不指向原来那个对象了,而指向了另一个String对象,内容为"Hello world!",原来那个对象还存在于内存之中,只是s这个引用变量不再指向它了。 xUK;?-KE|.zm
$RIF2` @

ko2J Zr)Nq{
p/bY x3T1lUD 通过上面的说明,我们很容易导出另一个结论,如果经常对字符串进行各种各样的修改,或者说,不可预见的修改,那么使用String来代表字符串的话会引起很大的内存开销。因为String对象建立之后不能再改变,所以对于每一个不同的字符串,都需要一个String对象来表示。这时,应该考虑使用StringBuffer类,它允许修改,而不是每个不同的字符串都要生成一个新的对象。并且,这两种类的对象转换十分容易。
nA5S h J al8n(~
4iiZ2Cz(k [ ` h y.Y4`oQVA(B

P0U9A+Zpgg s$` 同时,我们还可以知道,如果要使用内容相同的字符串,不必每次都new一个String。例如我们要在构造器中对一个名叫s的String引用变量进行初始化,把它设置为初始值,应当这样做:
*K/e"y;o A%u t-L&H mKXOz4M

CNr!]'C%N@ "W7KjRI^olu
H A@;T3kH\#?(I,a2QQc
public class Demo {
z!E:c!Yy0ok~
:S+@ C.QJ{k"ss
Q9Z,Wn XU private String s; 7ont Sh S8_ [ C

)\*FM2n#t&g 5z5`&MC3^&O(mV n
... 6i%^yA]9X/J

rgy4b/OR|:AD)K 3ye br9in
public Demo {
Ut+q?#SMT+A u(]9E1Ltz/R

$S4A'IhXk"] s = "Initial Value"; D7[%^3y4zbcM

]"~6qp+D"IM Ao
$P-R&n~{_s }
}5hk1]4j C@ B#}ZeFl S
z:VJb&D8n
... Cim(X'jX4Q5Ka%iyG

8~d%z*Wj
)u6`a |#B } dc"wj*a

)O6y'??G!q2?c
v T$N0\|!@3O JO
{m2Cm@$Ay 而非
i0s:k F$~ m0T
AD*?e/F(`+}(o
U"?|`R;pv1zjh
3wP h t{(|K e ]c!b c[FVu$QC0n
s = new String("Initial Value"); 8pj S'JP)D4H

5jMyS` 2P d} cR9F
"n Tl{L.? Q(zu
后者每次都会调用构造器,生成新对象,性能低下且内存开销大,并且没有意义,因为String对象不可改变,所以对于内容相同的字符串,只要一个String对象来表示就可以了。也就说,多次调用上面的构造器创建多个对象,他们的String类型属性s都指向同一个对象。
%[!@h:jju q'S%PcTC
1al'VH!w*m ty
上面的结论还基于这样一个事实:对于字符串常量,如果内容相同,Java认为它们代表同一个String对象。而用关键字new调用构造器,总是会创建一个新的对象,无论内容是否相同。
s`DU4`6b9g pZ*^F6BW^*h

-a A(s7~UC k:D7^
K fJ[Z4L'H 至于为什么要把String类设计成不可变类,是它的用途决定的。其实不只String,很多Java标准类库中的类都是不可变的。在开发一个系统的时候,我们有时候也需要设计不可变类,来传递一组相关的值,这也是面向对象思想的体现。不可变类有一些优点,比如因为它的对象是只读的,所以多线程并发访问也不会有任何问题。当然也有一些缺点,比如每个不同的状态都要一个对象来代表,可能会造成性能上的问题。所以Java标准类库还提供了一个可变版本,即StringBuffer。

80441301 发表于 2008-5-19 15:44

看来我还是初学者啊

念苍生 发表于 2008-5-19 16:59

:)11) :)11) :)11) 支持!!!!!!!!!!!!!!!!

fknu 发表于 2008-8-15 11:54

不错 不错 学习了

页: [1]

Powered by Discuz! Archiver 6.1.0  © 2001-2007 Comsenz Inc.