我们可以在不使用Java返回的情况下为参数传递的对象赋值吗?

时间:2014-09-26 17:23:37

标签: java recovery

这可能是任何人提出的最愚蠢的问题,但仅仅是为了好奇,我们可以为作为参数传递的对象赋值。例如

public class ForDemo3 {
    int i = 0;
    ArrayList cp = new ArrayList();

    public ForDemo3() {
        int j = 30;
        cp.add(j);
        cp.add(i);
        i = 5;
        j = 12;
        System.out.println("i = " + i + " & j= " + j);
        assignDvalues(i, j);
        System.out
                .println("After Restoring the values i = " + i + " & j= " + j);
    }

    public void assignDvalues(Object... obj) {
        for (int n = 0; n <= obj.length - 1; n++) {
            obj[n] = cp.get(n);
            System.out.println("" + obj[n]);
        }
    }

    public static void main(String[] args) {
        new ForDemo3();
    }
}

我正在尝试在java中开发基于检查点的恢复。 cp是一个检查点对象,可以存储值。

上面的输出是

  i = 5 & j= 12
  30
  0
  After Restoring the values i = 5 & j= 12 

编辑 道歉我不是stackoverflow的好用户。我正在研究并行计算的框架。并且将有一个函数savecheckpoint(List l),它将用于将列表或数组列表作为检查点存储到DB,另一个函数public object restoreCheckPoint(int i)将恢复列表。到目前为止,我能够保存检查点,然后将其恢复。但是,如何在不影响用户代码的情况下分配对象或变量的值?如上例所示,我想使用assignDvalues()恢复值。有没有办法让它成为可能?

6 个答案:

答案 0 :(得分:2)

在这种情况下,没有任何可检测的事情发生:对象数组是由编译器隐式创建的,因此您无法访问它。此外,原始int被自动装箱到不可变的Integer中,因此通过对象本身的可变性,方法中所做的更改不会对调用者可见。

但是,如果您明确传递数组,则分配将保留:

Object[] args = new Object[] {i, j};
assignDvalues(args);
System.out.println(args[0]+" "+args[1]);

Demo.

答案 1 :(得分:2)

这将是一个真正的黑客,所以明智地使用它。以下方法使用反射并具有两个局限性。无法处理原始类型(因为自动装箱)并且无法处理初始化为常量的字符串(即不支持String s = "some string",但String s = new String("some string")工作正常)。你也应该使用new Integer(X)而不是int x来混淆原始类型(与字符串类似的故事)

