📄 chapter12.html
字号:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<!--
This document was converted from RTF source:
By rtftohtml 4.19
See http://www.sunpack.com/RTF
Filename:Tjava14.rtf
Application Directory:c:\TOOLS\RTF2HTML\
Subject:
Author:Bruce Eckel
Operator:Bruce Eckel
Document Comments:
Version Comments:
Comments:
Keywords:
Translation Date:02/04/2000
Translation Time:23:25:29
Translation Platform:Win32
Number of Output files:27
This File:Chapter12.html
SplitDepth=1
SkipNavPanel=1
SkipLeadingToc=1
SkipTrailingToc=1
GenContents=1
GenFrames=1
GenIndex=1
-->
<HEAD lang="en"><META http-equiv="Content-Type" content="text/html">
<TITLE>12: Passing and returning objects</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF"><DIV ALIGN="CENTER">
<a href="http://www.MindView.net">
<img src="mindview-head.gif" alt="MindView Inc." BORDER = "0"></a>
<CENTER>
<FONT FACE="Verdana" size = "-1">
[ <a href="README-HTML.txt">Viewing Hints</a> ]
[ <a href="http://www.mindview.net/TIJ2/index.html">2nd Edition</a> ]
[ <a href="http://www.mindview.net/MailingList.html">Free Newsletter</a> ] <br>
[ <a href="http://www.mindview.net/Training.html">Seminars</a> ]
[ <a href="http://www.mindview.net/javaCD2.html">Seminars on CD ROM</a> ]
[ <a href="http://www.mindview.net/CPPServices/#ConsultingServices">Consulting</a> ]
</FONT>
<H2><FONT FACE="Verdana">
Thinking in Java, 1st edition</FONT></H2>
<H3><FONT FACE="Verdana">©1998 by Bruce Eckel</FONT></H3>
<FONT FACE="Verdana" size = "-1">
[ <a href="Chapter11.html">Previous Chapter</a> ]
[ <a href="SimpleContents.html">Short TOC</a> ]
[ <a href="Contents.html">Table of Contents</a> ]
[ <a href="DocIndex.html">Index</a> ]
[ <a href="Chapter13.html">Next Chapter</a> ]
</FONT>
</CENTER>
</P></DIV><A NAME="Chapter_14"></A><A NAME="_Toc375545421"></A><A NAME="_Toc407441456"></A><A NAME="_Toc408018653"></A><A NAME="Heading363"></A><FONT FACE = "Verdana"><H1 ALIGN="LEFT">
12: Passing and returning objects</H1></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Verdana" SIZE=4>By this time you should
be reasonably comfortable with the idea that when you’re
“passing” an object, you’re actually passing a
handle.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In many programming languages, if
not all of them, you can use that language’s “regular” way to
pass objects around and most of the time everything works fine. But it always
seems that there comes a point at which you must do something irregular and
suddenly things get a bit more complicated (or in the case of C++, quite
complicated). Java is no exception, and it’s important that you understand
exactly what’s happening with them as you pass them around and assign to
them. This chapter will provide that insight.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Another way to pose the question of
this chapter, if you’re coming from a programming language so equipped, is
“Does <A NAME="Index1438"></A><A NAME="Index1439"></A>Java have
pointers?” Some have claimed that pointers are hard and dangerous and
therefore bad, and since Java is all goodness and light and will lift your
earthly programming burdens, it cannot possibly contain such things. However,
it’s more accurate to say that Java has pointers; indeed, every object
identifier in Java (except for primitives) is one of these pointers, but their
use is restricted and guarded not only by the compiler but by the run-time
system. Or to put in another way, Java has pointers, but no pointer arithmetic.
These are what I’ve been calling “handles,” and you can think
of them as “safety pointers,” not unlike the safety scissors of
elementary school- they aren’t sharp so you cannot hurt yourself without
great effort, but they can sometimes be slow and
tedious.</FONT><A NAME="_Toc375545422"></A><A NAME="_Toc408018654"></A><BR></P></DIV>
<A NAME="Heading364"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
Passing handles around</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">When you pass a
<A NAME="Index1440"></A><A NAME="Index1441"></A><A NAME="Index1442"></A>handle
into a method, you’re still pointing to the same object. A simple
experiment demonstrates this: </FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: PassHandles.java</font>
<font color=#009900>// Passing handles around</font>
<font color=#0000ff>package</font> c12;
<font color=#0000ff>public</font> <font color=#0000ff>class</font> PassHandles {
<font color=#0000ff>static</font> <font color=#0000ff>void</font> f(PassHandles h) {
System.out.println(<font color=#004488>"h inside f(): "</font> + h);
}
<font color=#0000ff>public</font> <font color=#0000ff>static</font> <font color=#0000ff>void</font> main(String[] args) {
PassHandles p = <font color=#0000ff>new</font> PassHandles();
System.out.println(<font color=#004488>"p inside main(): "</font> + p);
f(p);
}
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The method <B>toString( )</B>
is automatically invoked in the print statements, and <B>PassHandles</B>
inherits directly from <B>Object</B> with no redefinition of
<B>toString( )</B>. Thus, <B>Object</B>’s version of
<B>toString( ) </B>is used, which prints out the class of the object
followed by the address where that object is located (not the handle, but the
actual object storage). The output looks like this:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>p inside main(): PassHandles@1653748
h inside f(): PassHandles@1653748</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">You can see that both <B>p</B> and
<B>h</B> refer to the same object. This is far more efficient than duplicating a
new <B>PassHandles</B> object just so that you can send an argument to a method.
But it brings up an important
issue.</FONT><A NAME="_Toc375545423"></A><A NAME="_Toc408018655"></A><BR></P></DIV>
<A NAME="Heading365"></A><FONT FACE = "Verdana"><H3 ALIGN="LEFT">
Aliasing<BR><A NAME="Index1443"></A><A NAME="Index1444"></A></H3></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">Aliasing means that more than one
handle is tied to the same object, as in the above example. The problem with
aliasing occurs when someone <I>writes</I> to that object. If the owners of the
other handles aren’t expecting that object to change, they’ll be
surprised. This can be demonstrated with a simple example:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: Alias1.java</font>
<font color=#009900>// Aliasing two handles to one object</font>
<font color=#0000ff>public</font> <font color=#0000ff>class</font> Alias1 {
<font color=#0000ff>int</font> i;
Alias1(<font color=#0000ff>int</font> ii) { i = ii; }
<font color=#0000ff>public</font> <font color=#0000ff>static</font> <font color=#0000ff>void</font> main(String[] args) {
Alias1 x = <font color=#0000ff>new</font> Alias1(7);
Alias1 y = x; <font color=#009900>// Assign the handle</font>
System.out.println(<font color=#004488>"x: "</font> + x.i);
System.out.println(<font color=#004488>"y: "</font> + y.i);
System.out.println(<font color=#004488>"Incrementing x"</font>);
x.i++;
System.out.println(<font color=#004488>"x: "</font> + x.i);
System.out.println(<font color=#004488>"y: "</font> + y.i);
}
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In the line:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>Alias1 y = x; <font color=#009900>// Assign the handle</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">a new <B>Alias1</B> handle is
created, but instead of being assigned to a fresh object created with
<B>new</B>, it’s assigned to an existing handle. So the contents of handle
<B>x</B>, which is the address of the object <B>x</B> is pointing to, is
assigned to <B>y</B>, and thus both <B>x</B> and <B>y</B> are attached to the
same object. So when <B>x</B>’s <B>i</B> is incremented in the
statement:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>x.i++;</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia"><B>y</B>’s <B>i </B>will be
affected as well. This can be seen in the output:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>x: 7
y: 7
Incrementing x
x: 8
y: 8</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">One good solution in this case is
to simply not do it: don’t consciously alias more than one handle to an
object at the same scope. Your code will be much easier to understand and debug.
However, when you’re passing a handle in as an argument – which is
the way Java is supposed to work – you automatically alias because the
local handle that’s created can modify the “outside object”
(the object that was created outside the scope of the method). Here’s an
example:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE><font color=#009900>//: Alias2.java</font>
<font color=#009900>// Method calls implicitly alias their</font>
<font color=#009900>// arguments.</font>
<font color=#0000ff>public</font> <font color=#0000ff>class</font> Alias2 {
<font color=#0000ff>int</font> i;
Alias2(<font color=#0000ff>int</font> ii) { i = ii; }
<font color=#0000ff>static</font> <font color=#0000ff>void</font> f(Alias2 handle) {
handle.i++;
}
<font color=#0000ff>public</font> <font color=#0000ff>static</font> <font color=#0000ff>void</font> main(String[] args) {
Alias2 x = <font color=#0000ff>new</font> Alias2(7);
System.out.println(<font color=#004488>"x: "</font> + x.i);
System.out.println(<font color=#004488>"Calling f(x)"</font>);
f(x);
System.out.println(<font color=#004488>"x: "</font> + x.i);
}
} <font color=#009900>///:~</font></PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The output is:</FONT><BR></P></DIV>
<BLOCKQUOTE><FONT SIZE = "+1"><PRE>x: 7
Calling f(x)
x: 8</PRE></FONT></BLOCKQUOTE>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">The method is changing its
argument, the outside object. When this kind of situation arises, you must
decide whether it makes sense, whether the user expects it, and whether
it’s going to cause problems.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">In general, you call a method in
order to produce a return value and/or a change of state in the object <I>that
the method is called for</I>. (A method is how you “send a message”
to that object.) It’s much less common to call a method in order to
manipulate its arguments; this is referred to as “calling a method for its
<A NAME="Index1445"></A><I>side effects</I>.” Thus, when you create a
method that modifies its arguments the user must be clearly instructed and
warned about the use of that method and its potential surprises. Because of the
confusion and pitfalls, it’s much better to avoid changing the
argument.</FONT><BR></P></DIV>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">If you need to modify an argument
during a method call and you don’t intend to modify the outside argument,
then you should protect that argument by making a copy inside your method.
That’s the subject of much of this
chapter.</FONT><A NAME="_Toc408018656"></A><BR></P></DIV>
<A NAME="Heading366"></A><FONT FACE = "Verdana"><H2 ALIGN="LEFT">
Making local copies</H2></FONT>
<DIV ALIGN="LEFT"><P><FONT FACE="Georgia">To review: all argument passing in
Java is performed by passing handles. That is, when you pass “an
object,” you’re really passing only a handle to an object that lives
outside the method, so if you perform any modifications with that handle, you
modify the outside object. In addition:</FONT><BR></P></DIV>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -