初识Java 2-1 操作符

目录

优先级

赋值

递减和递增操作符

关系操作符

逻辑操作符

字面量

字面量中的下划线

科学记数法

按位操作符

移位操作符

三元操作符

字符串操作符+和+=

类型转换操作符

截尾和舍入


 

本笔记参考自: 《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()方法。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ThreeJS 模型中内嵌文字

之前有过模型中内嵌html网页&#xff0c;地址☞threeJS 模型中加载html页面_threejs 加载dom元素_小菜花29的博客-CSDN博客 这次是纯粹的在模型中嵌入文本信息&#xff0c;进行简单的文字展示 展示效果图 1. 使用FontLoader文字加载器 引入文本json文件&#xff0c;代码如下…

掉了无数头发成地中海后,我整理出了这套40+的大屏模板,快收藏!

最近又有不少粉丝后台问我接不接做可视化大屏&#xff0c;看来可视化大屏是越来越火啦&#xff0c;但老李还是要说一下&#xff0c;老李本身工作就很忙&#xff0c;实在是顾不过来&#xff0c;但老李会在自己体验过后为大家挑选合适的工具和模板&#xff0c;提升大家做大屏的效…

江西武功山旅游攻略(周末两日游)

一、 往返路线 1: 出发路线 周五晚上上海出发坐火车&#x1f684;到江西萍乡(11.5小时,卧铺550左右) 打车到江西武功山景区,120-150元左右,人均30元,1小时10分左右到达 或者 &#x1f697;到达萍乡北之后 出站后步行200米到长途汽车站&#xff0c;乘旅游巴士直达武功山游…

2023高教社杯数学建模思路 - 案例:FPTree-频繁模式树算法

文章目录 算法介绍FP树表示法构建FP树实现代码 建模资料 ## 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 算法介绍 FP-Tree算法全称是FrequentPattern Tree算法&#xff0c;就是频繁模式树算法&#xff0c…

leetcode 1022.从根到叶的二进制数之和

⭐️ 题目描述 &#x1f31f; leetcode链接&#xff1a;https://leetcode.cn/problems/sum-of-root-to-leaf-binary-numbers/description/ 代码&#xff1a; class Solution { public:int sum (TreeNode* root , int num 0) {if (root nullptr) {return 0;}int cur num r…

无涯教程-分类算法 - 多项式逻辑回归模型函数

Logistic逻辑回归的另一种有用形式是多项式Lo​​gistic回归&#xff0c;其中目标或因变量可以具有3种或更多可能的unordered类型&#xff0c;即没有定量意义的类型。 用Python实现 现在&#xff0c;无涯教程将在Python中实现上述多项式逻辑回归的概念。为此&#xff0c;使用…

算法通关村第8关【白银】| 二叉树的深度和高度问题

1.最大深度问题 思路&#xff1a;递归三部曲 第一步&#xff1a;确定参数和返回值 题目要求求二叉树的深度&#xff0c;也就是有多少层&#xff0c;需要传递一个root从底层向上统计 int maxDepth(TreeNode root) 第二步&#xff1a;确定终止条件 当递归到null时就说明到底了…

【MCU】SD NAND芯片之国产新选择

文章目录 前言传统SD卡和可贴片SD卡传统SD卡可贴片SD卡 实际使用总结 前言 随着目前时代的快速发展&#xff0c;即使是使用MCU的项目上也经常有大数据存储的需求。可以看到经常有小伙伴这样提问&#xff1a; 大家好&#xff0c;请问有没有SD卡芯片&#xff0c;可以直接焊接到P…

科技探究之旅--亲子研学活动

2023年8月26日&#xff0c;广州市从化区齐家社会工作服务中心&#xff08;以下简称“齐家”&#xff09;的“星乐园-乡村儿童公益辅导服务项目”组织了新开村及西湖村助学点24对亲子到广州市白云区文搏3D打印基地进行“科技探究之旅--亲子研学”活动&#xff0c;旨在发现、点燃…

长胜证券:沪指高开回落涨1.13%,地产、券商等板块强势

28日&#xff0c;A股两市早盘大幅高开&#xff0c;盘中有所回落&#xff0c;沪指、深证成指涨幅收窄至1.5%以内。截至收盘&#xff0c;沪指涨1.13%报3098.64点&#xff0c;深成指涨1.01%&#xff0c;创业板指涨0.96%&#xff0c;两市合计成交11266亿元。盘面上&#xff0c;渔业…

