计算机专业论文译文.doc_第1页
计算机专业论文译文.doc_第2页
计算机专业论文译文.doc_第3页
计算机专业论文译文.doc_第4页
计算机专业论文译文.doc_第5页
免费预览已结束,剩余48页可下载查看

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

专业译文原文出处:/docs/think_java/TIJ319.htmPassing & Returning ObjectsBy now you should be reasonably comfortable with the idea that when youre “passing” an object, youre actually passing a reference.In many programming languages you can use that languages “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 its important that you understand exactly whats happening as you pass objects around and manipulate them. This appendix will provide that insight. Another way to pose the question of this appendix, if youre coming from a programming language so equipped, is “Does 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, its 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 it another way, Java has pointers, but no pointer arithmetic. These are what Ive been calling “references,” and you can think of them as “safety pointers,” not unlike the safety scissors of elementary schoolthey arent sharp, so you cannot hurt yourself without great effort, but they can sometimes be slow and tedious. Passing references aroundWhen you pass a reference into a method, youre still pointing to the same object. A simple experiment demonstrates this:/: appendixa:PassReferences.java/ Passing references around.import com.bruceeckel.simpletest.*;public class PassReferences private static Test monitor = new Test(); public static void f(PassReferences h) System.out.println(h inside f(): + h); public static void main(String args) PassReferences p = new PassReferences(); System.out.println(p inside main(): + p); f(p); monitor.expect(new String % p inside main(): PassReferencesa-z0-9+, % h inside f(): PassReferencesa-z0-9+ ); /:The method toString() is automatically invoked in the print statements, and PassReferences inherits directly from Object with no redefinition of toString(). Thus, Objects version of toString() is used, which prints out the class of the object followed by the address where that object is located (not the reference, but the actual object storage). The output looks like this: p inside main(): PassReferencesad3ba4h inside f(): PassReferencesad3ba4You can see that both p and h refer to the same object. This is far more efficient than duplicating a new PassReferences object just so that you can send an argument to a method. But it brings up an important issue. AliasingAliasing means that more than one reference is tied to the same object, as in the preceding example. The problem with aliasing occurs when someone writes to that object. If the owners of the other references arent expecting that object to change, theyll be surprised. This can be demonstrated with a simple example: /: appendixa:Alias1.java/ Aliasing two references to one object.import com.bruceeckel.simpletest.*;public class Alias1 private static Test monitor = new Test(); private int i; public Alias1(int ii) i = ii; public static void main(String args) Alias1 x = new Alias1(7); Alias1 y = x; / Assign the reference System.out.println(x: + x.i); System.out.println(y: + y.i); System.out.println(Incrementing x); x.i+; System.out.println(x: + x.i); System.out.println(y: + y.i); monitor.expect(new String x: 7, y: 7, Incrementing x, x: 8, y: 8 ); /In the line:Alias1 y = x; / Assign the referencea new Alias1 reference is created, but instead of being assigned to a fresh object created with new, its assigned to an existing reference. So the contents of reference x, which is the address of the object x is pointing to, is assigned to y, and thus both x and y are attached to the same object. So when xs i is incremented in the statement: x.i+;ys i will be affected as well. This can be seen in the output:x: 7y: 7Incrementing xx: 8y: 8One good solution in this case is simply not to do it; dont consciously alias more than one reference to an object at the same scope. Your code will be much easier to understand and debug. However, when youre passing a reference in as an argumentwhich is the way Java is supposed to workyou automatically alias, because the local reference thats created can modify the “outside object” (the object that was created outside the scope of the method). Heres an example: /: appendixa:Alias2.java/ Method calls implicitly alias their arguments.import com.bruceeckel.simpletest.*;public class Alias2 private static Test monitor = new Test(); private int i; public Alias2(int ii) i = ii; public static void f(Alias2 reference) reference.i+; public static void main(String args) Alias2 x = new Alias2(7); System.out.println(x: + x.i); System.out.println(Calling f(x); f(x); System.out.println(x: + x.i); monitor.expect(new String x: 7, Calling f(x), x: 8 ); /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 its going to cause problems. In general, you call a method in order to produce a return value and/or a change of state in the object that the method is called for. Its much less common to call a method in order to manipulate its arguments; this is referred to as “calling a method for its side effects.” 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, its much better to avoid changing the argument. If you need to modify an argument during a method call and you dont intend to modify the outside argument, then you should protect that argument by making a copy inside your method. Thats the subject of much of this appendix. Making local copiesTo review: All argument passing in Java is performed by passing references. That is, when you pass “an object,” youre really passing only a reference to an object that lives outside the method, so if you perform any modifications with that reference, you modify the outside object. In addition: l Aliasing happens automatically during argument passing. l There are no local objects, only local references. l References have scopes, objects do not. l Object lifetime is never an issue in Java. l There is no language support (e.g., “const”) to prevent objects from being modified and stop the negative effects of aliasing. You cant simply use the final keyword in the argument list; that simply prevents you from rebinding the reference to a different object.If youre only reading information from an object and not modifying it, passing a reference is the most efficient form of argument passing. This is nice; the default way of doing things is also the most efficient. However, sometimes its necessary to be able to treat the object as if it were “local” so that changes you make affect only a local copy and do not modify the outside object. Many programming languages support the ability to automatically make a local copy of the outside object, inside the method.116 Java does not, but it allows you to produce this effect. Pass by valueThis brings up the terminology issue, which always seems good for an argument. The term is “pass by value,” and the meaning depends on how you perceive the operation of the program. The general meaning is that you get a local copy of whatever youre passing, but the real question is how you think about what youre passing. When it comes to the meaning of “pass by value,” there are two fairly distinct camps: 1. Java passes everything by value. When youre passing primitives into a method, you get a distinct copy of the primitive. When youre passing a reference into a method, you get a copy of the reference. Ergo, everything is pass by value. Of course, the assumption is that youre always thinking (and caring) that references are being passed, but it seems like the Java design has gone a long way toward allowing you to ignore (most of the time) that youre working with a reference. That is, it seems to allow you to think of the reference as “the object,” since it implicitly dereferences it whenever you make a method call. 2. Java passes primitives by value (no argument there), but objects are passed by reference. This is the world view that the reference is an alias for the object, so you dont think about passing references, but instead say “Im passing the object.” Since you dont get a local copy of the object when you pass it into a method, objects are clearly not passed by value. There appears to be some support for this view within Sun, since at one time, one of the “reserved but not implemented” keywords was byvalue (This will probably never be implemented). Having given both camps a good airing, and after saying “It depends on how you think of a reference,” I will attempt to sidestep the issue. In the end, it isnt that importantwhat is important is that you understand that passing a reference allows the callers object to be changed unexpectedly. Cloning objectsThe most likely reason for making a local copy of an object is if youre going to modify that object and you dont want to modify the callers object. If you decide that you want to make a local copy, one approach is to use the clone() method to perform the operation. This is a method thats defined as protected in the base class Object, and that you must override as public in any derived classes that you want to clone. For example, the standard library class ArrayList overrides clone(), so we can call clone() for ArrayList: /: appendixa:Cloning.java/ The clone() operation works for only a few/ items in the standard Java library.import com.bruceeckel.simpletest.*;import java.util.*;class Int private int i; public Int(int ii) i = ii; public void increment() i+; public String toString() return Integer.toString(i); public class Cloning private static Test monitor = new Test(); public static void main(String args) ArrayList v = new ArrayList(); for(int i = 0; i 10; i+ ) v.add(new Int(i); System.out.println(v: + v); ArrayList v2 = (ArrayList)v.clone(); / Increment all v2s elements: for(Iterator e = v2.iterator(); e.hasNext(); ) (Int)e.next().increment(); / See if it changed vs elements: System.out.println(v: + v); monitor.expect(new String v: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, v: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ); /The clone() method produces an Object, which must then be recast to the proper type. This example shows how ArrayLists clone() method does not automatically try to clone each of the objects that the ArrayList containsthe old ArrayList and the cloned ArrayList are aliased to the same objects. This is often called a shallow copy, since its copying only the “surface” portion of an object. The actual object consists of this “surface,” plus all the objects that the references are pointing to, plus all the objects those objects are pointing to, etc. This is often referred to as the “web of objects.” Copying the entire mess is called a deep copy. You can see the effect of the shallow copy in the output, where the actions performed on v2 affect v:v: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9v: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10Not trying to clone() the objects contained in the ArrayList is probably a fair assumption, because theres no guarantee that those objects are cloneable. Adding cloneability to a classEven though the clone method is defined in the base-of-all-classes Object, cloning is not automatically available in every class. This would seem to be counterintuitive to the idea that base-class methods are always available in derived classes. Cloning in Java does indeed go against this idea; if you want it to exist for a class, you must specifically add code to make cloning work. Using a trick with protectedTo prevent default cloneability in every class you create, the clone() method is protected in the base class Object. Not only does this mean that its not available by default to the client programmer who is simply using the class (not subclassing it), but it also means that you cannot call clone() via a reference to the base class. (Although that might seem to be useful in some situations, such as to polymorphically clone a bunch of Objects.) It is, in effect, a way to give you, at compile time, the information that your object is not cloneableand oddly enough, most classes in the standard Java library are not cloneable. Thus, if you say:Integer x = new Integer(1);x = x.clone();You will get, at compile time, an error message that says clone() is not accessible (since Integer doesnt override it and it defaults to the protected version). If, however, youre in a method of a class derived from Object (as all classes are), then you have permission to call Object.clone() because its protected and youre an inheritor. The base class clone() has useful functionality; it performs the actual bitwise duplication of the derived-class object, thus acting as the common cloning operation. However, you then need to make your clone operation public for it to be accessible. So, two key issues when you clone are: Call super.clone() Make your clone publicYoull probably want to override clone() in any further derived classes; otherwise, your (now public) clone() will be used, and that might not do the right thing (although, since Object.clone() makes a copy of the actual object, it might). The protected trick works only once: the first time you inherit from a class that has no cloneability and you want to make a class thats cloneable. In any classes inherited from your class, the clone() method is available since its not possible in Java to reduce the access of a method during derivation. That is, once a class is cloneable, everything derived from it is cloneable unless you use provided mechanisms (described later) to “turn off” cloning. Implementing the Cloneable interfaceTheres one more thing you need to do to complete the cloneability of an object: implement the Cloneable interface. This interface is a bit strange, because its empty!interface Cloneable The reason for implementing this empty interface is obviously not because you are going to upcast to Cloneable and call one of its methods. The use of interface in this way is called a tagging interface because it acts as a kind of flag, wired into the type of the class. There are two reasons for the existence of the Cloneable interface. First, you might have an upcast reference to a base type and not know whether its possible to clone that object. In this case, you can use the instanceof keyword (described in Chapter 10) to find out whether the reference is connected to an object that can be cloned: if(myReference instanceof Cloneable) / .The second reason is that mixed into this design for cloneability was the thought that maybe you didnt want all types of objects to be cloneable. So Object.clone() verifies that a class implements the Cloneable interface. If not, it throws a CloneNotSupportedException exception. So in general, youre forced to implement Cloneable as part of support for cloning. Successful cloningOnce you understand the details of implementing the clone() method, youre able to create classes that can be easily duplicated to provide a local copy:/: appendixa:LocalCopy.java/ Creating local copies with clone().import com.bruceeckel.simpletest.*;import java.util.*;class MyObject implements Cloneable private int n; public MyObject(int n) this.n = n; public Object clone() Object o = null; try o = super.clone(); catch(CloneNotSupportedException e) System.err.println(MyObject cant clone); return o; public int getValue() return n; public void setValue(int n) this.n = n; public void increment() n+; public String toString() return Integer.toString(n); public class LocalCopy private static Test monitor = new Test(); public static MyObject g(MyObject v) / Passing a reference, modifies outside object: v.increment(); return v; public static MyObject f(MyObject v) v = (MyObject)v.clone(); / Local copy v.increment(); return v; public static void main(String args) MyObject a = new MyObject(11); MyObject b = g(a); / Reference equivalence, not object equivalence: System.out.println(a = b: + (a = b) + na = + a + nb = + b); MyObject c = new MyObject(47); MyObject d = f(c); System.out.println(c = d: + (c = d) + nc = + c + nd = + d); monitor.expect(new String a = b: true, a = 12, b = 12, c = d: false, c = 47, d = 48 ); /First of all, for clone() to be accessible, you must make it public. Second, for the initial part of your clone() operation, you should cal

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论