Java vs PHP – 方法参数中的引用

huangapple go评论73阅读模式
英文:

Java vs PHP - References in method parameters

问题

以下为您翻译的内容:

在Java中,当使用复杂/非基本/引用数据类型作为方法参数时,会将对象的引用传递给方法:

public class Test {

    public static void main(String[] args) {
        String[] array = {"a", "b", "c", "d", "e"};

        System.out.println(Arrays.toString(array)); // [a, b, c, d, e]
        reverseArray(array);
        System.out.println(Arrays.toString(array)); // [e, d, c, b, a]
    }

    private static void reverseArray(String[] array) {
        int startIndex = 0;
        int endIndex = array.length - 1;
        String tmp;
        while (endIndex > startIndex) {
            tmp = array[endIndex];
            array[endIndex] = array[startIndex];
            array[startIndex] = tmp;
            endIndex--;
            startIndex++;
        }
    }
}

相比之下,PHP似乎会“复制”方法参数:

<?php

Test::main();

class Test {
    
    public static function main() {
        $array = ["a", "b", "c", "d", "e"];
        
        print_r($array); // [a, b, c, d, e]
        self::reverseArray($array);
        print_r($array); // [a, b, c, d, e]
    }
    
    private static function reverseArray($array) {
        $startIndex = 0;
        $endIndex = count($array) - 1;
        $tmp;
        while ($endIndex > $startIndex) {
            $tmp = $array[$endIndex];
            $array[$endIndex] = $array[$startIndex];
            $array[$startIndex] = $tmp;
            $endIndex--;
            $startIndex++;
        }
    }
}

只有在方法参数前添加“&”时,PHP才会传递对象的引用:

<?php

Test::main();

class Test {
    
    public static function main() {
        $array = ["a", "b", "c", "d", "e"];
        
        print_r($array);  // [a, b, c, d, e]
        self::reverseArray($array);
        print_r($array);  // [e, d, c, b, a]
    }
    
    private static function reverseArray(&$array) {
        $startIndex = 0;
        $endIndex = count($array) - 1;
        $tmp;
        while ($endIndex > $startIndex) {
            $tmp = $array[$endIndex];
            $array[$endIndex] = $array[$startIndex];
            $array[$startIndex] = $tmp;
            $endIndex--;
            $startIndex++;
        }
    }
}

如果我想在Java中模拟PHP的“默认”行为(不使用“&”),我必须复制数组:

public class Test {

    public static void main(String[] args) {
        String[] array = {"a", "b", "c", "d", "e"};

        System.out.println(Arrays.toString(array)); // [a, b, c, d, e]
        reverseArray(Arrays.copyOf(array, array.length));
        System.out.println(Arrays.toString(array)); // [a, b, c, d, e]
    }

    private static void reverseArray(String[] array) {
        int startIndex = 0;
        int endIndex = array.length - 1;
        String tmp;
        while (endIndex > startIndex) {
            tmp = array[endIndex];
            array[endIndex] = array[startIndex];
            array[startIndex] = tmp;
            endIndex--;
            startIndex++;
        }
    }
}

现在我的问题是:

  1. 当我在PHP中不特别使用引用时,PHP是否会复制每个方法参数?如果是,这不是非常昂贵的(就计算时间和空间而言)吗?
  2. 为什么Java和PHP在传递方法参数时使用不同的“默认”方法?有明显的优势吗?
英文:

When using complex/non-primitive/reference data types as method parameters in Java, a reference to the object is passed to the method:

public class Test {

    public static void main(String[] args) {
        String[] array = {&quot;a&quot;, &quot;b&quot;, &quot;c&quot;, &quot;d&quot;, &quot;e&quot;};

        System.out.println(Arrays.toString(array)); // [a, b, c, d, e]
        reverseArray(array);
        System.out.println(Arrays.toString(array)); // [e, d, c, b, a]
    }

    private static void reverseArray(String[] array) {
        int startIndex = 0;
        int endIndex = array.length - 1;
        String tmp;
        while (endIndex &gt; startIndex) {
            tmp = array[endIndex];
            array[endIndex] = array[startIndex];
            array[startIndex] = tmp;
            endIndex--;
            startIndex++;
        }
    }
}

In contrast PHP seems to "copy" method parameters:

&lt;?php

Test::main();

class Test {
    
    public static function main() {
        $array = [&quot;a&quot;, &quot;b&quot;, &quot;c&quot;, &quot;d&quot;, &quot;e&quot;];
        
        print_r($array); // [a, b, c, d, e]
        self::reverseArray($array);
        print_r($array); // [a, b, c, d, e]
    }
    
