Java架构师成长之道之Java数据计算

Java架构师成长之道之Java数据计算

3.1 Java数据计算概述

计算机最主要的任务就是完成生产生活中的各种数据的运算,在Java中提供了诸多的运算符来完成相关数据的运算,在日常程序开发中最常用的有算术运算符、自增运算符、关系运算符、逻辑运算符以及三目运算符,在JDK源码中会使用到基于二进制的位运算符。

在了解具体运算符的使用之前,先了解关于数据计算的几个概念

package net.ittimeline.java.core.operator;
import static java.lang.System.out;
/**
 * 数据计算的相关概念
 * 运算符:数据执行的运算类型,例如算术、逻辑、关系、位运算
 * 操作数:参与运算的数据,例如2,(3+2)都是操作数
 * 表达式:由操作数和运算符组成,通常是完成某种业务数据计算而设计的,例如计算圆的面积
 * 优先级:在各种运算符参与运算时,有不同的执行顺序,由它们的优先级决定,可以使用()改变优先级
 * 结合性:执行运算的方向,例如从左到右或者从右到左
 * @author liuguanglei [email protected]
 * @create 2019-07-30 11:51
 * @website www.ittimeline.net
 * @since JDK11.03
 */
public class OperatorConceptTest {

    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;

        out.println("precedence = "+precedence);
    }
}
  • 运算符:指定数据执行的运算类型,例如算术,逻辑,关系,比较,位运算等等

  • 操作数:就是参与运算的数据,例如2,(3+2)都是一个操作数

  • 表达式:由操作数和运算符组成,通常都是完成某种业务数据计算而设计的,例如计算圆的面积等等。

  • 优先级:在各种运算符参与运算时,有不同的执行顺序,由它们的优先级决定,可以使用()来改变执行的顺序

  • 结合性:执行计算的方向,一般是从左到右,但是赋值运算是从右到左。

3.2 算术运算符

算术运算就是数学意义上的加减乘除以及取模运算,Java中支持的算术运算符包含加法(+)、减法(-)、乘法(*)、除法(/)、和取模(/)

  • 算术运算符的优先级是先乘除,后加减。
  • 算术运算符的结合性是从左到右
  • 算术运算计算结果的类型是参与运算的最大数据类型,例如整数和浮点数运算的结果是浮点型
  • 赋值(=)时会进行自动类型转换

算术运算案例

package net.ittimeline.java.core.operator.arithmetic;

/**
 * 算术运算符
 * Java中的算术运算符支持数学意义的+ - * /以及取模(%)运算(求余数)
 *
 * @author liuguanglei [email protected]
 * @create 2019-07-28 09:40
 * @website www.ittimeline.net
 * @since JDK11.03
 */
public class ArithmeticDivideTest {

    /**
     * 除法运算
     * @param args
     */
    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.operator.arithmetic;

/**
 * 取模(求余数)运算
 * 开发中经常用取模运算符(%)来判断数据是否能够整除的情况
 *
 * @author liuguanglei [email protected]
 * @create 2019-07-28 09:49
 * @website www.ittimeline.net
 * @since JDK11.03
 */
public class ArithmeticRemainderTest {

    /**
     * 求余数
     *
     * @param args
     */
    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.operator.arithmetic;

/**
 * 取模运算案例
 * 将一个五位整数反转
 * 例如12345变成54321
 * @author liuguanglei [email protected]
 * @create 2019-08-03 09:39
 * @website www.ittimeline.net
 * @since JDK11.03
 */
public class ArithmeticRemainderApp {

    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.operator.arithmetic;

/**
 * @author liuguanglei [email protected]
 * @create 2019-07-30 15:44
 * @website www.ittimeline.net
 * @since JDK11.03
 */
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);
    }
}

3.3 自增(自减)运算符

自增运算符主要用于变量自增1,用于变量自增1的运算符是++,但是++可以放在变量前面,也可以放在变量后面,放在前面时,变量先自增1,然后再参与运算,++放在变量的后面,变量先运算,再自增1。

自减运算符使用--表示,用于变量自减1,也可以放在变量的前面和后面,分别表示先减1,再参与运算和先参与运算,再减1。

  • 自增(自减)运算符不会改变变量本身的数据类型,因此在运算时需要考虑当前数据类型的极限值
  • 自增(自减)运算符如果是一条单独的语句,前置++(--)和后置++(--)的效果是一样的

