Java SE 八大基本类型

java中有8大基本类型。这8大基本类型是byte、short、char、int、long、float、double和boolean。这是java比其他语言美的地方。有些编程语言类型混乱,比如linux shell语言,这类语言被称为弱类型语言。但不是弱类型就不完美,linux shell也是一种非常强大的编程语言。Java和C语言一样,是一种强类型的语言。

和C语言不同的是,java的int永远是32位的,C语言中的int型,大部分是16位的,某些机器上是32位的。标准不一致,因为C语言是贴近机器的,32位的机器和64位的机器的int类型长度肯定不一致。

Java中byte是8位的,short和char是16位的。Short和char的不同就在于short是有符号的,而char是没有符号的。

Int已经说过了,long的长度肯定是最长的64位了。

剩下的就是float、double和boolean了。

Float是32位的小数类型。在计算机里,小数也叫浮点数。

Double是64位的小数。在java中double用得比float多一点。

剩下的就是布尔值了,也就是boolean类型。Boolean类型的数据只有两个值,一个是true,还有一个是false。

类型转换

先讲讲在java中的同一类类型的转换吧。对于8种数据类型,我们可以将它们分为3个大类别:整数,浮点数和布尔值。

整数有5种,它们都是按照补码来编码的,分别是byte、short、char、int、long五种类型。

浮点数有两种,float和double。

布尔值就只有boolean一种了。

同一大类别之间的转换规则其实很简单。范围小的可以自动转换为范围大。范围的大小肯定是数据类型的位数有关了。范围最小的肯定是byte,范围最大的是long。所以把byte转化成long,那是一点问题都没有。可以直接用等号连接起来,也叫做隐式转换。

比如下面几行代码:

int a = 2;

long b = a;

System.out.println(b);

但是反过来就不行了。比如下面两行代码:

long a = 8;

int b = a;

这两行代码会报编译错误。因为long类型不能隐式转换为int类型。这时候就需要显式转换了,也就是大家所说的强转。

下面就是强转的代码:

long a = 8;

int b = (int) a;

当然在C语言中还可以这样强转:

int b = int(a);

但是java中肯定不可以。但是还有一个问题,就是char和short的问题。两个人都是16位的,一个是有符号的,一个是没有符号的。这就需要对char这种数据类型做一番解释了。其实char是字符类型,但是计算机表示字符肯定不能用图片来表示字符。假如计算机用图片来表示字符,那么会有多少弊端呢?

第一 网络传输慢。图片本身就很大,如果你在浏览器上看小说的话估计光打开网页就需要半小时。

第二 难以处理。比如我想大写换小写,一大堆图片摆在那儿,我还必须去一个个解析图片,才能转换大小写。影响效率。再比如,我要换个字体,同样需要解析图片才能知道这是个什么字,然后再转换字体。

对文字的处理,聪明的人类就想到了一种办法,叫字符编码。就是用一个数字来代替一个字符。Char类型就是用来代替字符的数字。那么该怎么知道每个字符都是由哪个数字代替的呢?

下面用一小段代码来揭开这个神秘的谜底:

package net.cloudsun.foo;

 

public class Foo {

public static void main(String[] args) {

System.out.println('中'+0);

System.out.println('华'+0);

System.out.println('人'+0);

System.out.println('民'+0);

System.out.println('共'+0);

System.out.println('和'+0);

System.out.println('国'+0);

}

 

}

这个加0其实大有深度,加上0就可以让println方法不把字符当字符输出,而是当做数字来处理。下面是中华人民共和国这7个字的编码:

20013

21326

20154

27665

20849

21644

22269

Java使用的是unicode编码。但不是全部的unicode编码。当然还有很多种编码方案。讲了很多了,还没讲到short和char之间的类型转换呢。那么到底有什么转换规则呢?

这段代码是会报编译错误的:

char a = 6;

short b = a;

是不是意味着char的范围要比short大呢?这样还不能下结论,我们试一试下面的代码:

short a = 6;

char b = a;

同样报了编译错误,是不是很坑?这两张基本类型,由于都死16位,不能确定谁的范围更大,所以必须显式转换。

同一大类型转换有这个规则,那么不同类别的呢?比如int和float都是32位的,但是一个整数,一个是浮点数。这两者之间能不能互相转换呢?

当然可以,但是也有范围的。虽然int和float都是32位的,但是数字范围是不一样的。浮点数的范围要比int型大,所以int型可以直接转换成float型。如下面的代码:

int  a = 8;

float b = a;

System.out.println(b);

但是浮点型向int转换就需要强制类型转换了,如下面的代码:

