2010-08-31 45 views
7

这听起来很基本。但我是Java的全新。随着最初的几个小时的学习,我已经把在目前为止,我一直通过冗余在一个新的对象声明的语法困惑:Java对象创建语法的效率?

TypeName a = new TypeName(); 

尤其

String s = new String("abc"); 
Character c = new Character("A"); 

为什么在世界上有人想输入关键字TypeName(例如String,Character等)两次?我知道有短手:

String s = "abc"; 
char c = "A"; 

但这些是例外,而不是规则。那么请有人给我启发一下吗?谢谢。

+2

大概问语言设计师,而不是我们,大概问题是“语言设计师为什么这样做”。 – 2010-08-31 18:16:12

+0

我在这里问,因为即使我不知道答案,我确信它会归结为编程方便(因此最好由程序员回答),而不是没有直接影响的设计哲学,对吧? – Zhang18 2010-08-31 18:22:27

回答

4

这并不是多余的。有两个步骤,利用一个变量:

  1. 声明:这一步告诉VM会是怎样的变量的静态足迹。例如:Object a;将仅显示Object类中声明的足迹,而Integer b;将具有在Integer类中声明的所有足迹以及所有继承的父类,最多可见Object。这是针对静态部分的。

  2. instanciation:这一步告诉VM什么是变量的动态足迹。例如:List<String> c = new LinkedList<String>();,那么c.put("foo");将使用LinkedListput()方法的实现,即使可见的是List::put()。有时候,你会需要这种声明/实例,但需要重写才能访问静态脚注中不可见的非常具体的方法。例如,让我们考虑宣布为public void method1(Object obj)的方法和你知道obj实例实际上是一个Integer,因此你会具体铸造对象到它使用动态足迹:int value = ((Integer) obj).intValue();

现在,至于String a = "A";部分。为简单起见,Java已经简化了“简单”类的写法。更具体地说,由于Java 1。5,你可以这样做:

Integer n1 = 1; 
Integer n2 = new Integer(1); // same thing 
int n3 = n2; 

而且所有的作品。但有什么区别?考虑这段代码:

String a = new String("A"); 
String b = new String("A"); 
String c = "A"; 
String d = "A"; 

System.out.println("a:" + a.hashCode() + " = b:" + b.hashCode() + " == " + (a == b)); 
System.out.println("b:" + b.hashCode() + " = c:" + c.hashCode() + " == " + (b == c)); 
System.out.println("c:" + c.hashCode() + " = d:" + d.hashCode() + " == " + (c == d)); 

将输出

a:65 = b:65 == false 
b:65 = c:65 == false 
c:65 = d:65 == true 

为什么?由于JVM试图尽可能重用内存,并且由于ab正在创建String的新实例,因此它们不共享相同的内存空间。但是,cd正在使用常量字符串值(这是一个编译器优化),因此指向的对象完全相同String

5

有了这个语法,你可以轻松地创建X类型的对象,并将其分配给Y类型的变量:

List<String> myList = new ArrayList<String>(); 
19

因为有时你想要做的事,如:

// Explicitly force my object to be null 
String s = null; 

// Cast a child class to its parent 
MyParentClass mpc = new IneritedClassFromParent(); 

// Store and access a concrete implementation using its interface 
ISomeInterface isi = new ConcreteInterfaceImplementation(); 

换句话说,仅仅因为你声明一个类型来存储并不总是意味着你希望它使用该类的一个新实例进行初始化。

在使用接口时,您可能希望在使用继承或接口实现时使用子类的新实例。

或者有时您可能想要明确强制某些东西最初为空并稍后填充它。

+0

但是,这并不能解释为什么你需要在OP给出的例子中提到两次类型。类型推断已经存在了将近100年,比编程语言的时间长,并且当然比Java长,所以我想为什么Java缺少自80年代以来基本上标准化的功能仍然是开放的问题。 – 2010-08-31 19:16:26

+0

我同意w/Jorg W Mittag。特别是,我对你在这里展示的例子有一个跟进问题。为什么不能有人拥有'InheritedClassFromParent mpc = new InheritedClassFromParent()'?在父类中声明变量有什么好处? – Zhang18 2010-09-02 14:30:11