自增运算符案例

package net.ittimeline.java.core.operator.autoincrement;

/**
 * 前置++,后置++
 * 作用是针对整型变量加1
 * 自增运算不会改变变量本身的数据类型
 * @author liuguanglei [email protected]
 * @create 2019-07-28 10:32
 * @website www.ittimeline.net
 * @since JDK11.03
 */
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.operator.autoincrement;

/**
 * 自减运算符
 * @author liuguanglei [email protected]
 * @create 2019-07-28 10:42
 * @website www.ittimeline.net
 * @since JDK11.03
 */
public class AutoincrementSubTest {


    /**
     * 自减运算符
     * @param args
     */
    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);

    }
}

自增(自减)运算符的常用应用场景就是在for循环中改变循环条件的值。

package net.ittimeline.java.core.operator.autoincrement;

/**
 * 自增和自减运算符的应用场景
 *
 * @author liuguanglei [email protected]
 * @create 2019-08-03 09:23
 * @website www.ittimeline.net
 * @since JDK11.03
 */
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.operator.autoincrement;

/**
 * 自增运算符复杂案例
 *
 * @author liuguanglei [email protected]
 * @create 2019-08-03 09:40
 * @website www.ittimeline.net
 * @since JDK11.03
 */
public class AutoIncrementComplexTest {

    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.operator.autoincrement;

/**
 * 自增运算的本质
 *
 * @author liuguanglei [email protected]
 * @create 2019-08-03 10:33
 * @website www.ittimeline.net
 * @since JDK11.03
 */
public class AutoincrementEssence {

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


    }
}

3.4 赋值运算符

赋值通常是给变量赋值,Java中使用"="来表示赋值,而"=="表示相等,赋值是将=右边的值或者表达式赋值给左边的变量。

  • 当赋值号(=)两边的数据类型不一致时,可以使用自动类型转换或者强制类型转换进行处理
  • 支持连续赋值,即同时声明并赋值多个变量
  • 赋值运算不会改变变量本身的数据类型
package net.ittimeline.java.core.operator.assignment;

/**
 * 赋值运算符
 * =
 * 算术运算和赋值运算
 * += -= *= /= %=
 *
 * 赋值运算不会改变变量的数据类型
 *
 * @author liuguanglei [email protected]
 * @create 2019-07-28 10:59
 * @website www.ittimeline.net
 * @since JDK11.03
 */
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.operator.assignment;

import java.util.Random;

/**
 *产生0-99之间的随机数
 * @author liuguanglei [email protected]
 * @create 2019-07-28 10:59
 * @website www.ittimeline.net
 * @since JDK11.03
 */
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);
    }

}

赋值运算符还可以可算术运算符结合使用,例如+= ,-=,*=,/=,%=。

package net.ittimeline.java.core.operator.assignment;
import static java.lang.System.out;

import java.util.Random;

/**
 * 算术运算与赋值运算结合运算
 *
 * @author liuguanglei [email protected]
 * @create 2019-07-29 14:02
 * @website www.ittimeline.net
 * @since JDK11.03
 */
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);
    }
}

复杂的赋值运算符

package net.ittimeline.java.core.operator.assignment;

/**
 * 赋值运算符的复杂案例
 * 开发中尽量将表达式写的简单明了,有利于程序的维护
 * @author liuguanglei [email protected]
 * @create 2019-08-03 11:24
 * @website www.ittimeline.net
 * @since JDK11.03
 */
public class AssignmentComplex {

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



    }
}

3.5 关系运算符

关系运算符用于判断数据的关系,Java中常用的关系运算符有大于(>),小于(<),等于(==),不等于(!=),大于等于(>=),小于等于(<-=)。

  • 关系运算符的运算结果是boolean类型,也就是true或者false
  • 相等性用==表示,而不是=
  • =表示大于或者等于,>=表示小于或则等于

===的差别

package net.ittimeline.java.core.operator.relation;

