【问题标题】:passing a String array as argument传递一个字符串数组作为参数
【发布时间】:2012-07-17 02:43:15
【问题描述】:

可以通过以下方式声明和初始化字符串数组:

String[] str = {"A", "B"};

但是对于接受字符串数组作为参数的方法,为什么不能在那里使用相同的呢?

例如:如果在下面的代码中,我将对 show() 的调用从 show(str); 替换为 show({"A" "B"});,它会显示编译器错误。为什么?

public class StringArray {

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        String[] str = {"A", "B"};
        show(str);
    }
    static void show(String[] s) {
        System.out.println(s[0] + s[1]);
    }
}

显示的编译器错误是:

StringArray.java:9: illegal start of expression
                show({"A", "B"});
                     ^
StringArray.java:9: ';' expected
                show({"A", "B"});
                      ^
StringArray.java:9: illegal start of expression
                show({"A", "B"});
                         ^
StringArray.java:9: ';' expected
                show({"A", "B"});
                          ^
StringArray.java:9: illegal start of type
                show({"A", "B"});
                               ^
StringArray.java:11: class, interface, or enum expected
        static void show(String[] s) {
               ^
StringArray.java:13: class, interface, or enum expected
        }
        ^
7 errors

也允许使用show(new String[] {"A", "B"});new String[]{"A", "B"}{"A", "B"} 在将它们作为方法参数传递时有何不同? 提前谢谢!

【问题讨论】:

  • 看来 {"A", "B"} 本身不能转换为字符串数组类型
  • @Mob 很确定他是在寻求不同,而不是为什么要这样设计。
  • 不要刻薄,但是:鉴于有一种相当简单的方式来表达你想要的东西,正如你自己注意到的那样,如果问这样一个问题会导致任何有成效的事情,我会感到惊讶。为什么 Java 需要在 IF 表达式周围加上括号?为什么它说“切换”而不是“选择”?为什么它使用分号来结束语句而不是其他字符?等等等等。在某些时候,语言发明者只需要做出决定。也许在某些情况下,知道做出选择的原因会让您深入了解该语言。但我敢肯定,很多都是随意的。
  • @Jay:我建议您也阅读答案以了解我到底想问什么,因为仅凭这个问题对您来说显然是不够的。

标签: java arrays string


【解决方案1】:

语法{"A", "B"}(前面没有new String[])只能用作数组初始化表达式。在所有其他上下文中(包括方法调用),您需要使用new 运算符。

请参阅Java Tutorial on Arrays 了解更多信息。

【讨论】:

  • 我想通了,我想问的是两者之间的区别只是句法上的区别还是它们也有一些语义上的区别?
  • @SurenderThakran - 严格来说,这是一个语法快捷方式。血淋淋的细节可以在Java Language Specification, §10.6阅读。
【解决方案2】:

String[] str = {"A", "B"};String[] str = new String[]{"A", "B"}; 的缩小版,除非您明确提及,否则编译器不知道普通的{"A", "B"} 作为字符串数组。

【讨论】:

  • String[] str = new String[]{"A", "B"};本身就是String[] str = new String[2]; str[0] = "A"; str[1] = "B";的缩小版
【解决方案3】:

简答

它与内存管理有关。


长答案

背景

another question about passing arrays as arguments(标记为重复)询问相同的行为,但对更深层次的“为什么”感兴趣。

Other answers已经正确解释了两者的区别

一)

new String[]{"A", "B"} 

B)

{"A", "B"} 

当将它们作为方法参数传递时。

A) 在堆上构造一个数组的实例,表达式导致对该实例的引用。

B) 是定义数组的语法,但该语法仅在局部数组变量的初始化期间有效。该语法不是一个可以自行计算的表达式,它期望有一个已经实例化但未初始化的数组,然后将此块用作初始化器。

归咎于 Java

所有这一切都已经提到过,所以我想回答的是语言设计决策背后的原因。

Java 的基本原则之一是它管理内存以真正最小化当每个程序员必须了解所有细节和动态内存管理的所有边缘情况时引入的巨大问题。因此,当他们设计语言的类型系统时,他们希望每个变量都是引用类型,但为了提高效率,他们允许一些基本类型可以通过值作为参数传递给方法,结果是一个简单的克隆变量的内容,这些被称为原始类型、int、char 等。所有其他类型都需要对堆的引用,这样可以提高参数传递的效率,这些被称为引用类型。顾名思义,引用类型实际上是对通常在堆上分配的内存的引用,但也可以是堆栈上的内存。

数组初始化器