+1

@ Zhang18 - 这是出于同样的原因,你会使用一个接口。如果父母定义了你需要的成员,最好使用更通用的类型。然后,您可以自由地更改基础类型而不会破坏相关的代码。例如,您可以将'MyParentClass mpc = new InheritedClassFromParent();'更改为'MyParentClass mpc = SomeOtherChild();'而不会破坏使用mpc的代码,而使用具体类型...您可能会导致重大更改。 – 2010-09-02 14:34:10

0

这是因为没有隐式方式来分配复杂对象的值。

当你做int a = 3;double b = 2.5;,你可以隐式地在右侧声明类型。

在OOP中,你必须使用一个构造函数,这就是为什么你必须做new TypeName()。这也使您能够传入参数来设置对象。

另一个原因是当你使用接口。所以,你可以这样做:

MyInterface blah = new InterfaceImplementation(); 
MyInterface bar = new AnotherInterfaceImplementation(); 

,甚至:

ParentClass foo = new DerivedClass(); 

这是因为界面时,你通常不希望设定变量类型为接口的实现,但接口本身。否则,将无法指定要使用哪个实现。

另一个有用的是仿制药:

List<SomeType> myList = new ArrayList<SomeType>(); 

的Java 7将简化这

List<SomeType> myList = new ArrayList<>(); 

,这样你就不必键入<SomeType>两次(这特别是在地图变得痛苦)。

0

当你将得到的类仿制药(扩展):

class a extends b 

你会看到,你可以做这样的事情:

b=new a(); 
0

嘛,一个变量需要有型。当你创建一个对象的实例时,你需要知道它应该是哪个类型。当然,这些不一定是相同的。例如,您可以将字符串设置为对象变量。当然你可以有这样的事情使事情变得更容易:

var s = new TypeName(); 

这就是它在C#中所做的。但是我认为在Java中他们并没有看到这种需求。
我同意Java在现代标准中相当冗长,但它也很容易阅读,并且没有太多的语法糖来混淆你的意思。

1

这里有很多很好的答案,为什么它是必要的。你是正确的,因为它往往看起来多余。 java经常被(并非不公平地)批评,有点呃...冗长。有几个捷径。例如对于字符串String s="Abc"(实际上不是快捷键,它有点不同,更好,因为你没有明确地创建一个新对象)。泛型的java 7中的声明重复也会有所减少。

0

有强类型的语言支持“类型推断”(例如Scala)。 Java根本就不是其中之一(虽然在Java 7中会有一些对泛型参数的类型推断)。在这些语言中,虽然变量类型没有声明,但编译器可以毫不含糊地推断它,并仍然检测类型错误。例如(非Java):

val str = "This is not a number!"; 
val x = str.intValue(); // Compiler error, because str is implicitly a String. 

在Java中,会有很多情况下,你会在右边一个特定的具体类型分配给左侧更一般的类型。例如:

Set students = new TreeSet(); 

这是一个很好的风格,因为你的代码不会依赖于特定的实现。如果您需要切换实现(例如,您需要基于散列的Set更快的查找),则只有初始化程序的右侧会发生更改。

由于这个原因,使用正确的抽象而非具体类型来声明公共API是特别重要的。

5

世界为什么会有人想要 类型的关键字,类型名(如字符串, 字符等)的两倍?

因为你正在做两件事情:

  • 声明某种类型
  • 的变量创建某种类型

的对象的两种类型不一定是相同,例如

Map m = new HashMap(); 

您可能已经习惯了像PHP那样的“动态类型”语言,其中变量没有类型。 Java的静态类型声明带来的好处是编译器会捕获很多编程错误(即使在现代IDE中,当您键入内容时)。例如,如果你犯了一个简单的错误:

m.siez(); 

编译器会立即发出警告的事实,有什么问题你的程序 - 因为它知道该声明类型Map做它能做的只有没有方法siez()

一些现代静态类型语言,如C#和Scala使用type inference为您提供了“两全其美”,您可以省略类型声明,编译器会假定它与您指定的对象类型相同到它。但是,这样的语言总是允许显式类型声明,因为类型推断并不总是可行的或可以despibles的(例如上面的例子中变量应该使用接口而不是具体类)。