Java基础------运算符

运算符


什么是运算符?

计算机的最基本用途之一就是执行数学运算,而运算符就是指明对操作数的运算方式。而运算符在java里就是用来操作对象数据


运算符的种类:

运算符按其功能来分,有算术运算符赋值运算符关系运算符逻辑运算符位运算符其他运算符


  • 算术运算符(+、-、*、/、%、++、–)

    + : 相加运算符两侧的值

    - : 左操作数-右操作数

    * : 相乘运算符两侧的值

    / : 左操作数除以右操作数

    % : 取模: 左操作数除以右操作数的余数

    ++ : 自增:操作的值增加1(++A是将A先增1之后再做其他运算 A++是如果
    有其他赋值运算,先赋值,之后再将A增1)

    - - : 自减:操作的值减少1(–A和A–原理和自增相同)



  • 关系运算符(==、!=、>、<、>=、<=)

    扫描二维码关注公众号,回复: 11510267 查看本文章

    关系运算符生成的是一个“布尔”(Boolean)结果。它们评价的是运算对象值之间的关系。若关系是真实的,关系表达式会生成true(真);若关系不真实,则生成false(假)。

    == : 如果两个操作数的值是否相等,如果相等则条件为真。

    != : 如果两个操作数的值是否相等,如果值不相等则条件为真。

    > : 如果左操作数的值大于右操作数的值,则条件为真。

    <: 如果左操作数的值小于右操作数的值,则条件为真。

    >=: 如果左操作数的值大于等于右操作数的值,则条件为真。

    <=: 如果左操作数的值小于等于右操作数的值,则条件为真。


例:检查对象是否相等发生的情况:


1、用”==”比较两个对象是否相等

 public class Equivalence {
  public static void main(String[] args) {
    Integer n1 = new Integer(47);
    Integer n2 = new Integer(47);
    System.out.println(n1 == n2);  //false
    System.out.println(n1 != n2);  //true
  }
}

造成上述两个对象比较不相等的原因:

==和!=比较的是对象句柄(引用),n1 和 n2所指向的两个对象的内容虽然相同,但是句柄不相同,所以 n1 == n2 返回false, n2 != n2 返回true。


2、用“equals”比较两个对象是否相等

public class EqualsMethod {
  public static void main(String[] args) {
    Integer n1 = new Integer(47);
    Integer n2 = new Integer(47);
    System.out.println(n1.equals(n2));//true
  }
}

注意: 这个方法不适用于“基本类型”,那些类型直接使用==和!=即可。


特殊情况:

class Value {
  int i;
}

public class EqualsMethod2 {
  public static void main(String[] args) {
    Value v1 = new Value();
    Value v2 = new Value();
    v1.i = v2.i = 100;
    System.out.println(v1.equals(v2));  //此时返回false
  }
}

使用“equals”返回false的原因:

因为所有的类在默认情况下都继承Object,所以他们中的equals方法如果不进行重写,那么使用的将是父类“Object”中的“equals”方法,但是“Object”中的equals比较的是句柄(引用),而这里的句柄不同,指向的是不同的对象,所以返回false。

所以:如果有需要直接比较对象的内容,就必须实现相应的“equals”方法。


例:String类中的equals方法:

public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
}

比较的过程:

1、比较当前对象和传入的对象的句柄是否相同,如果相同,说明指向的是同一个对象,返回true。

2、判断传进来的anObject对象是不是String类型的实例,如果不是返回false。

3、如果anObject是String的实例,那么强转成String类型的变量,重新定义成变量anotherString。

4、循环变量字符串anotherString和原本的字符串,判断每个字符是否相等,如果其中有一个字符不相等,返回false。如果每个字符都相等,返回true。


  • 位运算符(&、|、^、~、<<、>>、>>>)

    & : 如果相对应位都是1,则结果是1,否则为0

    | : 如果相对应位都是0,则结果为0,否则为1

    ^ : 如果相对应位值相同,则结果为0,否则为1

    ~: 按位补运算符 翻转操作数的每一位,即0变成1,1变成0

    <<:按位左移运算符 左操作数按位左移右操作数指定的位数

    >>:按位右移运算符 左操作数按位右移右操作数指定的位数

    >>>:按位右移补零操作符。左操作数的值按右操作数指定的位数右移,移动得到的空位以零填充。


例:位的运算

   int a = 60; /* 0011 1100 */
   int b = 13;/* 0000 1101 */
   int c = 0;
   c = a & b; /* 12 = 0000 1100 */
   c = a | b; /* 61 = 0011 1101 */  
   c = a ^ b; /* 49 = 0011 0001 */
   c = ~a; /* -61 = 1100 0011 */
   c = a<<2; /* 240 = 1111 0000 */
   c = a>>2; /* 15 = 1111 */
   c = a>>>2; /* 15 = 0000 1111 */

  • 逻辑运算符(&&、||、!)

    && : 逻辑与运算符。当前仅当两个操作数都为真,条件才为真。

    ||:逻辑或运算符。任何两个操作数其中一个为真,条件为真。

    !:逻辑非运算符。反转操作数的逻辑状态。如果条件为true,则逻辑非运算符将得到false。


