四、运算符(2)

本章概要

  • 关系运算符
    • 测试对象等价
  • 逻辑运算符
    • 短路
  • 字面值常量
    • 下划线
    • 指数计数法
  • 位运算符

关系运算符

关系运算符会通过产生一个布尔(boolean)结果来表示操作数之间的关系。如果关系为真,则结果为 true,如果关系为假,则结果为 false。关系运算符包括小于 <,大于 >,小于或等于 <=,大于或等于 >=,等于 == 和不等于 !===!= 可用于所有基本类型,但其他运算符不能用于基本类型 boolean,因为布尔值只能表示 truefalse,所以比较它们之间的“大于”或“小于”没有意义。

测试对象等价

关系运算符 ==!= 同样适用于所有对象之间的比较运算,但它们比较的内容却经常困扰 Java 的初学者。下面是代码示例:

// operators/Equivalence.java
public class Equivalence {public static void main(String[] args) {Integer n1 = 47;Integer n2 = 47;System.out.println(n1 == n2);System.out.println(n1 != n2);}
}

输出结果:

true
false

表达式 System.out.println(n1 == n2) 将会输出比较的结果。因为两个 Integer 对象相同,所以先输出 true,再输出 false。但是,尽管对象的内容一样,对象的引用却不一样。==!= 比较的是对象引用,所以输出实际上应该是先输出 false,再输出 true(译者注:如果你把 47 改成 128,那么打印的结果就是这样,因为 Integer 内部维护着一个 IntegerCache 的缓存,默认缓存范围是 [-128, 127],所以 [-128, 127] 之间的值用 ==!= 比较也能能到正确的结果,但是不推荐用关系运算符比较,具体见 JDK 中的 Integer 类源码)。

那么怎么比较两个对象的内容是否相同呢?你必须使用所有对象(不包括基本类型)中都存在的 equals() 方法,下面是如何使用 equals() 方法的示例:

// operators/EqualsMethod.java
public class EqualsMethod {public static void main(String[] args) {Integer n1 = 47;Integer n2 = 47;System.out.println(n1.equals(n2));}
}

输出结果:

true

上例的结果看起来是我们所期望的。但其实事情并非那么简单。下面我们来创建自己的类:

// operators/EqualsMethod2.java
// 默认的 equals() 方法没有比较内容
class Value {int i;
}public class EqualsMethod2 {public static void main(String[] args) {Value v1 = new Value();Value v2 = new Value();v1.i = v2.i = 100;System.out.println(v1.equals(v2));}
}

输出结果:

false

上例的结果再次令人困惑:结果是 false。原因: equals() 的默认行为是比较对象的引用而非具体内容。因此,除非你在新类中覆写 equals() 方法,否则我们将获取不到想要的结果。不幸的是,在学习 复用(Reuse) 章节后我们才能接触到“覆写”(Override),并且直到集合主题,才能知道定义 equals() 方法的正确方式,但是现在明白 equals() 行为方式也可能为你节省一些时间。

大多数 Java 库类通过覆写 equals() 方法比较对象的内容而不是其引用。

逻辑运算符

每个逻辑运算符 &&AND)、||OR)和 !)根据参数的逻辑关系生成布尔值 truefalse。下面的代码示例使用了关系运算符和逻辑运算符:

// operators/Bool.java
// 关系运算符和逻辑运算符
import java.util.*;
public class Bool {public static void main(String[] args) {Random rand = new Random(47);int i = rand.nextInt(100);int j = rand.nextInt(100);System.out.println("i = " + i);System.out.println("j = " + j);System.out.println("i > j is " + (i > j));System.out.println("i < j is " + (i < j));System.out.println("i >= j is " + (i >= j));System.out.println("i <= j is " + (i <= j));System.out.println("i == j is " + (i == j));System.out.println("i != j is " + (i != j));// 将 int 作为布尔处理不是合法的 Java 写法//- System.out.println("i && j is " + (i && j));//- System.out.println("i || j is " + (i || j));//- System.out.println("!i is " + !i);System.out.println("(i < 10) && (j < 10) is " + ((i < 10) && (j < 10)) );System.out.println("(i < 10) || (j < 10) is " + ((i < 10) || (j < 10)) );}
}

输出结果:

在这里插入图片描述

在 Java 逻辑运算中,我们不能像 C/C++ 那样使用非布尔值, 而仅能使用 ANDORNOT。上面的例子中,我们将使用非布尔值的表达式注释掉了(你可以看到表达式前面是 //-)。但是,后续的表达式使用关系比较生成布尔值,然后对结果使用了逻辑运算。请注意,如果在预期为 String 类型的位置使用 boolean 类型的值,则结果会自动转为适当的文本格式(即 “true” 或 “false” 字符串)。

我们可以将前一个程序中 int 的定义替换为除 boolean 之外的任何其他基本数据类型。但请注意,float 类型的数值比较非常严格,只要两个数字的最小位不同则两个数仍然不相等;只要数字最小位是大于 0 的,那么它就不等于 0。

短路

逻辑运算符支持一种称为“短路”(short-circuiting)的现象。整个表达式会在运算到可以明确结果时就停止并返回结果,这意味着该逻辑表达式的后半部分不会被执行到。代码示例:

// operators / ShortCircuit.java 
// 逻辑运算符的短路行为
public class ShortCircuit {static boolean test1(int val) {System.out.println("test1(" + val + ")");System.out.println("result: " + (val < 1));return val < 1;}static boolean test2(int val) {System.out.println("test2(" + val + ")");System.out.println("result: " + (val < 2));return val < 2;}static boolean test3(int val) {System.out.println("test3(" + val + ")");System.out.println("result: " + (val < 3));return val < 3;}public static void main(String[] args) {boolean b = test1(0) && test2(2) && test3(2);System.out.println("expression is " + b);}
}

输出结果:

在这里插入图片描述

每个测试都对参数执行比较并返回 truefalse。同时控制台也会在方法执行时打印他们的执行状态。 下面的表达式:

test1(0&& test2(2&& test3(2

可能你的预期是程序会执行 3 个 test 方法并返回。我们来分析一下:第一个方法的结果返回 true,因此表达式会继续走下去。紧接着,第二个方法的返回结果是 false。这就代表这整个表达式的结果肯定为 false,所以就没有必要再判断剩下的表达式部分了。

所以,运用“短路”可以节省部分不必要的运算,从而提高程序潜在的性能。

注:关于“短路”的更多测试见:https://blog.csdn.net/GXL_1012/article/details/130119901

通常,当我们向程序中插入一个字面值常量(Literal)时,编译器会确切地识别它的类型。当类型不明确时,必须辅以字面值常量关联来帮助编译器识别。代码示例:

// operators/Literals.java
public class Literals {public static void main(String[] args) {int i1 = 0x2f; // 16进制 (小写)System.out.println("i1: " + Integer.toBinaryString(i1));int i2 = 0X2F; // 16进制 (大写)System.out.println("i2: " + Integer.toBinaryString(i2));int i3 = 0177; // 8进制 (前导0)System.out.println("i3: " + Integer.toBinaryString(i3));char c = 0xffff; // 最大 char 型16进制值System.out.println("c: " + Integer.toBinaryString(c));byte b = 0x7f; // 最大 byte 型16进制值  01111111;System.out.println("b: " + Integer.toBinaryString(b));short s = 0x7fff; // 最大 short 型16进制值System.out.println("s: " + Integer.toBinaryString(s));long n1 = 200L; // long 型后缀long n2 = 200l; // long 型后缀 (容易与数值1混淆)long n3 = 200;// Java 7 二进制字面值常量:byte blb = (byte) 0b00110101;System.out.println("blb: " + Integer.toBinaryString(blb));short bls = (short) 0B0010111110101111;System.out.println("bls: " + Integer.toBinaryString(bls));int bli = 0b00101111101011111010111110101111;System.out.println("bli: " + Integer.toBinaryString(bli));long bll = 0b00101111101011111010111110101111;System.out.println("bll: " + Long.toBinaryString(bll));float f1 = 1;float f2 = 1F; // float 型后缀float f3 = 1f; // float 型后缀double d1 = 1d; // double 型后缀double d2 = 1D; // double 型后缀// (long 型的字面值同样适用于十六进制和8进制 )}
}

输出结果:

在这里插入图片描述

在文本值的后面添加字符可以让编译器识别该文本值的类型。对于 Long 型数值,结尾使用大写 L 或小写 l 皆可(不推荐使用 l,因为容易与阿拉伯数值 1 混淆)。大写 F 或小写 f 表示 float 浮点数。大写 D 或小写 d 表示 double 双精度。

十六进制(以 16 为基数),适用于所有整型数据类型,由前导 0x0X 表示,后跟 0-9 或 a-f (大写或小写)。如果我们在初始化某个类型的数值时,赋值超出其范围,那么编译器会报错(不管值的数字形式如何)。在上例的代码中,charbyteshort 的值已经是最大了。如果超过这些值,编译器将自动转型为 int,并且提示我们需要声明强制转换(强制转换将在本章后面定义),意味着我们已越过该类型的范围界限。

八进制(以 8 为基数)由 0~7 之间的数字和前导零 0 表示。

Java 7 引入了二进制的字面值常量,由前导 0b0B 表示,它可以初始化所有的整数类型。

使用整型数值类型时,显示其二进制形式会很有用。在 Long 型和 Integer 型中这很容易实现,调用其静态的 toBinaryString() 方法即可。 但是请注意,若将较小的类型传递给 Integer.toBinaryString() 时,类型将自动转换为 int

字面值常量

下划线

Java 7 中有一个深思熟虑的补充:我们可以在数字字面量中包含下划线 _,以使结果更清晰。这对于大数值的分组特别有用。代码示例:

// operators/Underscores.java
public class Underscores {public static void main(String[] args) {double d = 341_435_936.445_667;System.out.println(d);int bin = 0b0010_1111_1010_1111_1010_1111_1010_1111;System.out.println(Integer.toBinaryString(bin));System.out.printf("%x%n", bin); // [1]long hex = 0x7f_e9_b7_aa;System.out.printf("%x%n", hex);}
}

输出结果:

3.41435936445667E8
101111101011111010111110101111
2fafafaf
7fe9b7aa

下面是合理使用的规则:

  1. 仅限单 _,不能多条相连。
  2. 数值开头和结尾不允许出现 _
  3. FDL的前后禁止出现 _
  4. 二进制前导 b 和 十六进制 x 前后禁止出现 _

[1] 注意 %n的使用。熟悉 C 风格的程序员可能习惯于看到 \n 来表示换行符。问题在于它给你的是一个“Unix风格”的换行符。此外,如果我们使用的是 Windows,则必须指定 \r\n。这种差异的包袱应该由编程语言来解决。这就是 Java 用 %n 实现的可以忽略平台间差异而生成适当的换行符,但只有当你使用 System.out.printf()System.out.format() 时。对于 System.out.println(),我们仍然必须使用 \n;如果你使用 %nprintln() 只会输出 %n 而不是换行符。

指数计数法

指数总是采用一种我认为很不直观的记号方法:

// operators/Exponents.java
// "e" 表示 10 的几次幂
public class Exponents {public static void main(String[] args) {// 大写 E 和小写 e 的效果相同:float expFloat = 1.39e-43f;expFloat = 1.39E-43f;System.out.println(expFloat);double expDouble = 47e47d; // 'd' 是可选的double expDouble2 = 47e47; // 自动转换为 doubleSystem.out.println(expDouble);}
}

输出结果:

1.39E-43
4.7E48

在科学与工程学领域,e 代表自然对数的基数,约等于 2.718 (Java 里用一种更精确的 doubleMath.E 来表示自然对数)。指数表达式 “1.39 x e-43”,意味着 “1.39 × 2.718 的 -43 次方”。然而,自 FORTRAN 语言发明后,人们自然而然地觉得e 代表 “10 的几次幂”。这种做法显得颇为古怪,因为 FORTRAN 最初是为科学与工程领域设计的。

理所当然,它的设计者应对这样的混淆概念持谨慎态度。但不管怎样,这种特别的表达方法在 C,C++ 以及现在的 Java 中顽固地保留下来了。所以倘若习惯 e 作为自然对数的基数使用,那么在 Java 中看到类似“1.39e-43f”这样的表达式时,请转换你的思维,从程序设计的角度思考它;它真正的含义是 “1.39 × 10 的 -43 次方”。

注意如果编译器能够正确地识别类型,就不必使用后缀字符。对于下述语句:

long n3 = 200;

它并不存在含糊不清的地方,所以 200 后面的 L 大可省去。然而,对于下述语句:

float f4 = 1e-43f; //10 的幂数

编译器通常会将指数作为 double 类型来处理,所以假若没有这个后缀字符 f,编译器就会报错,提示我们应该将 double 型转换成 float 型。

位运算符

位运算符允许我们操作一个整型数字中的单个二进制位。位运算符会对两个整数对应的位执行布尔代数,从而产生结果。

位运算源自 C 语言的底层操作。我们经常要直接操纵硬件,频繁设置硬件寄存器内的二进制位。Java 的设计初衷是电视机顶盒嵌入式开发,所以这种底层的操作仍被保留了下来。但是,你可能不会使用太多位运算。

若两个输入位都是 1,则按位“与运算符” & 运算后结果是 1,否则结果是 0。若两个输入位里至少有一个是 1,则按位“或运算符” | 运算后结果是 1;只有在两个输入位都是 0 的情况下,运算结果才是 0。若两个输入位的某一个是 1,另一个不是 1,那么按位“异或运算符” ^ 运算后结果才是 1。按位“非运算符” ~ 属于一元运算符;它只对一个自变量进行操作(其他所有运算符都是二元运算符)。按位非运算后结果与输入位相反。例如输入 0,则输出 1;输入 1,则输出 0。

位运算符和逻辑运算符都使用了同样的字符,只不过数量不同。位短,所以位运算符只有一个字符。位运算符可与等号 = 联合使用以接收结果及赋值:&=|=^= 都是合法的(由于 ~ 是一元运算符,所以不可与 = 联合使用)。

我们将 Boolean 类型被视为“单位值”(one-bit value),所以它多少有些独特的地方。我们可以对 boolean 型变量执行与、或、异或运算,但不能执行非运算(大概是为了避免与逻辑“非”混淆)。对于布尔值,位运算符具有与逻辑运算符相同的效果,只是它们不会中途“短路”。此外,针对布尔值进行的位运算为我们新增了一个“异或”逻辑运算符,它并未包括在逻辑运算符的列表中。在移位表达式中,禁止使用布尔值,原因将在下面解释。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/7846.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SpringBoot日志文件

1.日志有什么用&#xff1f; 日志是程序的重要组成部分&#xff0c;如果程序报错&#xff0c;我们可以通过日志发现和定位问题&#xff0c;同样日志也可以实现以下的功能&#xff1a; 记录用户登录日志&#xff0c;方便分析用户是否是正常登录还是恶意破解用户&#xff1b;记…

使用 OpenCV 和 GrabCut 算法进行交互式背景去除

一、说明 我想&#xff0c;任何人都可以尝试从图像中删除背景。当然&#xff0c;有大量可用的软件或工具能够做到这一点&#xff0c;但其中一些可能很昂贵。但是&#xff0c;我知道有人使用窗口绘画3D魔术选择或PowerPoint背景去除来删除背景。 如果您是计算机视觉领域的初学者…

network failed to load response data: no resource with given ide...

Chrome 开发者工具无法显示服务器正常返回的 HTTP 请求 - Failed to load response data 今天做开发时遇到一个问题&#xff0c;Chrome 开发者工具 network 标签里&#xff0c;虽然一个 HTTP 请求已经成功从服务器端返回&#xff0c;但是 Chrome 开发者工具里&#xff0c;仍然…

ArcGIS Engine 与 Visual Studio版本对照表

通过C#对于Arcgis的二次开发&#xff0c;需要Visual Studio版本需要与ArcGIS Engine对应&#xff0c;Visual Studio版本的或高或低都不能使ArcObjects SDK for microsoft.Net framework安装成功。下面是各个版本的对照表。 序号ArcEngine版本visual Studio版本Network版本110.…

Spring 的创建和使用

Spring 就是一个包含了众多工具方法的 IoC 容器。既然是容器那么它就具备两个最基本的功能 将对象存储到容器&#xff08;Spring&#xff09;中&#xff1b; 从容器中将对象取出来 在 Java 语言中对象也叫做 Bean&#xff0c;所以后面咱们再遇到对象就以 Bean 著称 一、创建 …

实训笔记7.25

实训笔记7.25 7.25笔记一、MapReduce的特殊使用场景1.1 通过MapReduce程序实现多文件Join操作1.1.1 通过在Reduce端实现join操作1.1.2 通过在Map端实现join操作 1.2 MapReduce中的计数器的使用1.2.1 计数器使用两种方式 1.3 MapReduce实现数据清洗 二、MapReduce的OutputFormat…

Linux笔记——搜索命令find、解压缩命令、vi编辑器、用户权限命令、系统信息相关命令讲解

系列文章目录 Linux笔记——磁盘进行分区与挂载介绍 Linux笔记——管道相关命令以及shell编程 Linux笔记——进程管理与网络监控技术讲解​​​​​​ Linux笔记——rpm与yum下载软件命令介绍 文章目录 系列文章目录 准备工作 一 搜索命令—— find 搜索 1.1 目标 1.…

html input 展示隐藏的密码

<!DOCTYPE html> <html> <meta http-equiv"Content-Type" content"text/html;charsetutf-8"/> <body><h2>登录表格</h2><form action"/action_page.php"><div class"container"><…

chrome查看浏览器内核日志

由于经常在网页上调试播放音视频&#xff0c;但是总遇到一些未知原因&#xff0c;导致无法正常播放&#xff0c;亟需查看浏览器内核日志&#xff0c;分析原因&#xff0c;做一下笔记。 (1) 查看浏览器快捷键属性 &#xff08;2&#xff09;在快捷键启动位置补充参数 --enable-…

菜鸡shader:L12 SD处理贴图制作时钟动画

文章目录 SD处理贴图Shader代码C#代码最后效果 SD处理贴图 呃呃感觉这节课&#xff0c;很大一部分都是在将怎么用SD来处理贴图&#xff0c;在这里就简单放一下课上的截图吧&#xff0c;我也跟着做了一下&#xff0c;虽然表盘十二个数排列间隔不一样&#xff0c;但还是稀碎地做…

脉冲频率、转速相关计算(信捷PLC C语言FC编程应用)

转速、线速度、角速度转换和计算关系请查看下面文章链接: 转速/线速度/角速度计算FC_RXXW_Dor的博客-CSDN博客里工业控制张力控制无处不在,也衍生出很多张力控制专用控制器,磁粉制动器等,本篇博客主要讨论PLC的张力控制相关应用和算法,关于绕线机的绕线算法,大家可以参看…

12.Netty源码之整体架构脉络

Netty 整体架构脉络 Netty 的逻辑处理架构为典型网络分层架构设计&#xff0c;共分为网络通信层、事件调度层、服务编排层&#xff0c;每一层各司其职。 网络通信层 网络通信层的职责是执行网络 I/O 的操作。它支持多种网络协议和 I/O 模型的连接操作。当网络数据读取到内核缓冲…

闲置旧手机搭建服务器?在安卓手机上使用Termux搭建web服务「公网远程访问」

文章目录 概述1.搭建apache2.安装cpolar内网穿透3.公网访问配置4.固定公网地址5.添加站点 概述 Termux是一个Android终端仿真应用程序&#xff0c;用于在 Android 手机上搭建一个完整的Linux 环境&#xff0c;能够实现Linux下的许多基本操作&#xff0c;不需要root权限Termux就…

docker系列5:docker安装nginx

传送门 前面介绍了docker的安装&#xff1a;docker系列1&#xff1a;docker安装 还有docker镜像加速器&#xff1a;docker系列2&#xff1a;阿里云镜像加速器 以及docker的基本操作&#xff1a; docker系列3&#xff1a;docker镜像基本命令 以及容器的基本命令&#xff1a;…

【iOS】多界面传值

文章目录 前言一、属性传值二、协议传值三、block传值四、KVO传值五、KVO的自动触发与手动触发六、通知传值总结 前言 在写网易云音乐以及3GShare包括后面的学生管理系统时&#xff0c;用到许多界面传值方法&#xff0c;特撰写博客记录目前学过的几种多界面传值方法 一、属性…

【Docker】Docker部署私有仓库的配置及应用

文章目录 一、Docker-registry 搭建本地私有仓库1. Registry 的概念2. Registry 的部署过程 二、Docker-harbor 搭建私有仓库1. 什么是Harbor2. Harbor 的特性3. Harbor的构成4. Harbor 的部署过程4.1 安装 harbor4.2 创建项目并进行上传下载4.3 上传镜像到私有仓库4.4 从私有仓…

Vue--插槽

一、插槽-默认插槽 1.作用 让组件内部的一些 结构 支持 自定义 2.需求 将需要多次显示的对话框,封装成一个组件 3.问题 组件的内容部分&#xff0c;不希望写死&#xff0c;希望能使用的时候自定义。怎么办 4.插槽的基本语法 组件内需要定制的结构部分&#xff0c;改用**…

mysql的主键选择

一.没有定义主键有什么问题 如果定义了主键&#xff0c;那么InnoDB会使用主键作为聚簇索引如果没有定义主键&#xff0c;那么会使用第一非空的唯一索引&#xff08;NOT NULL and UNIQUE INDEX&#xff09;作为聚簇索引如果既没有主键也找不到合适的非空索引&#xff0c;那么In…

【数据挖掘】时间序列的傅里叶变换:用numpy解释的快速卷积

一、说明 本篇告诉大家一个高级数学模型&#xff0c;即傅里叶模型的使用&#xff1b; 当今&#xff0c;傅里叶变换及其所有变体构成了我们现代世界的基础&#xff0c;为压缩、通信、图像处理等技术提供了动力。我们从根源上理解&#xff0c;从根本上应用&#xff0c;这是值得付…

STM32MP157驱动开发——按键驱动(线程化处理)

文章目录 “线程化处理”机制&#xff1a;内核函数线程化处理方式的按键驱动程序(stm32mp157)编程思路button_test.cgpio_key_drv.cMakefile修改设备树文件编译测试 “线程化处理”机制&#xff1a; 工作队列是在内核的线程的上下文中执行的 工作队列中有多个 work&#xff0…