上篇咱们介绍了java运行过程,jdk介绍等等,最后还运行了一个程序,我们再看一遍:
编译执行后仅仅就打出来一句Happy New Year,非常简单。先不管前面这两句代码 – public class Test以及public static void main(String[] args),它其实就是个程序的入口,程序就从main关键字这里开始执行。
当程序执行这句话的时候,其实是把这句话加载到内存里然后打出来。内存是干嘛用的?咱们每台电脑都有内存条,虽然所有程序写到文件里存在硬盘上,但执行的时候都要加载到内存里。内存条就是干这个用的。现在咱们先来看一组数和英文单词:
它们有什么特点?很明显,第一组是整数,第二组是小数,第三组是字母。我们刚才的程序打印happy new year很简单,可实际情况却很复杂。计算机计算机,你得让它计算吧,光打印不能满足需求。而计算靠的就是类似的英文字母或数,我们管它们叫作变量的值。那变量是什么?简单点说就是用来存储这些数值的空间:
我给大家举个形象的例子:假如把整个内存比喻成一个宾馆,变量就好比是一个个的客房,客房里的客人我们管它叫作变量值。这些客房每个都有一个门牌号,比如101, 102。我们管它们叫作变量名:
变量不是自然就有的,你得自己创建声明它。就好比这个宾馆一开始是没有客房的,程序运行时临时建的。刚才说了,程序是被加载到内存里执行的,那变量,或者说这些空间,自然就是在内存里被创建出来的。创建变量的格式是"变量类型 变量名 = 值":
还是用例子说话,在磁盘下新建一个叫Variables的文件夹,里面创建一个叫Variables.java的文件,把下段代码复制粘贴进去:
int i = 10; 这是一个声明整型变量的过程。单词int代表整型变量类型, 是英文integer的缩写,就是整型的意思。我们暂时就把它理解成是其中一种变量类型就可以了,一会儿就详细讲它是什么。i是变量名,10就是它的值。注意后面有个小分号,代表一句代码结束。每写一句代码都需要在最后加上一个分号,这是规定,否则编译不过去。System.out.println()我们上篇中已经见过了,它可以打印出括号里的东西,也就是变量i的值。我们现在编译执行一下,切换到Variables文件夹下,编译Variables.java并执行Variable.class。如果忘了怎么做的朋友可以再复习一下上篇中HappyNewYear那个例子。
当程序执行到int i = 10时,内存中就自动开辟了一个叫i的空间,来存10。这里面的等于号和咱们数学里的等于号不代表一个意思,等号在数学里代表的是相等,而这里代表的是把10赋给i,也就是把i里面存着10的意思。就好比你自己就是这个10,你去宾馆登记,宾馆自动分给你一个门牌号为i的整型房间。刚才也说了,这个变量i是临时开辟的,如果程序里没有这个i是不会开辟这个空间的。而且一个宾馆里的房间类型不一定都一样,可能有总统套间和普通单人间之分。变量也是如此,也有类型之分。再看一遍刚才那三组变量值:
很明显,这几组看起来类型都不同,有的是整数,小数,有的是字母。变量也有变量类型。第一组看上去是整数,我们称为整型;第二组看上去是小数,我们称为浮点类型;第三组是字母,我们称为字符类型。注意,我一直在说"看上去",为什么呢?因为这只是依照值进行判断,其实有可能一个整数10其实是个浮点型的。类型之间是可以转换的,我们一会儿说。
既然是变量,变量变量,一直在变,就好比客房的客人来来往往。现在我加一句话,i = 20;:
现在打印出来的就是20,因为存的值变了,客人换了。这里边10我们就管它叫变量的初值,int i =10就叫做变量的初始化。现在我在i = 20这句话前面加了两个斜杠:
它的意思是注释,也就是说程序走到这句代码时直接跳过,不执行。打印出来i还是等于10。那有人说我如果要是注释一整段代码呢?你当然可以每句话前都加两个斜杠,当然也可以用另外一种更简单的方法:在一开始写一个斜杠加一个星号,在最后把这两个符合位置倒过来,一个星号加一个斜杠。你看,整段都被注释掉了:
现在我把10和20都去掉,就保留int i;:
这么写可不可以?可以,没有初值,没有初始化,就好比客房建好了暂时没客人住呢。但是,这样做编译不过去,因为打印没被赋值的变量会报错:
这里我也给大家整理了一下:整型,字符型,浮点型还有布尔型统称为变量的基本数据类型:
刚才的例子中int代表整型,但它只是整型的其中一种。除此之外,整型还包括另外四种:分别是byte,short,long;浮点型包含单精度和双精度的小数,单精度英文是float,双精度是double。两者的区别就是双精度比单精度更加精准,小数点后面的位数更多;字符型是char,英文character的缩写;布尔型只有两个值 – true和false,代表对和错。现在咱们接着演示,把浮点型,字符型和布尔型都包含进来:
咱们运行一下,分别打印出整型变量i,双精度浮点类型变量d,字符类型变量'a',以及布尔类型变量b:
既然有基本类型就肯定有复杂类型,但这篇咱先不讲。这里有个题外话,咱们看整型有四种,那它们的区别是什么呢?先看byte。byte的意思是字节,不懂什么是字节的同学不要着急,不影响学习,你可以暂时理解成学数学时的一个计算单位。1个字节等于8个bit。bit就是位的意思,电脑里所有的运算其实都是位的运算。不懂没关系,不影响学习,先接着看。如果我写byte b; 会发生什么呢?肯定内存也会开辟出一个空间,那这个空间是多大呢?注意,存储空间大小,或是我之前讲的在内存里开辟区域什么的很多java课不讲的,即便是给开发培训的java课都不讲,我觉得有点耍流氓。知其然知其所以然,不光对咱们深入理解java写selenium自动化测试代码很有帮助,也对你学习别的编程语言有帮助。所以别嫌我啰嗦,我只想给大家讲清楚。看这个变量声明过程:当程序执行到byte b的时候,计算机首先检查变量类型,一看是byte整型,内存会开辟出来一个1个字节大小的区域来存储变量b的值。1个字节,也就是8位,位的英文是bit,1 byte = 8 bits。不懂的朋友索性就把它直接想成是一个单位,实际上位也是计算机最小的单位。这时如果给b赋了初值10,那10就存在这8位里。我们知道,计算机都是采用二进制进行计算的,数字只有0和1,并且逢二进一。所以10是以二进制的形式存在这个空间里:
这8位第1位是正负号,0表示正,1表示负,后7位表示值。那10是个正整数,第一位是0,10=2+8,2的1次方加2的3次方,也就是在第5位和第7位置1,所以它的二进制表示方法就为00001010。关于十进制和二进制的转换以后有机会我会单独讲,其实不难。
如果要是-10,转成二进制就会变成10001010。那byte类型的空间范围我们就可以计算了,负数最小是11111111,正数最大是01111111。再转回十进制就是-127到127:
这个范围看起来貌似没什么问题,但仔细想一想,这个范围肯定包含了00000000和10000000,第一位的0和1分别代表正负号,那换算成十进制是不是代表正0和负0?正0负0不都是0嘛?重了对不对?大家后来发现了这个事后说这不是逗b么?赶紧开会。有人说把其中一个删了吧,可开完会发现谁都不想轻易去掉一个数。这时候,一个国家的人勇敢地站出来对逗b说了不,他们就是印度人。他们说,你们不就是怕少个数么?没关系,拿走一个再加一个不就得了么?把-0去了,再加个-128不就得了?干嘛那么死心眼?真是的。大家觉得这个主意好,于是byte空间的取值范围就成了-128到127。就是这么来的。我们可以做个实验,把b赋值为127和-128,发现都能通过。可如果赋成128和-129,就发现报错了。
以此类推,short整型占两个字节,范围就是-32768到32767之间。int整型占4个字节,范围是-2147483648到2147483647,等等等等。如下图:
整型里面用得最多的其实就是int。不过还是像刚才说的,这段知识算是题外话,不需要我们都记住,也记不住,我也记不住,了解就够了,压力别太大。我介绍这段知识的目的就是让大家熟悉变量是如何存储的,把抽象的东西形象化,方便大家以后的学习。
再补充一句,不光是变量取值范围你记不住,其实任何一个编程语言都有很多的法则是咱们记不住的。记不住怎么办呢?我们有手啊,可以动手写啊,对不对?所以学习任何编程语言一定要勤动手,一动手你的印象就深刻了。没事声明几个变量,打印出来看看,慢慢自己总结,时间一长就顺手了。
咱们继续。刚才说了,这些变量类型都是"看上去的",因为某些情况下你不指定计算机是不懂的。来看这个例子:
发现了吧?他们打印出来的都是5,光从终端并看不出有任何不同。但实际上一个是整型,一个是字符型。有人说怎么5还可以当字符型?别急,看我再改一下:
我把c = 'a'改成c = 65。有人说你这不是字符型变量么?怎么能存个整数呢?这肯定有错误。咱们先别急着下结论,打印下试试:
我们惊奇地发现程序没报错,还打印出来个A。为什么呢?因为它打印出来的是这个十进制数65所对应的字符。点击ASCII网站,我觉得没毒。上面有好多十进制,二进制,十六进制还有一些字符,我们可以看到65对应的是A。这些对应字符的数叫作ASC码。咱们不用深究这个ASC码是什么,只要记住它就是用来表示字符的二进制十进制十六进制的一个数就行了。
那这么说这个65就真的是个数啰。确实,现在出现了一个很有意思的现象:等号左边是字符类型的,等号后边是一个数,回到刚才的问题,怎么能把一个数赋给一个字符呢?在java中,这个是可以被允许的,因为变量的类型是可以转换的,刚才演示的就是一个简单的把整型转成字符的例子。我们再来看看其它的。
比如整型和浮点型相互转换:int <-> float
重新编译执行一下,f = 10.0。因为f是单精度浮点型,要用小数表示:
那我要是想反过来呢?多加几行代码:
我们发现程序在a = f那行报错了:
错误信息的意思是从float转成int可能会丢精度。看清楚了吧,整型可以转成浮点型,但反过来就不行了。为什么会这样?因为我们看到的这种转换方式在java里被称为自动转换,而自动转换是隐形的。也就是你不用控制它,它自己转。自动转换需要遵循一个原则:只能是低精度转成高精度,而高精度不能自动转成低精度。精度高低的顺序我已经给大家总结出来了,从低到高依次是byte,short,int,long,float,double,你要是想自动转换必须遵循这个原则。所以int整型可以转成float浮点型,但反过来就不行。另外,布尔型就俩值true和false,asc码也变不成数,不用考虑。
咱们再举个例子:
编译执行一下,会发现又报错了,还是一样的错误原因 - 可能会丢精度。有些人要抓狂了,说没毛病啊,float是浮点型,3.4是个小数,怎么就丢精度呢?先消消气,java规定,单精度浮点型变量的值后面要加个f,双精度则不用。这也是float和double在声明时的区别。你如果在3.4后面不写f,就意味着3.4是一个double类型的值,而按照自动转换原则double类型不能直接转换成float类型,所以就报错了。看看吧,防不胜防。把3.4后面补个f,程序通过。
那如果我就想把高精度转成低精度可不可以?没问题,但必须使用强制转换。比如下面这个例子:
我这里把f前面加一个(int),我们再执行一下,发现这次没问题了,a = 3:
但我们同时也发现原来的3.4赋给a后损失了一些精度。对吧?没办法,因为你是把一个高精度强制转成低精度,必然要付出代价。
好了,这就是java变量和类型转换的基本概念。没事声明几个变量打印出来看看,多动手。下篇文章我们介绍java运算符。
本篇知识点及注意事项:
1. 变量必须声明才能使用,并且给变量赋值后才能打印;
2. 变量之间可以进行类型转换,低精度可以通过自动转换转成高精度,但高精度必须进行强制转换才能转成低精度。