高階Java工程師養成記-Java基礎程式設計之運算子
高階Java工程師養成記-Java基礎程式設計之運算子
高階Java工程師養成記運算子相關概念
計算機最主要的任務就是完成生產生活中的各種資料的運算,在Java中提供了諸多的運算子來完成相關資料的運算,在日常程式開發中最常用的有算術運算子、自增運算子、關係運算符、邏輯運算子以及三目運算子,在JDK原始碼中會使用到基於二進位制的位運算子。
在瞭解具體運算子的使用之前,先了解關於運算子的幾個概念
運算子相關概念的案例
package net.ittimeline.java.core.jdk.foundational.operator;
/**
* 運算子相關概念
*
* 運算子:資料執行的運算型別,例如算術、邏輯、關係、位運算
* 運算元:參與運算的資料,例如2,(3+2)都是運算元
* 表示式:由運算元和運算子組成,通常是完成某種業務資料計算而設計的,例如計算圓的面積
* 優先順序:在各種運算子參與運算時,有不同的執行順序,由它們的優先順序決定,可以使用()改變優先順序
* 結合性:執行運算的方向,例如從左到右或者從右到左
* @author liuguanglei [email protected]
* @version 2020/8/4 10:30 上午
* @since JDK11
*/
public class OperatorConcept {
public static void main(String[] args) {
// 3+5就是表示式,3,5就是運算元
int result=3+5;
System.out.println("result ="+result);
/**
* 算術運算的優先順序是先乘除後加減
* 可以使用()改變優先順序
*/
int precedence=(45+5)/5*10;
System.out.println("precedence = "+precedence);
}
}
-
運算子:指定資料執行的運算型別,例如算術,邏輯,關係,比較,位運算等等
-
運算元:就是參與運算的資料,例如2,(3+2)都是一個運算元
-
表示式:由運算元和運算子組成,通常都是完成某種業務資料計算而設計的,例如計算圓的面積等等。
-
優先順序:在各種運算子參與運算時,有不同的執行順序,由它們的優先順序決定,可以使用()來改變執行的順序
-
結合性:執行計算的方向,一般是從左到右,但是賦值運算是從右到左。
不同優先順序的表示式運算結果不同的案例
package net.ittimeline.java.core.jdk.foundational.operator;
/**
* 運算子的優先順序
*
* @author liuguanglei [email protected]
* @version 2020/8/4 10:38 上午
* @since JDK11
*/
public class Precedence {
public static void main(String[] args) {
int x=1,y=2,z=3;
//這裡會先執行2/2,然後會執行1+2-1+3 結果等於5
int a=x+y-2/2+z;
//這裡會執行1+0/5 結果等於1
int b=x+(y-2)/(2+z);
//相同的表示式,改變了優先順序後,運算的結果不相同
System.out.println("a ="+a);
System.out.println("b ="+b);
}
}
算術運算子
算術運算就是數學意義上的加減乘除以及取模運算,Java中支援的算術運算子包含加法(+)、減法(-)、乘法(*)、除法(/
)、和取模(%
)
- 算術運算子的優先順序是先乘除,後加減。
- 算術運算子的結合性是從左到右
- 算術運算計算結果的型別是參與運算的最大資料型別,例如整數和浮點數運算的結果是浮點型
- 賦值(=)時會進行自動型別轉換
算術運算的使用案例
package net.ittimeline.java.core.jdk.foundational.operator.arithmetic;
/**
* 算術運算子的使用
*
* @author liuguanglei [email protected]
* @version 2020/8/4 10:34 上午
* @since JDK11
*/
public class ArithmeticTest {
public static void main(String[] args) {
//算術計算的結果的資料型別是參與運算的最大資料型別
int source = 12;
int target = 5;
//int和int運算的結果是int
int result = source / target;
System.out.println("result = " + result);
//算術運算子結合性是從左向右運算
result = source / target * target;
//result=10
System.out.println("result = " + result);
//賦值時會執行自動型別轉換
double dblResult = source / target;
//運算結果是2.0
System.out.println("dblResult =" + dblResult);
//double和int運算結果是double
//通過強制型別轉換將int轉換為double
dblResult = (double) source / target;
//獲取精確的執行結果
System.out.println("dblResult =" + dblResult);
}
}
取模運算是求餘數,開發中通常用於判斷資料是否能夠整除。取模也會作為資料庫中介軟體MyCAT,ShardingJDBC實現取模演算法。
取模運算結果的符號型別(正負)和被模數一致
整數和浮點數可以進行取模運算
算術運算之取餘運算的案例
package net.ittimeline.java.core.jdk.foundational.operator.arithmetic;
/**
* 算術運算的取餘運算
*
* @author liuguanglei [email protected]
* @version 2020/8/4 10:43 上午
* @since JDK11
*/
public class ArithmeticRemainderTest {
public static void main(String[] args) {
int source = 12;
int target = 5;
int result = source % target;
//12/5=2..2 即餘數的結果就是2
System.out.println("result = " + result);
//運算結果表明求餘數結果的符號和被餘數相同
source = -12;
result = source % target;
System.out.println("result = " + result);
source = 12;
target = -5;
result = source % target;
System.out.println("result =" + result);
double dblSource = 12.5;
double dblTarget = 5.0;
double dblResult = dblSource % dblTarget;
//浮點數也可以取模
System.out.println("dblResult = " + dblResult);
}
}
算術運算之取餘運算:實現五位整數反轉的案例
package net.ittimeline.java.core.jdk.foundational.operator.arithmetic;
/**
* 算術運算之取餘運算-實現五位整數反轉
*
* @author liuguanglei [email protected]
* @version 2020/8/4 10:45 上午
* @since JDK11
*/
public class ArithmeticRemainderReversal {
public static void main(String[] args) {
int number=12345;
System.out.println("反轉之前的五位整數是"+number);
//首先使用取模運算獲取各個位的數字
//個位
int theUnit=number/10000;
//十位
int decade=number/1000%10;
//百位
int hundreds=number/100%10;
//千位
int kilobit=number%100/10;
//萬位
int myriabit=number%100%10;
int invert=myriabit*10000+kilobit*1000+hundreds*100+decade*10+theUnit;
System.out.println("反轉之後的五位整數是"+invert);
}
}
算術運算的案例:使用基本資料型別的包裝類、字串陣列以及算術運算根據距離和時間計算速度
package net.ittimeline.java.core.jdk.foundational.operator.arithmetic;
/**
* 使用基本資料型別的包裝類、字串陣列以及算術運算根據距離和時間計算速度
* @author liuguanglei [email protected]
* @version 2020/8/4 10:47 上午
* @since JDK11
*/
public class Velocity {
public static void main(String[] args) {
//例項化陣列
args=new String[]{"1200","300"};
//陣列中需要兩個元素
if(args.length<2){
System.err.println("需要倆引數");
//系統異常退出
System.exit(1);
}
//使用包裝類將字串轉換為float
float distance=Float.parseFloat(args[0]);
//args[0]表示訪問陣列的第一個元素
//args[1]表示訪問陣列的第二個元素
float time=Float.parseFloat(args[1]);
//float和float運算的型別是float
System.out.print("Velocity = ");
System.out.print(distance/time);
}
}
自增(自減)運算子
自增運算子主要用於變數自增1,用於變數自增1的運算子是++,但是++可以放在變數前面,也可以放在變數後面,放在前面時,變數先自增1,然後再參與運算,++放在變數的後面,變數先運算,再自增1。
自減運算子使用--表示,用於變數自減1,也可以放在變數的前面和後面,分別表示先減1,再參與運算和先參與運算,再減1。
- 自增(自減)運算子不會改變變數本身的資料型別,因此在運算時需要考慮當前資料型別的極限值
- 自增(自減)運算子如果是一條單獨的語句,前置++(--)和後置++(--)的效果是一樣的
自增運算子的示例
package net.ittimeline.java.core.jdk.foundational.operator.autoincrement;
/**
* 自增運算子
* 作用是針對整型變數加1
* 自增運算不會改變變數本身的資料型別
* @author liuguanglei [email protected]
* @version 2020/8/4 11:02 上午
* @since JDK11
*/
public class AutoincrementAddTest {
public static void main(String[] args) {
//自增運算子是單獨的語句,前置++和後置++的結果是一樣的
int tmp=10;
tmp++;
System.out.println("tmp = "+tmp);
int val=10;
++val;
System.out.println("val = "+val);
int num=10;
//後置++,先運算,後自增1
int result=num++;
//result=10
System.out.println("result = "+result);
//num=11
System.out.println("num ="+num);
int source=10;
//前置++ 先自增1,後運算
result=++source;
//result=11
System.out.println("result ="+result);
//source=11
System.out.println("source ="+source);
short shortVal=10;
//shortVal的型別還是short
System.out.println(shortVal++);
byte byteVal=10;
//byteVal的資料型別還是byte
System.out.println(byteVal++);
}
}
自減運算子的示例
package net.ittimeline.java.core.jdk.foundational.operator.autoincrement;
/**
* 自減運算
*
* @author liuguanglei [email protected]
* @version 2020/8/4 11:03 上午
* @since JDK11
*/
public class AutoincrementSubTest {
public static void main(String[] args) {
int num = 10;
//先運算,後減1
int result = num--;
System.out.println("result = " + result);
System.out.println("numb = " + num);
int source = 10;
//先減1再運算
int target = --source;
System.out.println("target = " + target);
System.out.println("source = " + source);
}
}
自增自減運算子的示例
package net.ittimeline.java.core.jdk.foundational.operator.autoincrement;
/**
* 自增和自減
*
* @author liuguanglei [email protected]
* @version 2020/8/4 1:59 下午
* @since JDK11
*/
public class AutoInc {
public static void main(String[] args) {
int i=1;
System.out.println("i = "+i);
//先自增1,再列印輸出結果2
System.out.println("++i = "+ ++i);
//先列印輸出結果 2,再果自增1
System.out.println("i++ = "+ i++);
// i=3
System.out.println("i = " + i);
//先自減1,再列印輸出結果2
System.out.println("--i = "+ --i);
//先列印輸出結果2,再自減1
System.out.println("i-- = " + i--);
// i=1
System.out.println("i = " + i);
}
}
自增(自減)運算子的常用應用場景就是在for迴圈中改變迴圈條件的值。
自增(自減)運算應用場景的示例
package net.ittimeline.java.core.jdk.foundational.operator.autoincrement;
/**
* 自增和自減運算子的應用場景
* @author liuguanglei [email protected]
* @version 2020/8/4 11:04 上午
* @since JDK11
*/
public class AutoincrementApp {
public static void main(String[] args) {
System.out.println("自增運算子的應用場景");
//自增運算子的應用場景
//定義一個字串
String content="Java架構師成長之道";
//將字串轉換為字元陣列
char[] contents=content.toCharArray();
//迴圈遍歷字元陣列 這裡的c++表示將迴圈的初始條件自增1
for (char c =0;c<content.length();c++){
System.out.print(contents[c]+" ");
}
//自減運算的應用場景
System.out.println("自減運算的應用場景");
String car="法拉利拉法";
char[] carDesc=car.toCharArray();
for(int i=carDesc.length-1;i>=0;i--){
System.out.print(carDesc[i]+"");
}
}
}
自增自減運算子混合運算的示例
package net.ittimeline.java.core.jdk.foundational.operator.autoincrement;
/**
* 自增自減運算子的混合運算
* @author liuguanglei [email protected]
* @version 2020/8/4 11:05 上午
* @since JDK11
*/
public class AutoincrementAddSubTest {
public static void main(String[] args) {
int i = 10;
int j = 20;
// i++ 等價於i+=1 等價於i=
int k = i++;
//k=10
System.out.println("k = " + k);
//i =11
System.out.println("i = " + i);
k = ++i;
//k = 12
System.out.println("k = " + k);
//i =12
System.out.println("i = " + i);
k = j--;
//k=20
System.out.println("k =" + k);
//j=19
System.out.println("j = " + j);
k=--j;
//k=18
System.out.println("k = " + k);
//j=18
System.out.println("j = " + j);
}
}
自增運算子的本質:讓變數自增的三種方式的示例
package net.ittimeline.java.core.jdk.foundational.operator.autoincrement;
/**
* 自增運算的本質
*
* @author liuguanglei [email protected]
* @version 2020/8/4 11:07 上午
* @since JDK11
*/
public class AutoincrementVariable {
public static void main(String[] args) {
int number = 12;
number++;
System.out.println("number = " + number);
//等價於
number = 12;
number += 1;
System.out.println("number = " + number);
//等價於
number = 12;
number = number + 1;
System.out.println("number = " + number);
}
}
賦值運算子
賦值通常是給變數賦值,Java中使用"="來表示賦值,而"=="表示相等,賦值是將=右邊的值或者表示式賦值給左邊的變數。
- 當賦值號(=)兩邊的資料型別不一致時,可以使用自動型別轉換或者強制型別轉換進行處理
- 支援連續賦值,即同時宣告並賦值多個變數
- 賦值運算不會改變變數本身的資料型別
賦值運算的示例
package net.ittimeline.java.core.jdk.foundational.operator.assignment;
/**
* 賦值運算子
* =
* 算術運算和賦值運算
* += -= *= /= %=
* @author liuguanglei [email protected]
* @version 2020/8/4 11:09 上午
* @since JDK11
*/
public class AssignmentTest {
public static void main(String[] args) {
//宣告變數並賦值
int i=10;
int j=10;
//連續賦值,同時宣告兩個變數k,l並賦值為10
int k=10,l=10;
System.out.println("i = "+i+ " j = "+j +" k = "+k+ " l = "+l);
//賦值的左邊必須是變數,右邊可以是變數值,也可以是表示式
//計算四個整數的和
int result=i+j+k+l;
System.out.println("result = "+result);
int o,p,q;
//同時賦值
o=p=q=20;
System.out.println("o = "+o+ " p = "+p +" q = "+q);
//賦值不會改變變數的資料型別
//通常情況下byte和int運算的型別是int
byte byteVal=12;
byteVal=(byte)(byteVal+1);
System.out.println("byteVal = "+byteVal);
//但是如果使用賦值運算,不會改變變數的資料型別
byteVal=12;
byteVal+=1;
System.out.println("byteVal = "+byteVal);
}
}
JDK提供了java.util.Random類用於生成隨機數,詳細的使用說明可以查閱JDK API文件,後續在編寫應用案例時,可以使用它來生成測試資料。
package net.ittimeline.java.core.jdk.foundational.operator;
import java.util.Random;
/**
* Random類生成隨機數
*
* @author liuguanglei [email protected]
* @version 2020/8/4 11:11 上午
* @since JDK11
*/
public class RandomTest {
public static void main(String[] args) {
/**
* 建立一個隨機數
* 88表示初始種子,種子相同,每次產生的序列相同,種子不同,每次產生的序列不同
*
*/
Random random = new Random(88);
//生成0-99之間的整數
int left = random.nextInt(100);
int right = random.nextInt(100);
System.out.println("left = " + left + " right =" + right);
}
}
賦值運算子還可以可算術運算子結合使用,例如+= ,-=,*=,/=,
%=`。
賦值運算和算術運算的混合使用示例1
package net.ittimeline.java.core.jdk.foundational.operator.assignment;
import java.util.Random;
import static java.lang.System.out;
/**
* 賦值運算和算術運算的混合使用示例(1)
* @author liuguanglei [email protected]
* @version 2020/8/4 11:12 上午
* @since JDK11
*/
public class MathOps {
public static void main(String[] args) {
//建立一個隨機數物件
Random random=new Random(88);
int i,j,k;
//隨機產生一個1-100之間的整數
j=random.nextInt(100)+1;
k=random.nextInt(100)+1;
out.println("j = "+j);
out.println("k = "+k);
i=j+k;
out.println("j + k = "+i);
i=j-k;
out.println("j - k = "+i);
i=j*k;
out.println("j * k = "+i);
i=j/k;
out.println("j / k = "+i);
i=j%k;
out.println("j % k = "+i);
j%=k;
out.println("j %= k "+j);
float u,v,w;
v=random.nextFloat();
w=random.nextFloat();
out.println("v = "+v);
out.println("w = "+w);
u=v+w;
out.println("v + w = "+u);
u=v-w;
out.println("v - w = "+u);
u=v*w;
out.println("v * w = "+u);
u=v/w;
out.println("v / w = "+u);
u+=v;
out.println("u+v = "+u);
u-=v;
out.println("u-v = "+u);
u*=v;
out.println("u*v = "+u);
u/=v;
out.println("u/v = "+u);
}
}
賦值運算和算術運算的混合使用示例2
package net.ittimeline.java.core.jdk.foundational.operator.assignment;
/**
* 賦值運算和算術運算的混合使用示例2
*
* @author liuguanglei [email protected]
* @version 2020/8/4 11:13 上午
* @since JDK11
*/
public class AssignmentArithmeticTest {
public static void main(String[] args) {
int m=2;
int n=3;
/**
* 拆解表示式
* n*=m++
* n=n*m++
* n=3*2
* n=6
* m=3
*/
n*=m++;
System.out.println("n = "+n);
System.out.println("m = "+m);
n=4;
/**
* 拆解表示式
* n+=(n++)+(++n);
* n=n+(n++)+(++n)
* n=4+4+6
* n=14
*/
n+=(n++)+(++n);
System.out.println("n = "+n);
}
}
引用資料型別賦值的示例
package net.ittimeline.java.core.jdk.foundational.operator.assignment;
/**
* 引用資料型別的賦值
* @author liuguanglei [email protected]
* @version 2020/8/4 1:51 下午
* @since JDK11
*/
public class Assignment {
public static void main(String[] args) {
// 如果是為物件賦值,那麼結果就不一樣了。對一個物件進行操作時,我們實際上操作的是它的引用。
Tank t1=new Tank();
Tank t2=new Tank();
t1.level=88;
t2.level=99;
System.out.println("t1.level = "+t1.level);
System.out.println("t2.level = "+t2.level);
// 所以我們將右邊的物件賦予給左邊時,賦予的只是該物件的引用。此時,兩者指向的堆中的物件還是同一個
//此時t2和t1的屬性值是一樣的,因為t1和t2指向的是同一個物件
t1=t2;
System.out.println("t1.level = "+t1.level);
System.out.println("t2.level = "+t2.level);
//修改t1的屬性值,t2也會跟著變化
t1.level=27;
System.out.println("t1.level = "+t1.level);
System.out.println("t2.level = "+t2.level);
}
}
class Tank{
int level;
}
方法引數傳遞機制的示例
如果方法的引數是引用資料型別,那麼傳遞的就是物件的引用地址,如果在方法內部修改物件的內容,那麼方法外的物件同樣會改變。
package net.ittimeline.java.core.jdk.foundational.operator.assignment;
/**
*
* 方法引數的傳遞機制
* @author liuguanglei [email protected]
* @version 2020/8/4 1:56 下午
* @since JDK11
*/
public class PassObject {
/**
* 將Letter物件的屬性值修改為z
* @param y
*/
static void f(Letter y){
y.c='z';
}
public static void main(String[] args) {
Letter x=new Letter();
x.c='a';
System.out.println("1:x.c = "+x.c);
f(x);
//輸出屬性值為z
System.out.println("2:x.c = "+x.c);
}
}
class Letter{
char c;
}
JDK1.5新特性-靜態匯入
靜態匯入是使用import static關鍵字加上類名[.變數名][.方法名],其中[.變數名]和[.方法名]是可選的,如果沒有,預設就是匯入類的所有變數和方法到當前類中,這樣就可以直接在當前類中使用。
JDK5新特性之靜態匯入的示例
package net.ittimeline.java.core.jdk.feature.java5;
import static java.lang.System.*;
/**
* JDK5新特性之靜態匯入
* import static關鍵字是直接匯入某個類的所有變數和方法到本類中
* 這樣就可以直接在當前類中引用匯入匯入類的變數和方法
* @author liuguanglei [email protected]
* @version 2020/8/4 11:21 上午
* @since JDK11
*/
public class StaticImport {
public static void main(String[] args) {
//因為靜態匯入過System類,這樣可以在當前類中呼叫System的成員變數out和err的println方法
out.println("Hello World");
err.println("Hello World Again");
}
}
使用靜態匯入和DateTimeFormatter實現輸出當前日期的示例
package net.ittimeline.java.core.jdk.feature.java5;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
/**
* 使用靜態匯入和DateTimeFormatter實現輸出當前日期
* @author liuguanglei [email protected]
* @version 2020/8/4 11:23 上午
* @since JDK11
*/
public class StaticImportCurrentDate {
/**
* 日期格式
*/
public static final String DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
public static void main(String[] args) {
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(DATE_TIME_FORMAT);
//按照指定的格式 格式化當前日期
String now = formatter.format(LocalDateTime.now());
System.out.println(now);
}
}
關係運算符
關係運算符用於判斷資料的關係,Java中常用的關係運算符有大於(>),小於(<),等於(==),不等於(!=),大於等於(>=),小於等於(<-=)。
- 關係運算符的運算結果是boolean型別,也就是true或者false
- 相等性用==表示,而不是=
>=
表示大於或者等於,<=表示小於或則等於>,>=,<,<=
只能用在基本資料型別的數值型別之間進行比較==
可以使用在基本資料型別和引用型別之間相等性判斷
==和=的差別示例
package net.ittimeline.java.core.jdk.foundational.operator.relation;
/**
* ==和=的差別
*
* @author liuguanglei [email protected]
* @version 2020/8/4 11:26 上午
* @since JDK11
*/
public class Equals {
public static void main(String[] args) {
int i = 10;
int j = 20;
//== 判斷相等性,運算結果是false
System.out.println("i == j = " + (i == j));
//=表示將右邊的值或者表示式賦值給左邊的變數
System.out.println("i = j = " + (i = j));
}
}
在使用關係運算符之前先對之前的輸出語句System.out.println()結合JDK5.0新特性之靜態匯入做一個更加"簡短"的輸出。
然後藉助JDK的Random類生成兩個1-100以內的隨機整數,用關係運算符運算,並輸出運算結果
package net.ittimeline.java.core.jdk.foundational.operator.relation;
import java.util.Random;
import static java.lang.System.out;
/**
*
* 在使用關係運算符之前先對之前的輸出語句System.out.println()結合JDK5.0新特性之靜態匯入做一個更加"簡短"的輸出。
* 然後藉助JDK的Random類生成兩個1-100以內的隨機整數,用關係運算符運算,並輸出運算結果
* @author liuguanglei [email protected]
* @version 2020/8/4 11:28 上午
* @since JDK11
*/
public class Bool {
public static void main(String[] args) {
Random random = new Random(88);
//產生兩個1-100之內的隨機整數
int i = random.nextInt(100)+1;
int j = random.nextInt(100)+1;
//輸出 i和 j的值
out.println("i = " + i);
out.println("j = " + j);
//使用關係運算符比較i和j的值
out.println("i > j is " + (i > j));
out.println("i < j is " + (i < j));
out.println("i >= j is " + (i >= j));
out.println("i <= j is " + (i <= j));
out.println("i == j is " + (i == j));
out.println("i != j is " + (i != j));
}
}
==和equals()的區別
==和equals都是比較的值是否相等,通常基本資料型別使用判斷,引用資料型別使用equals判斷,而如果使用判斷引用資料型別,比較的是物件的引用地址。
Java中所有的類(無論是JDK自帶的還是開發人員自己定義的)的直接或者間接父類都是java.lang.Object,該類有個成員方法equals,用於比較物件的相等性。
從Object的equals方法看的出來,預設比較的是物件的引用地址
public boolean equals(Object obj) {
return (this == obj);
}
那麼問題來了
EqualsMethod2類中,明明自定義類Value的兩個物件的屬性值都是100,理論上來說應該是相等的。
但是使用equals判斷的時候是不相等,因為只要使用關鍵字new建立物件時,會開闢新的堆記憶體空間儲存物件。
此時equals比較的兩個物件的引用地址是否相等
package net.ittimeline.java.core.jdk.foundational.operator.relation;
/**
* 自定義類(未重寫equals方法時)的相等性判斷
*
* @author liuguanglei [email protected]
* @version 2020/8/4 11:30 上午
* @since JDK11
*/
public class EqualsMethod2 {
public static void main(String[] args) {
Value v1 = new Value();
Value v2 = new Value();
v1.i = v2.i = 100;
//沒有重寫equals時,預設比較的是物件的地址
//這裡建立了兩個Value物件,因此equals()判斷為false
System.out.println("v1.equals(v2) = " + (v1.equals(v2)));
}
}
class Value {
int i;
}
因為Object類的equals方法比較的是物件的地址是否相等,但是在實際開發中,經常比較的是物件的成員變數是否相等,因此絕大多數類都重寫了equals方法。
這裡以Integer類為例子,分別使用==和equals來判斷Integer物件的相等性。
當建立Integer物件的值在-128-127之間時,無論是equals還是==都是相等的。
因為Integer類中有個內部類IntegerCache,用於快取Integer的值在-128-127之間的物件
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
而如果Integer的值超過了快取的範圍,那麼使用判斷物件相等時就不會相等了。
在開發過程中儘量使用equals方法來判斷,而不是使用==
來判斷。
相等性判斷的示例
package net.ittimeline.java.core.jdk.foundational.operator.relation;
/**
* 相等性判斷儘量使用equals而不是==
* @author liuguanglei [email protected]
* @version 2020/8/4 11:35 上午
* @since JDK11
*/
public class EqualsMethod {
public static void main(String[] args) {
//因為Integer類快取-128-127之間的例項
Integer n1= Integer.valueOf(Byte.MAX_VALUE);
Integer n2= Integer.valueOf(Byte.MAX_VALUE);
//因此無論是equals還是==都是相等
System.out.println("n1.equals(n2) = "+(n1.equals(n2)));
System.out.println("n1==n2 = "+(n1==n2));
//但是超過了快取的範圍
Integer n3=Integer.valueOf(300);
Integer n4=Integer.valueOf(300);
System.out.println("n3.equals(n4) = "+(n3.equals(n4)));
//==就為false了,因為物件的地址不同
System.out.println("n3==n4 = "+(n3==n4));
}
}
邏輯運算子
邏輯運算子用於布林型別變數或者布林表示式的邏輯運算,Java支援的邏輯運算子有如下幾種:
- 邏輯與(&):&兩邊同時為true,結果為true
- 短路與(&&):當&&兩邊為true,結果為true,如果&&的一邊為false,將不再執行剩餘的表示式,也就是短路特性。
- 邏輯或(|):當|兩邊只要有一個為true,結果為true,否則結果為false
- 短路或(||):當||兩邊只要有一個為true,結果為true,否則結果為false,並且不再執行剩餘表示式,也就是短路特性。
- 邏輯非(!):當一個變數或者表示式的結果為true,!的結果為false,否則結果為true,邏輯非就是相反的結果
- 邏輯異或(^):當兩邊結果同時為true或者false,結果為false,否則為true。
一般開發中使用短路與、短路或,不會使用邏輯與和邏輯或。
邏輯運算子的示例
package net.ittimeline.java.core.jdk.foundational.operator.logic;
import java.util.Random;
/**
* 邏輯運算子的使用
*
* @author liuguanglei [email protected]
* @version 2020/8/4 11:36 上午
* @since JDK11
*/
public class LogicTest {
public static void main(String[] args) {
Random random = new Random(88);
//建立兩個隨1-100之內的隨機數
int left = random.nextInt(100) + 1;
int right = random.nextInt(100 + 1);
System.out.println("left =" + left);
System.out.println("right =" + right);
//判斷兩個數都是大於100
boolean result = left > 100 && right > 100;
System.out.println("left>100&&right>100 = " + result);
//判斷左邊的數加上100大於100或者右邊的數大於100
result = left + 100 > 100 || right > 100;
System.out.println("left+100>100||right>100 = " + result);
//邏輯非
result = !result;
System.out.println("!result = " + result);
//邏輯亦或
//^左右兩邊都為true或者false,結果為false
result = left > 10 ^ right > 10;
System.out.println("left>10^right>10 = " + result);
result = left > 50 ^ right > 50;
//^左右兩邊結果不同結果為true
System.out.println("left>50^right>50 = " + result);
}
}
短路與的短路特性示例
package net.ittimeline.java.core.jdk.foundational.operator.logic;
import static java.lang.System.out;
/**
* 邏輯與的短路特性
*
* @author liuguanglei [email protected]
* @version 2020/8/4 11:39 上午
* @since JDK11
*/
public class LogicAndShowCircuit {
static boolean test1(int val){
out.println("test1("+val+")");
out.println("result: "+(val<1));
return val<1;
}
static boolean test2(int val){
out.println("test1("+val+")");
out.println("result: "+(val<2));
return val<2;
}
static boolean test3(int val){
out.println("test1("+val+")");
out.println("result: "+(val<3));
return val<3;
}
public static void main(String[] args) {
/**
* 邏輯與的短路特性
* 當執行到test1方法時,2<2的結果為false,整體表達式的結果為false,因此不會再執行test3方法
*/
boolean flag=test1(0)&&test1(2)&&test3(2);
out.println("expression is "+flag);
}
}
短路或的短路特性的示例
package net.ittimeline.java.core.jdk.foundational.operator.logic;
import static java.lang.System.out;
/**
* 邏輯或的短路特性
* @author liuguanglei [email protected]
* @version 2020/8/4 11:41 上午
* @since JDK11
*/
public class LogicOrCircuit {
static boolean test1(int val){
out.println("test1("+val+")");
out.println("result: "+(val<1));
return val<1;
}
static boolean test2(int val){
out.println("test1("+val+")");
out.println("result: "+(val<2));
return val<2;
}
static boolean test3(int val){
out.println("test1("+val+")");
out.println("result: "+(val<3));
return val<3;
}
public static void main(String[] args) {
/**
* 邏輯或的短路特性
* 當執行到test1方法時,0<2的結果為true,整體表達式的結果為false,因此不會再執行tes2和test3方法
*/
boolean flag=test1(0)||test2(2)||test3(2);
out.println("expression is "+flag);
}
}
邏輯與與短路與
- 邏輯與&左右兩邊都是true,結果為true,不具備短路特性
- 短路與&&左右兩邊都是true,結果為true,具備短路特性:明確整體的計算結果,不在計算剩餘的表示式
邏輯與和短路與的區別的示例
package net.ittimeline.java.core.jdk.foundational.operator.logic;
/**
* 邏輯與與短路與
* 邏輯與&左右兩邊都是true,結果為true,不具備短路特性
* 短路與&&左右兩邊都是true,結果為true,具備短路特性:明確整體的計算結果,不在計算剩餘的表示式
*
* @author liuguanglei [email protected]
* @version 2020/8/4 11:38 上午
* @since JDK11
*/
public class LogicAndShowCircuitTest {
public static void main(String[] args) {
boolean flag = false;
int num = 10;
//邏輯與不具備短路特性,雖然&左邊已經是false
boolean logicAnd = flag & num++ > 0;
//但是從輸出結果看出num依然自增1
System.out.println("num = " + num);
//而 短路與&&具備短路特性
num = 10;
// &&左邊已經為false,右邊的num沒有自增1
boolean logicAndCircuit = flag && num++ > 0;
//輸出結果依然為10
System.out.println("num = " + num);
}
}
邏輯運算子混合運算的示例
package net.ittimeline.java.core.jdk.foundational.operator.logic;
/**
* 邏輯運算子的混合運算示例
*
* @author liuguanglei [email protected]
* @version 2020/8/4 11:46 上午
* @since JDK11
*/
public class LogicAndOrTest {
public static void main(String[] args) {
int x = 1;
int y = 1;
//x++=1 y++=1
if (x++ == 2 & y++ == 2) {
x = 7;
}
//x=2 y=2
System.out.println("x = " + x + " y = " + y);
x = 1;
y = 1;
// ++x=2 ++y =2 if true
if (++x == 2 && ++y == 2) {
x = 7;
}
//x=7 y=2
System.out.println("x = " + x + " y = " + y);
x = 1;
y = 1;
//x++ =1 ++y=2 if true
if (x++ == 1 | ++y == 1) {
x = 7;
}
//x=7 y=2
System.out.println("x = " + x + " y = " + y);
x = 1;
y = 1;
// x++=1
// x=7 y=1
if (x++ == 1 || ++y == 1) {
x = 7;
}
//x=7 y=1
System.out.println("x = " + x + " y = " + y);
}
}
三元運算子
三元運算子用於布林變數或者布林表示式判斷,需要三個運算元,等價於if/else,其表現形式為
bool-exp?value0:value1,如果布林表示式的結果為true,三目運算的結果為value0,否則為value1。
使用三目運算子模擬扔硬幣的示例
package net.ittimeline.java.core.jdk.foundational.operator.ternary;
import java.util.Random;
/**
* 使用三元運算子模擬扔硬幣
* @author liuguanglei [email protected]
* @version 2020/8/4 11:47 上午
* @since JDK11
*/
public class CoinFlipping {
public static void main(String[] args) {
Random random=new Random(88);
boolean flip=random.nextBoolean();
System.out.print("OUTCOME :");
System.out.println(flip?"人頭":"字");
}
}
三目運算子和if/else比較的示例
package net.ittimeline.java.core.jdk.foundational.operator.ternary;
import static java.lang.System.out;
/**
* 三目運算子和if/else比較
* @author liuguanglei [email protected]
* @version 2020/8/4 12:24 下午
* @since JDK11
*/
public class TernaryIfElse {
/**
* 三目運算子
* @param i
* @return
*/
static int ternary(int i){
return i<10?i*100:i*10;
}
/**
* 標準的if/else
* @param i
* @return
*/
static int standardIfElse(int i){
if(i<10){
return i*100;
}
else{
return i*10;
}
}
public static void main(String[] args) {
out.println(ternary(9));
out.println(ternary(11));
out.println(standardIfElse(9));
out.println(standardIfElse(11));
}
}
使用巢狀if和三目運算子求三個整數最大值的示例
package net.ittimeline.java.core.jdk.foundational.operator.ternary;
/**
*
* 求三個數中的最大值
* 分別使用if/else if和三元運算子
* @author liuguanglei [email protected]
* @version 2020/8/4 12:25 下午
* @since JDK11
*/
public class TernaryMax {
public static void main(String[] args) {
int i=277;
int j=20;
int k=-99;
int max=0;
if(i>j){
if(i>k){
max=i;
}
else{
max=k;
}
}
System.out.println("使用if/else if實現求三個整數的最大值 max = "+max);
max= i>j&&i>k?i:k;
System.out.println("三個數中的最大值是"+max);
}
}
使用三元運算子判斷使用者輸入的整數是基數還是偶數的示例
如果想要使用者輸入資料,需要使用Java提供的Scanner類實現
package net.ittimeline.java.core.jdk.foundational.operator.ternary;
import java.util.Scanner;
/**
* 判斷使用者輸入的數字是基數還是偶數
* @author liuguanglei [email protected]
* @version 2020/8/4 12:26 下午
* @since JDK11
*/
public class ParityCheck {
public static void main(String[] args) {
//建立一個虛擬鍵盤
Scanner input = new Scanner(System.in);
System.out.println("請輸入一個整數");
int number = input.nextInt();
String result = number % 2 == 0 ? "這是一個偶數" : "這個是一個奇數";
System.out.println("你輸入的數字是" + number);
System.out.println(result);
}
}
程式執行結果
位運算子
位運算子是直接對整數的二進位制進行運算,在JDK的原碼中大量使用了位運算,
以下是擷取Integer類的numberOfLeadingZeros()方法
@HotSpotIntrinsicCandidate
public static int numberOfLeadingZeros(int i) {
// HD, Count leading 0's
if (i <= 0)
return i == 0 ? 32 : 0;
int n = 31;
if (i >= 1 << 16) { n -= 16; i >>>= 16; }
if (i >= 1 << 8) { n -= 8; i >>>= 8; }
if (i >= 1 << 4) { n -= 4; i >>>= 4; }
if (i >= 1 << 2) { n -= 2; i >>>= 2; }
return n - (i >>> 1);
}
位運算子操作的都是整數型別,因為是基於二進位制運算,其執行效率高。
在日常開發中幾乎不會使用到位運算,但是後期會閱讀大量JDK原始碼,瞭解底層實現機制,因此必須掌握位運算子的基本使用。
Java中支援的位運算子有如下幾種:
-
左移(<<): 左移N位相當於乘以2的n次方,空位補0,被移除的高位丟棄,空缺位補0
-
右移(>>):右移N位相當於除以2的n次方,被移位的二進位制最高位是0,右移後,空缺位補0,最高位是1,最高位補1
-
無符號右移(>>>):不管最高位的符號位,右移N位相當於除以2的N次方,被移位的二進位制最高位無論是0還是1,空缺位都補0
-
按位與(&):只有&兩邊都是1,結果是1,否則就是0
-
按位或(|):只要|兩邊都是0,結果是0,否則就是1
-
按位亦或(^):相同的二進位制位進行亦或運算,結果是0,不相同的二進位制位運算結果是1
-
取反運算(~):無論正負數取反運算,各二進位制位按照補碼取反
-
&和|、在操作布林型別的時候表示為邏輯與、邏輯或、邏輯亦或,&和|、在操作整數的時候表示為按位與與按位或、按位亦或。
左移運算的示例
package net.ittimeline.java.core.jdk.foundational.operator.bit;
/**
*
* 左移運算
* @author liuguanglei [email protected]
* @version 2020/8/4 12:33 下午
* @since JDK11
*/
public class BitLeftMoveTest {
public static void main(String[] args) {
//8的二進位制表示為
// 0000 0000 0000 0000 0000 0000 0000 1000
int number=8;
//因為8是正數,左移動2位,右邊補上0,相當於乘以2的兩次方 也就是乘以4
// 0000 0000 0000 0000 0000 0000 0010 0000
number=number<<2;
System.out.println("8<<2 ="+number);
/**
* 在進行移位運算時需要考慮資料越界的問題
*/
int value=21;
// 0000 0000 0000 0000 0000 0000 0101 0100
//左邊移動26位
//101 0100 0000 0000 0000 0000 0000 0000 0
//最高位1 表示結果為負數
System.out.println("21<<26 = "+(value<<26));
}
}
左移運算的示例:使用左移計算2*16
package net.ittimeline.java.core.jdk.foundational.operator.bit;
/**
* 使用左移計算2*16的結果
*
* @author liuguanglei [email protected]
* @version 2020/8/4 12:38 下午
* @since JDK11
*/
public class BitLeftMoveArithmetic {
public static void main(String[] args) {
int number = 2;
//2 * 16 用左移就是2左移4位
int result = number << 4;
System.out.println("2*16=" + result);
}
}
右移運算的示例
package net.ittimeline.java.core.jdk.foundational.operator.bit;
/**
* 右移運算
*
* @author liuguanglei [email protected]
* @version 2020/8/4 12:39 下午
* @since JDK11
*/
public class BitRightMoveTest {
public static void main(String[] args) {
//0000 0000 0000 0000 0000 0000 0001 0000
int number = 16;
//右移N位,相當於除以2的N次方 16/4 結果是4
//00 0000 0000 0000 0000 0000 0000 0001 00
number = number >> 2;
System.out.println("16 >> 2 = " + (number));
number = -16;
// -16/4 結果是-4
number = number >> 2;
System.out.println("-16 >> 2 =" + (number));
}
}
按位與、按位或、按位亦或、按位非運算的示例
package net.ittimeline.java.core.jdk.foundational.operator.bit;
/**
* 按位與、按位或、按位亦或、按位非運算
* @author liuguanglei [email protected]
* @version 2020/8/4 12:40 下午
* @since JDK11
*/
public class BitAndOrXorNotOperator {
public static void main(String[] args) {
/**
* 5的二進位制表示方式為 0000 0000 0000 0000 0000 0000 0000 0101
* 9的二進位制方式表示為 0000 0000 0000 0000 0000 0000 0000 1001
*
* 0101&1001 =0001 因此 5&9的結果是1
* 0101|1001 = 1101 因此 5|9的結果是13
* 0101^1001 = 1100 因此 5^9的結果是12
* 9 ->0000 0000 0000 0000 0000 0000 0000 1001
* ~9 1111 1111 1111 1111 1111 1111 1111 0110 原碼
* 1000 0000 0000 0000 0000 0000 0000 1001 反碼
* 1000 0000 0000 0000 0000 0000 0000 1010 補碼
* ~9 最終的結果是-10
*
*
*/
System.out.println("5&9 = "+(5&9));
System.out.println("5|9 = "+(5|9));
System.out.println("5^9 = "+(5^9));
System.out.println("~9 = "+(~9));
System.out.println(Integer.toBinaryString(-10));
}
}
有符號右移的示例
package net.ittimeline.java.core.jdk.foundational.operator.bit;
import static java.lang.System.out;
/**
* 有符號右移運算
* @author liuguanglei [email protected]
* @version 2020/8/4 12:41 下午
* @since JDK11
*/
public class SignedRightShift {
public static void main(String[] args) {
int i = 0x80000000;
out.println(Integer.toBinaryString(i));
//等價於i=i>>1
i >>= 1;
out.println(Integer.toBinaryString(i));
i >>= 1;
out.println(Integer.toBinaryString(i));
i >>= 1;
out.println(Integer.toBinaryString(i));
i >>= 1;
out.println(Integer.toBinaryString(i));
i >>= 1;
out.println(Integer.toBinaryString(i));
i >>= 1;
out.println(Integer.toBinaryString(i));
i >>= 1;
out.println(Integer.toBinaryString(i));
i >>= 1;
out.println(Integer.toBinaryString(i));
i >>= 1;
out.println(Integer.toBinaryString(i));
i >>= 1;
out.println(Integer.toBinaryString(i));
i >>= 1;
out.println(Integer.toBinaryString(i));
i >>= 1;
out.println(Integer.toBinaryString(i));
i >>= 1;
out.println(Integer.toBinaryString(i));
i >>= 1;
out.println(Integer.toBinaryString(i));
i >>= 1;
out.println(Integer.toBinaryString(i));
i >>= 1;
out.println(Integer.toBinaryString(i));
i >>= 1;
out.println(Integer.toBinaryString(i));
out.println(Integer.toBinaryString(i));
out.println(Integer.toBinaryString(i));
out.println(Integer.toBinaryString(i));
out.println(Integer.toBinaryString(i));
out.println(Integer.toBinaryString(i));
out.println(Integer.toBinaryString(i));
out.println(Integer.toBinaryString(i));
out.println(Integer.toBinaryString(i));
out.println(Integer.toBinaryString(i));
out.println(Integer.toBinaryString(i));
out.println(Integer.toBinaryString(i));
out.println(Integer.toBinaryString(i));
out.println(Integer.toBinaryString(i));
out.println(Integer.toBinaryString(i));
}
}
無符號右移的示例
package net.ittimeline.java.core.jdk.foundational.operator.bit;
import static java.lang.System.out;
/**
* 無符號右移
* @author liuguanglei [email protected]
* @version 2020/8/4 12:43 下午
* @since JDK11
*/
public class UnsignedRightShift {
public static void main(String[] args) {
int i = -1 << 1;
out.println(Integer.toBinaryString(i));
i >>>= 1;
out.println(Integer.toBinaryString(i));
i >>>= 1;
out.println(Integer.toBinaryString(i));
i >>>= 1;
out.println(Integer.toBinaryString(i));
i >>>= 1;
out.println(Integer.toBinaryString(i));
i >>>= 1;
out.println(Integer.toBinaryString(i));
i >>>= 1;
out.println(Integer.toBinaryString(i));
i >>>= 1;
out.println(Integer.toBinaryString(i));
i >>>= 1;
out.println(Integer.toBinaryString(i));
i >>>= 1;
out.println(Integer.toBinaryString(i));
i >>>= 1;
out.println(Integer.toBinaryString(i));
i >>>= 1;
out.println(Integer.toBinaryString(i));
i >>>= 1;
out.println(Integer.toBinaryString(i));
i >>>= 1;
out.println(Integer.toBinaryString(i));
i >>>= 1;
out.println(Integer.toBinaryString(i));
i >>>= 1;
out.println(Integer.toBinaryString(i));
i >>>= 1;
out.println(Integer.toBinaryString(i));
i >>>= 1;
out.println(Integer.toBinaryString(i));
i >>>= 1;
out.println(Integer.toBinaryString(i));
i >>>= 1;
out.println(Integer.toBinaryString(i));
i >>>= 1;
out.println(Integer.toBinaryString(i));
i >>>= 1;
out.println(Integer.toBinaryString(i));
i >>>= 1;
out.println(Integer.toBinaryString(i));
i >>>= 1;
i >>>= 1;
i >>>= 1;
i >>>= 1;
i >>>= 1;
i >>>= 1;
i >>>= 1;
i >>>= 1;
i >>>= 1;
out.println(Integer.toBinaryString(i));
out.println(Integer.toBinaryString(i));
out.println(Integer.toBinaryString(i));
out.println(Integer.toBinaryString(i));
out.println(Integer.toBinaryString(i));
out.println(Integer.toBinaryString(i));
out.println(Integer.toBinaryString(i));
out.println(Integer.toBinaryString(i));
out.println(Integer.toBinaryString(i));
}
}
亦或運算的示例:使用亦或運算實現變數的交換
package net.ittimeline.java.core.jdk.foundational.operator.bit;
/**
* 使用亦或因運算實現變數交換
*
* @author liuguanglei [email protected]
* @version 2020/8/4 12:44 下午
* @since JDK11
*/
public class BitXorVariableSwap {
public static void main(String[] args) {
int left = 10;
int right = 20;
System.out.println("變數交換之前 left = " + left + " right = " + right);
left = left ^ right;
right = left ^ right;
left = left ^ right;
System.out.println("變數交換之後 left = " + left + " right = " + right);
}
}