Java 运算符(引用和对象)

1. 算数运算符

就是+、-、*、/、%、++、--这些,没什么好说的,稍微强调下自加,自减:

  • 前缀自增自减法(++i,--i): 先进行自增或者自减运算,再进行表达式运算。
  • 后缀自增自减法(i++,i--): 先进行表达式运算,再进行自增或者自减运算。
  • 结果的精度取操作数中精度高的那个。
  • char和byte类型数据运算结果的精度是int型数据的精度。
  • Math类里面提供了许多数学函数,如sqrt,pow,log,exp等,还有表示圆周率和e常量的近似值:PI,E。
  • 可移植性是Java语言的设计目标之一。无论在哪个虚拟机上运行,同一运算应该得到同样的结果。对于浮点数的运算结果,实现这样的可移植性是十分困难的。double类型使用64位存储一个double数值,但是有些处理器使用80位浮点寄存器。这些寄存器增加了中间过程的精度,可以使运算结果更精确,同时也可以避免产生指数溢出。但是,这个结果可能和始终在64位机器上运算的结果不一样,这是我们所不愿意看到的。Java虚拟机的最初规范是要求所有的中间计算必须截断。但是这样不仅可能导致溢出,还要耗费额外的时间。现在是默认允许中间计算采用扩展的精度,对使用关键字strictfp的方法,则必须使用严格的浮点计算来产生理想的结果。区别就是前者不会产生溢出,而后者则有可能。不过,对大多数程序来说,浮点溢出不属于大问题,关键字strictfp并不经常使用。

2. 逻辑运算符

就是与(&&)、或(||)、非(!),异或(^)。

短路逻辑运算符

使用与逻辑运算符时,若两个操作数都为true,则结果为true,但是当得到第一个操作为false时,其结果就必定是false,这时就不会再判断第二个操作。
举例:

public class Test{
  public static void main(String[] args){
      int a = 5;
      boolean b = (a < 4) && (a++ < 10);
      System.out.println("b= " + b);
      System.out.println("a= " + a);
  }
}

运行结果:

ps:或逻辑类似的,前一个为真就直接为真,像Python中的 and 和 or 的短路计算也是这样(参见Python2.7 - IMOOC - 4)。

3. 位运算符

位运算时先转换为二进制,再按位运算。

表格中的例子中,变量a的值为60(0011 1100),变量b的值为13(0000 1101):

  • 没有"<<<"运算符。
  • 1<<35与1<<3或8是等价的,long类型则是模64。
  • 通过运用2的幂次方的&运算可以屏蔽掉其它位,检测某一位是否为1:(n & 2 ^ n) / 2 ^ n(用移位符实现屏蔽显然更易理解)。
  • 可以用异或位运算进行加密:原文字符和约定数进行异或位运算得到密文,密文再和约定数进行异或位运算得到原文。
  • 位运算也可以操作字符数据,但运算结果是int型数据。
  • 位运算也可以操作逻辑型数据,规则类似,不同的是位运算符要计算完两边的操作数之后再给出运算的结果。下面的例子可以看出短路逻辑和位运算的不同
public class Test{
   public static void main(String[] args){
       int x;
       int y = 10;
       if(((x = 0) == 0) || (y = 20) == 20){
           System.out.println("now y = " + y);
       }

       int a;
       int b = 10;
       if(((a = 0) == 0) | (b = 20) == 20){
           System.out.println("now b = " + b);
       }            
   }
}

运行结果:

4. 赋值运算符

=、+=、-=之类,还可以<<=、&=这样。

5. 关系运算符

写在判断条件里的==、!=、>、<......

==和!=也适用于所有对象,可以比较对象的引用是否相同。

引用:Java 中一切都是对象,但操作的标识符实际是对象的一个引用。

例:

  String s;

在这里,创建的是引用,不是对象。创建引用后,一种安全的做法是对其进行初始化。

  String s = "abc";
  String s =  new String("abc");

通常采用new操作符生成一个新对象,然后将引用与该对象相关联。

来看下面的代码:

public class Test {
	public static void main(String args[]){
		Integer n1 = new Integer(2);
		Integer n2 = new Integer(2);
		System.out.println(n1 == n2);
		System.out.println(n1 != n2);
	}
}

在上面的代码中,输出的结果是什么呢,可能认为先是true,再是false,因为两个对象是相同的。尽管两个对象的内容相同,但它们的引用却不相同,==和!=比较的就是对象的引用,所以结果false,再是true,如下:

