`

java有值类型吗

 
阅读更多

转自:http://www.yinwang.org/blog-cn/2016/06/08/java-value-type

 

Java 有值类型吗?

有人看了我之前的文章『Swift 语言的设计错误』,问我:“你说 Java 只有引用类型(reference type),但是根据 Java 的官方文档,Java 也有值类型(value type)和引用类型的区别的。比如 int,boolean 等原始类型就是值类型。” 现在我来解释一下这个问题。

Java 有值类型,原始类型 int,boolean 等是值类型,其实是长久以来的一种误解,它混淆了实现和语义的区别。不要以为 Java 的官方文档那样写就是权威定论,就可以说“王垠不懂” :) 当你认为王垠不懂一个初级问题的时候,都需要三思,因为他可能是大智若愚…… 看了我下面的论述,也许你会发现自己应该怀疑的是,Java 的设计者到底有没有搞明白这个问题 :P

胡扯结束,现在来说正事。Java,Scheme 等语言的原始类型,比如 char,int,boolean,double 等,在“实现”上确实是通过值(而不是引用,或者叫指针)直接传递的,然而这完全是一种为了效率的优化(叫做 inlining)。这种优化对于程序员应该是不可见的。Java 继承了 Scheme/Lisp 的衣钵,它们在“语义”上其实是没有值类型的。

这不是天方夜谭,为了理解这一点,你可以做一个很有意思的思维实验。现在你把 Java 里面所有的原始类型都“想象”成引用类型,也就是说,所有的 int, boolean 等原始类型的变量都不包含实际的数据,而是引用(或者叫指针),指向堆上分配的数据。然后你会发现这样“改造后”的 Java,仍然符合现有 Java 代码里能看到的一切现象。也就是说,原始类型被作为值类型还是引用类型,对于程序员完全没有区别。

举个简单的例子,如果我们把 int 的实现变成完全的引用,然后来看这段代码:

int x = 1;    // x指向内存地址A,内容是整数1
int y = x;    // y指向同样的内存地址A,内容是整数1
x = 2;        // x指向另一个内存地址B,内容是整数2。y仍然指向地址A,内容是1。

由于我们改造后的 Java 里面 int 全部是引用,所以第一行定义的 x 并不包含一个整数,而是一个引用,它指向堆里分配的一块内存,这个空间的内容是整数 1。在第二行,我们定 int 变量 y,当然它也是一个引用,它的值跟 x 一样,所以 y 也指向同一个地址,里面的内容是同一个整数:1。在第三行,我们对 x 这个引用赋值。你会发现一个很有意思的现象,虽然 x 指向了 2,y 却仍然指向 1。对 x 赋值并没能改变 y 指向的内容,这种情况就跟 int 是值类型的时候一模一样!所以现在虽然 int 变量全部是引用,你却不能实现共享地址的引用能做的事情:对 x 进行某种操作,导致 y 指向的内容也发生改变。

出现这个现象的原因是,虽然现在 int 成了引用类型,你却并不能对它进行引用类型所特有(而值类型没有)的操作。这样的操作包括:

  1. deref。就像 C 语言里的 * 操作符。
  2. 成员赋值。就像对 C struct 成员的 x.foo = 2 。

在 Java 里,你没法写像 C 语言的 *x = 2 这样的代码,因为 Java 没有提供 deref 操作符 *。你也没法通过 x.foo = 2 这样的语句改变 x 所指向的内存数据(内容是1)的一部分,因为 int 是一个原始类型。最后你发现,你只能写 x = 2,也就是改变 x 这个引用本身的指向。x = 2 执行之后,原来数字 1 所在的内存空间并没有变成 2,只不过 x 指向了新的地址,那里装着数字 2 而已。指向 1 的其它引用变量比如 y,不会因为你进行了 x = 2 这个操作而看到 2,它们仍然看到原来那个1……

在这种 int 是引用的 Java 里,你对 int 变量 x 能做的事情只有两种:

  1. 读出它的值。
  2. 对它进行赋值,使它指向另一个地方。

这两种事情,就跟你能对值类型能做的两件事情没有区别。这就是为什么你没法通过对 x 的操作而改变 y 表示的值。所以不管 int 在实现上是传递值还是传递引用,它们在语义上都是等价的。也就是说,原始类型是值类型还是引用类型,对于程序员来说完全没有区别。你完全可以把 Java 所有的原始类型都想成引用类型,之后你能对它们做的事情,你的编程思路和方式,都不会因此有任何的改变。

从这个角度来看,Java 在语义上是没有值类型的。值类型和引用类型如果同时并存,程序员必须能够在语义上感觉到它们的不同,然而不管原始类型是值类型还是引用类型,作为程序员,你无法感觉到任何的不同。所以你完全可以认为 Java 只有引用类型,把原始类型全都当成引用类型来用,虽然它们确实是用值实现的。

一个在语义上有值类型的语言(比如 C#,Go 和 Swift)必须具有以下两种特性之一(或者两者都有),程序员才能感觉到值类型的存在:

  1. deref 操作。这使得你可以用 *x = 2 这样的语句来改变引用指向的内容,导致共享地址的其它引用看到新的值。你没法通过 x = 2 让其他值变量得到新的值,所以你感觉到值类型的存在。
  2. 像 struct 这样的“值组合类型”。你可以通过 x.foo = 2 这样的成员赋值改变引用数据(比如 class object)的一部分,使得共享地址的其它引用看到新的值。你没法通过成员赋值让另一个 struct 变量得到新的值,所以你感觉到值类型的存在。

实际上,所有的数据都是引用类型就是 Scheme 和 Java 最初的设计原理。原始类型用值来传递数据只是一种性能优化(叫做 inlining),它对于程序员应该是透明(看不见)的。那些在面试时喜欢问“Java 是否所有数据都是引用”,然后当你回答“是”的时候纠正你说“int,boolean 是值类型”的人,都是本本主义者。

思考题

有人指出,Java 的引用类型可以是 null,而原始类型不行,所以引用类型和值类型还是有区别的。但是其实这并不能否认本文指出的观点,你可以想想这是为什么吗?

分享到:
评论

相关推荐

    sqlserver数据库类型对应Java中的数据类型

    SQL Server 数据库类型对应 Java 中的数据类型 SQL Server 是一个关系数据库管理系统,Java 是一个广泛使用的编程语言。在 Java 中,对数据库的操作需要通过 JDBC(Java Database Connectivity)来实现,而在 JDBC ...

    MySql于Java数据类型对应列表

    ### MySQL与Java数据类型对应详解 #### 概述 在进行数据库操作时,尤其是在使用Java进行MySQL数据库开发的...此外,了解这些类型的映射关系也有助于避免因类型转换问题导致的错误,确保应用程序能够高效稳定地运行。

    Java数据类型和MySql数据类型对应一览

    在 Java 编程语言中,数据类型是指变量或函数可以持有的值的类型。 MySql 数据库也具有其自己的数据类型,用于存储和管理数据。在本文中,我们将对 Java 数据类型和 MySql 数据类型进行对应一览,帮助开发者更好地...

    JAVA数据类型与Hibernate的类型映射

    首先,Java的基本数据类型在Hibernate中有对应的类型映射。例如,int型在Hibernate中通常映射为Integer类型,因为数据库字段可能允许为空(NULL),而Java的基本类型不能是NULL。同样,boolean型在Hibernate中映射为...

    Java数据类型和MySql数据类型对应表

    每种数据类型都有其特点和用法。 在 MySql 中,整数类型包括 TINYINT、SMALLINT、MEDIUMINT、INT、BIGINT 等。这些数据类型都可以用来存储整数值,但它们的长度和取值范围不同。例如,TINYINT 可以存储从 0 到 255 ...

    Java中的基本类型和引用类型变量的区别

    Java 中的基本类型和引用类型变量的区别 Java 中的基本类型和引用类型变量是两个不同的概念,它们在内存分配、变量赋值和函数传递等方面有着本质的区别。 基本类型是 Java 中的八种基本类型,包括 byte、short、...

    java枚举结果类、根据状态值获取枚举值

    java枚举结果类、根据状态值获取枚举值 Controller: /** 模块类型枚举 */ model.addAttribute("mType", ModuleTypeEnum.ModuleTypeShow()); ftl: value="${mType.key}:${mType.value}” </#list>

    简单总结java枚举类型

    Java枚举类型是Java语言中一种特殊的类,用于定义固定的常量集合,它提供了一种安全、类型化的方式来表示一组有限的值。枚举在Java中被引入为一个完整的类型,不同于C++中的枚举,这使得Java枚举更加强大且易于使用...

    Java如何获取泛型类型

    秒懂Java类型(Type)系统 Java 运行时如何获取泛型参数的类型 Java类型Type 之 ParameterizedType,GenericArrayType,TypeVariabl,WildcardType 从实现的接口获取泛型参数 定义一个泛型父类: public interface ...

    C#和java数据类型的等价类型

    * long:C#中的 long 类型对应Java中的 long 类型,都是有符号整数,取值范围为 -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807。 三、浮点数类型 C#和Java都支持浮点数类型,包括 float 和 double 两...

    Java中数据类型和MYSQL中数据类型的对比

    首先,Java 有两类数据类型:基本数据类型(如 int、char、float)和引用数据类型(如类、接口、数组)。基本数据类型是预定义的,它们的大小和值的范围是固定的。例如,`int` 在 Java 中占用 4 个字节,可存储 -2^...

    JAVA数据类型思维导图

    在Java编程语言中,数据类型是程序设计的基础,它决定了变量可以存储的值的种类和范围。本资源“JAVA数据类型思维导图”提供了一种直观的方式来理解和记忆Java中的数据类型,包括基本数据类型和引用数据类型。下面将...

    java 解析Excel 并判断解析的数据类型__Eclipse工程

    5. **判断数据类型**:单元格有多种数据类型,如STRING, NUMERIC, BOOLEAN, DATE等。可以使用`Cell.getCellType()`方法获取单元格类型。例如,如果是`CELL_TYPE_NUMERIC`,则表示该单元格包含数字;如果是`CELL_TYPE...

    java 求最大值、最小值和平均值

    在Java编程语言中,求一个数组的最大值、最小值和平均值是常见的操作,尤其在数据分析、统计计算或算法实现中。以下将详细介绍如何在Java中实现这些功能。 首先,让我们来了解一下数组。数组是一种数据结构,它可以...

    Java各种数据类型的使用

    ### Java各种数据类型的使用 Java 是一种广泛使用的面向对象编程语言,它支持多种内置的数据类型,这些数据类型被用于存储不同类型的信息。对于初学者来说,理解这些数据类型及其使用方式至关重要。本文将详细介绍 ...

    常见java中数据类型之间的转换.txt

    ### 常见Java中数据类型之间的转换 在Java编程语言中,数据类型的转换是非常常见的操作之一。通过数据类型转换,可以确保不同数据类型之间的兼容性,并满足特定场景下的需求。本文将详细介绍Java中常见的数据类型...

    java变量数据类型运算符

    在Java编程语言中,变量是存储数据的容器,而数据类型则是定义变量可以存储哪种类型的数据。Java提供了两种主要的数据类型:基本数据类型和引用数据类型。基本数据类型包括整型(byte, short, int, long)、浮点型...

    Java:按值传递还是按引用传递详细解说

    综上所述,在Java中,对于基本数据类型的参数传递实际上是按值传递,而对于对象类型的参数传递则是按引用传递的一种特殊形式——传递的是对象引用的副本,但由于这些副本指向同一个对象,因此对对象的修改会影响到...

Global site tag (gtag.js) - Google Analytics