    private static function reverseArray($array) {
        $startIndex = 0;
        $endIndex = count($array) - 1;
        $tmp;
        while ($endIndex &gt; $startIndex) {
            $tmp = $array[$endIndex];
            $array[$endIndex] = $array[$startIndex];
            $array[$startIndex] = $tmp;
            $endIndex--;
            $startIndex++;
        }
    }
}

Only when adding & to the method parameter, PHP will pass a reference to the object:

&lt;?php

Test::main();

class Test {
    
    public static function main() {
        $array = [&quot;a&quot;, &quot;b&quot;, &quot;c&quot;, &quot;d&quot;, &quot;e&quot;];
        
        print_r($array);  // [a, b, c, d, e]
        self::reverseArray($array);
        print_r($array);  // [e, d, c, b, a]
    }
    
    private static function reverseArray(&amp;$array) {
        $startIndex = 0;
        $endIndex = count($array) - 1;
        $tmp;
        while ($endIndex &gt; $startIndex) {
            $tmp = $array[$endIndex];
            $array[$endIndex] = $array[$startIndex];
            $array[$startIndex] = $tmp;
            $endIndex--;
            $startIndex++;
        }
    }
}

If I want to simulate PHP's "default" behaviour (without the &) in Java, I have to copy the array:

public class Test {

    public static void main(String[] args) {
        String[] array = {&quot;a&quot;, &quot;b&quot;, &quot;c&quot;, &quot;d&quot;, &quot;e&quot;};

        System.out.println(Arrays.toString(array)); // [a, b, c, d, e]
        reverseArray(Arrays.copyOf(array, array.length));
        System.out.println(Arrays.toString(array)); // [a, b, c, d, e]
    }

    private static void reverseArray(String[] array) {
        int startIndex = 0;
        int endIndex = array.length - 1;
        String tmp;
        while (endIndex &gt; startIndex) {
            tmp = array[endIndex];
            array[endIndex] = array[startIndex];
            array[startIndex] = tmp;
            endIndex--;
            startIndex++;
        }
    }
}

My questions are now:

  1. Does PHP copy every method parameter when I do not specifically use references? If yes, is this not very expensive (in regards of computation time and space)?
  2. Why are Java and PHP using different "default" methods of passing method parameters? Is there a clear advantage to one way or another?

答案1

得分: 1

1). 不,PHP不会复制每个参数。相反,它会计算引用的数量,并且只在你修改对象时才会复制对象。

<?php
  $a = array('Hello');
  debug_zval_dump($a); // array(1) refcount(3){   [0]=>  string(5) "Hello" refcount(1) }

  $b = $a; // 复制引用,而不是内容

  debug_zval_dump($a); // array(1) refcount(4){  [0]=>  string(5) "Hello" refcount(1) }
  // 注意引用计数增加;

  $b[] = 'World'; // 修改数组会导致对象复制

  debug_zval_dump($a); // array(1) refcount(3){  [0]=>  string(5) "Hello" refcount(2) }
  // 注意引用计数减少,但是 "Hello" 的引用计数增加,因为现在它在两个数组中都被使用

也就是说,如果将字符串、数组等传递给函数,并且在函数内部没有修改,那么在效果上与在 Java 中传递引用相同。

  1. 注意,在Java中,数组是对象,而在PHP中它们是单独的类型。如果在PHP中创建真正的对象,它们总是按引用传递的。
<?php
  function doit($o) { // 不带 & 直接传递
    $o->x = 'Good bye';
  }

  $a = new \stdClass();
  $a->x = 'Hello';

  doit($a); // 对象始终作为引用处理

  print $a->x; // Good bye;
英文:

1). No PHP does not copy every parameter. Instead, it counts number of references, and copies the object only when you're modifying it.