此处输入图片的描述

而想要比较对象的内容是否相同时,Java 提供了一个特殊的方法equals(),它不适用于基本类型,基本类型使用==和!=进行比较。

来看下面的代码:

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

上述的代码运行结果:

此处输入图片的描述

注:但是当自己创建了一个类时,情况又会相反,因为equals()默认比较引用,除非在自己的类中覆盖equal()方法,这些知识将在之后的章节进行详细讲解。

这都是什么鬼,稍微了解了一下java的对象和引用:

new是在内存中为对象开辟空间。具体来说,new是在内存的 (heap)上为对象开辟空间。这一空间中,保存有对象的数据和方法。对象连名都没有,没法直接访问它。我们只能通过对象引用来间接访问对象。对象引用(reference)并不是对象本身,而是类似于一个指向对象的指针,存在于内存的(stack)中。在Java中,引用起到了指针的作用,但我们不能直接修改指针的值,比如像C语言那样将指针值加1。我们只能通过引用执行对对象的操作。这样的设计避免了许多指针可能引起的错误。用等号赋值时,是将右侧new在堆中创建对象的地址赋予给对象引用。当我们将一个引用赋值给另一个引用时,我们实际上复制的是对象的地址。两个引用将指向同一对象。引用可以指向不同的对象,对象也可以被多个引用操纵。

栈的读取速度比堆快,但栈上存储的数据受到有效范围的限制。在C语言中,当一次函数调用结束时,相应的栈帧(stack frame)要删除,栈帧上存储的参量和自动变量就消失了。Java的栈也受到同样的限制,当一次方法调用结束,该方法存储在栈上的数据将清空。在 Java中,所有的(普通)对象都储存在堆上。因此,new关键字的完整含义是,在堆上创建对象。基本类型(primitive type)的对象,比如int, double,保存在栈上。当我们声明基本类型时,不需要new。一旦声明,Java将在栈上直接存储基本类型的数据。所以,基本类型的变量名表示的是数据本身,不是引用。

随着方法调用的结束,引用和基本类型变量会被清空。由于对象存活于堆,所以对象所占据的内存不会随着方法调用的结束而清空。进程空间可能很快被不断创建的对象占满。Java内建有垃圾回收(garbage collection)机制,用于清空不再使用的对象,以回收内存空间。垃圾回收的基本原则是,当存在引用指向某个对象时,那么该对象不会被回收; 当没有任何引用指向某个对象时,该对象被清空,它所占据的空间被回收。实际上这不确切。正确地说,它已成为垃圾回收机制的处理对象。至于什么时候真正被回收,那要看垃圾回收机制的心情了。由此看来,下面的语句应该不合法吧?至少是没用的吧?

  new Vehicle();

不对。它是合法的,而且可用的。譬如,如果我们仅仅为了打印而生成一个对象,就不需要用引用变量来系住它。最常见的就是打印字符串:

  System.out.println(“I am Java!”);

字符串对象“I am Java!”在打印后即被丢弃。有人把这种对象称之为临时对象

当我们分离了引用和对象的概念后,Java方法的参数传递机制实际上非常清晰: Java的参数传递为值传递。也就是说,当我们传递一个参数时,方法将获得该参数的一个拷贝。实际上,我们传递的参数,一个是基本类型的变量,另一个为对象的引用。基本类型变量的值传递,意味着变量本身被复制,并传递给Java方法。Java方法对变量的修改不会影响到原变量引用的值传递,意味着对象的地址被复制,并传递给Java方法。Java方法根据该引用的访问将会影响对象。但是,需要理解的是,不能改变引用,使其指向另外一个对象。

6. 条件运算符

唯一的一个三目运算符条件运算符?

语法形式:布尔表达式?表达式1 : 表达式2

运算过程:如果布尔表达式的值为true ,则返回 表达式1 的值,否则返回 表达式2 的值。

7. instanceof 运算符

该运算符用于操作对象实例,检查该对象是否是一个特定类型(类类型或接口类型)。
instanceof运算符使用格式如下:

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

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

String name = "James";
boolean result = name instanceof String; // 由于 name 是 String 类型,所以返回真

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

8. 运算符的优先级

建议直接加括号来控制,简单易读,不用特别去记。
运算符的优先级
运算符的优先级2

参考资料

posted @ 2017-03-21 23:32  archeroc  阅读(660)  评论(0编辑  收藏  举报