在下面的示例中,我将int i替换为Integer i,将j替换为public class Demo2 { Integer i = 0; ArrayList cp = new ArrayList(); public Demo2() { Integer j = 30; String s = "def"; cp.add(j); cp.add(i); cp.add(s); i = new Integer(5); // you need to use "i = new Integer(5)" instead of "i = 5" because you risk with messing the primitive int 5 j = new Integer(12); // here also you need to use new Integer(12) s = new String("some string"); // NOTE: don't even try this with s ="some string" because this will lead to unexpected behaviour //s = "some string" - if you pass string initialized this way you will mess up the string "some string" in jvm string pool System.out.println("i = " + i + " & j= " + j + " & s= " + s); assignDvalues(i, j, s); System.out.println("After Restoring the values i = " + i + " & j= " + j + " & s= " + s); } public void assignDvalues(Object... obj) { try { for (int n = 0; n <= (obj.length - 1); n++) { if (obj[n] instanceof Integer) { Integer curInt = (Integer) obj[n]; Field field = curInt.getClass().getDeclaredField("value"); // Integer stores the real value in private field "value" field.setAccessible(true); field.set(curInt, cp.get(n)); System.out.println("" + obj[n]); } else if (obj[n] instanceof String) { String curS = (String) obj[n]; Field field = curS.getClass().getDeclaredField("value"); // String stores the text as char sequence in private field "value" field.setAccessible(true); Field modifiersField = Field.class.getDeclaredField("modifiers"); modifiersField.setAccessible(true); modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); // need to reset final flag to override value of String field.set(curS, ((String) cp.get(n)).toCharArray()); System.out.println("" + obj[n]); } // else if (obj[n] insteanceOf SomeOtherType) { // you should provide implementation specific for each type/class you want to support with assignDvalues method } } catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } } public static void main(String[] args) throws Exception { new Demo2(); } } ,因为我说原语不受支持。

用户唯一需要的是使用“Integer”而不是“int”,“Long”而不是“long”等等,并且可能会很痛苦,避免将字符串定义为常量。

{{1}}

答案 2 :(得分:1)

Java支持按值调用。这意味着在调用函数时,对象引用将作为值传递。 但是,如果你有一个vector或ArrayList,那么你可以传递vector或ArrayList对象引用作为函数的参数,比如f(ArrayList a),并且在函数内,你可以将项目插入刚刚传递的vector或arraylist。

答案 3 :(得分:0)

就调用者而言,您无法将传递给方法的对象更改为其他对象。虽然您可以更改传递的对象的状态

在您的示例中,您尝试为参数数组分配新值将不会影响调用代码。

答案 4 :(得分:0)

不是这种情况。

Java中的参数具有基本类型或是引用。它们通过值传递,这意味着该方法具有其原始类型的每个参数的自己的本地副本,或每个引用的本地副本。如果您的方法分配给参数,则该方法会修改其自己的本地副本,但该分配不会影响调用者将看到的任何内容。但是,如果该方法使用引用来修改另一个对象中的字段,则调用者看到引用对象中的更改;该方法创建引用的本地副本,但不是对象本身的本地副本。

数组是一个对象,所以如果你有一个数组参数,则传递一个引用;如果该方法使用该引用来修改数组的元素,则调用者将能够看到对数组元素的更改。

vararg参数是一种特殊情况。它作为数组传递,通过引用传递。所以,如果你写

public void assignSomething(Object... obj) {
    obj[0] = "abcde";
}

如果你这样称呼它:

Object[] objects = new Object[1];
assignSomething(objects);
System.out.println(objects[0]);

它实际上会打印"abcde"。但是如果你传递单个参数而不是数组:

Object obj1;
Object obj2;
assignSomething(obj1, obj2);
System.out.println(obj1);

该程序创建一个临时数组,其值是对您传递的对象的引用。然后,该方法将使用新数组替换临时数组(obj[0])中的第一个引用,但不会使用数组元素的先前值,即obj1的引用。因此,即使分配数组元素通常是调用者可以看到的更改,在这种情况下,被分配的元素是“隐藏”数组的一部分,没有人能看到。

所以,不,obj1不会受到影响 - 在您的示例中,ij同样不会受到影响。

答案 5 :(得分:0)

您只能通过将对象打包到另一个对象来实现此目的。假设您创建了通用包装类:

class ObjectHolder<T> {
  private T obj;

  private ObjectHolder(T obj) {
    this.obj = obj;
  }

  public static ObjectHolder of(Object o) {
    return new ObjectHolder(o);
  }

  public T getValue() {
    return obj;
  }

  public void setValue(T value) {
    obj = value;

  }

  @Override
  public String toString() {
    return obj.toString();
  }
}

现在您可以使用该包装器重写您的程序:

public class ForDemo3 {
  ObjectHolder<Integer> i = ObjectHolder.of(0);
  ArrayList cp = new ArrayList();

  public ForDemo3() {
    ObjectHolder<Integer> j = ObjectHolder.of(30);
    cp.add(j.getValue());
    cp.add(i.getValue());
    i.setValue(5);
    j.setValue(12);
    System.out.println("i = " + i + " & j= " + j);
    assignDvalues(i, j);
    System.out.println("After Restoring the values i = " + i + " & j= " + j);
  }

  public void assignDvalues(ObjectHolder... obj) {
    for (int n = 0; n <= (obj.length - 1); n++) {
      obj[n].setValue(cp.get(n));
      System.out.println("" + obj[n].getValue());
    }
  }

  public static void main(String[] args) {
    new ForDemo3();
  }
}