初识Java 2-1 操作符

news2025/1/21 4:59:18

目录

优先级

赋值

递减和递增操作符

关系操作符

逻辑操作符

字面量

字面量中的下划线

科学记数法

按位操作符

移位操作符

三元操作符

字符串操作符+和+=

类型转换操作符

截尾和舍入


 

本笔记参考自: 《On Java 中文版》


        Java的操作符大多继承自C++,但Java对其进行了一些改进和简化。操作符是对数据进行操作,能接受一个或多个参数,生成一个新的值。

    有些操作符能修改操作数自身的值,这被称为“副作用”。由“副作用”生成的值也可供使用。

优先级

        当多个操作符存在时,就需要进行优先级的决定。最简单的规则就是先乘除,后加减。但复杂的规则往往难以记住,因此使用括号“()”是一个不错的选择。例如:

public class Precedence {
    public static void main(String[] args) {
        int x = 1, y = 2, z = 3;
        int a = x + y - 2 / 2 + z;
        int b = x + (y - 2) / (x - 2);

        System.out.println("a = " + a);
        System.out.println("b = " + b);
    }
}

        上述程序使用了两条相似的语句,但语句的输出结果因为括号而不同:

        另外,在System.out.println()语句中使用的操作符+,这里的+代表着字符串的连接(如果必要,也会进行字符串的转换)。

赋值

        通过操作符=可以进行将符号右边的值赋给等号左边。其中左值必须是一个独特的命名变量(必须有一个可以储存值的物理空间)。

        对于基本类型而言,赋值很好理解,因为基本类型储存的是实际的值,赋值的过程就是将内容复制到了另一个地方。

int a = 10;
int b = a;

        但在为对象进行赋值的时候,情况就不一样了。因为操作对象,实际上是在操作这个对象的引用。如果“将一个对象赋值给另一个对象”,那么真正发生的是将一个引用复制到了另一个地方。例如:

class Tank {
    int level;
}

public class Assignment {
    public static void main(String[] args) {
        Tank t1 = new Tank();
        Tank t2 = new Tank();
        t1.level = 10;
        t2.level = 20;
        System.out.println("t1.level = " + t1.level + ", t2.level = " + t2.level);

        t1 = t2;        //此处t1的引用关联的是t2的指向的对象,t1原本的对象被覆盖了。
        System.out.println("t1.level = " + t1.level + ", t2.level = " + t2.level);

        t1.level = 30;
        System.out.println("t1.level = " + t1.level + ", t2.level = " + t2.level);
    }
}

        程序执行的结果如下:

        在上述的代码中,t1 = t2t1原本关联的对象覆盖了,此后t1关联的对象就变为了t2关联的对象。这种现象被称为“别名”,是Java操作对象的一种基本方式。若不想产生这个别名,可以这样做:

t1.level = t2.level;

    但是直接操作对象内部的字段违背了Java的设计原则,这是需要注意的。

别名陷阱

        将一个对象作为参数传递给方法时,也会产生别名。

class Letter {
    char c;
}

public class PassObject {
    static void f(Letter y) {
        y.c = 'z';
    }
}

        在上述代码中,方法f()会在其作用域的范围内生成一个参数Letter y的副本。由于实际上传递的是一个引用,所以若方法f()中的 y.c = 'z'; 实际上会改变f()之外的对象

递减和递增操作符

        Java中存在两种快捷运算符:递减操作符--(减少一个单位)和递增操作符++(增加一个单位)。这两种操作符都有两种用法,即前缀式后缀式,用法的区别在于操作符与变量的相对位置:在变量之前是前缀,反之是后缀。

        对于前缀的递增和递减,程序会先执行运算,再返回生成的结果;而对于后缀的二者而言,程序会优先返回变量的值,再执行运算。以递增为例:

public class AutoInc {
    public static void main(String[] args) {
        int i = 1;
        System.out.println("i = " + i);
        System.out.println("++i = " + ++i);
        System.out.println("i++ = " + i++);
        System.out.println("i = " + i);
    }
}

        程序的执行结果如下:

关系操作符

        关系操作符会根据操作数的值之间的关系生成一个布尔结果truefalse)。关系操作符有六种:<><=>===!=。在Java中,等于和不等于可用于所有类型,但是其他的比较操作不适用于boolean类型。因为boolean值只有真(true)和假(false)。

对象是否相等

        尽管操作符==!=适用范围广,但是在面对一些和对象相关的比较时,就会出现问题。例如:

public class Equivalence {
    static void show(String desc, Integer n1, Integer n2) {
        System.out.println(desc + ":");
        System.out.printf(
                "%d==%d %b %b%n", n1, n2, n1 == n2, n1.equals(n2));
    }    //%b表示布尔值

    public static void test(int value) {
        Integer i1 = value;
        Integer i2 = value;
        show("直接创建对象时", i1, i2);

        Integer r1 = Integer.valueOf(value);
        Integer r2 = Integer.valueOf(value);
        show("使用Integer.valueOf()创建对象时", r1, r2);

        int x = value;
        int y = value;
        System.out.println("使用原始的int时:");
        System.out.printf("%d==%d %b%n", x, y, x == y);
    }

    public static void main(String[] args) {
        test(127);
        System.out.println("\n");
        test(128);
    }
}

        该程序的输出结果是:

        上述比较之所以出现差异,关键在于:操作符==比较的是Integer对象的引用。

    Integer会根据值的大小范围使用两种不同的缓存方式:

        · 若值的范围在-128~127之间,Integer会使用享元模式进行缓存,因此即使多次调用Integer.valueOf(127),其生成的也是同一个对象。

        · 若在上述范围之外,Integer每次产生的将是不同的对象,因此两次调用Integer.valueOf(127)返回的是不同的对象。

        上述程序中,在test(127)的执行过程中,之所以操作符==可以返回正确的布尔值,不是因为该操作符进行了正确的比较,而只是因为两者的引用恰好相同罢了。因此,如果要对Integer对象进行比较,就需要使用equals()

        但equals()也不是万能的, 该方法的默认行为是比较引用。这意味着,如果对一个自定义的类使用equals(),例如:

class ValA {
    int i;
}

public class EqualsMethod {
    public static void main(String[] args) {
        ValA va1 = new ValA();
        ValA va2 = new ValA();
        va1.i = va2.i = 100;
        System.out.println(va1.equals(va2));
    }
}

        上述程序的结果是false,这就是因为默认的equals()比较的是引用。所以为了能够正确比较对象的值,就需要通过重写equals()等方式。

        另一种相等比较问题发生在进行极小数的比较时。例如:

public class DoubleEquivalence {
    static void show(String desc, Double n1, Double n2) {
        System.out.println(desc + ":");
        System.out.printf(
                "%e==%e %b %b%n", n1, n2, n1 == n2, n1.equals(n2));
    }

    public static void test(double x1, double x2) {
        System.out.printf("%e==%e %b%n", x1, x2, x1 == x2); // %e表示科学记数法
        Double d1 = x1;
        Double d2 = x2;
        show("直接创建对象时", d1, d2);

        Double r1 = Double.valueOf(x1);
        Double r2 = Double.valueOf(x2);
        show("使用Double.valueOf()创建对象时", r1, r2);
    }

    public static void main(String[] args) {
        test(Double.MAX_VALUE, Double.MAX_VALUE - Double.MIN_VALUE * 1_000_000);
    }
}

        程序运行的结果如下:

        这种错误出现的原因是因为,当一个非常大的值减去一个较小的值时,非常大的值不会出现明显变化,这就是舍入误差。而这种误差的出现,则来自于机器无法储存足够的信息来表示这种大数值的小变化。

逻辑操作符

        逻辑操作符“与”(&&)、“或”(||)和“非”(!)会根据参数的逻辑关系,生成布尔值结果。但在Java中,“与”和“或”操作只能用于布尔值

短路

        逻辑操作符支持“短路”现象:一旦表达式当前部分的计算结果能准确表达整个表达式的值,那么表达式剩余的部分将不再被执行。

public class ShortCircuit {
    static boolean test1(int val) {
        System.out.println("test1(" + val + ")");
        System.out.println("结果:" + (val < 1));
        return val < 1;
    }