/**
 * ==和=的差别
 * @author liuguanglei [email protected]
 * @create 2019-08-03 11:36
 * @website www.ittimeline.net
 * @since JDK11.03
 */
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新特性之静态导入
做一个更加"简短"的输出。

  • 静态导入是使用import static关键字加上类名[.变量名][.方法名],其中[.变量名]和[.方法名]是可选的,如果没有,默认就是导入类的所有变量和方法到当前类中,这样就可以直接在当前类中使用。
package net.ittimeline.java.core.jdk.feature.jdk5;
/**
 * import static关键字是直接导入某个类的所有变量和方法到本类中
 * 这样就可以直接在当前类中引用导入导入类的变量和方法
 *
 * import static java.lang.System.*表示导入System的所有变量和方法
 */

import  static java.lang.System.*;


/**
 * JDK5新特性之静态导入
 * @author liuguanglei [email protected]
 * @create 2019-08-03 11:38
 * @website www.ittimeline.net
 * @since JDK11.03
 */
public class StaticImport {

    public static void main(String[] args) {
        //因为静态导入过System类,这样可以在当前类中调用System的成员变量out和err的println方法
        out.println("Hello World");
        err.println("Hello World Again");
    }
}

使用静态导入和SimpleDateFormat实现输出当前日期

package net.ittimeline.java.core.operator;
import java.text.SimpleDateFormat;
import java.util.Date;

import static java.lang.System.out;
/**
 * 静态导入
 *
 * @author liuguanglei [email protected]
 * @create 2019-07-29 13:48
 * @website www.ittimeline.net
 * @since JDK11.03
 */
public class HelloDate {

    /**
     *     声明日期格式
     */
    public static final String PATTERN="yyyy-MM-dd HH:mm:ss";
    public static void main(String[] args) {
        //创建日期格式化对象
        SimpleDateFormat simpleDateFormat=new SimpleDateFormat(PATTERN);
        String now =simpleDateFormat.format(new Date());
        //显示输出当前日期
        out.print(now);

    }
}

然后借助JDK的Random类生成两个1-100以内的随机整数,用关系运算符运算,并输出运算结果

package net.ittimeline.java.core.operator.relation;

import java.util.Random;

import static java.lang.System.out;

/**
 * 关系运算符
 *
 * @author liuguanglei [email protected]
 * @create 2019-07-29 13:19
 * @website www.ittimeline.net
 * @since JDK11.03
 */
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));


    }

}

3.6 ==和equals()的区别

  • ==和equals都是比较的值是否相等,通常基本数据类型使用判断,引用数据类型使用equals判断,而如果使用判断引用数据类型,比较的是对象的引用地址。

Java中所有的类(无论是JDK自带的还是开发人员自己定义的)的直接或者间接父类都是java.lang.Object,该类有个成员方法equals,用于比较对象的相等性。

从Object的equals方法看的出来,默认比较的是对象的引用地址

 public boolean equals(Object obj) {
        return (this == obj);
    }

那么问题来了

EqualsMethod2类中,明明Value的成员变量i的值是100,理论上来说应该是相等的。
但是使用equals判断的时候是不相等,因为只要使用关键字new创建对象时,会开辟新的堆内存空间存储对象。

package net.ittimeline.java.core.operator.relation;

/**
 * 相等性判断
 *
 * @author liuguanglei [email protected]
 * @create 2019-07-29 13:40
 * @website www.ittimeline.net
 * @since JDK11.03
 */
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.operator.relation;

/**
 * 相等性判断
 *
 * @author liuguanglei [email protected]
 * @create 2019-07-29 13:34
 * @website www.ittimeline.net
 * @since JDK11.03
 */
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));


    }
}

3.7 逻辑运算符

逻辑运算符用于布尔类型变量或者布尔表达式的逻辑运算,Java支持的逻辑运算符有如下几种:

  • 逻辑与(&):&两边同时为true,结果为true
  • 短路与(&&):当&&两边为true,结果为true,如果&&的一边为false,将不再执行剩余的表达式,也就是短路特性。
  • 逻辑或(|):当|两边只要有一个为true,结果为true,否则结果为false
  • 短路或(||):当||两边只要有一个为true,结果为true,否则结果为false,并且不再执行剩余表达式,也就是短路特性。
  • 逻辑非(!):当一个变量或者表达式的结果为true,!的结果为false,否则结果为true,逻辑非就是相反的结果
  • 逻辑亦或():当两边结果同时为true或者false,结果为false,否则为true。

