摘要:,变量里存的是什么在规范中,对于有这么一句话一个可以是一个类的实例或者是一个数组一个数组其实是一个,不过这是另一个话题了。由于的设计是不可变的,在一个实例上的任何增删操作都会产生一个新的实例,效果与重新为变量设定新的引用值是一样的。
考虑下面这个例子:
Long l1 = 1L; Long l2 = 2L; Long l3 = 3L; long l4 = 3L; Long l5 = 1 + 2L; System.out.println(l3 == 3); System.out.println(l4 == 3); System.out.println(l3.equals(3)); System.out.println(l3.equals(l4)); System.out.println(l3.equals(l5));
输出的结果是
true true false true true
相信这个例子很多初学者都犯过迷糊:l3、l4 不都是 3 吗,怎么 l3.equals(3) 是 false 呢。
这里面有很多点可以讲的,我们一个一个来看:
Long 和 long在 Java 里只有两种类型: primitive types 原始类型 和 reference types 引用类型。
null 是一种特殊的类型
规范说明:4.1. The Kinds of Types and Values
原始类型里包括:boolean、byte、short、int、long、char、float、double;
引用类型有四种:class、interface、type、array (其中 type 我们平时遇到过的就是泛型 T,详细内容可以查阅规范 4.4. Type Variables)
所以这里,long 是原始类型,Long 是引用类型,这很重要,是接下来讨论的基础。
Boxing Conversion 和 Unboxing Conversion其实这个就是拆箱装箱,这个知识点应该不陌生吧,就是 Java 会自动帮你把原始数值类型和原始浮点类型转换为对应的引用类型,如 long 转换为 Long。
举个栗子:
public void func(Long l) { System.out.println(l); } func(1L);
这段代码是可以跑起来的,但是如果调用时是这样的 func(1),那么就会报错,因为 1 是整数型,它即便自动装箱也是 Integer 而不是 Long。
Objects,变量里存的是什么在规范中,对于 Obejct 有这么一句话:
An object is a class instance or an array.
一个 Object 可以是一个类的实例或者是一个数组 (一个数组其实是一个 Object,不过这是另一个话题了。)
The reference values (often just references) are pointers to these objects, and a special null reference, which refers to no object.
引用值(通常是引用)是指向这些对象的指针和一个特殊的null引用,它不引用任何对象。
有些学过 C++ 的或是有 引用 这个概念的其他语言的同学,可能在这里要犯迷糊了:不是说 Java 里没有引用吗,怎么规范里又提到这个词了。
注意,C 里面没有引用只有指针,它跟 Java 一样是值传递的。
其实可以这么不严谨地认为:C++ 里的 引用 是动词,Java 里的 引用 是名词。
C++ 里的引用是对一个变量定义一个别名:
int a = 2, int &ra = a; // a为目标原名称,ra为目标引用名。给ra赋值:ra = 1; 等价于 a = 1;
C++ 引用
Java 里的引用就是一个值,一个指向 对象 的值。
public static void func(String s) { s = "bar"; } String s1 = "foo"; func(s1); System.out.println(s1); // "foo"
在这里,s1 的值并不是 foo,而是一个指向 其字段value值为 ["f", "o", "o"]的 String 实例 的引用。
比如说,再声明一个 String s2 = "foo";,然后在 func(s1); 处下断点,可以看到:
可以看到,String{@xxx} 和 value:byte[]{@xxx} 都是一样的,因为它们就是同一个对象,s1 和 s2 这两个变量的值是 指向了同一个对象(String{@674})的引用。
如果我们在 func(String s) 里打断点,会发现在 func(s1) 的情况下,s 和 s1 的 引用值 是一样的。
因为 Java 是值传递,只不过在引用类型的情况下,传递的这个值,就是 引用值。
当 func 内部对这个 s 进行操作后,我们再来看看func内部断点的情况:
public static void func(String s) { s = "bar"; // 断点处,此时 s 的引用值已经变为 String{@674} // 即此时的 s 的引用值已经不再是 s1 的引用值,自此它们已经指向的是不同的对象了。 }
由于 String 的设计是不可变的,在一个 String 实例上的任何增删操作都会产生一个新的 String 实例,效果与重新为变量设定新的引用值是一样的。
我们再看看一个原始类型的断点情况:
int i = 0;
对于原始类型的变量而言,它们的值就是本身。
==和 equals== 操作符在规范里其实分了三种情况:
15.21.1. Numerical Equality Operators == and !=
15.21.2. Boolean Equality Operators == and !=
15.21.3. Reference Equality Operators == and !=
equals 是 Object 的方法,但是任何类都可以覆写这个方法来实现自定义的实例间判断,比如 Long.equals 就改成了这个样子:
/** * Compares this object to the specified object. The result is * {@code true} if and only if the argument is not * {@code null} and is a {@code Long} object that * contains the same {@code long} value as this object. * * @param obj the object to compare with. * @return {@code true} if the objects are the same; * {@code false} otherwise. */ public boolean equals(Object obj) { if (obj instanceof Long) { return value == ((Long)obj).longValue(); } return false; }
也就是说,只要待判定对象不是 Long 或其子类,那么就直接返回 false。
结合前边讲的 int 在方法调用时会被自动装箱成 Integer(如果参数不显式要求 int 类型),很显然,l3.equals(3) 会直接因为 Integer 3 不是 Long 而返回 false。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/68602.html
摘要:如线程执行后,线程执行,相当于线程向线程发送了消息。我们可以利用这种互斥性来进行线程间通信。 你是否真正理解并会用volatile, synchronized, final进行线程间通信呢,如果你不能回答下面的几个问题,那就说明你并没有真正的理解: 对volatile变量的操作一定具有原子性吗? synchronized所谓的加锁,锁住的是什么? final定义的变量不变的到底是什么...
摘要:文章目录一数据类型二整型在内存中的存储原码反码补码大小端三例题练习一数据类型在语言中有整型浮点型构造类型指针类型等。正数的原反补码都相同对于整形来说数据在内存中存放的都是补码。 ...
摘要:入门的导语废话最近两年你要说函数式编程不火的话那是不可能的是人都知道函数式编程很火为什么函数式编程会火呢在于它的思想很强大很强势尤其是前端的更是在上完全使用纯函数函数式的好处渐渐被发掘出来笔者最近看了一些函数式方面的东东现在发出来给大家学习 0x00 入门的导语(废话) 最近两年你要说函数式编程不火的话, 那是不可能的, 是人都知道函数式编程很火.为什么函数式编程会火呢, 在于它的思想...
阅读 2934·2023-04-26 02:25
阅读 2206·2023-04-25 18:05
阅读 615·2021-09-30 09:57
阅读 2875·2021-09-27 14:10
阅读 1612·2019-08-30 15:44
阅读 967·2019-08-29 15:28
阅读 2502·2019-08-29 14:10
阅读 2233·2019-08-29 13:30