    static boolean test2(int val) {
        System.out.println("test2(" + val + ")");
        System.out.println("结果:" + (val < 2));
        return val < 2;
    }

    static boolean test3(int val) {
        System.out.println("test3(" + val + ")");
        System.out.println("结果:" + (val < 3));
        return val < 3;
    }

    public static void main(String[] args) {
        boolean b = test1(0) && test2(2) && test3(2);
        System.out.println("\n表达式的值是:" + b);
    }
}

        上述程序的运行结果是:

        很明显,test3()没有被执行。这是因为test2()的结果是false,因此整个表达式的值肯定是false,剩下的表达式就没有必要执行了。通过这种短路的形式,可以减少资源的消耗。

字面量

        一般,编译器能够准确识别一个字面量的类型。但是在一些类型模糊的情况下,就必须使用与这些字面量相关的字符来进行引导。例如:

public class Literals {
    public static void main(String[] args) {
        int l1 = 0x2f; // 十六进制(小写)
        System.out.println("l1: " + Integer.toBinaryString(l1));

        int l2 = 0x2F; // 十六进制(大写)
        System.out.println("l2: " + Integer.toBinaryString(l2));

        int l3 = 0177; // 八进制(前置0)
        System.out.println("l3: " + Integer.toBinaryString(l3));

        char c = 0xffff; // char类型的最大十六进制值
        System.out.println("c: " + Integer.toBinaryString(c));

        byte b = 0x7f; // byte类型的最大十六进制值
        System.out.println("b: " + Integer.toBinaryString(b));

        long n1 = 200L; // L是long类型后缀
        long n2 = 200l; // L的大小写都可以
        long n3 = 200;

        float f1 = 2L;
        //以此类推...

        double d1 = 1D;
        //以此类推...
    }
}

        程序执行的结果如下:

        若试图将一个变量初始化为超出其范围的值,编译器会报错。上述程序展示了charbyte类型的最大十六进制值,当赋值超出了这一范围,编译器会自动将值转换为int类型,并说明本次赋值需要进行“窄化转型”。

    若将较小的类型传递给方法Integer.toBinaryString()方法,该类型会被自动转换为int类型。

字面量中的下划线

        在Java7之后,在数字字面量里使用下划线是被允许的。这在表示大数值时大有帮助。例如,可以这样初始化:

int bin = 0b0010_1111_1010_1111_1001_1000;

        在使用下划线时有几条规则:

  1. 只能使用单个的下划线,不能连续使用多个;
  2. 数字的开头和结尾不能存在下划线;
  3. 类似于FDL的后缀周围不能有下划线。
  4. 在二进制或十六进制标识符bx周围不能有下划线。

     在方法System.out.printf()中可以使用%n替代惯用的\n。这样替代的理由是在不同的平台上,换行符的表达是不同的。使用%n,Java会自动匹配各个平台的换行符,因此可以省去不必要的麻烦。


科学记数法

        在科学与工程领域,“e”表示自然对数的基数(约为2.718)。但是在一些程序语言中(Java也包含在内),e被用来表示“10的幂次”。所以如果在Java中看到类似于1.28e-43f的表达式时,其的含义实际上是:1.28\times 10^{-43}

        一般,若编译器能够准确识别类型,那么就不需要在数值后面添加后缀名。但是如果出现类似于下方展示的情况:

float f = 1e-23f;

在编译器中,指数通常被作为double类型处理,此时尾部的f就是必须的(否则我们将收到一条报错提示)。

按位操作符

        按位操作符可以操作整数基本类型中的单个二进制位(bit)。总共有四个操作符:按位与(&)、按位或(|)、按位异或(^)和按位非(~)。和加减乘除类似,按位操作符也可以和等号(=)联用:&=|=^=~是一元操作符,不能与等号联用)。

    布尔值只能进行按位与、或和异或操作,而不能执行按位非操作。另外,对于布尔值,按位操作符和逻辑操作符有相同的效果,但是按位操作符不会“短路”。

