1、Java 基 本 类 型基本类型,或者叫做内置类型,是 JAVA 中不同于类的特殊类型。它们是我们编程中使用最频繁的类型,因此面试题中也总少不了它们的身影,在这篇文章中我们将从面试中常考的几个方面来回顾一下与基本类型相关的知识。 基本类型共有九种,它们分别都有相对应的包装类。对于基本类型 void 以及它的包装类 java.lang.Void,我们都无法直接进行操作。基本类型可以分为三类,字符类型 char,布尔类型 boolean 以及数值类型 byte、short、int、long、float、double。数值类型又可以分为整数类型byte、short、int、long 和浮点数类型 f
2、loat、double。JAVA 中的数值类型不存在无符号的,它们的取值范围是固定的,不会随着机器硬件环境或者操作系统的改变而改变。对于数值类型的基本类型的取值范围,我们无需强制去记忆,因为它们的值都已经以常量的形式定义在对应的包装类中了。请看下面的例子:Java 代码1. public class PrimitiveTypeTest public static void main(String args) 2. / byte 3. System.out.println(“基本类型:byte 二进制位数:“ + Byte.SIZE); 4. System.out.println(“包装类:ja
3、va.lang.Byte“); 5. System.out.println(“最小值:Byte.MIN_VALUE=“ + Byte.MIN_VALUE); 6. System.out.println(“最大值:Byte.MAX_VALUE=“ + Byte.MAX_VALUE); 7. System.out.println(); 8.9. / short 10.System.out.println(“基本类型:short 二进制位数:“ + Short.SIZE); 11.System.out.println(“包装类:java.lang.Short“); 12.System.out.pri
4、ntln(“最小值:Short.MIN_VALUE=“ + Short.MIN_VALUE);13.System.out.println(“最大值:Short.MAX_VALUE=“ + Short.MAX_VALUE);14.System.out.println(); 15.16./ int 17.System.out.println(“基本类型:int 二进制位数:“ + Integer.SIZE); 18.System.out.println(“包装类:java.lang.Integer“); 19.System.out.println(“最小值:Integer.MIN_VALUE=“
5、+ Integer.MIN_VALUE); 20.System.out.println(“最大值:Integer.MAX_VALUE=“ + Integer.MAX_VALUE); 21.System.out.println(); 22.23./ long 24.System.out.println(“基本类型:long 二进制位数:“ + Long.SIZE); 25.System.out.println(“包装类:java.lang.Long“); 26.System.out.println(“最小值:Long.MIN_VALUE=“ + Long.MIN_VALUE); 27.Syste
6、m.out.println(“最大值:Long.MAX_VALUE=“ + Long.MAX_VALUE); 28.System.out.println(); 29.30./ float 31.System.out.println(“基本类型:float 二进制位数:“ + Float.SIZE); 32.System.out.println(“包装类:java.lang.Float“); 33.System.out.println(“最小值:Float.MIN_VALUE=“ + Float.MIN_VALUE);34.System.out.println(“最大值:Float.MAX_VA
7、LUE=“ + Float.MAX_VALUE);35.System.out.println(); 36.37./ double 38.System.out.println(“基本类型:double 二进制位数:“ + Double.SIZE); 39.System.out.println(“包装类:java.lang.Double“); 40.System.out.println(“最小值:Double.MIN_VALUE=“ + Double.MIN_VALUE); 41.System.out.println(“最大值:Double.MAX_VALUE=“ + Double.MAX_VAL
8、UE); 42.System.out.println(); 43.44./ char 45.System.out.println(“基本类型:char 二进制位数:“ + Character.SIZE); 46.System.out.println(“包装类:java.lang.Character“); 47./ 以数值形式而不是字符形式将 Character.MIN_VALUE 输出到控制台 48.System.out.println(“最小值:Character.MIN_VALUE=“ 49.+ (int) Character.MIN_VALUE); 50./ 以数值形式而不是字符形式将
9、Character.MAX_VALUE 输出到控制台 51.System.out.println(“最大值:Character.MAX_VALUE=“ 52.+ (int) Character.MAX_VALUE); 53. 54. 运行结果:基本类型:byte 二进制位数:8 包装类:java.lang.Byte 最小值:Byte.MIN_VALUE=-128 最大值:Byte.MAX_VALUE=127 基本类型:short 二进制位数:16 包装类:java.lang.Short 最小值:Short.MIN_VALUE=-32768 最大值:Short.MAX_VALUE=32767 基
10、本类型:int 二进制位数:32 包装类:java.lang.Integer 最小值:Integer.MIN_VALUE=-2147483648 最大值:Integer.MAX_VALUE=2147483647 基本类型:long 二进制位数:64 包装类:java.lang.Long 最小值:Long.MIN_VALUE=-9223372036854775808 最大值:Long.MAX_VALUE=9223372036854775807 基本类型:float 二进制位数:32 包装类:java.lang.Float 最小值:Float.MIN_VALUE=1.4E-45 最大值:Float
11、.MAX_VALUE=3.4028235E38 基本类型:double 二进制位数: 64 包装类:java.lang.Double 最小值:Double.MIN_VALUE=4.9E-324 最大值:Double.MAX_VALUE=1.7976931348623157E308 基本类型:char 二进制位数:16 包装类:java.lang.Character 最小值:Character.MIN_VALUE=0 最大值:Character.MAX_VALUE=65535 Float 和 Double 的最小值和最大值都是以科学记数法的形式输出的,结尾的“E+数字”表示 E 之前的数字要乘以
12、 10 的多少倍。比如 3.14E3 就是3.141000=3140,3.14E-3 就是 3.14/1000=0.00314。 大家将运行结果与上表信息仔细比较就会发现 float、double 两种类型的最小值与 Float.MIN_VALUE、Double.MIN_VALUE 的值并不相同,这是为什么呢?实际上 Float.MIN_VALUE 和 Double.MIN_VALUE 分别指的是 float 和 double 类型所能表示的最小正数。也就是说存在这样一种情况,0 到Float.MIN_VALUE 之间的值 float 类型无法表示,0 到Double.MIN_VALUE 之间
13、的值 double 类型无法表示。这并没有什么好奇怪的,因为这些范围内的数值超出了它们的精度范围。 基本类型存储在栈中,因此它们的存取速度要快于存储在堆中的对应包装类的实例对象。从 Java5.0(1.5)开始,JAVA 虚拟机(Java Virtual Machine)可以完成基本类型和它们对应包装类之间的自动转换。因此我们在赋值、参数传递以及数学运算的时候像使用基本类型一样使用它们的包装类,但这并不意味着你可以通过基本类型调用它们的包装类才具有的方法。另外,所有基本类型(包括 void)的包装类都使用了 final 修饰,因此我们无法继承它们扩展新的类,也无法重写它们的任何方法。 各种数值
14、类型之间的赋值与转换遵循什么规律呢?我们来看下面这个例子:Java 代码 public class PrimitiveTypeTest public static void main(String args) / 给 byte 类型变量赋值时,数字后无需后缀标识 byte byte_a = 1; /以 int 赋值给 byte/ 编译器会做范围检查,如果赋予的值超出了范围就会报错 / byte byte_b = 1000; / 把一个 long 型值赋值给 byte 型变量,编译时会报错,即使这个值没有超出 byte 类型的取值范围 / byte byte_c = 1L; / 给 short
15、类型变量赋值时,数字后无需后缀标识 short short_a = 1; / 编译器会做范围检查,如果赋予的值超出了范围就会报错 / short short_b = 70000; / 把一个 long 型值赋值给 short 型变量,编译时会报错,即使这个值没有超出 short 类型的取值范围 / byte short_c = 1L; / 给 short 类型变量赋值时,数字后无需后缀标识 int int_a = 1; / 编译器会做范围检查,如果赋予的值超出了范围就会报错 / int int_b = 2200000000; / 把一个 long 型值赋值给 int 型变量,编译时会报错,即使
16、这个值没有超出 int 类型的取值范围 / int int_c = 1L; / 可以把一个 int 型值直接赋值给 long 型变量,数字后无需后缀标识 long long_a = 1; / 如果给 long 型变量赋予的值超出了 int 型值的范围,数字后必须加 L(不区分大小写)标识 long long_b = 2200000000L; / 编译器会做范围检查,如果赋予的值超出了范围就会报错 / long long_c = 9300000000000000000L; / 可以把一个 int 型值直接赋值给 float 型变量 float float_a = 1; / 可以把一个 long
17、型值直接赋值给 float 型变量 float float_b = 1L; / 没有 F(不区分大小写)后缀标识的浮点数默认为 double 型的,不能将它直接赋值给float 型变量 / float float_c = 1.0; / float 型数值需要有一个 F(不区分大小写)后缀标识 float float_d = 1.0F; / 把一个 double 型值赋值给 float 型变量,编译时会报错,即使这个值没有超出 float 类型的取值范围 / float float_e = 1.0D; / 编译器会做范围检查,如果赋予的值超出了范围就会报错 / float float_f = 3
18、.5000000E38F; / 可以把一个 int 型值直接赋值给 double 型变量 double double_a = 1; / 可以把一个 long 型值直接赋值给 double 型变量 double double_b = 1L; / 可以把一个 float 型值直接赋值给 double 型变量 double double_c = 1F; / 不带后缀标识的浮点数默认为 double 类型的,可以直接赋值 double double_d = 1.0; / 也可以给数字增加一个 D(不区分大小写)后缀标识,明确标出它是 double 类型的 double double_e = 1.0D;
19、 / 编译器会做范围检查,如果赋予的值超出了范围就会报错 / double double_f = 1.8000000000000000E308D; / 把一个 double 型值赋值给一个 byte 类型变量,编译时会报错,即使这个值没有超出byte 类型的取值范围 / byte byte_d = 1.0D; / 把一个 double 型值赋值给一个 short 类型变量,编译时会报错,即使这个值没有超出short 类型的取值范围 / short short_d = 1.0D; / 把一个 double 型值赋值给一个 int 类型变量,编译时会报错,即使这个值没有超出 int类型的取值范围
20、/ int int_d = 1.0D; / 把一个 double 型值赋值给一个 long 类型变量,编译时会报错,即使这个值没有超出long 类型的取值范围 / long long_d = 1.0D; / 可以用字符初始化一个 char 型变量 char char_a = a; / 也可以用一个 int 型数值初始化 char 型变量 char char_b = 1; / 把一个 long 型值赋值给一个 char 类型变量,编译时会报错,即使这个值没有超出 char类型的取值范围 / char char_c = 1L; / 把一个 float 型值赋值给一个 char 类型变量,编译时会报
21、错,即使这个值没有超出 char类型的取值范围 / char char_d = 1.0F; / 把一个 double 型值赋值给一个 char 类型变量,编译时会报错,即使这个值没有超出char 类型的取值范围 / char char_e = 1.0D; / 编译器会做范围检查,如果赋予的值超出了范围就会报错 / char char_f = 70000; 从上面的例子中我们可以得出如下几条结论:1. 未带有字符后缀标识的整数默认为 int 类型;未带有字符后缀标识的浮点数默认为 double 类型。 2. 如果一个整数的值超出了 int 类型能够表示的范围,则必须增加后缀“L”(不区分大小写,
22、建议用大写,因为小写的 L 与阿拉伯数字 1 很容易混淆),表示为 long 型。 3. 带有“F”(不区分大小写)后缀的整数和浮点数都是 float 类型的;带有“D”(不区分大小写)后缀的整数和浮点数都是 double 类型的。 4. 编译器会在编译期对 byte、short、int、long、float、double、char型变量的值进行检查,如果超出了它们的取值范围就会报错。 5. int 型值可以赋给所有数值类型的变量;long 型值可以赋给long、float、double 类型的变量;float 型值可以赋给 float、double类型的变量;double 型值只能赋给 do
23、uble 类型变量。 下图显示了几种基本类型之间的默认逻辑转换关系: 图中的实线表示无精度损失的转换,而虚线则表示这样的转换可能会损失一定的精度。如果我们想把一个能表示更大范围或者更高精度的类型,转换为一个范围更小或者精度更低的类型时,就需要使用强制类型转换(Cast)了。不过我们要尽量避免这种用法,因为它常常引发错误。请看下面的例子,如果不运行代码,你能预测它的结果吗?Java 代码 1. public class PrimitiveTypeTest 2. public static void main(String args) 3. int a = 123456; 4. short b =
24、 (short) a; 5. / b 的值会是什么呢? 6. System.out.println(b); 7. 8. 运行结果:1. -7616 1.1 运算符对基本类型的影响 当使用+、-、*、/、%运算符对基本类型进行运算时,遵循如下规则:1. 只要两个操作数中有一个是 double 类型的,另一个将会被转换成double 类型,并且结果也是 double 类型; 2. 否则,只要两个操作数中有一个是 float 类型的,另一个将会被转换成float 类型,并且结果也是 float 类型; 3. 否则,只要两个操作数中有一个是 long 类型的,另一个将会被转换成long 类型,并且结果
25、也是 long 类型; 4. 否则,两个操作数(包括 byte、short、int、char)都将会被转换成int 类型,并且结果也是 int 类型。 当使用+=、-=、*=、/=、%=、运算符对基本类型进行运算时,遵循如下规则: 运算符右边的数值将首先被强制转换成与运算符左边数值相同的类型,然后再执行运算,且运算结果与运算符左边数值类型相同。 了解了这些,我们就能解答下面这个常考的面试题了。请看:引用short s1=1;s1=s1+1;有什么错?short s1=1;s1+=1;有什么错?乍一看,觉得它们都应该没有错误,可以正常运行。我们来写个例子试试:Java 代码 1. public
26、class PrimitiveTypeTest 2. public static void main(String args) 3. short s1 = 1; 4. / 这一行代码会报编译错误 ?5. / s1 = s1 + 1; 6. / 这一行代码没有报错 7. s1 = 1 + 1; 8. / 这一行代码也没有报错 9. s1 += 1; 10. 11. 从例子中我们可以看出结果了。利用上面列举的规律,也很容易解释。在s1=s1+1;中,s1+1 运算的结果是 int 型,把它赋值给一个 short 型变量 s1,所以会报错;而在 s1+=1;中,由于是 s1 是 short 类型的,
27、所以 1 首先被强制转换为 short 型,然后再参与运算,并且结果也是 short 类型的,因此不会报错。那么,s1=1+1;为什么不报错呢?这是因为 1+1 是个编译时可以确定的常量,“+”运算在编译时就被执行了,而不是在程序执行的时候,这个语句的效果等同于 s1=2,所以不会报错。前面讲过了,对基本类型执行强制类型转换可能得出错误的结果,因此在使用+=、-=、*=、/=、%=等运算符时,要多加注意。 当使用“=”运算符在基本类型和其包装类对象之间比较时,遵循如下规则:1. 只要两个操作数中有一个是基本类型,就是比较它们的数值是否相等。 2. 否则,就是判断这两个对象的内存地址是否相等,即
28、是否是同一个对象。下面的测试例子则验证了以上的规则:Java 代码 1. public class EqualsTest 2. public static void main(String args) 3. / int 类型用 int 类型初始化 4. int int_int = 0; 5. / int 类型用 Integer 类型初始化 6. int int_Integer = new Integer(0); 7. / Integer 类型用 Integer 类型初始化 8. Integer Integer_Integer = new Integer(0); 9. / Integer 类型用
29、 int 类型初始化 10.Integer Integer_int = 0; 11.12.System.out.println(“int_int = int_Integer 结果是:“ 13.+ (int_int = int_Integer); 14.System.out.println(“Integer_Integer = Integer_int 结果是:“ 15.+ (Integer_Integer = Integer_int); 16.System.out.println(); 17.System.out.println(“int_int = Integer_Integer 结果是:“
30、18.+ (int_int = Integer_Integer); 19.System.out.println(“Integer_Integer = int_int 结果是:“ 20.+ (Integer_Integer = int_int); 21.System.out.println(); 22.23./ boolean 类型用 boolean 类型初始化 24.boolean boolean_boolean = true; 25./ boolean 类型用 Boolean 类型初始化 26.boolean boolean_Boolean = new Boolean(true); 27./
31、 Boolean 类型用 Boolean 类型初始化 28.Boolean Boolean_Boolean = new Boolean(true); 29./ Boolean 类型用 boolean 类型初始化 30.Boolean Boolean_boolean = true; 31.32.System.out.println(“boolean_boolean = boolean_Boolean 结果是:“ 33.+ (boolean_boolean = boolean_Boolean); 34.System.out.println(“Boolean_Boolean = Boolean_bo
32、olean 结果是:“ 35.+ (Boolean_Boolean = Boolean_boolean); 36.System.out.println(); 37.System.out.println(“boolean_boolean = Boolean_Boolean 结果是:“ 38.+ (boolean_boolean = Boolean_Boolean); 39.System.out.println(“Boolean_Boolean = boolean_boolean 结果是:“ 40.+ (Boolean_Boolean = boolean_boolean); 41. 42. 运行结
33、果:1. int_int = int_Integer 结果是:true 2. Integer_Integer = Integer_int 结果是:false 3. int_int = Integer_Integer 结果是:true 4. Integer_Integer = int_int 结果是:true 5. boolean_boolean = boolean_Boolean 结果是:true 6. Boolean_Boolean = Boolean_boolean 结果是:false 7. boolean_boolean = Boolean_Boolean 结果是:true 8. Boo
34、lean_Boolean = boolean_boolean 结果是:true 为了便于查看,上例中变量命名没有采用规范的方式,而是采用了“变量类型”+“_”+“初始化值类型”的方式。 1.2 Math.round()方法 java.lang.Math 类里有两个 round()方法,它们的定义如下:Java 代码 1. public static int round(float a) 2. /other code 3. 4.5. public static long round(double a) 6. /other code 7. 它们的返回值都是整数,且都采用四舍五入法。运算规则如下:1
35、. 如果参数为正数,且小数点后第一位=5,运算结果为参数的整数部分+1。 2. 如果参数为负数,且小数点后第一位5,运算结果为参数的整数部分-1。 3. 如果参数为正数,且小数点后第一位5“); 14.System.out.println(“正数:Math.round(11.68)=“ + Math.round(11.68); 15.System.out.println(“负数:Math.round(-11.68)=“ + Math.round(-11.68); 16. 17. 运行结果:1. 小数点后第一位=5 2. 正数:Math.round(11.5)=12 3. 负数:Math.rou
36、nd(-11.5)=-11 4. 小数点后第一位5 8. 正数:Math.round(11.68)=12 9. 负数:Math.round(-11.68)=-12 根据上面例子的运行结果,我们还可以按照如下方式总结,或许更加容易记忆:1. 参数的小数点后第一位5,运算结果为参数整数部分绝对值+1,符号(即正负)不变。 3. 参数的小数点后第一位=5,正数运算结果为整数部分+1,负数运算结果为整数部分。 但是上面的结论仍然不是很好记忆。我们来看看 round()方法的内部实现会给我们带来什么启发?我们来看这两个方法内部的代码:Java 代码 1. public static int round(
37、float a) 2. return (int)floor(a + 0.5f); 3. 4.5. public static long round(double a) 6. return (long)floor(a + 0.5d); 7. 看来它们都是将参数值+0.5 后交与 floor()进行运算,然后取返回值。那么floor()方法的作用又是什么呢?它是取一个小于等于参数值的最大整数。比如经过 floor()方法运算后,如果参数是 10.2 则返回 10,13 返回 13,-20.82 返回-21,-16 返回-16 等等。既然是这样,我们就可以用一句话来概括 round()方法的运算效果
38、了: Math 类的 round()方法的运算结果是一个=(参数值+0.5)的最大整数。 1.3 switch 语句 哪些类型可以用于 switch 语句的判断呢?我们做个测试就知道了:Java 代码 1. public class MathTest 2. / 枚举类型,Java5.0 以上版本可用 3. static enum enum_e 4. A, B 5. 6.7. public static void main(String args) 8. / byte 9. byte byte_n = 0; 10.switch (byte_n) 11.case 0: 12.System.out.println(“byte 可以用于 switch 语句“); 13.break; 14. 15./