&lt;?php
  $a = array(&#39;Hello&#39;);
  debug_zval_dump($a); // array(1) refcount(3){   [0]=&gt;  string(5) &quot;Hello&quot; refcount(1) }

  $b = $a; // copy reference, not the content

  debug_zval_dump($a); // array(1) refcount(4){  [0]=&gt;  string(5) &quot;Hello&quot; refcount(1) }
  // Note refcount increased;

  $b[] = &#39;World&#39;; // modifying the array causes the object to copy

  debug_zval_dump($a); // array(1) refcount(3){  [0]=&gt;  string(5) &quot;Hello&quot; refcount(2) }
  // Note refcount decreased back, but &quot;Hello&quot; refcount increased, because now it is used in two arrays

I.e. passing strings, arrays etc. into a function, if it is not modified inside, will be the same effective as passing a reference in java.

  1. Note, arrays in Java are objects, while in php they are the separate type. If you create real objects in php, they are always passed by reference
&lt;?php
  function doit($o) { // Passing as is, without &amp;
    $o-&gt;x = &#39;Good bye&#39;;
  }

  $a = new \stdClass();
  $a-&gt;x = &#39;Hello&#39;;

  doit($a); // Objects are always work as a reference

  print $a-&gt;x; // Good bye;

答案2

得分: 1

PHP端的情况由@AterLux的回答很好地解释了。

但是,关于您的后续问题有一些见解:

为什么Java和PHP在传递方法参数的“默认”方法上使用不同的方式?某种方式明显优于另一种方式吗?

“简单”是观察者眼中的一个术语。例如,以标识符命名的方法名称的方法。在Java中,标识符正是它们所是的(因此,区分大小写,例如)。在PHP中,它们是不区分大小写的。

在某种“心态”中,PHP的选择是“更简单的”:作为脚本编写者,我不必担心复制大小写。有关“为什么不起作用”的Stack Overflow问题,问题在于他们将方法命名为“checkFoo”,然后调用“checkfoo”。

在另一种“心态”中,Java的选择是“更简单的”:在土耳其区域设置中,点状i和无点i之间存在差异。因此,在土耳其语中,"i".toUpperCase() 实际上是 "İ""I".toLowerCase() 实际上是 "ı"。PHP执行“不区分大小写”的方式是通过内部将所有内容转换为大写并将其放入大的哈希映射中(我稍微过于简化了一点),这导致了众所周知的问题,在土耳其语环境中,大多数PHP脚本根本无法正常工作,因为混淆'i'和'I'将不再转换为相同的大写字符序列。从这个角度看,PHP的解决方案比Java的复杂得多。明白了吗?这取决于观察者的眼光。

但是这里有一个趋势:如果您完全理解了底层的工作原理,那么Java几乎总是比PHP简单得多。如果您只是脑子里闪过的想法,PHP往往更“简单”。PHP试图“帮助”您,猜测您的意图并相应地采取行动。例如,让我们以另一种“试图猜测”的语言为例:JavaScript,1 是“真值” - 如果作为布尔值使用,JavaScript会查看其水晶球,并猜测您打算将其设置为true。Java不会这样做。true 就是true,仅此而已。在Java中,if (1) {} 甚至无法编译。

在这方面,Java在Java的方式上是“简单”的:基本数据类型是它们自己,所有的非基本数据类型都是引用。这些引用按值传递。没有C风格的 &* - 就是这样。

PHP以PHP的方式是“简单”的:它试图传递所有内容都是按值传递的概念,并为了使其在不过于低效的情况下工作,数组被包装传递,因此不需要实际的复制,但是如果传递数组的函数对其进行更改,那么它会进行复制。除了对对象来说太棘手了,所以那些总是按引用传递。

我会说PHP对一致性的不懈追求导致了极不一致的语言

这也意味着从一个随意的角度来看,Java往往有点奇怪,因为经常“一旦您完全理解了底层的工作原理,就会变得有意义”,而这与“在不太理解的情况下进行胡乱尝试时的意图相左”。然而,鉴于我们仍然没有发明一种可以解决愚蠢程序员问题的语言,作为一个规则,我倾向于支持Java关于“简单”的思想,而不是PHP的思想。因此,我会用以下方式回答您的问题:

Java试图使模型简单,PHP试图使体验简单,这解释了差异。然而,Java的做法明显有优势。

英文:

The PHP side is well explained by @AterLux's answer.

But, some insights on your followup question:

> Why are Java and PHP using different "default" methods of passing method parameters? Is there a clear advantage to one way or another?

'Simple' is a term that is in the eye of the beholder. For example, take the approach to identifier naming (the name of a method). In java, identifiers are precisely what they are (so, case sensitive, for example). In PHP, they are case insensitive.

In one 'mindset', PHP's choice is 'simpler': As a script writer I don't have to worry about copying casing. There are SO questions about 'why does this not work', and the problem is that they named their method 'checkFoo' and are calling 'checkfoo'.

In another 'mindset', Java's choice is 'simpler': In the turkish locale, there is a difference between the dotted i and the dotless i. So, in turkish, &quot;i&quot;.toUpperCase() is actually &quot;İ&quot;, and &quot;I&quot;.toLowerCase() is actually &quot;ı&quot;. The way PHP does the 'case insensitive' thing is by internally uppercasing everything and tossing that into a big hashmap (I'm oversimplifying a bit), leading to the rather well known issue that, in turkish locale, most PHP scripts just straight up do not work, as mixing up 'i' and 'I' will now no longer translate to the same uppercase sequence of characters. In that way, the PHP solution is much more complicated than the java one. See? It is in the eye of beholder.

