【问题标题】:When returning a private member, is the returned value a direct reference to the member or a separate copy?返回私有成员时,返回的值是对该成员的直接引用还是单独的副本?
【发布时间】:2019-02-13 23:39:38
【问题描述】:

为了澄清这一点,请考虑以下代码:

public class Foo {
    private Object thing;

    public Foo() { ... }

    public Object getThing() {
        return this.thing;
    }
}

public class Bar {
    public Bar() { ... }

    public Object makeNewThing() { ... }

    public void changeThing() {
        Foo foo = new Foo();
        Object thing = foo.getThing();
        thing = makeNewThing();
    }
}

Bar mutates 修改它从foo.getThing() 接收的Object 的值时,foo 中的原始私有成员mutated 是否被修改?

编辑:一些词

【问题讨论】:

  • Reference 是 JVM 用来识别对象的数字(整数)。引用变量将保存像 123 这样的返回值,因为这恰好是 JVM 的对象标识符。每个变量都有自己的保留空间,以便将值复制到该空间。如果你有两个引用变量持有相同的引用,它们都可以修改同一个对象。
  • 对象的变异是通过使用.点运算符并调用mutator方法(如果可用且可访问)或直接更改属性(如果可用且可访问)来完成的。 ) = 更改特定引用所指的对象。
  • 最后一个语句不应该包括makeNewThing而不是makeThing吗?

标签: java access-modifiers


【解决方案1】:

foo 中的原始私有成员是否发生了变异?

不,因为您正在为changeThing 中的变量thing 分配新的东西,而不是改变对象。如果您确实对对象进行了变异,则变异也将反映在私有成员中。

注意getThing返回Foo.thing引用的原始对象,不是它的副本。

由于在您的代码中,thing 的类型为 Object,因此这种行为有点难以看到。我将使用一个可变类Baz 来演示这一点:

public class Baz {
    public int x = 0;
}

public class Foo {
    private Baz thing;

    public Foo() { thing = new Baz(); }

    public Baz getThing() {
        return this.thing;
    }
}

public class Bar {
    public Bar() { ... }

    public Object makeNewThing() { ... }

    public void changeThing() {
        Foo foo = new Foo();
        Baz thing = foo.getThing();
        thing.x = 10;
        System.out.println(foo.thing); // this will print 10, not 0
    }
}

因此,为避免对Foo.thing 进行任何修改,您可以:

  • 手动创建thing 的副本并将其返回(Java 不会自动为您进行复制!)或者,
  • 使thing的类型不可变

【讨论】:

  • 很好的答案,谢谢。我应该说“修改”而不是“变异”;原始帖子已被编辑。
【解决方案2】:

getThing() 返回的东西是对私有变量thing引用

如果需要修改,请使用foo.getThing().doSomeMethod()

您当前在changeThing 中所做的事情:获取对foos 事物的引用并将其分配给方法变量thing。然后你分配给变量thing 一个完全不同的东西。这不会更改实例内部的任何内容foo

【讨论】:

    【解决方案3】:

    是的,如果您实际上改变了对象,则 foo 中的原始成员会发生改变。目前,您只是重新分配您持有的变量,它不会对对象本身执行任何操作。

    考虑一下

    public class Foo {
        private FooInner thing = new FooInner();
    
        public FooInner getThing() {
            return this.thing;
        }
    
        public String toString() {
            return Integer.toString(thing.i);
        }
    
        class FooInner{
            int i = 0;
        }
    
    
        public static void main(String[] args) {
    
            Foo foo = new Foo();
    
            System.out.println(foo); //Prints 0 
    
            FooInner thing = foo.getThing();
            thing.i = 10;
    
            System.out.println(foo); //Prints 10
    
        }
    }
    

    【讨论】:

      【解决方案4】:

      首先你应该了解引用类型的概念...

      考虑以下代码: Foo f = new Foo();

      这其实是两个命令

      1, new foo() => 它在称为堆的内存中创建一个 foo,它是你真正的变量或内容

      2, Foo f => 上面的 var(content) 地址放在 f 中。所以 f 是对内容的引用。

      在你的代码中,当你返回this.thing,其实就是返回一个对象的引用或地址。

      当你写的时候:

      foo.getThing() 返回 Foo 类中的东西,它是一个对象的引用或指针,然后

      对象事物 = foo.getThing();

      将 Foo 的东西的地址放在另一个名为 thing 的引用中。 这意味着它们都指向相同的内容......

      【讨论】:

        【解决方案5】:

        见 cmets:

        public class Foo {
            private Object thing;
        
            //constructor to create an Foo-Object-Instance
            public Foo() { ... }
        
            //method to return a characteristic of a Foo-Object, 
            //whereas this characteristic is an object, not a primitive data type
            public Object getThing() {
                return this.thing;
            }
        }
        
        public class Bar {
        
            //constructor to create an Bar-Object-Instance
            public Bar() { ... }
        
            //returns an object and does whatever it's supposed to do within the { ... }
            //I wonder how's object connected to bar?
            public Object makeNewThing() { ... }
        
            public void changeThing() {
                //is Foo a subtype of Bar?
        
                //creating a Foo-Class-Instance (object)
                //that contains a variable Object (which is not the data as it would be with
                //primitive data types, but a reference to the data)
                Foo foo = new Foo();
        
                //and with "foo.getThing()" you get the reference ("address") 
                //of the Object foo's characteristic "thing" (which is an object 
                //as well, so you copy the address to the data) and add it to the 
                //new "Object thing", which
                //you have declared on the left side of the =
        
                //so you have two object variables pointing/with reference to the same data
                Object thing = foo.getThing();
                //when you use makeAnewThing - whatever stands in there - to change the 
                //object ("thing") variables data, then the data of the object will 
                //be changed if so stated in the { } and
                //hence basically the thing-object (or "variable") 
                //within the foo-object as well, 
                //because both variable names are pointing to the same data
        
                thing = makeThing();
            }
        }
        

        可能会造成混淆,因为您将两者都命名为“事物”,即。 e.在 foo-object 之外声明的对象 thing 和在 foo-class 中声明的对象变量“thing”。

        重要的是对象和原始数据类型之间的区别,而对象变量或名称如“String objectname”或“Object foo”包含对数据的引用:

        //declare an object named abs, which does not point to data
        Object abc;
        //declare an object named def and (right side of =) initiate it with data or variable or whatever the constructor says what's to be done
        Object def = new Object();
        //copy the reference ("address" of def-Object) to abc
        abc = def;
        //now we have one data thingi with two references to it, i. e. abc and def
        

        如果您现在向 abc 添加更改,那么当在方法中声明时,更改将被添加到 abc 指向的数据中。如果你随后使用 def.getData() 之类的东西,将返回更改后的数据,因为 abc 和 def 都指向相同的数据。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-06-29
          • 1970-01-01
          • 1970-01-01
          • 2011-01-18
          • 1970-01-01
          • 2018-05-25
          • 1970-01-01
          相关资源
          最近更新 更多