一般开发中使用短路与、短路或,不会使用逻辑与和逻辑或。

package net.ittimeline.java.core.operator.logic;

import java.util.Random;

/**
 * 逻辑运算
 * @author liuguanglei [email protected]
 * @create 2019-08-03 13:02
 * @website www.ittimeline.net
 * @since JDK11.03
 */
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.operator.logic;


/**
 * 逻辑与与短路与
 * 逻辑与&左右两边都是true,结果为true,不具备短路特性
 * 短路与&&左右两边都是true,结果为true,具备短路特性:明确整体的计算结果,不在计算剩余的表达式
 * @author liuguanglei [email protected]
 * @create 2019-08-03 13:02
 * @website www.ittimeline.net
 * @since JDK11.03
 */
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.operator.logic;

import static java.lang.System.out;
/**
 * 逻辑运算的短路现象
 *
 * @author liuguanglei [email protected]
 * @create 2019-07-29 19:41
 * @website www.ittimeline.net
 * @since JDK11.03
 */
public class ShowCircuit {

    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.operator.logic;

import static java.lang.System.out;
/**
 * 逻辑运算的短路现象
 *
 * @author liuguanglei [email protected]
 * @create 2019-07-29 19:41
 * @website www.ittimeline.net
 * @since JDK11.03
 */
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);

    }
}

逻辑与、短路与和逻辑或、短路或的应用案例

package net.ittimeline.java.core.operator.logic;

/**
 * 逻辑运算符的复杂案例
 * 结合自增运算、if(true){}
 * @author liuguanglei [email protected]
 * @create 2019-08-03 15:55
 * @website www.ittimeline.net
 * @since JDK11.03
 */
public class LogicComplex {

    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);
    }


}

3.8 三元运算符

三元运算符用于布尔变量或者布尔表达式判断,需要三个操作数,等价于if/else,其表现形式为
bool-exp?value0:value1,如果布尔表达式的结果为true,三目运算的结果为value0,否则为value1。

使用三目运算符模拟扔硬币

package net.ittimeline.java.core.operator.ternary;

import java.util.Random;

/**
 * 使用三元运算符模拟扔硬币
 * @author liuguanglei [email protected]
 * @create 2019-07-30 16:07
 * @website www.ittimeline.net
 * @since JDK11.03
 */
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.operator.ternary;
import static java.lang.System.out;
/**
 * 三目运算符和if/else
 *
 * @author liuguanglei [email protected]
 * @create 2019-07-30 10:21
 * @website www.ittimeline.net
 * @since JDK11.03
 */
public class TernaryIfElese {

    /**
     * 三目运算符
     * @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));
    }
}

3.9 位运算符

位运算符是直接对整数的二进制进行运算,在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.operator.bit;

/**
 * 左移运算
 *
 * @author liuguanglei [email protected]
 * @create 2019-08-03 16:26
 * @website www.ittimeline.net
 * @since JDK11.03
 */
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));


    }
}

右移运算

package net.ittimeline.java.core.operator.bit;

/**
 * 右移运算
 *
 * @author liuguanglei [email protected]
 * @create 2019-08-03 16:45
 * @website www.ittimeline.net
 * @since JDK11.03
 */
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.operator.bit;

/**
 * 按位运算符
 * & 两边都是1,结果为1
 * |
 * ^
 * @author liuguanglei [email protected]
 * @create 2019-08-03 17:22
 * @website www.ittimeline.net
 * @since JDK11.03
 */
public class BitAndOrXor {

    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.operator.bit;

import static java.lang.System.out;

/**
 * 有符号右移
 *
 * @author liuguanglei [email protected]
 * @create 2019-07-30 16:23
 * @website www.ittimeline.net
 * @since JDK11.03
 */
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.operator.bit;

import static java.lang.System.out;

/**
 * 无符号右移
 *
 * @author liuguanglei [email protected]
 * @create 2019-07-30 16:33
 * @website www.ittimeline.net
 * @since JDK11.03
 */
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));
    }
}

猜你喜欢

转载自www.cnblogs.com/ittimeline/p/11295963.html