But there is a trend here: If you fully grok exactly how it all works under the hood, java is, almost always, a lot simpler than PHP is. If you are merely just sort of doing the first thing that comes to mind, PHP tends to be 'simpler'. PHP tries to 'help' you, second guess, attempt to divinate your intentions and act accordingly. For example, and let's use as example that other 'tries to guess' language: javascript, 1 is 'truthy' - if used as a boolean, javascript looks in its crystal ball and will guess you intend for that to be true. Java doesn't do that. true is true, and that's it. if (1) {} in java doesn't even compile.

In that vein, java is 'simple' in the java way: primitives are themselves, and all non-primitives are a reference. These references are passed by value. There is no C-style &amp; or * - that's what you get.

PHP is 'simple' in the PHP way: It tries to peddle the notion that everything is pass-by-value, and to make that work without being incredibly inefficient, arrays are passed wrapped up, so that no actual copy is needed, but IF the function you pass the array to changes it, THEN it makes a copy. Except that's too tricky for objects, so those ARE always passed by reference.

I'd say PHP's haphazard approach to consistency leads to an incredibly inconsistent language.

It also means java tends to be a bit bizarre at a casual glance, because often 'making sense once you fully understand how it all works' is at odds with 'does what you intended as you flail away without understanding much'. However, given that we still haven't invented a language that can work around idiotic programmers, as a rule I'd advocate for the java school of thought on what 'simple' means, and not the PHP school of thought. Therefore, I would answer your question with:

Java tries to make the model simple, PHP tries to make the experience simple, which explains the difference. There is a clear advantage to the way java does it, however.

答案3

得分: 0

Java 和 PHP 在这方面的行为基本上是相同的:对象是按引用传递的,其他所有内容都是按值传递的。关键的区别在于,在 Java 中,数组是对象,但在 PHP 中不是。是的,这是一个过于简化的描述,但作为一个经验法则应该足够好。

值得一提的是,在 2004 年之前,PHP 的行为是不同的。在 PHP/4 及更早的版本中,除非使用 & 操作符,否则一切都是按值传递的。也许这可以解释为什么关于这一点(无意冒犯)的引用不幸地分散在几个地方,但通常在面向对象编程的章节中可以找到:

  • 类和对象(PHP/5+)

    PHP 处理对象的方式与引用或句柄相同,这意味着每个变量包含的是对象引用,而不是整个对象的副本。

  • 对象和引用

    PHP 引用是一个别名,允许两个不同的变量写入相同的值。从 PHP 5 开始,对象变量不再包含对象本身作为值。它只包含一个对象标识符,允许对象访问者找到实际的对象。当对象作为参数发送、返回或分配给另一个变量时,不同的变量不是别名:它们保存标识符的副本,该标识符指向同一个对象。

  • 按引用传递

    您可以将变量按引用传递给函数,以便函数可以修改变量。

    (不涉及对象的引用)

最完整的概述可能是 解释引用别名引用 之间的区别非常微妙,但在大多数情况下,我认为您实际上不需要完全掌握它。

英文:

Java and PHP roughly have the very same behaviour on this regard: objects are passed by reference, everything else is passed by value. The key difference is that arrays are objects in Java but not in PHP. Yes, this is an oversimplification, but it should be good enough as thumb rule.

For what it's worth, PHP used to behave differently before 2004. In PHP/4 and earlier everything was passed by value unless you used the &amp; operator. Perhaps that explains that references to this (no pun intended) are unfortunately scattered in several places but typically in OOP chapters:

  • Classes and Objects (PHP/5+):

    > PHP treats objects in the same way as references or handles, meaning that each variable contains an object reference rather than a copy of the entire object.

  • Objects and references:

    > A PHP reference is an alias, which allows two different variables to write to the same value. As of PHP 5, an object variable doesn't contain the object itself as value anymore. It only contains an object identifier which allows object accessors to find the actual object. When an object is sent by argument, returned or assigned to another variable, the different variables are not aliases: they hold a copy of the identifier, which points to the same object.

  • Passing by Reference:

    > You can pass a variable by reference to a function so the function can modify the variable.

    (No reference to objects)

The most complete overview is probably References Explained. The difference between alias and reference is pretty subtle but, IMHO, you don't really need to master it for most use cases.

huangapple
  • 本文由 发表于 2020年9月29日 20:33:24
  • 转载请务必保留本文链接:https://go.coder-hub.com/64119727.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定