float  a = 8;

int b = (int) a;

经过上面的代码测试和分析,我们可以得出结论,同样的位数,浮点型的范围肯定要大于整型的范围。浮点型的范围是如此之大,那么float类型会大于long类型的范围吗?口说无凭,代码为证:

long a = 1;

float b = a;

但是下面这两行代码就会报编译错误:

float a = 1;

long b = a;

编译是一点错都不会出的。事实就是浮点型数的范围就是这么地大,连long类型都吃不消。那么double类型就不用讲了,既是浮点数,又有64位的长度,是当之无愧的老大哥了。

我们还忘了这个类型——boolean。那么,boolean能不能向其他类型转换呢?肯定是不可以的。在C语言中,0就是false,非0就是true。在linux shell中,0就是true,非0就是false。这两种语言没有boolean这个类型,都是用int类型来表示boolean值,所以也不存在什么类型转换的问题了。

但是java既然有了这个类型,就必须解决类型转换的问题。因为boolean类型只有两个取值,所以向其他7种类型相互转换肯定是不适合的。所以java的语法不准boolean类型和其他7种类型相互转换。即便是显式转换也不能。

浮点数是有精度损失的。比如我们做个试验。写上下面这几行代码:

System.out.println(3.0-2.9);

执行结果会是什么呢?是下面的这个结果:

0.10000000000000009

可见浮点数的运算是非常不精确的。所以在要求精确计算的地方可以用int型来计算。比如钱币的加减运算。因为钱币的最小为分,所以我们可以以分为单位,3块钱减去2块9毛钱,可以写成300-290得出10,这个10再转换为1毛钱。

除了这个问题外,java有没有别的需要注意的地方呢?

整数就不会出问题吗?整数也会出问题的,请看看下面的代码:

byte a = 1;

byte b = a+2; 

这段代码竟然会报编译错误,再看看这段代码:

byte c = 1+2;

很奇怪的是,这段代码不会报任何编译错误。这是怎么回事呢?先解释第一段代码为什么会报错。因为java在进行整数之间的运算时,会将数值隐式转换为int型,那么b是byte型的,范围比int小,所以报编译错误。

那么第二段代码怎么不报错呢?因为是1+2。1+2是两个赤裸裸的数字相加,编译器在编译的时候就会去计算数字的和。所以编译器会将byte c = 1+2;转换为这样的代码:

byte c = 3;

既然编译器知道了那个数字就是3,3明显没有超出byte的范围,所以不报错。那编译器为什么不知道上面的b也等于3呢?

因为b是等于a+1。对于编译器来说,它只知道a是个变量,不知道它的值,就算之前定义了a=1,而且这两行语句中间没有插入任何语句,它还是不知道b的值,所以按照整数运算隐式转换为int的规则,就报了编译错误。

这个说法真的说得过去吗?

我们再做一次试验,代码如下

int a = 2147483648;

int b = 2147483647 + 1;

第一行语句报了编译错误,因为这个数字已经超出了int型的范围。但是第二行语句就没有报错。这又是为什么?

答案就在第二行,编译器在计算 2147483647 + 1的时候并不会将它计算成2147483648,而是计算成-2147483648.这是为什么呢?因为整数之间的运算时按照int型来进行运算的,运算的结果也是int型的。但是如果运算结果超出了int型的范围了怎么办?那么就溢出呗!一溢出就变成-2147483648了。

但是这样会出现问题了,如果我类型是long类型的我不怕溢出,但是编译器默认给我溢出怎么办?

就比如下面这段代码:

long a =  2147483647+ 1;

System.out.println(a);

输出结果:

-2147483648

可见这默认按int型计算是十分坑爹的。Long类型本来可以存储那么大的数字的,但是却溢出了。这该怎么办?解决办法不是没有,我提供了两种解决方案。请看代码:

long a =  (long)2147483647+ 1;

System.out.println(a);

输出结果为:

-2147483648

这种解决方案就是让int型数据显式转换为long来进行计算,这样编译器就不会按默认的int型来进行计算了。但是我不推荐这种方法,为什么呢?因为第二种方法是更好的方法。请看代码:

long a =  2147483647l+ 1;

System.out.println(a);

输出结果为:

2147483648

但是很多童鞋会很郁闷,2147483647l+1不是2147483672吗?怎么会是2147483648呢?其实2147483647l的最后一个字符不是阿拉伯数字1,而是英文小写字母l。请注意这一点,阿拉伯数字的1最上面是个尖尖的角。但是英文字母的l最上面是平的。就这么点区别。这个l放在数字后面什么意思呢?就是将数字转换为long类型。L就是long的缩写。

