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. 运算符的优先级
建议直接加括号来控制,简单易读,不用特别去记。
参考资料
- Java基础11 对象引用
- 实验楼:Java编程语言(新版)
- JAVA 对象引用,以及对象赋值
- Java核心技术 卷Ⅰ基础知识 原书第8版
- Java2实用教程 (第三版)_ 耿祥义,张跃平