面试中的代码写作:如何撰写清晰、高效的示例代码

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

leetcode刷题(字符串相加、包含每个查询的最小区间、模拟行走机器人、环形子数组的最大和、满足不等式的最大值、四数之和、树中距离之和)

目录 1、字符串相加 2、包含每个查询的最小区间 3、模拟行走机器人 4、环形子数组的最大和 5、满足不等式的最大值 6、四数之和 7、 树中距离之和 1、字符串相加 class Solution:def addStrings(self, num1: str, num2: str) -> str:i len(num1) - 1 # num1的末…

HarmonyOS Codelab 优秀样例——购物应用,体验一次开发多端部署魅力

一. 样例介绍 本篇Codelab基于自适应布局和响应式布局&#xff0c;实现购物应用在手机、折叠屏、平板不同屏幕尺寸设备上按不同设计显示。通过三层工程结构组织代码&#xff0c;实现一次开发&#xff0c;多端部署 。 手机运行效果如图所示&#xff1a; 折叠屏运行效果图&#x…

【Leetcode】124.二叉树中的最大路径和(Hard)

一、题目 1、题目描述 二叉树中的 路径 被定义为一条节点序列,序列中每对相邻节点之间都存在一条边。同一个节点在一条路径序列中 至多出现一次 。该路径 至少包含一个 节点,且不一定经过根节点。 路径和 是路径中各节点值的总和。 给你一个二叉树的根节点 root ,返回其…

ResNet详解:网络结构解读与PyTorch实现教程

目录 一、深度残差网络&#xff08;Deep Residual Networks&#xff09;简介深度学习与网络深度的挑战残差学习的提出为什么ResNet有效&#xff1f; 二、深度学习与梯度消失问题梯度消失问题定义为什么会出现梯度消失&#xff1f;激活函数初始化方法网络深度 如何解决梯度消失问…

概念解析 | 无人机集群形状与轨迹建模: 集群舞蹈的艺术

注1:本文系“概念解析”系列之一,致力于简洁清晰地解释、辨析复杂而专业的概念。本次辨析的概念是:无人机集群形状和轨迹建模 无人机集群形状与轨迹建模: 集群舞蹈的艺术 无人机集群的形状和轨迹建模可能听起来像是一部科幻小说的标题,但它实际上是现实中的一个重要研究领…

随机化快速排序(Java 实例代码)

随机化快速排序 一、概念及其介绍 快速排序由 C. A. R. Hoare 在 1960 年提出。 随机化快速排序基本思想&#xff1a;通过一趟排序将要排序的数据分割成独立的两部分&#xff0c;其中一部分的所有数据都比另外一部分的所有数据都要小&#xff0c;然后再按此方法对这两部分数…

安防监控视频平台EasyCVR视频汇聚平台调用接口出现跨域现象的问题解决方案

视频监控汇聚EasyCVR可拓展性强、视频能力灵活、部署轻快&#xff0c;可支持的主流标准协议有GB28181、RTSP/Onvif、RTMP等&#xff0c;以及厂家私有协议与SDK接入&#xff0c;包括海康Ehome、海大宇等设备的SDK等&#xff0c;能对外分发RTSP、RTMP、FLV、HLS、WebRTC等格式的视…

【程序猿书籍大放送:第二期】《强化学习:原理与Python实战》

&#x1f339;欢迎来到爱书不爱输的程序猿的博客, 本博客致力于知识分享&#xff0c;与更多的人进行学习交流 爱书不爱输的程序猿&#xff1a;送书第二期 一、搞懂大模型的智能基因&#xff0c;RLHF系统设计关键问答1.RLHF是什么&#xff1f;2.RLHF适用于哪些任务&#xff1f;3…

1.1 数据库系统简介

1.1.数据库系统简介 前言&#xff1a; 数据库系统是一个软件系统&#xff0c;用于管理和操作数据库。它提供了一个组织良好、高效并能够方便存取的数据存储机制&#xff0c;并且能够支持各种数据操作、事务管理、并发控制和恢复功能。以下是数据库系统的一些主要特点和组件&a…