短路逻辑运算:

只有明确得出整个表达式真或假的结论,才会对表达式进行逻辑求值。因此,一个逻辑表达式的所有部分都有可能不进行求值。

例:

int A = 60,B = 70,C = 80

boolean result = (A>B) && (A<C) 

A>B 不成立 返回false 整个表达式返回false
所以此时不会去判断A是否小于C 这样对整个表达式来说,造成了短路的一种现象。


短路运算的优势和劣势:

短路运算的优势: 如果中途有一个表达式返回false,那么它不会进行下面的逻辑判断,直接返回false

短路运算的劣势: 如果有大量的逻辑判断 那么使用短路运算会造成代码逻辑的不清晰 ,代码的臃肿。


  • 赋值运算符(=)

    = : 赋值运算符 C = A+B

    += : 加和赋值操作符 C += A 等价于 C = C + A

    -= : 减和赋值操作符 C-= A 等价于 C = C - A

    = : 乘和赋值操作符 C = A 等价于 C = C * A

    /= : 除和赋值操作符 C /= A 等价于 C = C / A

    %= : 取模和赋值操作符 C %= A 等价于 C = C % A

    <<= : 左位移赋值运算符 C <<= 2 等价于 C = C << 2

    >>= : 右位移赋值运算符 C>>=2 等价于 C= C >> 2

    &= : 按位与赋值运算符 C &= 2 等价于 C = C & 2

    ^= : 按位异或赋值运算符 C ^= 2 等价于 C = C ^ 2

    |= : 按位或赋值操作符 C |= 2 等价于 C = C | 2


赋值运算符的注意点:

赋值是用“=”进行的,意思是取得右边的值,把它赋值给左边。

右边的值可以是任何常数变量或者表达式,前提是能产生一个值。

左边的值必须是一个明确的已命名的变量,也就是说,必须要有一个物理空间来保存右边所产生的值。


对基本类型进行赋值操作:

基本类型之间的赋值,是直接把值内容赋值给左边的变量


对“对象”进行赋值操作:

而对一个对象进行赋值操作的时候,真正操作的是它的句柄(引用)。
倘若“从一个对象到另一个对象”赋值,实际上就是将相应对象的句柄(引用),复制到另外一个对象上。

例:加入有C和D两个对象 现在使得C=D 那么C和D最终都会指向最初只有D才指向的那个对象。


对象赋值操作中的”别名”现象:

class Number {
  int i;
}

public class Assignment {
  public static void main(String[] args) {
// 创建Number类实例 n1
    Number n1 = new Number();
// 创建Number类实例 n2
    Number n2 = new Number();
// 给每个实例中的i变量附上不同的值
    n1.i = 9;
    n2.i = 47;
    System.out.println("1: n1.i: " + n1.i + ", n2.i: " + n2.i);
// 将n2的句柄(引用)赋值给n1 此时n1的句柄(引用被改变)
    n1 = n2;
    System.out.println("2: n1.i: " + n1.i +
      ", n2.i: " + n2.i);
// 此时如果改变实例n1中属性i的值 实例n2中i的值也会随之改变(n2相当于有了一个"别名") 因为此时 他们的句柄(引用)相同,都指向同一个对象 而最初的n1的句柄(引用)则会被gc(垃圾收集器)自动清楚。
    n1.i = 27;
    System.out.println("3: n1.i: " + n1.i +
      ", n2.i: " + n2.i);
  }
} 

上面这种特殊的现象通常叫“别名”,是Java操作对象的一种基本方式。


方法调用中的“别名”现象:

将一个对象传递到方法内部时,也会产生“别名”现象。

class Letter {
  char c;
}

public class PassObject {

//f()方法表面上似乎要在方法的作用域内制作自己的自变量Letter 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);
    System.out.println("2: x.c: " + x.c);
  }
}

方法的调用中也出现了“别名”现象,因为实际上传递的是一个句柄,所以x变量和y变量其实是具有相同的句柄(引用),指向的是同一个对象。


如果想把两个对象独立起来,而不是将n1和n2绑定到相同的对象,避免产生“别名”这种现象,那么可以这样操作:

n1.i = n2.i;

这样编写的缺陷:
会使对象内部的字段处理发生混乱,并与标准的面向对象设计准则相悖。
所以对“别名”的处理等到以后讲,这里只做对赋值运算符的说明。


  • 其他运算符(instanceof、三元运算符、逗号运算符、字符串运算符 +、造型运算符)

instanceof运算符:

该运算符用于操作对象实例,检查该对象是否是一个特定类型(类的类型或接口类型)

使用格式:

(Object reference variable) instanceof (class/interface type)

如果运算符左侧变量所指的对象,是操作符右侧类或接口(class/interface)的一个(实例)对象,那么结果为真。


