【Java基础】基本类型与运算【重要】
0. Java基本数据类型
Java的位运算(bitwise operators)直接对整数类型的位进行操作,这些整数类型包括long、int、short、char和 byte,位运算符具体如下表:
运算符 |
说明 |
<< |
左移位,在低位处补0 |
>> |
右移位,若为正数则高位补0,若为负数则高位补1 |
>>> |
无符号右移位,无论正负都在高位补0 |
& |
与(AND),对两个整型操作数中对应位执行布尔代数,两个位都为1时输出1,否则0。 |
| |
或(OR),对两个整型操作数中对应位执行布尔代数,两个位都为0时输出0,否则1。 |
~ |
非(NOT),一元运算符。 |
^ |
异或(XOR),对两个整型操作数中对应位执行布尔代数,两个位相等0,不等1。 |
<<= |
左移位赋值。 |
>>= |
右移位赋值。 |
>>>= |
无符号右移位赋值。 |
&= |
按位与赋值。 |
|= |
按位或赋值。 |
^= |
按位异或赋值。 |
Java一共提供了8种原始的数据类型(byte、short、int、long、float、double、char、boolean),这些数据类型不是对象,而是Java中不同于类的特殊类型,这些基本类型的数据变量在声明之后就会立即在栈上被分配空间。除了这些基本类型外,其他类型都是引用类型,这类变量在声明时不会被分配内存空间,只是存储了一个内存地址。
注意:
1) 在Java中,默认声明的小数是double类型的,而整数默认是int类型。
2) 在Java中,null不是一个合法的Object实例,所以编译器并没有为其分配内存,它仅仅用于表明该引用目前没有指向任何对象。null是将引用变量的值全部置0。
注意:如果是临时变量不会赋初值,只有成员属性才会赋初值。
(1)如下:临时变量编译时候会报错
@Test public void test5(){ byte by; int i; short s; long l; boolean b; char c; float f; double d; System.out.println(by); System.out.println(i); System.out.println(s); System.out.println(l); System.out.println(f); System.out.println(l); System.out.println(b); System.out.println(c); }
报错:
The local variable by may not have been initialized
(2)成员属性不会报错:
byte by; int i; short s; long l; boolean b; char c; float f; double d; @Test public void test5(){ System.out.println(by); System.out.println(i); System.out.println(s); System.out.println(l); System.out.println(f); System.out.println(l); System.out.println(b); System.out.println(c); }
结果:
0
0
0
0
0.0
0
false
注意:包装类型 直接 =的时候是调用valueOf()实现,byte、short、编译之后都是通过int型:--浮点数默认是double类型,如果声明是float类型需要加F或者f,否则编译报错。
而且8种包装类型基本实现了Cache,内部类实现。例如:
package zd.dms.test; public class ArrayTest { public static void main(String[] args) { Byte by1 = 1; Byte by2 = new Byte((byte) 2); Short s1 = 1; Short s2 = new Short((short) 2); Integer i1 = 1; Integer i2 = new Integer(1); Long l1 = 5l; Long l2 = new Long(10l); Float f1 = 1f; Float f2 = new Float(2f); Double d1 = 1d; Double d2 = new Double(2d); Boolean b1 = false; Boolean b2 = new Boolean(true); Character c1 = 'c'; Character c2 = new Character('c'); byte b = (byte)1; short s = (short)1; int in = 1; long l = 1; float f = 1.0f;//默默人是double类型,需要声明为float,否则编译不通过 double d = 2.0; } }
反编译:
package zd.dms.test; public class ArrayTest { public static void main(String[] paramArrayOfString) { Byte localByte1 = Byte.valueOf(); Byte localByte2 = new Byte((byte)2); Short localShort1 = Short.valueOf((short)1); Short localShort2 = new Short((short)2); Integer localInteger1 = Integer.valueOf(1); Integer localInteger2 = new Integer(1); Long localLong1 = Long.valueOf(5L); Long localLong2 = new Long(10L); Float localFloat1 = Float.valueOf(1.0F); Float localFloat2 = new Float(2.0F); Double localDouble1 = Double.valueOf(1.0D); Double localDouble2 = new Double(2.0D); Boolean localBoolean1 = Boolean.valueOf(false); Boolean localBoolean2 = new Boolean(true); Character localCharacter1 = Character.valueOf('c'); Character localCharacter2 = new Character('c'); int i = 1; int j = 1; int k = 1; long l = 1L; float f = 1.0F; double d = 2.0D; } }
1.不可变类
http://www.cnblogs.com/qlqwjy/p/7944456.html
不可变类:所谓的不可变类是指这个类的实例一旦创建完成后,就不能改变其成员变量值。如JDK内部自带的很多不可变类:Interger、Long和String(8种基本类型的包装类和String)等。
可变类:相对于不可变类,可变类创建实例后可以改变其成员变量值,开发中创建的大部分类都属于可变类。
下面的理解可能会易懂一些:
{概念:不可变类的意思是创建该类的实例后,该实例的属性是不可改变的。Java中的8个包装类和String类都是不可变类。所以不可变类并不是指该类是被final修饰的,而是指该类的属性是被final修饰的。
自定义不可变类遵守如下原则:
1、使用private和final修饰符来修饰该类的属性。
2、提供带参数的构造器,用于根据传入的参数来初始化属性。
3、仅为该类属性提供getter方法,不要提供setter方法。
4、如果有必要,重写hashCode和equals方法,同时应保证两个用equals方法判断为相等的对象,其hashCode也应相等。}
2. 值传递与引用传递的区别
1)值传递:方法调用时,实际参数把它的值传递给对应的形式参数,形式参数只是用实际参数的值初始化自己的存储单元内容,是两个不同的存储单元,所以方法执行中形式参数值的改变不影响实际参数的值。
2)引用传递:也称为传地址。方法调用时,实际参数是对象(或数组),这时实际参数与形式参数指向同一个地址,在方法执行中,对形式参数的操作实际上就是对实际参数的操作,所以方法执行中形式参数的改变将会影响实际参数。
注意:
在Java中,原始数据类型在传递参数时都是按值传递,而包装类型在传递参数是是按引用传递,但包装类型在进行计算的时候会自动拆箱。
参考:JDK1.5新特性
一个例子理解不可变类与引用传递
package immutable; public class Test { public static void main(String[] args) { String s = "SSSS"; String s1 = s; System.out.println("改变s前--------"); System.out.println("s=" + s); System.out.println("s1=" + s1); System.out.println("s的hashCode=" + s.hashCode()); System.out.println("s1的hashCode=" + s1.hashCode()); s = "WWWW"; System.out.println("改变s后--------"); System.out.println("s=" + s); System.out.println("s1=" + s1); System.out.println("s的hashCode=" + s.hashCode()); System.out.println("s1的hashCode=" + s1.hashCode()); } }
结果:
改变s前-------- s=SSSS s1=SSSS s的hashCode=2555072 s1的hashCode2555072 改变s后-------- s=WWWW s1=SSSS s的hashCode=2678208 s1的hashCode2555072
解释:可以看出=使用的是引用传递,传递给s1的是s的地址,所以改变前两者指向同一内存区域,因此值相等。改变s后,由于String是不可变类,因此改变s相当于改变新创建一个对象,也就是s指向区域发生变化,但s1指向的区域仍然没变,因此s值改变,s1没有变。
3. 不同数据类型的转换规则
当参与运算的两个对象的类型不同的时候就需要进行类型的转换(显式或者隐式),隐式转换规则是从低精度到高精度,byte
自动类型转换
低级数据可以自动转换为高级数据类型。
1. char转换为高级类型的时候如int或者long等,会自动转换为其对应的ASCII码
2. byte char short在参与运算的时候会自动转换为int,但是使用+=运算的时候不会发生转换
3. 基本数据类型和boolean之间是不能发生转换的
多种类型数据混合运算的时候,会自动转换为最高级类型之后进行计算。
强制类型转换
从高级数据类型转换为低级数据类型的时候就需要进行强制类型转换
byte -> char
char -> byte char
short -> byte char
int -> byte short char
long -> byte short char int
float -> byte short char int long
double -> byte short char int long double
强制转换注意:
例如对于short s = 1; s = s + 1; 由于s + 1会强制转化为int进行计算,所以在赋值(s =
s+1)的时候就出现int型赋值给short,就会报错(cant conver XXX)。所以需要显式的转换一下s=(short)(s+1);
但是由于+=运算符是Java语言规定的算法,所以s+=1并不会报错。
4. 运算符的优先级
public class Test{ publicstatic void main(String[] args){ bytea = 5; intb = 10; intc = a >> 2 + b >> 2; System.out.println(c); } }
答案为0,由于“+”的优先级比“>>”高,因此程序中的表达式等价于a>>(2 + b)>>2。
5. 左移右移操作
1) 左移(<<):将参与运算的对象对应的二进制数左移指定位数,右位补0,高位左移后溢出就舍弃。左移n位表示原来的值乘2的n次方,经常用来代替乘法操作,因为CPU直接支持位运算,故位运算比乘法运算的效率高。
2) 右移分为有符号右移(>>)和无符号右移(>>>)。二者的不同点在于“>>”在执行右移操作时,若参与运算的数字为正数,则在高位补0;若为负数,则在高位补1.而”>>>”则不同,无论参与位运算的数字为正数或为负数,在执行运算时,都会在高位补0.
注意:
1) 在对char、byte、short等类型的数进行移位操作前,编译器都会自动地将数值转化为int类型,然后才进行移位操作。
2) 在Java中,为了保证移位的有效性,采用了取余的操作,即a>>n等价于a>>(n%32)。
6. char型变量存储中文汉字
在Java中,默认使用的Unicode编码方式,即每个字符占用两个字节,因此可以用来存储中文。虽然String是由char所组成的,但是它采用了一种更灵活的方式来存储,即英文占用一个字符,中文占用两个字符,采用这种存储方式的一个重要作用就是可以减少所需存储空间,提高存储效率。
在上例中,首先通过字节长度和字符串长度判断字符串是否包含中文字符,若包含,则用正则表达式匹配的方式找出字符串中的所有中文字符