Skip to content

Latest commit

 

History

History
227 lines (148 loc) · 11.5 KB

重拾Java(1)-基本数据类型与字面值.md

File metadata and controls

227 lines (148 loc) · 11.5 KB

Java是一种强类型化的语言,每个变量都有一种类型,每个表达式也都有一种类型,并且每一种类型都是严格定义的。所有的赋值操作不管是显式的还是在方法中调用中通过参数传递的,都要经过类型兼容性检查

一、基本数据类型

1.1、概述

Java定义了八种基本数据类型:byte,short,int,long,char,float,double,boolean

基本数据类型也称为简单类型,这些类型可以分为四组:

  1. 整型。包括byte,short,int,long。用于表示有符号整数
  2. 浮点型。包括float,double。用于表示带小数位的数字
  3. 字符型。包括char。用于表示字符集中的符号
  4. 布尔型。包括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定义了几个应用于表达式的类型提升规则

  1. 对于一元操作符来说,如果操作数的类型是byte,short或char,运算结果提升为int类型
  2. 对与二元操作符来说,提升规则是从以下几条依次选择一条执行
    • 如果操作数类型均为byte、short或char,那么两个数均转为int类型,结果数也将为int类型
    • 如果操作数包含double类型,那么另一个操作数也转为double,结果数也将为double类型
    • 如果操作数包含float类型,那么另一个操作数也转为float,结果数也将为float类型
    • 如果操作数包含long类型,那么另一个操作数也转为long,结果数也将为long类型