例子:

String name = “James”;
boolean result = name instanceof String;//由于 name是String类型,所以返回true

如果被比较的对象兼容于右侧类型,该运算符仍然返回true。

例子:

class Vehicle {}

public class Car extends Vehicle {

   public static void main(String args[]) {
         Car c1 = new Car();

         Vehicle v2 = new Car(); 
         Vehicle v3 = new Vehicle();

         boolean  result1 = c1 instanceof Vehicle;   //true  Vechicle向后兼容Car
         boolean  result2 = v2 instanceof Car;      //true
         boolean  result3 = v2 instanceof Vehicle   //true
         boolean  result4 = v3 instanceof Car;     //false 
}

} 

三元运算符:

表达式: 布尔表达式? 值0:值1

若“布尔表达式”的结果为true,就计算“值0”,而且它的结果成为最终由运算符产生的值。但若“布尔表达式”的结果为false,计算的就是“值1”,而且它的结果成为最终由运算符产生的值。

而三元运算符也可以用if-else语句代替,从代码的可读性上来看,三元运算符不如if-else,但是从代码的整洁性来说,三元运算符要比if-else语句整洁,具体取舍,看你自己的需求。


逗号运算符:

在C和C++里,逗号不仅作为函数自变量列表的分隔符使用,也作为进行后续计算的一个运算符使用。在Java里需要用到逗号的唯一场所就是for循环。


字符串运算符 +

这个运算符在java里还有连接字符串的功能,不仅如此,如果他的两端,一边连接的是字符串,一边连接的是其他不同类型的变量,会自动转成字符串类型。


造型运算符

造型的作用: 与一个模型匹配

适当的时候,Java会将一种数据类型自动转换成另一种

从小类型转到大类型时,会自动转换,造型运算符可加可不加。

但是从大类型转到小类型时,由于会发生信息丢失的危险。但是,我们需要这种转型,所以必须用造型运算符,进行强转。

Java允许我们将任何主类型(基本类型)“造型”为其他任何一种主类型(基本类型),但布尔值(bollean)要除外,后者根本不允许进行任何造型处理。“类”不允许进行造型。为了将一种类转换成另一种,必须采用特殊的方法。

通常,表达式中最大的数据类型是决定了表达式最终结果大小的那个类型。若将一个float值与一个double值相乘,结果就是double;如将一个int和一个long值相加,则结果为long。


运算符中的字面值:

最开始的时候,若在一个程序里插入“字面值”(Literal),编译器通常能准确知道要生成什么样的类型。但在有些时候,对于类型却是暧昧不清的。若发生这种情况,必须对编译器加以适当的“引导”。方法是用与字面值关联的字符形式加入一些额外的信息。


class Literals {
  char c = 0xffff; // max char hex value
  byte b = 0x7f; // max byte hex value
  short s = 0x7fff; // max short hex value
  int i1 = 0x2f; // Hexadecimal (lowercase)
  int i2 = 0X2F; // Hexadecimal (uppercase)
  int i3 = 0177; // Octal (leading zero)
  // Hex and Oct also work with long.
  long n1 = 200L; // long suffix
  long n2 = 200l; // long suffix
  long n3 = 200;
  //! long l6(200); // not allowed
  float f1 = 1;
  float f2 = 1F; // float suffix
  float f3 = 1f; // float suffix
  float f4 = 1e-45f; // 10 to the power
  float f5 = 1e+9f; // float suffix
  double d1 = 1d; // double suffix
  double d2 = 1D; // double suffix
  double d3 = 47e47d; // 10 to the power
} 

十六进制(Base 16)——它适用于所有整数数据类型——用一个前置的0x或0X指示。并在后面跟随采用大写或小写形式的0-9以及a-f。若试图将一个变量初始化成超出自身能力的一个值(无论这个值的数值形式如何),编译器就会向我们报告一条出错消息。注意在上述代码中,最大的十六进制值只会在char,byte以及short身上出现。若超出这一限制,编译器会将值自动变成一个int,并告诉我们需要对这一次赋值进行“缩小造型”。这样一来,我们就可清楚获知自己已超载了边界。


八进制(Base 8)是用数字中的一个前置0以及0-7的数位指示的。在C,C++或者Java中,对二进制数字没有相应的“字面”表示方法。


字面值后的尾随字符标志着它的类型。若为大写或小写的L,代表long;大写或小写的F,代表float;大写或小写的D,则代表double。

指数总是采用一种我们认为很不直观的记号方法:1.39e-47f。

在科学与工程学领域,“e”代表自然对数的基数,约等于2.718(Java一种更精确的double值采用Math.E的形式)。

它在象“1.39×e的-47次方”这样的指数表达式中使用,意味着“1.39×2.718的-47次方”。然而,自FORTRAN语言发明后,人们自然而然地觉得e代表“10多少次幂”。


猜你喜欢

转载自blog.csdn.net/zxzzxzzxz123/article/details/78051224