移位操作符

        移位操作符也能操作二进制位,且只能用来处理基本类型中的整数类型。移位操作符包括了三种操作符:

  • 左移(<<):将操作符左侧的操作数向左移动,移动的位数在操作符右侧指定。
  • 有符号右移(>>):将操作符左侧的操作数向有移动,若符号为正,在高位补0,否则补1。
  • 无符号右移(>>>):无论符号正负,都在高位补0。

    当对charbyteshort这些较小的类型进行移位运算时,在开始运算前会自动将它们的类型转变为int,并且结果也是int类型。

        若对int类型进行移位操作,在进行运算的时候,操作符右侧的可移位数只会用到低5位。而如果是处理long类型,那么只会用到可移位数的低6位。通过这种方式,可以放置移位超出类型允许的范围。

        若使用的是<<=>>=>>>=,那么在对charbyteshort等类型进行移位时,可能发生截断(此时得到的结果会是-1):

public class URshift {
    public static void main(String[] args) {
        short s = -1;
        System.out.println("没有移位时: " + Integer.toBinaryString(s));
        System.out.println("正确的移位: " + Integer.toBinaryString(s >>> 10));

        s >>>= 10;
        System.out.println("错误的移位: " + Integer.toBinaryString(s));
    }
}

        程序执行的结果如下:

        语句Integer.toBinaryString(s >>> 10)之所以没有出错,就是因为在移位之后,结果没有重新赋值给short类型的变量s,而是在完成运算之后直接打印,因此没有发生截断。

    在程序中表示的数字的二进制形式是“有符号的二进制补码”。

三元操作符

        三元操作符,即条件操作符,它有三个操作数。其形式如下:

boolean-exp ? value0: value1

        若boolean-exp的结果是true,则执行value0,其结果就是该三元操作符的结果值;反之,则执行value1,同样得到一个来自value1的结果值。

    三元操作符的应用场景主要在于从两个值中选择一个给变量赋值。

字符串操作符+和+=

        在Java中,++=操作符可以用来连接字符串。

    C++可以通过操作符重载来达成这一功能,但由于这一特性被认为过于复杂,所以Java无法像C++这样进行操作符的重载。

        若一个表达式以字符串开头,则其后面的所以操作数都得是字符串类型(编译器会强制转换):

public class StringOperators {
    public static void main(String[] args) {
        int x = 0, y = 1, z = 2;
        String s = "x, y, z ";

        System.out.println(s + x + y + z); // x、y和z的内容会被转变为字符串
        System.out.println(x + " " + s); // 开头的x的内容会被转换为字符串

        s += "(总和) = ";
        System.out.println(s + (x + y + z)); // 使用括号控制先后顺序

        System.out.println("" + x);       //类似于Integer.toString()的用法,但更简单
    }
}

        程序执行的结果如下:

        对语句System.out.println(s + x + y + z);而言,编译器会先将xyz转换为对应的字符串形式,这也是为什么没有发生整数求和,得到一个结果3。而在语句System.out.println(s + (x + y + z));中,由于通过括号控制了字符串转换的先后顺序,所以能够得到一个最终的结果。

类型转换操作符

        在适合的时候,Java会自动将一种类型转变为另一个类型。例如将整数值赋值给浮点变量时,编译器会自动将int类型转换为float类型。

        编译器可以自动地将较小的类型提升为较大的类型,但是有时候,我们会需要将较大的类型转变为较小的类型,即窄化转型

int i = 200;
long lng = i; // 向上提升,可以省略强制类型转换
i = (int)lng; // 窄化转型,需要强制类型转换

        在Java中,类型转换是比较安全的,但是在进行窄化转型时,依旧存在丢失信息的风险。这是因为较小的数据类型无法容纳更多的信息。

    boolean是应该特殊的类型,它不允许任何的类型转换。除此之外,“类”类型也不被允许转换为其他类型。

截尾和舍入

        通常地,若将一个较大的类型转换为较小的类型,程序往往会对数值进行截尾:

public class CastingNumbers {
    public static void main(String[] args) {
        double above = 0.7, below = 0.4;

        System.out.println("(int)above: " + (int)above);
        System.out.println("(int)below: " + (int)below);
    }
}

        程序执行的结果是:

        在上述程序中,我们将double类型的数据强制转换为int类型,并且发生了截尾,double类型数值的小数位被截断了。

    若需要进行舍入,可以使用java.lang.Math中的round()方法。

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

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

