From C# to Java (1) - 类型、引用与相等关系

  这组文字第一篇文章 From C# to Java (0) - 目标 被博客园的管理员移出了首页。果然没有干货是行不通的呐。需要说明一下这组文字的目标用户,是需要用较快速度适应 Java 编程习惯的原 C# 开发人员,避免直接阅读 Java 相关书籍带来的效率不高的问题。所以把这组文字挂在 C# 阵营的博客园上也不会太招黑吧,哈哈。

  第一篇干货文的主要参考资料是 Why Java Sucks and C# Rocks(2):基础类型与面向对象,关于赵姐夫的这组文章《Why Java Sucks and C# Rocks》的完整目录,在我的上一篇文章有列出。同时,成文过程中,笔者也参考了知乎等社区的相关问题。

  由于笔者也是初学 Java,文字中难免出现错误。如有错误恳请指出,这样可以减少其他读者接收到错误信息的危害。

0 总结

  1. Java 定义了一组基础数据类型,它们与 Object 没有继承关系;除此之外的所有类型都是 class,继承自 Object。
  2. 在 Java 中,所有的 class 类型均为引用类型。class 类型的变量的本质是一个指针。
  3. Java 不支持运算符重载。
  4. Java 的“==”运算符严格执行引用相等。
  5. 所有比较值的相等性的场合,一律使用“.equals()”可以避免不必要的麻烦。

 

1 Java 并非“严格”的“面向对象”编程语言

  与 C# 的规则“一切数据类型均继承自 System.Object”不同,Java 定义了一组基础数据类型,它们与 Object 数据类型没有继承关系。它们包括 int, double , boolean 等等。

  如果需要遵循“严格”的“面向对象”,可以使用它们的封装类(Wrapper Class)。下面是几个例子。

int → Integer
double → Double
boolean → Boolean

我们更为习惯 C# 的一切皆 Object,在日常使用的时候,我们可能就会倾向于更多地使用这些封装类的数据类型。显然,这样的使用也是有装箱和拆箱带来的时空代价和潜在问题的。事实上,我们应优先使用基本类型,而非这些封装类。

 

2 对象即为类,引用皆指针

  C# 中,主要的数据类型被分为引用类型与值类型。而 Java 不仅不包含 C# 中含有的 struct、union 等类型标识符,而且所有的非基础数据类型(也就是 class)均为引用类型。因此可以说,虽然 Java 不支持指针类型,但所有 class 类型的变量的本质都是一个指针(这一点区别于 C# 中的值类型)。比如,在 Java 下,我们可以写出这样的代码:

Integer value0 = new Integer(3);

而在 C# 中,至少 Int32 类型并不支持这样的构造。

  对于 Integer 类型,除了这种初始化的办法之外,我们还可以这样实现:

Integer value1 = 3;
Integer value2 = Integer.valueOf(3);

  但是,稍后的章节 3 会提到,value1 和 value2 指向的内存相同,而它们和 value0 指向的内存不同。

 

3 Java 的“==”运算符严格执行引用相等

  首先给出结论:Java 的“==”运算符严格执行引用相等,所以所有比较值的相等性的场合,一律使用“.equals()”可以避免不必要的麻烦。

  在给出解释前,首先说一句题外话,Java 不支持运算符重载,所以我们也无法通过运算符重载的办法实现对“==”的重写。

  我们通过以下一段程序的输出结果来说明“==”会带来的麻烦。

public static void main(String[] args) {
String str0
= new String("Flaris");
String str1
= new String("Flaris");
System.out.println(Boolean.valueOf(str0
== str1).toString() + " " + Boolean.valueOf(str0.equals(str1)).toString());
Integer value0
= new Integer(3);
Integer value1
= 3;
System.out.println(Boolean.valueOf(value0
== value1).toString() + " " + Boolean.valueOf(value0.equals(value1)).toString()); Integer value2 = Integer.valueOf(3); System.out.println(Boolean.valueOf(value1 == value2).toString() + " " + Boolean.valueOf(value1.equals(value2)).toString()); Integer value3 = 3000; Integer value4 = Integer.valueOf(3000); System.out.println(Boolean.valueOf(value3 == value4).toString() + " " + Boolean.valueOf(value3.equals(value4)).toString()); }
Output:
false true
false true
true true
false true

  显而易见,如果以比较值作为目标,所有使用“.equals()”进行判断的情况,都获得了正确的输出。然而使用“==”的结果就五花八门了。

  第一行,因为 Java 的“==”严格执行引用相等,所以,虽然 str0 和 str1 包含相同的内容,但由于分属两个实例,所以引用是不同的。因此输出了 false。

  第二行与第一行的原因类似。事实上,

Integer value1 = 3;

这句话执行了一个自动装箱操作,将 int 类型的“3”装箱后再赋引用给 value1。它和直接从 Integer 的静态方法中获得的 value2 的引用是相同的。所以第三行输出了 true。

  不幸的是,仅仅值的大小发生变化,value3 和 value4 的关系就没有 value1 和 value2 那样亲密了,对于它们来说,装箱的引用与直接从 Integer 的静态方法获得的引用并不是同一个引用。具体原因与装箱的具体机制有关,这里就不再展开。

  总之,在 Java 中,对于绝大多数比较“值”的情况,“.equals()”都可以满足条件,然而“==”需视情况而定。在 C# 中,上述的大多数问题都是不存在的。

  如果我们用 C++ 来表示 Java 中“==”和“.equals()”的区别,则类似于下面的一段代码。对于可能出现的混淆,我们需要注意。

int main()
{
    std::string *a = new std::string("Flaris");
    std::string *b = new std::string("Flaris");
    std::cout << a << std::endl;
    std::cout << b << std::endl; std::cout
<< (*a == *b) << std::endl; }
Output:
00381CC8
003818D8
// 地址不同, 1 // 对值比较,返回了 true。

 

Flaris 原创
转载请注明出处
http://www.cnblogs.com/flaris
http://www.zhihu.com/people/flaris

posted @ 2015-04-29 13:33  Flaris  阅读(798)  评论(0编辑  收藏  举报