好的,Java Primer 就到这里了,但为什么这很重要?这是因为当您尝试将数组作为参数传递但使用文字语法时,该语言需要引用类型,但数组初始值设定项构造不会解析为引用。

现在,下一个问题可能是编译器是否有可能采用初始化语法并将其转换为正确分配的数组实例。那将是一个公平的问题。答案可以追溯到使用初始化子句的语法:

String[] str = {"A", "B"}

如果你只有等号右边的表达式,你怎么知道应该构造什么类型的数组?简单的答案是你没有。如果我们采用相同的初始化程序并像这样使用它

Circle[] cir = {"A", "B"}

为什么会这样变得更清楚了。首先,您可能会注意到似乎缺少“新”关键字。它并没有丢失,而是被编译器隐式包含在内。这是因为初始化语法是以下代码的缩写形式

Circle[2] cir = new Circle[]();
cir[0] = new Circle("A");
cir[1] = new Circle("B");

编译器使用数组变量的构造函数根据提供的列表实例化数组的每个元素,所以当你尝试传递时

{"A", "B"}

编译器不知道应该构造什么类型的数组,也不知道如何构造数组的各个元素,因此需要使用显式分配内存的形式。

语言学生

引用的类型和数组中每个元素的类型之间的这种分离也使得数组类型可以成为元素的父类型,比如

Circle[2] shapes = new Circle[]();
shapes[0] = new Circle();  // parent
shapes[1] = new Ellipse(); // child of Circle 

Java 对父类的使用,Object 用于所有类允许具有完全不相关对象的数组

Object[2] myThings = new Object[]();
myThings[0] = new Car();  
myThings[1] = new Guitar(); // unrelated object

【讨论】:

  • 请不要像 here 那样将内容编辑到帖子中。为此使用 cmets。
  • 我通常会这样做。那可能是禁用 cmets 的帖子。我正在考虑发表一篇关于它的 Meta 帖子。
  • 这个构造:Circle[] cir = {"A", "B"} 将不起作用。您将收到一个编译器错误,抱怨“无法将字符串转换为圆形”。 Circle 有一个接受 String 参数的构造函数并不重要。
【解决方案4】:

当您传递 {"A", "B"} 时,没有对象引用它,因为该数组尚未在内存中创建,需要传递该引用。 但是我们可以直接[没有引用]将像“A”这样的字符串传递给接受String的方法,因为String是Java的特殊对象,为它维护了String pool。而数组则不是这种情况,它就像简单的 java 对象。

【讨论】:

    【解决方案5】:

    由于String[] str = {"A", "B"}; 定义它是一个字符串数组,{"A", "B"} 没有说这是一个字符串数组,因为对象数组也可以这样定义,因此编译器不知道你是什么类型的数组指的是:)

    【讨论】:

      【解决方案6】:

      如果你这样做,它会起作用

      public class StringArray 
      {
      
          /**
           * @param args
           */
          public static void main(String[] args) 
          {
              show(new String[]{"A", "B"});
          }
          static void show(String[] s)
          {
              System.out.println(s[0] + s[1]);
          }
      }
      

      因为您实际上是在创建一个新的数组“对象”。另一方面,{"A", "B"} 没有任何意义。 {"A", "B"} 不是数组对象,所以它不起作用。第一种方法有效,因为您实际上指定传递给函数的是数组对象。

      【讨论】:

      • @Surender 已经知道它会以这种方式工作,这就是为什么他已经在他的问题中提到了
      • 他不是在问它是如何工作的。他非常清楚它将如何工作。他在问为什么 {"A", "B"} 不起作用。具体回答
      • {"A", "B"} 不是数组对象,所以它不起作用。第一种方法有效,因为他实际上指定他正在传递一个数组对象,这是预期的
      【解决方案7】:

      显示({"A" "B"});此表达式未传递数组对象。传递数组对象你必须首先声明并初始化数组对象,然后将数组引用传递给方法。

      String[] str = {"A", "B"}; 显示(str);

      String[] str = {"A", "B"}; 显示(新字符串[]{"A", "B"});

      【讨论】:

        【解决方案8】:

        在上面的例子中,show() 方法的签名引导编译器在调用时期望一个 String 引用,因此我们在调用 show() 方法时只能传递一个 String 类型的引用变量.另一方面 {"A","B"} 只是一个表达式而不是引用,这就是为什么它会给出像“Illegal Start of Expression”这样的编译错误。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2017-06-14
          • 2019-01-07
          • 2020-05-01
          • 1970-01-01
          • 1970-01-01
          • 2012-05-25
          • 1970-01-01
          相关资源
          最近更新 更多