相关文章

胜券汇:科创板正在见底回升 再次重申看好科创板战略性机会

【胜券汇&#xff1a;科创板正在见底上升 再次重申看好科创板战略性时机】胜券汇表示&#xff0c;方针推进下&#xff0c;科创板正在见底上升&#xff0c;再次重申看好科创板战略性时机。从短期来看&#xff0c;IPO阶段性收紧&#xff0c;大股东减持行为标准&#xff0c;科创板…

画流程图都可以用哪些工具?

在日常生活中&#xff0c;我相信我们很多人都看到过流程图。对于设计师来说&#xff0c;它还需要涉及流程图来反映用户的旅程和交互方式。那么你知道哪些流行的流程图设计软件呢&#xff1f;作为高级设计师&#xff0c;我今天推荐10款流程图设计软件。你可以和我一起读这篇文章…

苹果手机怎么截图?苹果截图的3个方法!

求助&#xff01;之前一直用的安卓手机&#xff0c;截图只要在屏幕三指下滑即可。但最近刚换了苹果&#xff0c;不知道该怎么截图&#xff0c;有什么快捷的截图方法吗&#xff1f; 在我们的学习或者工作中&#xff0c;我们经常需要用到截图工具来将手机上的一些重要内容保存下来…

Hard Problems简介

1. 引言 computational complexity的核心问题在于&#xff1a; 计算机容易实现什么&#xff1f;计算机几乎不可能实现什么&#xff1f; 某hard problem从根本上有多困难&#xff1f;这是计算机科学家的基本任务&#xff0c;他们希望将问题分类为所谓的“complexity classes”…

美团笔试题之合并 K 个升序链表

文章目录 题目详情分析暴力求解两两合并链表 Java完整实现代码总结 题目详情 23 美团笔试真题 给你一个链表数组&#xff0c;每个链表都已经按升序排列。 请你将所有链表合并到一个升序链表中&#xff0c;返回合并后的链表。 分析 暴力求解 将所有数值存入一个数组&#xff…

深入解析G1垃圾回收器

本文已收录至GitHub&#xff0c;推荐阅读 &#x1f449; Java随想录 微信公众号&#xff1a;Java随想录 原创不易&#xff0c;注重版权。转载请注明原作者和原文链接 文章目录 基于Region的堆内存布局可预测的停顿时间模型跨Region引用对象对象引用关系改变运作过程CMS VS G1 …

部队物资仓库管理系统|DW-S301是一套成熟系统

根据军队物资装备管理的实际需求&#xff0c;集驰电子设计了部队物资仓库管理系统&#xff08;智装备&#xff1a;DW-S301&#xff09;。 随着军事装备物资的使用频率与消耗数量日益增加&#xff0c;部队对于仓库保障工作的要求越来越高&#xff0c;同时也带来仓库管理工作任务…

电子仓库预测水浸事件,他怎么做到的?

仓库环境中水浸事件可能导致严重的损失&#xff0c;不仅对货物造成损害&#xff0c;还可能影响设备的正常运行甚至威胁安全。 因此&#xff0c;为了应对这一挑战&#xff0c;引入一套完善的仓库水浸监控系统成为了不可或缺的措施。 客户案例 广东某电子公司是一家领先的电子设…

kafka调优配置

Kafka生产者核心参数配置 来源于尚硅谷 参数名称描述bootstrap.servers生产者连接集群所需的broker地址清单。例如hadoop102:9092,hadoop103:9092,hadoop104:9092&#xff0c;可以设置1个或者多个&#xff0c;中间用逗号隔开。注意这里并非需要所有的broker地址&#xff0c;因…

bpmnjs Properties-panel拓展(ExtensionElements拓展篇)

接上文bpmnjs Properties-panel拓展&#xff08;属性设置篇&#xff09;&#xff0c;继续记录下第三个拓展需求的实现。 需求简述 在ExclusiveGateway标签的extensionElements标签中增加子标签<activiti:executionListener>子标签&#xff0c;可增加复数子标签。子标签…

vLLM 实战

