Java 自增自减运算符和移位运算符介绍
Posted JavaGuide
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java 自增自减运算符和移位运算符介绍相关的知识,希望对你有一定的参考价值。
摘自 JavaGuide (「Java学习+面试指南」一份涵盖大部分 Java 程序员所需要掌握的核心知识。准备 Java 面试,首选 JavaGuide!)
自增自减运算符
在写代码的过程中,常见的一种情况是需要某个整数类型变量增加 1 或减少 1,Java 提供了一种特殊的运算符,用于这种表达式,叫做自增运算符(++)和自减运算符(--)。
++ 和 -- 运算符可以放在变量之前,也可以放在变量之后,当运算符放在变量之前时(前缀),先自增/减,再赋值;当运算符放在变量之后时(后缀),先赋值,再自增/减。例如,当 b = ++a
时,先自增(自己增加 1),再赋值(赋值给 b);当 b = a++
时,先赋值(赋值给 b),再自增(自己增加 1)。也就是,++a 输出的是 a+1 的值,a++输出的是 a 值。用一句口诀就是:“符号在前就先加/减,符号在后就后加/减”。
移位运算符
移位运算符是最基本的运算符之一,几乎每种编程语言都包含这一运算符。移位操作中,被操作的数据被视为二进制数,移位就是将其向左或向右移动若干位的运算。
移位运算符在各种框架以及 JDK 自身的源码中使用还是挺广泛的,HashMap
(JDK1.8) 中的 hash
方法的源码就用到了移位运算符:
static final int hash(Object key)
int h;
// key.hashCode():返回散列值也就是hashcode
// ^ :按位异或
// >>>:无符号右移,忽略符号位,空位都以0补齐
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
在 Java 代码里使用 <<
、 >>
和>>>
转换成的指令码运行起来会更高效些。
掌握最基本的移位运算符知识还是很有必要的,这不光可以帮助我们在代码中使用,还可以帮助我们理解源码中涉及到移位运算符的代码。
Java 中有三种移位运算符:
<<
:左移运算符,向左移若干位,高位丢弃,低位补零。x << 1
,相当于 x 乘以 2(不溢出的情况下)。>>
:带符号右移,向右移若干位,高位补符号位,低位丢弃。正数高位补 0,负数高位补 1。x >> 1
,相当于 x 除以 2。>>>
:无符号右移,忽略符号位,空位都以 0 补齐。
由于 double
,float
在二进制中的表现比较特殊,因此不能来进行移位操作。
移位操作符实际上支持的类型只有int
和long
,编译器在对short
、byte
、char
类型进行移位前,都会将其转换为int
类型再操作。
如果移位的位数超过数值所占有的位数会怎样?
当 int 类型左移/右移位数大于等于 32 位操作时,会先求余(%)后再进行左移/右移操作。也就是说左移/右移 32 位相当于不进行移位操作(32%32=0),左移/右移 42 位相当于左移/右移 10 位(42%32=10)。当 long 类型进行左移/右移操作时,由于 long 对应的二进制是 64 位,因此求余操作的基数也变成了 64。
也就是说:x<<42
等同于x<<10
,x>>42
等同于x>>10
,x >>>42
等同于x >>> 10
。
左移运算符代码示例 :
int i = -1;
System.out.println("初始数据: " + i);
System.out.println("初始数据对应的二进制字符串: " + Integer.toBinaryString(i));
i <<= 10;
System.out.println("左移 10 位后的数据 " + i);
System.out.println("左移 10 位后的数据对应的二进制字符 " + Integer.toBinaryString(i));
输出:
初始数据: -1
初始数据对应的二进制字符串: 11111111111111111111111111111111
左移 10 位后的数据 -1024
左移 10 位后的数据对应的二进制字符 11111111111111111111110000000000
由于左移位数大于等于 32 位操作时,会先求余(%)后再进行左移操作,所以下面的代码左移 42 位相当于左移 10 位(42%32=10),输出结果和前面的代码一样。
int i = -1;
System.out.println("初始数据: " + i);
System.out.println("初始数据对应的二进制字符串: " + Integer.toBinaryString(i));
i <<= 42;
System.out.println("左移 10 位后的数据 " + i);
System.out.println("左移 10 位后的数据对应的二进制字符 " + Integer.toBinaryString(i));
右移运算符使用类似,篇幅问题,这里就不做演示了。
Java自增自减运算符
Java10个基础题
1: 自增自减运算符
- ++和–运算符可以放在变量之前,也可以放在变量之后,当运算符放在变量之前时(前缀),先自增/减,再赋值;当运算符放在变量之后时(后缀),先赋值,再自增/减
- 例如,当 b = ++a 时,先自增(自己增加 1),再赋值(赋值给 b);当 b = a++ 时,先赋值(赋值给 b),再自增(自己增加 1)。也就是,++a 输出的是 a+1 的值,a++输出的是 a 本身值。
2: 自动装箱与拆箱
- 装箱:将基本类型用它们对应的引用类型包装起来
- 拆箱:将包装类型转换为基本数据类型
- 装箱其实就是调用了 包装类的valueOf()方法,拆箱其实就是调用了 xxxValue()方法。
Integer i = 10; //装箱
int n = i; //拆箱
Integer i = 10 等价于 Integer i = Integer.valueOf(10)
int n = i 等价于 int n = i.intValue();
3: ==和equals比较
- == 比较基本数据类型的时候,比较的是值,引用数据类型比较的是地址(new的对象,==比较永远是false)
- equals:属于Object类的方法,如果我们没有重写过equals方法,那么它就是 ==,但是字符串里面的equals被重写过了,比较的是值
String str1 = "abc";
String str2 = new String("abc");
String str3 = "abc";
String str4 = "xxx";
String str5 = "abc" + "xxx";
String str6 = s3 + s4;
str1 == str2:false
str1.equals(str2):true
str1 == str5:false
str1 == str6:false
str5 == str6:false
str5.equals(str6):true
4: 为什么重写 equals 时必须重写 hashCode 方法?
- 比如在HasMap中,put一个值,则先会计算出其hashCode值,得到对应的索引位置,那么可能该索引位置上已经有值了,那么此时就会调用equals方法,比较两个值是不是一样的。所以重写 equals 时必须重写 hashCode 方法
5: String、StringBuffer、StringBuilder
- String是final修饰的,不可变,每次操作都会产生新的String对象
- StringBuffer是线程安全的,StringBuilder线程不安全的
- 性能:StringBuilder > StringBuffer > String,所以经常需要改变字符串内容时使用前面两个效率会更高
6: 重载和重写的区别
- 重载: 发生在同一个类中,方法名必须相同,参数类型不同、个数不同、顺序不同,方法返回值和访问修饰符可以不同,发生在编译时。
- 重写: 发生在父子类中,方法名、参数列表必须相同,返回值范围小于等于父类,抛出的异常范围小于等于父类,访问修饰符范围大于等于父类;如果父类方法访问修饰符为private则子类就不能重写该方法。
7: 接口和抽象类的区别
- 抽象类可以存在普通成员函数,而接口中只能存在public abstract 方法。
- 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型的。
- 抽象类只能继承一个,接口可以实现多个。
8: List和Set的区别
- List:有序,按对象进入的顺序保存对象,可重复,允许多个Null元素对象,可以使用Iterator取出所有元素,在逐一遍历,还可以使用get(int index)获取指定下标的元素
- Set:无序,不可重复,最多允许有一个Null元素对象,取元素时只能用Iterator接口取得所有元素,在逐一遍历各个元素
9: ArrayList和LinkedList区别
- Arraylist 底层使用的是 Object 数组;LinkedList 底层使用的是 双向链表 数据结构
- LinkedList 不支持高效的随机元素访问,而 ArrayList 支持。快速随机访问就是通过元素的序号快速获取元素对象(对应于get(int index)方法)。
- ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全;
- ArrayList 采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响;LinkedList 采用链表存储,所以对于add(E e)方法的插入,删除元素时间复杂度不受元素位置的影响
10: continue、break、和 return 的区别是什么?
- continue :指跳出当前的这一次循环,继续下一次循环。
- break :指跳出整个循环体,继续执行循环下面的语句。
- return; :直接使用 return 结束方法执行,用于没有返回值函数的方法
- return value; :return 一个特定值,用于有返回值函数的方法
以上是关于Java 自增自减运算符和移位运算符介绍的主要内容,如果未能解决你的问题,请参考以下文章