但是java开发标准一般不允许用小写的l,那怎么办?那就用大写的L啊。因为小写的l和数字1真的是长得太像了。

数字后面加大写的L和小小写的l效果是一样的。除了这个大小写的l,还有两个字母也可以加载数字后面。那就是float的缩写F和double的缩写D。和L不同的是,这两个字母都是大小写皆宜的。

OK,8种基本数据类型,我就说完了。但是光这点还不够,我们还需要深入地了解低层原理。就比如下面的这道面试题,曾经就难倒了无数面试者:

System.out.println(~3);

面试官肯定问这行语句的输出结果是什么。很多人懵了。~是取反的符号,因为计算机内部都是0和1组成的,取反就是把0变成1,1变成0,就是这么简单。这道题的答案是-4。那么3取反怎么会是-4呢?

这就涉及到了补码,只是针对整数来说的。小数不是采用补码编码的。补码是这样一回事,对于每个整数来说,最高位是符号位,0表示整数,1表示负数。0这个数字也是整数,它的编码是所有位都为0。那么编码规律是不是这样,假如是byte型数据,0000 0001代表+1,1000 0001代表-1。是不是这样的呢?肯定不是了其实补码的负数都是用0去减去一个正数得来的。

比如-2的编码,是0-2的结果,假如数据类型为byte型,忽略符号位,下面是运算过程:

0 二进制编码: 000 0000

2 二进制编码: 000 0010

相减的结果 111 1110

所以-2的编码就是1111 1110。那么-1的编码就是1111 1111.无论整数有多少位,拿0去减1,,我们可以得出结论,-1的编码都是所有位都为1。

所有位都为1,就有了一个奇怪的现象。那就是取反的问题。我们知道取反,一个数和它的取反后的数是非常对称的,每个位上,我是0,你就是1,我是1,你就是0.所以两个互反的数相加的话,每个位上都是1个0,一个1,那么加起来肯定为1,每个位都为1,那么这个相加后的数就是-1.

我们可以写这样一个公式。

A + (~ a) = -1

这个公式可以任意推导。我们有时候觉得计算负数的补码很麻烦是吧。可以这样将公式变形:

~a = -1 - a

A = ~(-1 - a)

A = ~(-a - 1)

这样一个负数的补码计算公式就出来了。比如-5=~(5-1),就是4取反了。所以那道面试题也很容易做出来了。 

经过我的一讲,我们对整数的编码都相当熟悉了。但是浮点数呢?Java只有两种浮点数,一种就是float,一种是double。在接触浮点数之前,我们先接触下定点数这个概念。其实整数就是一种定点数,因为它的小数点永远在最后。还有种表示种纯小数的数,叫定点小数,表示的是永远大于-1,并且小于+1的小数。定点小数太让人蛋疼了,根本无法满足运算的需要。

相信大家都知道科学计数法吧。科学计数法里将每个小数都变得非常规则。比如456.789这个数字,科学计数法就将其表示为4.56789*102这个数字。这样管理就相当方便了。当然这是十进制的运算,但是也可以得到一些通用性的概念。也就是科学计算法这样表示方法比其他方法更有效。

通过科学计数法,我们知道一个小数可以用如下的式子表示:

N=M*RE

其中N是你想表示的数,M被称作尾数,也就是上面例子中的4.56789,R是基数,对于10进制来说,那就是10,2就是阶。

计算机里也是用这种表示方法来表示小数的。对于计算机老说,基数永远为2,那么所需要处理的就只有阶和尾数了。尾数怎么处理呢?比如一个二进制小数1.11(也就是十进制的1.75)。它是1.11*20还是0.111*21,还是11.1*2-1呢?

我们都很聪明,知道不可能用11.1*2-1这种表示方法。但是到底是0.XXX做尾数还是1.XXX做尾数呢?

按照计算机界的标准,尾数除非为0,否则必须用1.XXX表示。貌似用0.XXX的形式更好,但是规范不允许。既然尾数的编码问题我们解决了,那么我们接下来就讨论阶的问题。

浮点数的阶采用移码的方法表示。什么叫移码呢?这是聪明的人类为了表示负数的一种脑残的办法。就是将所有数都加上一个数,这样负数都变了,都变成正数了。但是也要有个限度,这个限度就是让最小的负数变成0.拿byte来说吧,假如用移码来表示的话,最小的负数-128,用移码表示就是0了,二进制就是0000 0000。那么最大的正数127,用移码表示就是255了,二进制表示就是1111 1111。

