繁体   English   中英

Java对象创建语法的效率?

[英]Java object creation syntax efficiency?

这听起来可能很基础。 但是我是Java的新手。 到目前为止,我已经投入了最初的几个小时的学习时间,但新对象声明语法的冗余使我不断困惑:

TypeName a = new TypeName();

特别是,

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

为什么世界上有人要两次键入关键字TypeName (例如StringCharacter等)? 我了解以下方面的捷径:

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

但是这些是例外而不是规则。 那么任何人都可以启发我吗? 谢谢。

因为有时候您想做类似的事情:

// 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();

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

使用继承时可能要使用子类的新实例,使用接口时可能要使用接口实现。

或者有时您可能想明确地强制某些内容最初为null,然后再填充。

使用此语法,您可以轻松创建X类型的对象并将其分配给Y类型的变量:

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

为什么在世界上有人想要两次键入关键字TypeName(例如,字符串,字符等)?

因为您正在做两件事:

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

两种类型不一定相同,例如

Map m = new HashMap();

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

m.siez();

编译器会立即提醒您程序存在问题-它之所以能够这样做,仅是因为它知道声明的类型Map没有方法siez()

一些现代的静态类型化语言(例如C#和Scala)使用类型推断为您提供“两全其美”,您可以在其中省略类型声明,并且编译器会假定该声明与您为其分配的对象的类型相同。 但是,此类语言始终允许显式类型声明,因为类型推断并非总是可能或不合需要的(例如,在上面的示例中,该变量应该使用接口而不是具体的类)。

这根本不是多余的。 利用变量有两个步骤:

  1. 声明 :此步骤告诉VM变量的静态足迹是多少。 例如: Object a; 仅在类Object声明的覆盖区可见,而Integer b; 将在Integer类和所有继承的父类中声明所有足迹,直到Object可见。 这是针对静态部分的。

  2. 实例化 :此步骤告诉VM变量的动态足迹是多少。 例如: List<String> c = new LinkedList<String>(); ,然后c.put("foo"); 即使List::put()可见,也将使用LinkedListput()方法的实现。 有时,您将需要这种声明/实例化,但是将需要重写以访问静态足迹不可见的非常特定的方法。 例如,让我们考虑一个声明为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对象。

为何需要这样做,这里有许多很好的答案。 您是对的,因为它通常看起来很多余。 Java经常(不公平地)受到批评,有点...冗长。 有一些快捷方式。 例如,对于字符串String s="Abc" (实际上不是快捷方式,它有所不同,并且更好,因为您没有显式创建新对象)。 Java 7中针对泛型的声明中的重复项也将有所减少。

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

当你做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>两次(这在Maps中尤其<SomeType> )。

当您到达类的泛型(扩展名)时:

class a extends b

您将看到可以执行以下操作:

b=new a();

好吧,变量需要具有类型。 在创建对象的实例时,您需要确定对象的类型。 当然,这些不必相同。 例如,您可以将String设置为Object变量。 当然,您可以使用类似的方法使事情变得容易一些:

var s = new TypeName();

这就是在C#中完成的方式。 但是我想在Java中他们没有看到这样做的必要。
我同意Java在现代标准上相当冗长,但是它也很容易阅读,并且没有太多语法上的糖令您感到困惑。

有一些强类型语言支持“类型推断”(例如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尤为重要。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM