磨刀不误砍材工 – Java的基础语言要素(从变量/常量切入,看8种基本数据类型)

最后更新于:2022-04-01 20:08:28

变量与常量是一个Java程序组成的重要部分。 我们可以将变量与常量理解为数据的载体,而从名称上我们也可以看出二者的不同: 常量代表不能改变的数据值,而变量的值则存在可变性。 在我们回顾Java中的关键字的使用时,说道:被Java中的关键字final所修饰的变量,其值一经初始化,便不能再次进行赋值。该特性恰恰符合常量的定义。 ~~~ String var = "字符串变量"; //java中,关键字final用于声明数据常量 final String CONSTANT = "字符串常量"; ~~~ 既然我们知道了变量与常量是作为数据的载体使用;那么,就如同我们如果使用一个杯子作为载体,那么其搭载的介质可能是水,咖啡,果汁等等一样; 我们自然要了解Java中的变量与常量作为数据载体,其搭载的数据究竟有哪些? 大体来说,Java的数据类型分为:基本数据类型,对象数据类型以及数组,但数组实际也属于对象。 所以,Java中的变量/常量就是用以作为基本数据类型和对象数据类型的载体的。 对象数据类型的回顾需要结合一个关键的概念:类。 所以在这里,我们先将变量和常量作为切入点,首先来重新系统的总结一下Java中的8种基本数据类型的特性及使用。 总的来说,Java中的8种数据类型可以分为三类:数字类型,字符类型和一种特殊的数据类型布尔型。 **一、数字数据类型** 数字数据类型共有6种,其中4种用于表示整数,2种用于表示浮点数。 首先需要明确的是:Java中的6种数字类型都是有符号数,也就是说它们都有正负之分。而具体又是如何区别表示正数与负数的呢? 我们知道所谓的"1","3","101"这样的数,是我们日常生活中习惯使用的十进制数。 但在计算机中,所有数字都是以二进制数来表示的,也就是说,是一串由“0”和"1"组成的数字。 这样的一串数字中,其最高有效位是用于表示符号的,就是所谓的符号位。 符号位为“0”,代表是一个正数;符号位为“1”,代表是一个负数。而剩余位则用于表示值。 **1.1、整数类型** Java中,用于表示整数的4种数字类型分别为:byte(字节型)、short(短整型)、int(整型)、long(长整形) ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-17_57b431601660e.jpg) 我们说在计算机中,数字都是由二进制形式表示的,那么自然的,其位数越多,可能的取值范围就越大。 所以我们看到从byte到long,随着所占位数的增加,其取值范围也就越大。 计算机中,一个字节等于8个比特位。而byte长度正是8位,这也是为什么它被取名为字节型;而剩下的short,int,long分别对应2个字节,4个字节和8个字节。 **进制转换:** 我们刚刚已经说到了二进制和十进制,而在Java中,整数还有另外两种进制表现形式,分别是:八进制和十六进制。 在了解进制转换之前,我们先通过一段简单的代码了解一下Java中八进制和十六进制数的声明形式是怎么样的: ~~~ // 十进制定义形式 int num_10 = 10; // 八进制定义形式,以“0”作为前缀,表示定义的是一个八进制整数 int num_8 = 012; // 十六进制定义形式,以“0x”作为前缀,表示定义的是一个十六进制整数 int num_16 = 0xef; ~~~ 了解了不同进制的定义形式,我们就可以看一下进制之间的相互转换了。首先我们应该知道,所谓进制,其实原理都是一样的: 所谓二进制,就是指”逢二进一“,也就是说二进制中只可能存在”0“和”1“两种可能值;而所谓十进制,就是指”逢十进一“,也就是说只可能存在0-9的可能值。 那么同样的,八进制就是指”逢八进一“,所以只可能存在0-7的可能值;同理的,十六进制就存在0-15的可能值,但传统定义数字中,”9“已经是单位的最大可能值,所以十六进制中以英文字母a - f分别代表 10 - 15。 那么,进制之间究竟是如何完成相互之间的转换工作的呢? **1、二进制数、八进制数、十六进制数转十进制数** 有一个公式:二进制数、八进制数、十六进制数的各位数字分别乖以各自的**基数**的(N-1)次方,其和相加之和便是相应的十进制数。例如: 二进制数:0000-0110转换为十进制数:1*2的2次方+1*2的1次方+0*2的0次方=0+4+2+0=6,也就是说转换为十进制数的值为:6。 **2、十进制数转二进制数、八进制数、十六进制数** 方法是对应的的,即整数部分用除**基**取余的算法,小数部分用乘基取整的方法,然后将整数与小数部分拼接成一个数作为转换的最后结果。 **3、二进制数转换为八进制数或十六进制数** 其原理很简单:我们已经知道了八进制只有0-7的可能值,十六进制则只有0-15这的可能值。 而我们观察到这样一种情况:对于一个二进制的数,如果只取一个有效位的数,所能能表达的最大数为:1;而取两个有效位的数,所能表达的最大值则为:“11”,也就是十进制的3;取三个有效位的数,能表达的最大的数为”111“,则是十进制的7;而取四个有效位的数“1111”,则正是十进制的15. 由此我们发现:如果将二进制数每三位取出,则正好能表示一个八进制的数;而将二进制数每四位取出,则正好能表示一个十六进制数。 而事实上,二进制与八进制和十六进制的转换原理也正是这样的。举例来说: 以二进制数:0000-1010为例,转换为八进制数为:000/001/010,也就是12;而如果转换为十六进制则为:0000/1010,也就是a。 **1.2、浮点数类型** 正如数学中数字分为整数和小数一样,Java中也是一样的。但Java不称为小数,称为浮点数。 而Java中,用于表示浮点数的两种种数字类型分别为:float(单精度浮点型)和double(双精度浮点型)。 ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-17_57b43160314db.jpg) Java里默认的浮点数形式是双精度形式,也就是double。所以在定义一个float时,必须加上后缀F(f):float f = 2.3F。而定义double,后缀D(d)的添加则是可选的。 到了这里,我们已经了解了Java中所有的数字类型。 之所以了解它们各自不同的特性,是为了在实际编写代码的过程中,可以根据实际需求选取最合适的数据类型来定义自己的变量(常量)。举例来说: 如果想要表示全世界的人口数量,那么可能选择long型来表示更为合适;而如果要表示某个公司的职员每月的工资情况,那么选用float可能更为合适。 **三、字符类型** Java中,另一种基本数据类型:char型,代表字符类型。 在开发中,可能经常需要存储一些字符,如‘A’,‘c’等等。char型就是用于存储单个字符的数据类型。 同时,char型数据也可以通过Unicode码表示字符;简单的来说,就是我们也可以通过在Unicode码表中有效的整数来表示一个字符。 其实很好理解,就像我们在小时候学习拼音的时候,可能都会接触到“拼音字母学习表”一样: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-08-17_57b431604c339.jpg) Unicode码表也是类似于这样的一张字符编码表,所谓编码就是对表中的每一个字符编排一个“号码”。 这个号码就像我们每个人的身份证,是独特对应的关系,通过身份证号码就可以查出我们每个人的信息。 到了这里就不难理解了,就假设:我们以97表示一个字符,Java会根据该值到Unicode码表中进行查询,然后发现号码“97”对应的字符是"a"。 Unicode码是用'\uxxxx'表示的。x表示的是16进制数值。并且Unicode编码字符是用16位无符号整数表示的。也就是说,Unicode码表共有0-65535的编码值。 **四、布尔类型** Java中一种特殊的数据类型,只有“true”和”false“两种可能值。通常用于关系运算或条件判断表达式返回的结果。 **数据类型的转换** Java自身是一门强数据类型语言,所以在遇到不同数据类型同时操作,就需要进行数据类型的转换。 数据类型转换需要满足的最基本的要求,就是数据类型必须兼容。例如要将一个布尔型的数据转换为整数类型,肯定是不能成功的。 而在Java中,数据类型的转换又分为两种:自动类型转换和强制类型转换。 所谓自动转换就是指无需认为做任何额外的工作,虚拟机会自动的完成对数据类型的转换工作。 而强制转换则是指我们必须人为的进行声明后,才能完成想要的数据类型转换。 也就是说,自动数据类型转换是隐式的转换,而强制类型转换则是显式的转换。 那么首先来看一下**自动类型转换**: 第一种情况:低位数的数据类型可以自动转换为高位数的数据类型。 ~~~ // 低位数的数据类型自动转换为高位数的数据类型 byte b = 1; short s = b; int i = s; long l = i; float f = 1.5f; double d = f; /* * 另外,Java中整数的默认形式为int型, * 所以下面的声明形式实际也是: * 虚拟机自动完成了一次隐式的数据转换工作 */ long num = 1000; ~~~ 第二种情况:整数类型可以自动转为浮点数类型,但是这种转换后的值可能会出现误差。 第三种情况:字符类型可以自动转换为整型或长整形。这是因为Java中char型数据也可以通过Unicode码表示,长度为16位,所以也可以转换为长度更大的int和long型。 ~~~ char c1 = 'a'; int i1= c1; char c2 = 'b'; int i2 = c2 + 10; char c3 = 'c'; long l = c3; ~~~ 接下来,就是Java中的**强制类型转换**:强制转换的格式为:(type)value。 第一种情况:对应于自动转换,那么当高位数的数据类型转换为低位数的数据类型时,就需要做强制转换。 既然我们看到了”强制",那可能我们自然就会想到在这样的转换过程中,是不是存在一定的“风险”? Java自身是一门严谨的编程语言,如果不存在风险,为何还需要我们作人为的"强制“性转换呢? 而事实上也正是如此。我们首先应当了解,Java中对一个高位数数据转换为低位数数据类型时,实际上是在对二进制表现形式做有效位的截取。 我们知道一个二进制数的位数越多,其取值范围也就越大,也就是说它的可能值越多。 这也就意味着,如果将一个高位数的数据类型转换为低位数的数据类型,那么便可能发生:很多高位数能够表达的可能值,低位数表达不了的情况。 这也正是其”风险“所在:转换的过程中,可能造成数据丢失! 我们举个例子来说: 假设我们有一个int型的变量,值为128。相应的,我们将其转换为二进制表现形式就是:0000 0000 - 0000 0000 - 0000 0000 - 1000 0000。 如果我们要将其转换为byte类型。那么byte类型的数据长度为8位,所以我们进行有效位的截取后,值变为了:1000 0000。 我们知道二进制数的最高有效位用以表示符号,所以这里转后的值的实际值变为了十进制当中的-128。所以128在这个转换中,值由原本的128变为了-128。 既然高位数数据类型转换低位数数据类型存在这样的风险,那么作为一门健壮的语言,Java自然是不支持这样的转换的。 所以,为我们了提供了(type)value这样的强制转换方式,我们这样做的意义就在于,告诉编译器,我了解这样做可能承担风险,但这个风险由我来承担。 最后,我们通过代码来验证我们刚刚的转换过程: ~~~         int i1 = 127;         byte b1 = (byte) i1;         System.out.println("b1="+b1);                  int i2 = 128;           byte b2 = (byte) i2;           System.out.println("b2="+b2); ~~~ 其运行的输出结果为: b1=127 b2=-128 通过其结果恰恰验证了我们提到的转换过程。 因为127本身在byte的取值范围之内,所以强制转换过后,数据仍然正确。但128超出了byte的取值范围,所以在经过有效位的截取之后,值发生了变化,变为了-128. 第二种情况:浮点数类型转换为整数类型需要进行强制转换,因为小数点后的数在转换过程中会被丢弃 ~~~ double d = 128.123; //转换后的值变为了128 int i = (int) d; System.out.println(i); ~~~ 到此,我们以Java的变量(常量)为切入点,又重新回顾了Java中8种基本数据类型的特点和使用。
';