按照这种理论和规则,我们很容易知道1.75的浮点数编码了。假如浮点数只有8位,一个符号位,3位表示尾数,4位表示阶。那么浮点数的编码就是

符号位 0

尾数 1.11

阶 1000

N=M*RE

浮点数的表示,尾数在最后,这样才叫尾数嘛。所以这个数的二进制编码就是01000111。Java中浮点数和IEEE 754标准一样。Float型和double都有符号位。Float型有8位阶码,23位尾数。而double型有11位阶码和52位尾数。

那么现在我们就可以来弄清楚为什么float的范围比long大了。

先看long类型数据的范围。Long是64位的,用1位符号位,那么还有63位,所以long类型的数据范围是:

-263~+263-1

再看浮点数,最小值,肯定是这个数

符号位1 

移码表示的阶:移码的1111 1111 = 127

尾数1111 1111 1111 1111 1111 111 = 1.111... ≈ 2

那么最小值就是接近于-2*2127的一个数,那么最大值肯定是接近于2*2127的一个数。再和long类型相比,就可以知道两者的范围了。

在计算机世界二进制是如此地重要,但是二进制书写实在是太麻烦了。有木有一种更好的书写方法呢?肯定是有的,那就是十六进制和八进制。十六进制就是将4个二进制数字替换为一个十六进制数字。八进制就是将3个二进制数字替换为一个十进制数字。

在java中,八进制的数字就是前面多了个0.但是java中坑爹的一点是不能用八进制表示小数,只能用八进制表示整数。

但是好消息是十六进制可以!

要表示16个数字,0到9是肯定不够用的,因此加入了ABCDEF6个英文字母。我简单地介绍一下java中怎么用十六进制吧,十六进制就是简单地以0x或者0X开头。

比如2012,用十六进制表示就是0x7DC,大小写无所谓。

但是小数怎么表示呢?要表示小数,也不需要那么深入地去研究浮点数的编码,只需要加个字母p来运用科学计数法那就可以了,比如下面这样的十六进制小数是完全合法的:

0x222.19P12

P前面肯定是尾数,P后面是阶数,你懂的。

还有一种基本类型我们没有深入地去掌握它,那就是伟大的char。Char有几种表示方法,比较普通的方法就是用数字来表示,比如:

char c = 65;

这个65,很多人都懂得,是大写的字母A。当然这种表示方法似乎过于高端,还可以用这种表示方法,那就是一对单引号。如下面的代码:

char c = 'A';

这种方式比上面那种方式似乎更简单,也最常用。但是这种方法有时也会出问题,就比如单引号本身也是一个字符,那我该如何表示这个字符呢?

直接写肯定会出错的。像下面的代码:

char c= ''';

因为第二个单引号和第一个单引号匹配了,所以第三个单引号孤立了,为此报错。为了解决这个问题,java借鉴了C语言和linux shell语言,使用了转义符号。转义符号就是将某些有特殊意义的字符转化为没有特殊意义的字符,使得它们成为普通的字符。这个神奇的转义字符就是大家嘴上常说的反斜杠,就是下面这个符号:

\

因此,我们就可以这样写以避免错误:

char c= '\'';

如果当成字符的话,双引号是不需要转义的,就比如下面的语句是完全合法的:

char c= '"';

当然你也可以写成这样,没有任何问题:

char c= '\"';

需要转义的还有转义符号本身,这样写肯定出错:

char c= '\';

所以必须这样:

char c= '\\';

转义符不但可以将屏蔽特殊意义的字符的意义,而且还可以将原本没有任何意义的字符变得有意义。下面是大家耳熟的一些转义:

\n换行 \b退格 \f换页 \t缩进 \r回车 \0终止

注意,某些在C语言中能使用的转义,在java中不能使用,比如:\a \v。比较好记忆的办法就是将AV合起来记忆,多念几遍java不能转义AV就可以记牢了。

当然,转义符不仅仅只有这么点用处,只有这么点用处也不能叫转义符。它还可以加3个八进制的数字来表示ASCII码字符。

比如下面这行代码,输出结果就是大写字母A:

System.out.println('\101');

C语言除了八进制以外还可以使用十六进制,十六进制自然会用到那个神奇的,总是代表十六进制的字母X,但是java不允许这样转义。

但是有种转义是java允许的,但是C语言不允许,那就是unicode转义。语法很简单,就是转义符+字母u,再加上十六进制的unicode编码就可以了。比如,下面的代码就是打印一个中国的中字:

System.out.println('\u4e2d');

需要注意的是,转义字符后面只能接小写的字母,大写字母是会报编译错误的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值