实例化对象时,为什么要指定两次类?
OddEven number = new OddEven();
为什么你不能说number = new OddEven();
?当我声明一个字符串时,我只说String
一次:
String str = "abc";
实际上,我的问题不是“为什么你这样做” - 显然,你这样做是因为你必须 - 但是,为什么创作者选择让Java语法像这样工作呢?
我的想法是:
答案 0 :(得分:43)
因为你可以这样做:
Superclass x = new Subclass();
引用的类型可以是要声明的实际对象的超类,因此您需要同时指定它们。例如,你可以这样做:
List<String> stringList = new ArrayList<String>();
您的程序与实现List的对象交互,而您不关心实现。
答案 1 :(得分:5)
看似冗余的类型名称的原因是您正在执行两个单独的操作,每个操作都要求您指定一个类型。
在左侧,您要声明具有特定类型的变量(存储位置)。在右侧,您将创建具有特定类型的新对象。中间的'='会引用您创建的新对象,并将其放置在您创建的存储位置中。
每侧的类型不必相同。例如,这是合法代码:
Object number = new OddEven();
关键字String仅在第二个示例中显示一次的原因是String类型隐含在右侧,因为“xxx”是String常量。它只是简写:
String string = new String("xxx");
答案 2 :(得分:4)
当你写:
OddEven number = new OddEven();
您实际上做了两件事:1)您声明了number
类型的变量OddEven
和 2)您为类{{1}的新实例分配了引用}}。但是因为变量可以包含类型的任何子类型,所以编写OddEven
对于编译器来说不足以知道number = new OddEven();
变量的实际类型。所以,你必须声明它。 Java是一种强类型语言,这意味着每个变量和每个表达式都具有在编译时已知的类型。您可能希望阅读Java语言规范(JLS)的全部Chapter 4. Types, Values, and Variables以了解更多信息。
现在,当你写:
number
事情有点不同。用双引号括起来的字符String str = "abc";
在这里被称为字符串文字,它已经是对"abc"
实例的引用,并且总是引用类{{{}}的同一个实例。 1}}。引用JLS的3.10.5 String Literals部分:
每个字符串文字都是一个参考 (§4.3)到一个实例 上课的§4.3.1,§12.5)
String
(§4.3.3)。String
对象具有常量值。串 文字 - 或者更一般地说,字符串 这是常数的值 表达式(§15.28) - 是 “实习”以便分享独特 实例,使用该方法String
。
因此,String
肯定不会转换为String.intern
,这绝对不等同于我在一些评论和答案中所读到的。运行以下类:
String str = "abc";
生成以下输出:
String str = new String("abc");
并证明public class Test {
public static void main(String[] args) {
String one = "abc";
String two = "abc";
String abc = new String("abc");
System.out.println(one == two);
System.out.println(one == abc);
}
}
和true
false
是对同一个实例的引用,但one
是对另一个实例的引用(即额外的不必要对象具有已创建)。
实际上,使用two
是构造新字符串的低效方法,并且只应用于强制子字符串复制到新的基础字符数组,如
abc
答案 3 :(得分:0)
第一个OddEven
是类型,第二个是实例。它不一定是OddEven
,它可以是OddEven
的任何子类。这并不意味着你输入了两次。任何IDE都有代码模板,您只需键入一次名称。
答案 4 :(得分:0)
第一个声明是你想在你拥有的作用域中使用的变量类型,在这种情况下它是OddEven,第二个声明是用于实例化(在这种情况下初始化)引用的构造函数。 p>
你可以说INumberInstance = new OddEven(),其中INumberInstance是OddEven可以被强制转换的类(例如,像OddEven的超级)。
答案 5 :(得分:0)
在java中创建新对象的方法是:
Class_name reference_variable = new Class_name(param_if_any);
但字符串类是一个例外。
您可以创建一个新的字符串对象
String s = "abc";
或
String s = new String("abc");
答案 6 :(得分:0)
除了Jim所说的,Java是一种静态类型的语言。这意味着每个varable都有一个在编译时就知道的类型。
例如:
public class A
{
public void foo() { }
}
public class B
{
public void foo() { }
}
public class Main
{
public static void main(final String[] argv)
{
A a = new A();
B b = new B();
a.foo();
b.foo();
}
}
编译器查看“a.foo()”和“b.foo()”并检查a是否为A类,A有一个名为“foo”的方法,该方法不带参数。编译器对“b.foo()”执行相同的操作。
如果你能写这样的主要内容:
public class Main
{
public static void main(final String[] argv)
{
a = new A(); // in Java you would really do Object a = new A();
b = new B(); // in Java you would really do Object b = new B();
a.foo();
b.foo();
}
}
然后编译器无法进行验证,而且必须在运行时进行。
答案 7 :(得分:0)
将'OddEven number'视为定义Object和'new OddEven();'填充对象。
我不打算详细介绍超类和子类,因为其他人已经解释过了。
答案 8 :(得分:0)
Java的设计者没有拥有来使语法冗余。 Scala是另一种使用JVM的语言,它也是静态类型的。 Scala使用类型推理来减少冗长。例如,这是一个名为MyPair的MyPair类型变量的声明。 MyPair将两个变量相互关联。它是一个泛型类,因此您可以指定第一个变量的类型为Int,第二个变量的类型为String:
var x: MyPair[Int, String] = new MyPair[Int, String](1, "scala")
Scala类型推理允许您删除冗余类型声明:
var x = new MyPair[Int, String](1, "scala")
Scala甚至根据构造函数参数推断类型,因此您可以这样写:
var x = new MyPair(1, "scala")
答案 9 :(得分:0)
当您说String name = "foo"
时,内部Java编译器会创建一个值为“foo”的String对象,并将其引用分配给name
变量。因此,这里不是创建一个新的String对象,而是分配对另一个String对象的引用。
顺便说一下,编译器总是为我们创建“foo”。它首先在字符串池中查找,如果它不存在,则只创建“foo”。否则,Compiler从String池返回一个引用。这是Java编译器在内部执行的一些优化。
String name = "foo"
与OddEvenNumber oddEven = anotherOddEvenObject;
答案 10 :(得分:0)
考虑以下示例,
我们可以按如下方式指定对象类型,
List<String> abc;
在method1()中,如果你想使用最适合它的数组列表,那么我们可以像下面一样实例化,
abc = new ArrayList<String>();
在method2()中,如果你想使用最适合它的链接数组列表,那么我们可以像下面那样实例化,
abc = new LinkedList<String>();
因此,我们的想法是,我们可以指定“SuperClass”的类型,并在适当的操作中动态地使用适合不同要求的子类(如“LinkedList”和“ArrayList”)进行实例化。
答案 11 :(得分:0)
数组示例:
声明和初始化——当你知道数组的长度时:
int[] numberArray = new int[10];
声明然后初始化 - 当您还不知道数组的长度并且可能从方法或用户输入中获取时
int[] numberArray;
int length = 10; // let’s say we got this from the user
numberArray = new int[length];
仅初始化 - 当您不需要重用时:
return new int[10];