JAVA数据类型
byte类型
一个字节,取值范围 -128~127。
-
当直接量赋值超出范围时会自动转型,这时会有编译错误。除此之外在进行运算时也会自动进行类型转换。
在取值范围内,直接赋值,类型不会改变。 -
同样还有隐式的数据溢出
byte a=127; a+=1; //a=a+1 会报错,why? ----->自动转型 //++a ,也不会自动转型 System.out.println("溢出值为:"+a);
输出结果为:
溢出值为:-128
从上面的代码我们可以看出一些问题:
-
+= 和 = : 功能基本相同,但是+=不会产生类型的自动转换,而 = (后面有运算)会按照一定的规则向高位转型 byte->int
-
直接量赋值超出数据类型的范围会有编译错误,因为此时会自动转型整数类型默认为int。byte i = 128;
-
数字类型默认为int类型。
同样的上述问题也对其他的正数类型有效。
数据溢出
char类型
使用单引号,java中使用Unicode编码集,每个字符使用2个字节表示。
字符类型常量表示:
- 单个字符 ‘A’ ,‘1’,
- 转移字符 ‘\t’,‘\n’,‘\r’,‘\b’
- unicode ‘\uXXXX’, 例如常用汉字的汉字的表示(\u9e11我的随便写的,范围要比这个大) :‘\u4e00’- ‘\u9e11’
- 八进制 ‘\XXX’
char c1 = '\u0064'; //输出结果: d
char c2 = '\101';//输出结果: A
int 类型
int 是最常用的数据类型,所以java整数常量默认的数据类型就是int类型。这也是为什么上面错误提示found : int
的原因。
java中整数的表示方法有四种,二进制,八进制,十进制,十六进制。
byte b = 0b10101; //二进制
byte b1 = 0x1F;//十六进制
byte b2 = 0101;//八进制
16进制: 0x和0X等效, 同时a~f也不区分大小写。
二进制: 0b和0B等效
float 类型
java中的浮点数有两种,float,double。默认浮点数类型是double,所以定义float类型的变量时需要指定f,F
float f= 1.2f;
基本类型数据转型方式
Java所有数值类型变量可以相互转换。
整数类型: 数字的默认类型位int类型。 但是如果该数值被赋予byte,short类型的变量,并且在其范围内的时候,该数字类型为编译时的类型。
浮点数: 默认的类型位双精度double,如果要设为单精度则需要加后缀f或F
有两种转型方式,自动类型转型和强制类型转型
-
自动类型转型:系统支持某种基本类型直接赋值给另一种基本类型,则成为自动转型。表示范围小的类型可以直接赋值给范围大的类型,这就是自动转型 范围小的变量可以直接赋值给表示范围大的变量就是自动转型。
-
强制转型。将大范围的变量赋值给表示范围小的变量就是强制转型。强转可能会导致部分数据丢失。
注:低类型自动向高类型转换,高类型转低类型需要强转
- byte->short ->int ->long ->float->double.
- char ->int ->long ->float->double.
此处大家可能有疑问:long类型占8个字节,为什么可以自动转型到只有四个字节的float类型。
这就要说到浮点数在计算机中的存储结构了。浮点数在内存中的表示 浮点数在内存中的存储分为三个部分:符号位,指数位,位数部分。简单的说使用科学计数法的形式来存储数据
.111111111 * 10^12
- 表达式的类型自动提升
当一个算术表达式包含多个基本类型的时候,整个算数表达式的数据类型将自动发生提升。提升规则如下:- 所有的byte 类型,short类型和char类型将被提升到int类型
- 整个算数表达式提升到表达式中最高等级的数据类型
比较常见的就是
byte a=12;
a=a+1; //此时就会提示 类型不匹配的错误,需要byte类型而给定的是int类型。就是是规则以提到的,byte->int
包装类型
Java是面向对象的语言,所以基本类型都有其对应的引用类型。而且对于集合而言,其类型参数是不能为基本类型,只能用其对应的包装类型。
List<Integer> list=new ArrayList();
集合只能使用包装类型,但是可以直接添加基本类型数据,因为会有装箱的过程。
除此之外还有一个表示高精度的类,BigDecimal,这是由于浮点数容易引起精度丢失。
可以直接把基本类型赋值给其对应包装类型,这个过程称之为自动装箱。当然也可以把包装类型变量赋值给基本类型变量,称之为自动拆箱。
有些面试题,经常会出包装类型的比较运算。
将一个数字类型赋给对应的引用类型时,通过XXX.valueOf() 进行自定装箱。
Integer.valueOf();
//该方法的返回值,在-128~127 范围内的值,返回缓存中的对象。超出范围会生成新的对象
Integer v1=Integer.valueOf("127");
Integer v2=Integer.valueOf("127");
Integer v3=Integer.valueOf(128);
Integer v4=Integer.valueOf(128);
// true。
System.out.println(v1==v2);
//false 。 此时创建了两个对象。== 对于引用类型比较,比较地址
System.out.println(v3==v4);
//true。 Integer重写了equals方法。使用 obj.intValue() 返回基本类型进行比较
System.out.println(v3.equals(v4));
int v5=127;
int v6 = 128;
//true
System.out.println(v1==v5);
//true 使用基本类型和引用类型比较,该处有拆箱操作
System.out.println(v6==v3);
//装箱操作
Object object = v5;
Integer i1=128;
Integer i2=128;
//false。 自动装箱时,使用Integer.valueOf(), -128~127范围内使用缓存
System.out.println(i1==i2);
关于==的一些辨析
-
引用类型和基本类型比较会有拆箱操作。 ("=="比较)
Integer v1=128; int v2=128; //true System.out.println(v1==v2); Float f1=12.0f; float f2=12.0f; int f3=12; //true System.out.println(f1==f2); //true System.out.println(f3==f2); //true System.out.println(f3==f1); // false。 此处有装箱操作, Integer 和Float类型,是不能比较的 System.out.println(f1.equals(f3)); //true , 此处有装箱操作,然后比较float值 System.out.println(f1.equals(f2));
-
相同引用类型之间比较不会拆箱比较。
Integer i=128;Integer i2=128;
,返回false -
不同包装引用类型无法无法直接比较。
Long v1=123l; Integer v2=123; //System.out.println(v1==v2);// 编译异常 System.out.println(v1.equals(v2));//返回false
-
== 比较: 此时编译异常
operator == cannaot be applied to 'java.lang.Integer' ,'java.lang.Long'
-
equals比较: 返回false。
//Long 重写了equals方法 public boolean equals(Object obj) { if (obj instanceof Long) { return value == ((Long)obj).longValue(); } return false; }
-
关于equals操作
-
相同包装引用类型,比较其基本类型的值。 obj.floatValue()
-
如果自动装箱操作后,引用类型不一致直接返回false
public boolean equals(Object obj) { if (obj instanceof Integer) { return value == ((Integer)obj).intValue(); } return false; }
小结
易错点
- 相同包装类型之间的比较最好不要使用==。因为整数包装类型中存在缓存
- Byte,Short,Integer,Long 都为【-128~127】
- Character 为字符ASCII 码表中的128个字符
- 不同的包装类型之间不要使用 equals比较。
- 因为当包装类型不一致时直接返回false
- 即便类型一致,如果值不在【-128~127】范围内,也是返回false
类型转化相关
虽然java会自动进行类型转化,但还是有几种情况不会进行类型转化
- 表达式 +=,++
byte b1=Byte.MAX_VALUE;
++b1;
//-128
System.out.println(b1);
byte b2=Byte.MAX_VALUE;
b2+=2;
//-127
System.out.println(b2);
- 变量使用final修饰
final byte b3=3;
byte b2 = b3+10; //如果变量b3没有使用final修饰符,会有编译错误
当然了如果越界了,也会有编译异常
final 类型定义基本类型变量,相当于c中的宏(记忆中。。),会在使用的该变量的位置直接使用其值替换,并尝试进行运算。所以上面的代码相当于
byte b3=128;
这只是我的一己之见。
运算符
位运算
java种支持的位运算有7个:
- &: 按位与
- | :按位或
- ~:按位非
- ^:按位异或
- <<:左移运算
- >>:右移运算
- >>>:无符号右移
合理使用位运算,就会有比较好的运行效率。因为底层数据存储就是0| 1存储的。
@Test
public void test1(){
byte by=-5;
System.out.println(by | 3);
System.out.println(~by);
System.out.println(by<<1);
System.out.println(by<<29);
System.out.println(by>>1);
}
-5
4
-10
1610612736
-3
进行位运算(~,<<,>>)可能会修改数字的正负符号。
下面以byte类型为例:
底层运算都是以补码进行的操作的。比如byte 类型的 -5
原码: 10000101
//除符号位,取反
反码: 11111010
//反码加1
补码: 111111011
-5 | 3 => -5
-5: 11111011
3: 00000011
-5 | 3 11111011
~-5 => 4
-5 11111011
~-5 00000100
-5 << 1 => -10
-5 111111011
-5<<1 111110110
111110110
//反码
100001001
//补码
100001010