你所不知道的,Java 中操作符的秘密?
在 Java 编程的过程中,我们对数据的处理,都是通过操作符来实现的。例如,用于赋值的赋值操作符、用于运算的运算操作符等、用于比较的比较操作符,还包括逻辑操作符、按位操作符、移位操作符、三元操作符等等。操作符的种类很多,用法也各不相同,下面让我们一起来看看 Java 的各类操作符中,一些值得我们特别注意的知识点。
赋值操作符
对于赋值操作,相信大家都玩的特别熟,例如为基本数据类型赋值,
int love = 521;
在「对象漫谈」中,我们知道了基本数据类型存储在堆栈中,而不是 new
出来的。此外, 基本数据类型存储了实际的数值,而不是指向对象的引用,所以在为其赋值的时候,是直接将一个地方的内容复制到了另一个地方 。而为一个对象进行赋值操作的时候,我们实际上是操作对象的引用,即将一个对象的引用赋值给另一个对象,因此两个对象通过同一个引用指向同一块存储空间。感兴趣的同学,可以运行如下程序进行测试,
package com.hit.chapter3; /**
* author:Charies Gavin
* date:2017/12/09,12:40
* https:github.com/guobinhit
* description:测试赋值操作符
*/
public class AssignmentOperator {
public static void main(String[] args) {
// 创建两个对象
Apple apple1 = new Apple("green");
Apple apple2 = new Apple();
// 输出两个初始化对象,apple1 初始化 color 为 green,apple2 初始化 color 为 null
System.out.println("Initial: apple1 color is " + apple1.color + ", apple2 color is " + apple2.color);
// 将 apple1 的引用赋值给 apple2
apple2 = apple1;(java学习群669823128)
// 输出赋值后的两个对象,两个对象拥有同一个引用
System.out.println("Assignment: apple1 color is " + apple1.color + ", apple2 color is " + apple2.color);
// 修改 apple2 的引用
apple2.color = "red";
// 输出 apple2 修改后的两个对象,两个对象都发生变化
System.out.println("Modify: apple1 color is " + apple1.color + ", apple2 color is " + apple2.color);
} } class Apple {
// 成员变量
String color; /**
* 默认构造器
*/
Apple() {
} /**
* 有参构造器
*
* @param color
*/
Apple(String color) {
this.color = color;
}
}
如上图所示,当我们把对象 apple1
赋值给对象 apple2
的时候,两个对象就拥有了同一个引用,因此在我们修改 apple2
的值之后, apple1
的值也受到了影响,这种现象,我们称之为「同名现象」。如果想要避免上述的同名现象,我们可以修改赋值代码为
apple2.color = apple1.color;
这样的话,我们仅是将 apple1
的 color
值赋值给了 apple2
的 color
值,而不是操作对象的引用。
算术操作符
在 Java 的算术操作符中,整数的除法( /
)会直接省略掉结果的小数位,而不是四舍五入。
package com.hit.chapter3; import java.util.Random; /**
* author:Charies Gavin
* date:2017/12/09,13:50
* https:github.com/guobinhit
* description:测试算术操作符
*/
public class ArithmeticOperator {
public static void main(String[] args) {
/**
* 测试随机整数除法
*/
randomDivide();
} /**
* 随机整数除法
*/
private static void randomDivide() {
Random random = new Random();
int x = random.nextInt(10) + 1;
int y = random.nextInt(10) + 1;
int z = x / y;
System.out.println("整数除法,默认省略结果的小数位:" + x + " / " + y + " = " + z);
}
}
如上所示,我们创建了一个随机整数除法,并测试了整数除法会默认省略结果的小数位。在 randomDivide()
方法中,我们使用了 Random
类, 如果我们在创建 Random
对象的时候没有传入任何参数,那么 Java 就会将当前时间作为随机数生成器的种子 ,因此在每次执行上述程序的时候,都会得到不同的结果。其中,随机数生成器的种子用于随机数生成器的初始化值,对于相同的种子,总会产生相同的随机数序列。此外,在我们调用 nextInt()
方法的时候,我们进行了+1
操作,这是因为 传递给 nextInt()
的参数设置了所能产生随机数的上限,而其下限为 0
,下限可以取到,上限取不到 ,因此 +1
操作可以防止除数为 0
的情况。
在算术操作符中,一元减号( -
)和一元加号( +
)与二元减号和二元加号都使用相同的符号。根据表达式的书写形式,编译器会自动判断出使用哪一种算术操作符。其中,一元减号用于转变数据的符号,而一元加号只是为了一元减号相对于,它 ( +
)唯一的作用就是将较小类型的操作数提升为 int
类型 。
此外,在算术操作符中有两个比较特殊的操作符,那就是递增( ++
)和递减( --
),递增和递减操作符不仅改变了变量,并且以变量的值作为其生产的结果。 对于前缀递增和前缀递减(如 i++
或者 i--
),会先执行运算,再生成值;对于后缀递增和后缀递减(如 ++i
或者 --i
),会先生成值,再执行运算 。
关系操作符
在 Java 语言中,关系操作符包括 >
、 <
、 >=
、 <=
、 ==
和 !=
等,其生成的结果为 boolean
类型,其中有两个关系操作符需要我们特别注意,那就是 ==
和 !=
,执行下面的程序,进行测试:
package com.hit.chapter3; /**
* author:Charies Gavin
* date:2017/12/10,14:43
* https:github.com/guobinhit
* description:测试关系操作符
*/
public class RelationOperator {
public static void main(String[] args) {
// 创建两个 Integer 对象
Integer i1 = new Integer(521);
Integer i2 = new Integer(521);
// 调用两个私有的静态方法进行比较判断
equivalentOperator(i1, i2);
equalsFunction(i1, i2);
} /**
* 通过恒等运算符比较两个对象
*
* @param o1
* @param o2
*/
private static void equivalentOperator(Object o1, Object o2) {
System.out.println(o1 + " == " + o2 + " : " + (o1 == o2));
System.out.println(o1 + " != " + o2 + " : " + (o1 != o2));
} /**
* 通过 equals() 方法比较两个对象
*
* @param o1
* @param o2
*/
private static void equalsFunction(Object o1, Object o2) {
System.out.println("(" + o1 + ").equals(" + o2 + ") : " + (o1).equals(o2));
System.out.println("!(" + o1 + ").equals(" + o2 + ") : " + (!(o1).equals(o2)));
}
}
如上所示,我们创建了两个 Integer
类型的对象,通过关系操作符来比较,发现结果出人意料,两个 Integer
类型的 521
竟然被判断为 false
,也就是不相等。实际上,这是正常的,因为 ==
和 !=
比较的是对象的引用 ,我们通过 new
创建了两个 Integer
类型的对象,虽然这两个对象的内容相同,但它们在堆上拥有不同的存储空间,也就拥有了不同的对象引用。通过对 equalsFunction
的测试,我们发现 调用 java.lang
包(默认导入)的 equals()
方法,可以正确的比较两个对象的内容 。But,下面的程序可能就要让我们对之前的判断持怀疑态度了,
package com.hit.chapter3; /**
* author:Charies Gavin
* date:2017/12/10,14:43
* https:github.com/guobinhit
* description:测试关系操作符
*/
public class RelationOperator {
public static void main(String[] args) {
// 创建两个自定义的 Cartoon 对象
Cartoon c1 = new Cartoon();
Cartoon c2 = new Cartoon();
// 为两个 Cartoon 对象赋值
c1.name = c2.name = "Naruto";
// 调用 equals() 方法进行比较
equalsFunction(c1, c2);
} /**
* 通过 equals() 方法比较两个对象
*
* @param o1
* @param o2
*/
private static void equalsFunction(Object o1, Object o2) {
System.out.println("(" + o1 + ").equals(" + o2 + ") : " + (o1).equals(o2));
System.out.println("!(" + o1 + ").equals(" + o2 + ") : " + (!(o1).equals(o2)));
}
} /**
* 自定义卡通类
*/
class Cartoon {
String name;
}
如上所示,我们创建了两个自定义类的对象,然后同样是通过 equals()
方法对两个含有相同内容的对象进行比较判断,其结果竟然是 false
?不是说好 equals()
方法比较的是对象的内容吗?怎么转眼就被打脸了呢?好吧,在这里纠正一下, equals()
方法默认是比较对象的引用 ,不过在大多数的 Java 类库中都实现了 equals()
方法,以便用来比较对象的内容,而非比较对象的引用。因此,如果我们想使用 equals()
方法来比较我们自定义类型的内容而非引用的话,则需要覆盖 Object
类(终极根类)中的 equals()
方法,而在覆盖 equals()
方法的同时,建议同时覆盖 hashCode()
方法。下面给出一个同时覆盖 equals()
和 hashCode()
的示例:
/**
* 自定义卡通类
*/
class Cartoon {
String name; /**
* 覆盖 Object 根类中的 hashCode() 方法
* @return 哈希值
*/
@Override
public int hashCode() {
return name.hashCode();
} /**
* 覆盖 Object 根类中的 equals() 方法
* @param o
* @return true or false
*/
@Override
public boolean equals(Object o) {
if (o instanceof Cartoon) {
if (this.name.hashCode() == ((Cartoon) o).name.hashCode())
return true;
return false;
} else {
return false;
}
}
}
在此,强烈建议: 不要用 ==
操作符来检测两个字符串是否相等 !因为 ==
操作符只能确定两个字符串是否放在同一个位置上。当然,如果两个字符串放置在同一个位置上,它们必然相等,但是完全有可能将内容相同的多个字符串的拷贝位置放置在不同的位置上。如果虚拟机始终将相同的字符串共享,就可以使用 ==
操作符来检测两个字符串是否相等。但实际上, 只有字符串常量是共享的 。
其他操作符
在逻辑操作符中,与( &&
)、或( ||
)、非( !
)操作只能作用于布尔值。如果在应该使用 String
值的地方使用了布尔值,那么布尔值会自动转换成适当的文本形式。对于布尔值,按位操作符和逻辑操作符具有相同的效果,只不过按位操作符不会中途“短路”而已。
在移位操作符中,如果 char
、 byte
或者 short
类型的数值进行移位操作,那么在进行移位之前,它们会先被转换为 int
类型,并且得到的结果也是 int
类型的值。对于二进制数,如果最高位(最前面)的数字(符号位)是 0
,则为正数;是 1
,则为负数。
在对基本数据类型执行算术运算或者按位运算的时候,只要类型比 int
小(即 char
、 byte
或者 short
),那么在运算之前,这些值会自动被转换成 int
类型。通常,表达式中出现的最大的数据类型决定了表达式结果的数据类型。
如果表达式以一个字符串开头,那么后续所有操作数都必须是字符串型,如果后续的操作数不是字符串型,则会被自动转换为字符串型,并且编译器会把双引号内的字符序列自动转成字符串。
在将 float
或者 double
转型为整型值时,总是对该数字执行截尾操作。如果想要得到舍入的结果,则需要使用 java.lang.Math
中的 round()
方法。除 boolea
类外,任何一种基本数据类型都可以通过类型转换变为其他基本类型。
此外,在使用指数计数法的时候,例如
float loveu = 5.21e2F;
编辑器 通常会将指数作为双精度数( double
)来处理 ,如果在初始化值后面没有 F
或者 f
的话,编译器会报错,提示我们必须使用类型转换将 double
转换为 float
类型。
java学习群669823128
你所不知道的,Java 中操作符的秘密?的更多相关文章
- 【总结】你所不知道的Java序列化
我们都知道,Java序列化可以让我们记录下运行时的对象状态(对象实例域的值),也就是我们经常说的对象持久化 .这个过程其实是非常复杂的,这里我们就好好理解一下Java的对象序列化. 1. 首先我们要搞 ...
- 你所不知道的java编程思想
读thinking in java这本书的时候,有这么一句话“在编译单元的内部,可以有一个公共(public)类,它必须拥有与文件相同的名字” 有以下疑问: 在一个类中说可以有一个public类,那是 ...
- 你所不知道的 Java 之 HashCode
之所以写HashCode,是因为平时我们总听到它.但你真的了解hashcode吗?它会在哪里使用?它应该怎样写? 相信阅读完本文,能让你看到不一样的hashcode. 使用hashcode的目的在于: ...
- 你所不知道的 C# 中的细节
前言 有一个东西叫做鸭子类型,所谓鸭子类型就是,只要一个东西表现得像鸭子那么就能推出这玩意就是鸭子. C# 里面其实也暗藏了很多类似鸭子类型的东西,但是很多开发者并不知道,因此也就没法好好利用这些东西 ...
- 你所不知道的Python | 字符串连接的秘密
字符串连接,就是将2个或以上的字符串合并成一个,看上去连接字符串是一个非常基础的小问题,但是在Python中,我们可以用多种方式实现字符串的连接,稍有不慎就有可能因为选择不当而给程序带来性能损失. 方 ...
- 你所不知道的库存超限做法 服务器一般达到多少qps比较好[转] JAVA格物致知基础篇:你所不知道的返回码 深入了解EntityFramework Core 2.1延迟加载(Lazy Loading) EntityFramework 6.x和EntityFramework Core关系映射中导航属性必须是public? 藏在正则表达式里的陷阱 两道面试题,带你解析Java类加载机制
你所不知道的库存超限做法 在互联网企业中,限购的做法,多种多样,有的别出心裁,有的因循守旧,但是种种做法皆想达到的目的,无外乎几种,商品卖的完,系统抗的住,库存不超限.虽然短短数语,却有着说不完,道不 ...
- 你所不知道的五件事情--java.util.concurrent(第二部分)
这是Ted Neward在IBM developerWorks中5 things系列文章中的一篇,仍然讲述了关于Java并发集合API的一些应用窍门,值得大家学习.(2010.06.17最后更新) 摘 ...
- Android中Context详解 ---- 你所不知道的Context
转自:http://blog.csdn.net/qinjuning/article/details/7310620Android中Context详解 ---- 你所不知道的Context 大家好, ...
- Android中Context详解 ---- 你所不知道的Context(转)
Android中Context详解 ---- 你所不知道的Context(转) 本文出处 :http://b ...
随机推荐
- ArcEngine开发中“错误类型"****"未定义构造函数”
from:http://blog.csdn.net/mengdong_zy/article/details/8990593 问题 在ArcEngine开发的时候,在编译时,发现出现这样的错误,出错的地 ...
- 微信小程序学习笔记(5)--------框架之视图层
这一系列转载:http://blog.csdn.net/zsp45212/article/details/53518238 视图层 框架的视图层由wxml与wxss编写,由组件进行展示.将逻辑层的数据 ...
- Maven实战--- dependencies与dependencyManagement的区别
在上一个项目中遇到一些jar包冲突的问题,之后还有很多人分不清楚dependencies与dependencyManagement的区别,本篇文章将这些区别总结下来. 1.DepencyManagem ...
- 存在单点故障的namenode宕机恢复测试
前提:如果namenode没有做HA,那么至少应该启用secondarynamenode,以便namenode宕机之后手动恢复数据 实验环境:3个节点(cenos 6.10) 测试前数据: 1.为了确 ...
- with as (cte common table expression) 公共表表达式
SQL中 with as 的用法——使用公用表表达式(CTE) 公用表表达式 (CTE) 可以认为是在单个 SELECT.INSERT.UPDATE.DELETE 或 CREATE VIEW 语句的 ...
- Spring 之混合配置
[JavaConfig 导入另外一个 JavaConfig & JavaConfig 导入 XML] package soundsystem.config; import org.spring ...
- js自执行函数&扩展方法
我们通常将JS代码写在一个单独的JS文件中,然后在页面中引入该文件.但是,有时候引入后会碰到变量名或函数名与其它JS代码冲突的问题.那么如何解决这个问题呢?作用域隔离.在JS中,作用域是通过函数来划分 ...
- 智能DNS
DNS查找下一个服务的地址 一.智能DNS APP通过域名访问DNS服务器,DNS根据域名对应一组IP中随机选择一个,发给APP.从这个意义说智能DNS,智能DNS相当一个七层的负载均衡. 二.H ...
- tp添加分页
//分页开始 $count=M('article')->where($condition)->count(); $p = intval($p) > 0 ? $p : 1; $page ...
- scala学习手记15 - 独立对象和伴生对象
上一节中的单例对象MarkerFactory 就是一个独立对象的例子.尽管它管理着Marker类,但是它并没有关联到任何类上. scala也可以创建关联到类上的对象.这样的对象同类共享同一个名字,这样 ...