Java是一种强类型化的语言,每个变量都有一种类型,每个表达式也都有一种类型,并且每一种类型都是严格定义的。所有的赋值操作不管是显式的还是在方法中调用中通过参数传递的,都要经过类型兼容性检查。
一、基本数据类型
1.1、概述
Java定义了八种基本数据类型:byte,short,int,long,char,float,double,boolean。
基本数据类型也称为简单类型,这些类型可以分为四组:
- 整型。包括byte,short,int,long。用于表示有符号整数。
- 浮点型。包括float,double。用于表示带小数位的数字。
- 字符型。包括char。用于表示字符集中的符号。
- 布尔型。包括boolean。用于表示true/false值。
开发者可以直接使用这些类型,也可以使用它们来构造数组以及自定义类型。因此,它们形成了所有可以创建的其他类型的基础。
Java在其他方面是完全面向对象的,但基本数据类型并不是面向对象的,这样设计的原因是为了效率。将基本数据类型设计为对象会极大地降低性能。
因为Java语言的特色之一就是具备可移植性,即不管在哪个平台下运行,一份代码无需修改就可以直接运行。为了确保这一点,基本数据类型被定义为具有明确的范围和数学行为,与C和C++这类语言“允许整数的大小随着执行环境的要求而变化”不同,Java语言的数据类型都具有严格定义的范围。无论在那种平台下,int总是32位的。
虽然严格指定基本数据类型的范围在某些环境下会造成性能损失,但这是为了实现可移植性而必须付出的。
1.2、整型
Java定义了四种整数类型:byte,short,int,long。所有这些类型都是有符号的、正的整数或者负的整数。Java不支持无符号(正值)的整数。
名称 | 宽度 | 范围 |
---|---|---|
long | 64 | -9 223 372 036 854 775 808 ~ 9 223 372 036 854 775 807 |
int | 32 | -2 147 483 648 ~ 2 147 483 647 |
short | 16 | -32 768 ~ 32767 |
byte | 8 | -128 ~ 127 |
当中,最常用的整数类型是int。int类型经常用于控制循环变量和索引数组。对于那些不需要更大范围的int类型数值的情况,你可能会认为使用范围更小的byte和short类型效率会更高,然而事实并非如此。因为在表达式中使用byte和short值时,当对表达式求值时它们会被提升为int类型。所以,当需要使用整数时,int通常是最好的选择。
1.3、浮点型
浮点数也称为实数,当计算需要小数精度的表达式时使用。
名称 | 宽度 | 范围 |
---|---|---|
float | 32 | 1.4e-045 ~ 3.4e+038 |
double | 64 | 4.9e-324 ~ 1.8e+308 |
1.3.1、float
float类型表示使用32位存储的单精度数值。在某些处理器上,单精度运行速度更快,并且占用的空间是双精度的一半,但是当数值非常大或者非常小时会变得不精确。如果需要小数部分,且精确度要求不高时,就可以考虑使用float类型。
1.3.2、double
double类型表示使用64位存储的双精度数值。在sin()、cos()和sqrt()这类数学函数中,返回值都是double类型。如果需要在很多次迭代运算中保持精度,或是操作非常大的数值时,double类型是最佳选择。
1.4、字符型
char是用于存储字符的数据类型。Java的设计初衷是允许程序员编写在世界范围内均可使用的语言,因此采用了Unicode标准来表示字符。Unicode定义了一个完全国际化的字符集,能够表示全部人类语言中的所有字符,为此需要使用十六位宽度来存储。char的范围是0 ~ 65536,没有负的char值。
对于一些语种,例如英语、德语等,可以使用八位宽度来表示这类语言的字符,使用Unicode在一定程度上会降低效率,但这是为了在全球获得可移植性而必须付出的代价。
尽管char被设计为容纳Unicod字符,但也可以用作整数类型,可以对char类型的变量执行算术运算。
1.5、布尔型
boolean用于表示逻辑值,只能是true或false两个值之一。所有的关系运算都返回boolean类型值。
二、字面值
2.1、整型字面值
所有的整数值都是整型字面值,例如10、07、0x15、0B1010等。
除了最常用的十进制外,Java还提供了其他的进制类型。
在Java中,八进制以0开头,例如06、04等。
十六进制以0x或0X开头,十六进制数字的范围是015,因此使用AF(或af)替代数字1015,例如0x12f、0xa等。
从JDK 7开始,可以使用二进制指定整型字面值。为此,使用0b或0B作为数值的前缀。例如,下面用二进制字面值指定十进制值10:
int x = 0b1010;
此外,从JDK 7开始,在整型字面值中还可以嵌入一个或多个下划线,用于分隔开很大的整数,帮助阅读。例如:
int x = 123_456_789;
下划线只能用于分隔数字,不能位于字面值的开头和结尾。此外,连续使用多个下划线是允许的。
需要注意的是,整型字面值用于创建int类型数值。既然Java是强类型化的,那如何将整型字面值赋值给其他整数类型,如byte或long,而不会导致类型匹配错误呢?
其实,当将整型字面值赋值给byte变量时,如果字面值位于目标类型的范围之内,就不会产生错误。整型字面值总是可以赋给long变量,也可以将整数赋给char,只要在char类型的范围之内即可。
2.2、浮点型字面值
浮点数表示具有小数部分的十进制数值,可以使用标准计数法或科学计数法表示浮点数。
标准计数法由前面的整数部分、其后的小数点以及小数点后面的小数部分构成。例如,10.001、1.456等。
科学技术法使用一个由标准计数法表示的浮点数加上一个后缀表示,其中的后缀指定为10的幂,与前面的浮点数是相乘的关系。指数部分用E(或e)后面跟上一个十进制数表示,该十进制数正负都可以。例如,7.22E20、24.3e-6等。
在Java中,浮点数字面值默认是双精度的。为了指定浮点型字面值,需要为常量添加一个F或f为后缀,也可以通过附加D或d为后缀来显式地指定double字面值。
从JDK 7开始,在浮点型字面值中同样可以嵌入一个或多个下划线。
2.3、布尔型字面值
布尔型字面值只有两个逻辑值——true和false。true和false不能转换成任何数字表示形式。
2.4、字符型字面值
Java中的字符被索引到Unicode字符集,它们是可以可以转换成整数的十六位值,并且可以使用整数运算符进行操作。字符型字面值使用位于一对单引号中的字符来表示。对于那些不能直接输入的字符,可以通过转义字符序列输入需要的字符。
转义序列 | 含义 |
---|---|
' | 单引号 |
" | 双引号 |
\\ | 反斜杠 |
\r | 回车键 |
\n | 换行符 |
\f | 换页符 |
\t | 制表符 |
\b | 回格符 |
2.5、字符串字面值
指定字符串字面值的方法与其他大多数语言一样,使用位于一对双引号中的字符序列来表示。例如:"Hello world"。
三、类型转换
在Java中,当将某种类型的值赋给另外一种类型的变量时,如果两种类型是兼容的,那么Java会自动进行类型转换,例如,int类型总是可以赋给long类型的变量。然而,并不是所有类型都是兼容的,因此并不是所有类型转换默认都是允许的。例如,不能从double类型默认转到byte类型,在这种情况下,需要进行强制转换(cast)。
3.1、自动类型转换
当将某种类型的数据赋给另一种类型的变量时,如果满足如下条件,则可以发生自动类型转换:
- 两种类型是兼容的
- 目标类型大于源类型
当满足这两个条件时,会发生扩宽转换。例如,要保存所有有效的byte值,int类型是足够的,所以不需要进行显式的强制转换语句。
对于扩宽转换,数值类型(包括整型和浮点型)是相互兼容的。然而,不存在从数值类型到char和boolean类型的自动转换,char和boolean相互之间也是不兼容的。
当将字面整数常量保存到byte、short、long或char类型的变量中时,Java会执行自动类型转换。
int i=1;
double j=1.2;
//错误,不存在从整型到char类型的自动转换
char c1=i;
//错误,不存在从浮点型到char类型的自动转换
char c2=j;
//正确,字面整数常量保存到char类型会自动类型转换
char c3=10;
3.2、强制类型转换
因为byte类型的范围比int类型小,所以如果将int类型的值赋给byte变量,不会自动执行类型转换。
为了实现两种不兼容类型之间的转换,需要使用强制类型转换。这种转换有时被称为缩小转换,因为是显式地使数值变得更小以适应目标类型。
int a=10;
byte b;
//编译器报错
b=a;
//强制类型转换,编译器不报错
b=(byte) a;
当将int类型的值强制转换为byte类型时,如果int类型的值超出了byte类型的范围,转换结果将会发生很大变化。
当将浮点值赋给整数类型时会发生另一种类型的转换:截尾。因为整数没有小数部分,将浮点数赋给整数类型时,小数部分会丢失。
例如,参考以下一段程序的输出结果:
public static void main(String[] args) {
int i=10000000;
double d=111.11;
byte b=(byte) i;
float f=(float) d;
System.out.println("b的值:"+b);
System.out.println("f的值:"+f);
b=(byte) d;
System.out.println("b的值:"+b);
}
b的值:-128
f的值:111.11
b的值:111
四、表达式中的自动类型提升
除了赋值外,在表达式中也可能会发生类型转换。在表达式中,中间值要求的精度有时会超出操作数的范围。
例如:
byte a = 40;
byte b = 50;
byte c = 100;
int d = a * b / c;
中间部分 a * b 很容易超出byte操作数的范围。为了解决这类问题,当对表达式求值时,Java会自动将每个byte,short或char操作数提升为int类型。这意味着使用int类型而不是byte类型执行子表达式a * b。因此,即时a和b都被指定为byte类型,中间表达式(50 * 40)的结果2000是合法的。
自动类型提升很有用,但有时候会导致难以理解的编译时错误。例如:
byte b=10;
//错误
b= b*2;
如上代码试图将 10 * 2 的结果(一个完全有效的byte值)保存到byte变量中,但是编译器却提示错误。
当计算表达式的值时,操作数被自动提升为int类型,所以结果也被提升为int类型。因此,现在是试图将一个int类型值转为byte变量,如果不使用强制类型转换,就不能将结果赋给byte变量。
Java定义了几个应用于表达式的类型提升规则。
- 对于一元操作符来说,如果操作数的类型是byte,short或char,运算结果提升为int类型
- 对与二元操作符来说,提升规则是从以下几条依次选择一条执行
- 如果操作数类型均为byte、short或char,那么两个数均转为int类型,结果数也将为int类型
- 如果操作数包含double类型,那么另一个操作数也转为double,结果数也将为double类型
- 如果操作数包含float类型,那么另一个操作数也转为float,结果数也将为float类型
- 如果操作数包含long类型,那么另一个操作数也转为long,结果数也将为long类型
网友评论