引言 随着人工智能技术的飞速发展&#xff0c;以及今年以来 ChatGPT 的爆火&#xff0c;大语言模型 (Large Language Model, LLM) 受到越来越多的关注。 为了实现 LLM 部署时的推理优化&#xff0c;全球各地有众多团队做出了各种优化框架。本文以加州大学伯克利分校开发的 vLLM…

R语言之基础绘图

文章和代码已经归档至【Github仓库&#xff1a;https://github.com/timerring/dive-into-AI 】或者公众号【AIShareLab】回复 R语言 也可获取。 文章目录 1. 函数 plot( )2.直方图和密度曲线图3.条形图4. 饼图5. 箱线图和小提琴图6. 克利夫兰点图7. 导出图形小结 R 的基础绘图系…

大模型开发05:PDF 翻译工具开发实战

大模型开发实战05:PDF 翻译工具开发实战 PDF-Translator 机器翻译是最广泛和基础的 NLP 任务 PDF-Translator PDF 翻译器是一个使用 AI 大模型技术将英文 PDF 书籍翻译成中文的工具。这个工具使用了大型语言模型 (LLMs),如 ChatGLM 和 OpenAI 的 GPT-3 以及 GPT-3.5 Turbo 来…

【通用消息通知服务】0x4 - 目前进展 阶段复盘

【通用消息通知服务】0x4 - 阶段复盘 达成 基本的API已经写完✍️了(消息查看发送, 模板crud,终端crud,发送渠道crud,计划crud,计划执行查看)拆分server, executor, planner三个入口, 方便针对性水平扩展整体架构初步形成&#xff0c;通过队列实现了事件驱动模型和消息订阅发…

Spring Boot(Vue3+ElementPlus+Axios+MyBatisPlus+Spring Boot 前后端分离)【六】

&#x1f600;前言 本篇博文是关于Spring Boot(Vue3ElementPlusAxiosMyBatisPlusSpring Boot 前后端分离)【六】&#xff0c;希望你能够喜欢 &#x1f3e0;个人主页&#xff1a;晨犀主页 &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是晨犀&#xff0c;希望我的文章…

Apache RocketMQ 5.0 消息进阶:如何支撑复杂的业务消息场景?

作者&#xff1a;隆基 一致性 首先来看 RocketMQ 的第一个特性-事务消息&#xff0c;事务消息是 RocketMQ 与一致性相关的特性&#xff0c;也是 RocketMQ 有别于其他消息队列的最具区分度的特性。 以大规模电商系统为例&#xff0c;付款成功后会在交易系统中订单数据库将订单…

三---开关稳压器

通过控制系统反馈&#xff0c;当电压上升时通过反馈降低&#xff0c;当电压下降时通过反馈升高&#xff1b;形成一个控制环路&#xff1b;控制电路&#xff1a;PWM&#xff08;脉宽调制&#xff09;&#xff0c;PFM&#xff08;频率控制方式&#xff09;&#xff0c;移相控制方…

2023开学季有哪些电容笔值得买?平价电容笔推荐

在日常生活中&#xff0c;这支电容笔的使用范围很广&#xff0c;不管是和电脑、IPAD、手机一起使用&#xff0c;都可以说是一种很好的办公工具。首先要弄清楚自己的需求&#xff0c;再根据自己的需求来挑选合适的商品。苹果的Pencil有一种独特的重力压感&#xff0c;让人在上面…

【MTK平台】根据kernel log分析wifi scan的时候流程

一 概要: 本文主要讲解根据kernel log分析下 当前路径下(vendor/mediatek/kernel_modules/connectivity/wlan/core/gen4m/)wifi scan的时候代码流程 二. Log分析: 先看Log: 2.1)在Framework层WifiManager.java 方法中,做了一个标记,可以精准的确认时间 这段log可以…

基于Qt5开发图形界面——WiringPi调用Linux单板电脑IO

Qt5——WiringPi Qt5WiringPi示例教程 Qt5 Qt是一种跨平台的应用程序开发框架。它被广泛应用于图形用户界面&#xff08;GUI&#xff09;开发&#xff0c;可以用于构建桌面应用程序、移动应用程序和嵌入式应用程序。Qt提供了丰富的功能和工具&#xff0c;使开发人员可以快速、高…