1. 程式人生 > 實用技巧 >高階Java工程師養成記-Java基礎程式設計之運算子

高階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);

    }
}