第3章 Java语言基础
本章介绍Java语言的基础知识,本章也是继续学习后续章节的基础内容。虽然这些内容学起来有些乏味,但是仍希望读者耐心读完,并尝试着编写测试的例子程序。一旦掌握这些知识会对读者编写程序发挥积极的作用。
本章主要介绍的内容有:
❑Java中的基本数据类型
❑引用数据类型
❑隐式数据类型的转换
❑数据变量与数据常量
❑Java的运算符
3.1 Java命名规则
俗话说无规矩不成方圆,使用Java语言也需要遵循一定的命名规则,如类的命名、属性命名、方法命名和常量命名等;Java程序的编写也需要遵循一定的规则,如缩进、对齐等。本节将详细介绍Java的命名规则和代码编写规则。
❑类的命名:类由有意义的单词或单词的组合组成,要求每个单词的第一个字母要大写,一般由名词或名词词组组成,如SaveTableData(保存表格数据类)。
❑属性命名:类由属性和方法组成,属性代表类的静态特性,它可以由一个单词或多个单词组成,要求有一定意义,明确表示属性本身的特性。
❑方法命名:方法表示类的动态特性,方法的命名规则除第一个字母是小写外,其他和类的命名一样,如updateTableData()(更新表格数据)。Java对于取值或设置值的方法有特殊的要求,取值的方法是getXXX(),设置值的方法是setXXX()。
❑常量命名:常量的命名是由一个或多个单词组成,每个单词要求大写,单词需有一定意义,如MAX_INTEGER(最大整数)、MIN_INTEGER(最小整数)、RATE(汇率)等。
3.2 数据类型
一提到数据类型,初学者首先想到的是整数或小数,当然这是读者的直观理解,这种理解是正确的。但是在学习Java语言时只知道整数类型和小数类型还不够,因为一门计算机语言需要处理各种数据信息,如图像、视频、文字、语音等,所以还需要其他的数据类型从底层支持多样的数据处理。本章笔者将重点介绍Java支持的各种数据类型,虽然介绍起来有些烦琐,但只要读者把例子运行一下,再看看书,理解基本的概念就足够了。
Java是一种对数据类型要求严格的高级开发语言,严格性体现在,在编译程序时运行环境知道每个变量(变量的概念在3.4节介绍)或表达式的确定类型。一旦Java知道了数据类型,也就确定了其相应的运算方法。Java把数据类型分为两大类:一类是基本数据类型,另一类是引用数据类型。
3.2.1 基本数据类型
Java的基本数据类型分为4类,即整型、浮点型、字符型和布尔型。这4种数据类型都有自己的表达范围和相应的操作符,下面依次详细介绍。
1. 整型数据
整型数据又分为4种类型,即byte、short、int和long,这些又称为关键字,即在Java中不能随便使用这些关键字,它们只能用来表达特定的数据类型。它们的区别是取值范围不同,表达的数据范围精度不同,在存储时它们占用的字节数也不同,分别为1字节、2字节、4字节和8字节。在使用时一定要注意数据类型的取值范围,如果取值范围在类型取值范围之外,会得到不同的数值,而编译环境无法判断这样的错误,会给程序的调试带来很多麻烦。为了清楚地描述4种整型数据类型的区别和特点,见表3.1。
表3.1 Java的整型数据类型
【范例3-1】下面以一个具体的例子代码3.1,说明使用整型数据类型的注意事项。
代码3.1 整型数据类型测试
1 public class IntegerTest{; //定义一个类,类名为IntegerTest 2 public static void main(String[] args){ 3 int i=1000000; //定义一个整型变量i并赋初值 4 System.out.println(i*i); //输出i的平方 5 long l=i; //把 int 型i赋给long型变量l, 6 System.out.println(l*l); //输出变量l的平方 7 System.out.println(1000/(l - i)); //输出1000除以o,因为l-i=0; 8 } 9 }
【运行效果】程序执行结果如图3.1所示。
图3.1 类IntegerTest的执行结果
【代码说明】首先输出了-727379968,这个结果是System.out.println(i*i)输出的,但因为整型类型int的取值范围是-2147483648~2147483647无法描述i*i的值,所以输出结果被截断了。而第二个输出结果是1000000000000,因为它是整型类型long的l*l的结果,而long的取值范围是-9223372036854775808~9223372036854775807,显然输出结果在取值范围内。接下来有异常(Exception)输出,指出分母除零错误(/by zero),说明Java一旦知道数据类型,也就知道了其相应的计算规则,如这里对于整型类型分母除零是不允许的。
2. 浮点型数据
浮点型数据又分为两种:单精度浮点型(float)和双精度浮点型(double)。float类型用32位存储,而double类型用64位存储,二进制位数越多说明其表达数字的精度越高。注意二者都是表示非整数的。计算机里表示小数通过浮点数来实现。
浮点数的表示方法:以float类型为例,1978.0418f或1.9780418E3f。其中E3表示103,而f表示数据类型为float类型,当然f也可以为大写F,不影响数据的表达。double类型的表示方法与此类似。
说明
如果数字后没有任何字母,如1978.0418,则默认是double类型。
3. 字符型数据
在Java规范中把字符型数据归为整型数据,但是由于理解起来不够直观,这里将其单独分为一类,读者只要认为它是数据类型的一种就可以了。
字符型数据实际上是未指定符号的16位Unicode编码,取值范围是从\u0000 到 \uffff(包括该值),用十进制表示就是从0~65535。
字符变量必须赋予初值,赋初值有两种方式:一是通过直观的字符赋值,如char i=‘b’;二是用整数(0~65535范围内)赋初值,如char j=96。
说明
其实Java把字符和整数(取值范围内)一一对应起来,无论用单引号的单个字符,还是用整数,其实表达的是一个内容。如果用整数表达,需要事先知道该整数表达的是哪个字符。
4. 布尔型数据
布尔型数据有两种,即true和false。布尔类型通常用于逻辑判断,尤其多用在程序的流程控制中。布尔类型的默认值是false,即如果定义了一个布尔变量但没有赋初值,默认的该布尔变量值是false。
【范例3-2】下面以具体的例子验证布尔型数据,如代码3.2所示。
代码3.2 测试布尔型数据
1 public class BooleanTest{ 2 static boolean isA; //定义一个布尔值,默认为false 3 static boolean isB=true; //定义一个布尔值,赋予初始值为true 4 public static void main(String[] args){ 5 System.out.println("isA : "+isA); //输出布尔值isA的结果 6 System.out.println("isB : "+isB); //输出布尔值isB的结果 7 if(isA){ 8 System.out.println("isA is true"); //如果isA为true则输出isA is true 9 } 10 if(isB){ 11 System.out.println("isB is true"); //如果isB为true则输出isB is true 12 } 13 } 14 }
说明
第2~3行的static是修饰符,读者暂时不必深究。
【运行效果】执行结果如图3.2所示。
【代码说明】首先程序输出的是第5~6行代码的执行结果,因为程序中布尔变量isA没有赋予初值,默认为false,isB为true,输出结果正确。第7~12行测试布尔变量在控制语句中的使用,如果if后“()”内的变量或表达式为true,则执行“{ }”内的指令。如果if后“()”内的变量或表达式为false,则不执行“{ }”内的指令。代码3.2中因为isB为true,所以程序执行第11行的指令,输出“isB is true”。
图3.2 类BooleanTest的执行结果
3.2.2 引用数据类型
在学习引用数据类型前,最好先学习第9章的面向对象技术导论,读者也可以略过本节,在学完第9章后再回头学习本节内容。这里首先假设读者已经具备了面向对象的基本知识,尤其对类和接口有了良好的认识。
Java把引用类型分为3类,即类类型(class type)、接口类型(interface type)和数组类型(array type)。
1. 类类型
【范例3-3】类类型表示一个特定的类引用,该引用仿佛是该具体类的操纵杆,通过赋予初值的引用操纵该类的实例。代码3.3为类类型的例子。
代码3.3 类类型例子
1 public class People{ 2 public String name; 3 int age; 4 private void walk(){ 5 } 6 public private void talk(){ 7 } 8 }
代码3.3定义了一个People类,该类具有属性姓名(name)、年龄(age)和动态行为如走路(walk)、说话(talk)。下面定义一个类引用类型,并为该类的引用类型赋予初值。
1 People p1; 2 People p2; 3 p1=new People(); 4 p2=new People();
【代码说明】上述第1~2行声明了类People的引用类型p1和p2,在第3~4行分别对该类型赋予了初值,即一个People类实例,此时,p1和p2就可以操作类People的属性或方法(类的动态行为)。
说明
本例因为只是一个架构,目前还不可以运行出结果。读者只需简单了解这个类类型。
2. 接口类型
首先声明一个接口,接口可以指向实现该接口的类。例如:
Interface Walk {void walk( ) };
这里定义了一个接口Walk,“walk=new MyWalk();”就实现了接口引用类型的初始化。当然,如果有另一个类HisWalk也实现了该接口,则“walk = new HisWalk();”也是正确的,引用类型可以指向任何实现它的类对象。
3. 数组类型
数组类型指向特定的数组,如double[] doublearray,声明了数组引用类型,该引用可以用任何double类型的数组初始化或指向任何的double类型的数组,如“double array = new double[20];”。对于数组我们将在第5章介绍。
本节重点讲了Java的数据类型,包括基本类型和引用类型。对于基本类型,读者主要掌握其取值范围和定义方式;对于引用类型,读者起初理解起来比较抽象,但学习了面向对象技术后,会有更直观深入的了解和把握。
3.3 数据类型转换
表3.2 Java的隐式数据类型转换关系
Java在数据计算时支持混合数据类型的计算,如整型数据与浮点型数据可以进行加、减、乘、除运算,所以Java支持数据类型的转换。下面介绍两种数据类型的转换方式。
3.3.1 隐式数据类型转换
对于整型数据,Java支持隐式的数据类型转换,但该转换是有规则的,即取值范围小的可以隐式转化为取值范围大的数据类型,如int型整型数据可以自动转换为long型整型数据。表3.2就是Java的隐式数据类型转换关系表。
说明
在混合运算时,表3.2中箭头左边的类型数据可以隐式转化为箭头右边的类型数据,当转化成一致的数据类型后,再继续运算。
3.3.2 显式数据类型转换
显式数据类型转换也常叫做强制数据类型转换,即取值范围大的必须强制转化为取值范围小的数据类型,如long型整型数据可以强制转型为int型整型数据,但这种转化使用时一定要谨慎。因为如果取值范围大的数据无法用取值范围小的数据类型表达,则会失去数据表达的准确性。
【范例3-4】代码3.4为显式数据类型转换。
代码3.4 显式数据类型转换
1 public class DataTypeChange{ 2 public static void main(String[] args){ 3 long i=100; 4 int l=i; //把long型数据转化成int型数据,此时需要强制类型转化,否则会提示如图3.3所示的错误 5 } 6 }
【运行效果】图3.3显示当试图把long型数据转化成int型数据时,编译时出现的错误提示。
【代码说明】如果把第4行改为如下所示,实现强制类型转换,则可通过编译。
4 int I=(int)i; //把long型数据强制转化成int型数据
图3.3 类DataTypeChange的执行结果
3.4 变量与常量
变量是指具体内容没有确定的量,其内容需要在一定的条件下指定。而常量是不会变化的量,无论什么条件下其内容都是固定不变的。本节将详细介绍变量和常量。
3.4.1 变量
Java中的变量是和数据类型相关联的一段存储空间,通过变量来操纵存储空间中的数据,但此存储空间里到底要放该类型的什么数据是不确定的,这就是变量的含义。如定义一个double型变量“double mynumber;”,此时编译器为该变量mynumber提供一个64位的存储空间,而该存储空间中到底放置double数据类型范围内的哪个数值是不确定的。
变量由两部分组成,即变量类型和变量名,如int age=30、double rate、char a等。
❑变量类型:变量的类型可以是基本数据类型,如int型、float型等;也可以是引用类型,如类类型、接口类型等。
❑变量名:变量名也称为标识符,Java中把用户自己定义的属性、方法、类名等都称为标识符。定义标识符有严格的规定:由字母、数字、下划线和$自由组合,不能以数字开头,但标识符的长度可任意。
虽然变量名的定义对用户来讲已经提供了很大的灵活性,但是必须注意Java保留了一些关键字,这些关键字是不允许定义为变量名的,如int、float、static、char等。下面是Java的关键字:
abstract finally public assert float return boolean for short break static byte if strictfp case implements super catch import switch char instanceof synchronized class int this interface throw continue long throws default native transient do new try double package void else private volatile extends protected while final。
注意
在定义变量时,如果不初始化,编译器会自动为其赋予默认值(3.2节有说明),但最好在定义时就初始化,至少在使用时要初始化为有意义的初始值,否则使用默认的变量值没有实际意义。
3.4.2 常量
常量是相对变量而言的,常量是程序执行过程中不发生变化的量,一旦初始化该常量,在内存中的数值就不会发生变化。例如,在Java中常量的定义如下:
final double RATE=0.234;
使用修饰符final 说明是常量,double说明数据类型,RATE是常量名字。按照Java的规范,常量都用大写字母表示。
3.5 Java运算符
运算符是进行数值计算或字符串计算(或称为操作)的标识符,如自然数计算的“+、-、*、/”等都是Java运算符中的一种,下面将依次介绍Java中常用的运算符。
3.5.1 数学运算符
数学运算符号实现整型数据类型和浮点型数据类型的计算。表3.3详细描述了数学运算符的相关内容。
表3.3 数学运算符
说明
表3.3中的++、--运算符的作用说明中的“=”是赋值运算符,把右边的值赋予左边的变量,左边原来的变量值被覆盖。
下列代码演示表3.3中运算符的用法。
1 //定义一个int型变量x并赋予初值1000 2 int x=1000; 3 //定义一个int型变量y并赋予初值2000 4 int y=2000; 5 //把x和y相加,得到的和赋予x,此时x的值为3000,y的值不变,仍为2000 6 x=x+y; 7 //把x和y相减,得到的差赋予y,此时x的值不变,y的值为-1000 8 y=x-y; 9 //把y乘以2,得到的积赋予x,此时x的值是4000,y的值仍为2000 10 x=y*2 11 //把x除以4,得到的商赋予y,此时x的值是1000,y的值为250 12 y=x/4 13 //把y的值自加1再赋予y,此时y的值为2001 14 y=y++ 15 //把x的值自减1再赋予x,此时x的值为999 16 x=x-- 17 //把x的值取反后再赋予x 18 x=- x 19 //求y除以2000所得的余数,如果y为2001,所得余数为1,此时y的值为1 20 y=y%2000
说明
以上运算没有前后关系,假设x和y的初始值一直是1000和2000。
在Java的数学运算符中还有一种运算符号即数学赋值运算符,如x+=100相当于x=x+100。对于其他几类运算符也有类似的数学赋值运算符,可见数学赋值运算符仅仅是一种计算表达式的简写,详见表3.4。
表3.4 数学赋值运算符
说明
数学运算符的示例程序不再过多介绍,读者只要理解了其含义,再参照表3.3的例子完全可以掌握。
3.5.2 关系运算符
关系运算符直观来说是进行关系运算的标识符,这里的关系是指两个数值或表达式的比较结果,如对于两整数x=100,y=200,表达式x<y的结果是真(true),而表达式x>y的结果是假(false)。关系运算的计算结果只有两种:真(true)与假(false),显然这是布尔数据类型。Java中的关系运算符有6种,其类别及作用详见表3.5。
表3.5 关系运算符
注意
1)区别关系运算符“==”和赋值运算符“=”,前者是比较符号左右两边的数据是否相等,而后者是把符号右边的数据赋予左边的变量。
2)“==”、“!=”可以用于对象的比较,而对象的比较通常不是很简单地通过对象名字比较或对象类型比较,而是有自己的equal()函数,有些情况下两个对象是否相等的函数需要程序员自己编写。这里读者需要了解该知识点,在深入学习了面向对象技术后会有切身的理解。
【范例3-5】代码3.5是关系运算符的例子。
代码3.5 关系运算符
1 public class ReOperator{ 2 public static void main(String[] args){ 3 int x=100; 4 int y=200; 5 boolean bl; 6 bl=x < y; //把表达式x < y 的计算结果赋予布尔变量bl,显然bl的值为true 7 System.out.println("x < y is :"+ bl); 8 } 9 }
【运行效果】执行该类的输出结果是:
x < y is :true
【代码说明】第3~4行定义了两个int变量。第5行定义布尔型变量,主要用来在第6行获取比较结果。第7行输出布尔型的比较结果。
3.5.3 逻辑运算符
逻辑运算符的计算对象是布尔变量。一提到逻辑读者或许会觉得有些抽象,其实只要理解并记住运算符的逻辑规则,就能很好掌握该运算符号的使用。在表3.6中,假设x和y都是布尔变量,读者记住逻辑运算符的写法和相应的功能就可以了。
表3.6 逻辑运算符
【范例3-6】代码3.6是逻辑运算符的例子。
代码3.6 逻辑运算符
1 public class LogicalOperator{ 2 public static void main(String[] args){ 3 boolean x=true; 4 boolean y=false; 5 System.out.println(x & y); //输出x & y的计算结果,显然结果为false 6 System.out.println(x | y); //输出x | y的计算结果,显然结果为true 7 System.out.println(!x); //输出!x的计算结果,显然结果为false 8 System.out.println(x && y); //输出x && y的计算结果,显然结果为false 9 System.out.println(x || y); //输出x||y的计算结果,显然结果为true 10 System.out.println(x ^ y); //输出x ^ y的计算结果,显然结果为true 11 System.out.println(x^x); //输出x ^ x的计算结果,显然结果为false 12 } 13 }
【运行效果】
false true false false true true false
【代码说明】读者可以根据运行效果依次对照上述逻辑运算的结果。
说明
读者或许发现“&&”与“&”、“|”与“||”计算结果相同,但是二者之间还是有些许区别:对于“&&”和“||”只要计算完左边的值便可以确定整个表达式的值,右边不必再进行计算;但是对于“&”和“|”必须把左右两边都计算完后才可以计算结果值。
3.5.4 位运算符
首先从直观的概念可以猜测位运算操作的对象是位。事实上Java也是这样考虑的。但是位运算是两个数据类型相同的数据,转化成二进制表达后再进行相应位的运算。Java定义了3种位运算符:与(&)、异或(^)、或(|)。例如:
int x=10; int y=20; x & y;//即00001010&00010100 //计算结果是0
其他位运算的计算方法类似。
3.5.5 位移运算符
首先解释位移的概念,位移顾名思义就是位置移动,这里移动的对象是二进制位。Java提供了3种位移运算符:左移运算符(<<)、不带符号右移运算符(>>>)、带符号右移运算符(>>)。
运算规则:首先把运算对象转化成二进制位,如把20的二进制位表达为00010100。
1)左移运算符计算规则:把数据对象的二进制位依次左移n位,右边空出的位置补0,如20<<2 ,计算结果为01010000,十进制值为80。
2)不带符号右移运算符规则:把数据对象的二进制位依次向右移动n位,左边空出的位置补0,如20>>>2,计算结果为00000101,十进制值为5。
3)带符号的右移运算规则:把数据对象的二进制位依次右移n位,移出的数补到左边,如20>>2,计算结果为00000101,十进制值为5。这里恰巧和不带符号右移运算结果相同。再举例如15>>2,15二进制位表达为00001111。15>>2的计算结果为11000011,十进制值为195。而15>>>2的计算结果为00000011,十进制值为3。显然带符号右移与不带符右移有明显区别。
注意
如果读者仔细分析可以看出左移n位运算相当于把十进制数乘以2的n次方,不带符号右移n位运算相当于把十进制数除以2的n次方。如1)、2)的计算规则中的例子,前者20<<2=20*22=80,后者20>>>2=20/22=5。
表3.7描述了位移运算符。
表3.7 位移运算符
3.5.6 “? :”运算符
在众多高级语言中,“?∶”运算符都得到了很好的运用,Java也继续采用该运算符号。其使用格式为:
操作数1 ? 操作数2:操作数3
表3.8 运算符优先顺序表
格式解释:操作数1也可以是表达式,但操作数1的结果必须是布尔值。如果操作数1的值为true,则选择操作数2,否则选择操作数3。例如,对两个不相等的整型变量x和y:x>y?x:y。
如果x > y即关系表达式x>y为真,则取x,否则取y,即取x和y中的最大数。
3.5.7 运算符的优先顺序
运算符是有优先顺序的,当一个表达式中有多个运算符时,它们之间遵守一定的计算顺序,其优先顺序见表3.8。
说明
运算符优先顺序看起来很复杂,无法记忆,其实读者只要记住几个简单的运算符就可以了。在需要考虑优先顺序时,只要在必要的位置加“()”运算符就可以,没必要把过多的心思用在记忆和理解复杂的优先顺序上。
3.6 常见面试题分析
3.6.1 简述变量及其作用范围
Java变量可以分为静态变量、成员变量和局部变量3种。静态变量指的是在类里用static修饰的变量,它的生存周期是由类来决定的。成员变量则是在类里没有用static修饰的变量,它的生存周期由对象来决定。局部变量则是定义在方法里的变量、参数或在代码块里定义的变量,它们的作用范围用大括号“{}”来界定。
3.6.2 Java的变量分哪两种大的数据类型
Java的变量分为基本数据类型和引用数据类型。它们最大的区别在于:引用数据类型存放的是数据所在的地址,而基本数据类型则是直接存放数据的值。
3.6.3 Java中equal()和“==”的区别是什么
equal和“==”两者均表示相等的意思,但是它们相等的含义却有所区别。
“==”用于比较基本数据类型的时候,通过比较它们实际的值来判定是否相同;而用于比较引用类型的时候,则是比较两个引用的地址是否相等,也就是是否指向同一个对象。
equal()方法是java.lang.Object的方法,也就是所有的Java类都会有的方法。它可以被程序员覆盖重写,通过自定义的方式来判定两个对象是否相等。对于字符串java.lang.String类来说,它的equal()方法用来比较字符串的字符序列是否完全相等。
3.6.4 Java中的三元运算符是什么
Java中唯一一个三元运算符为:“表达式1?表达式2:表达式3”。在问号“?”之前是一个布尔表达式,它只能返回true或false。如果表达式1返回的是true则执行表达式2,否则执行表达式3,并产生相应的返回值。它的主要作用是为了使代码更简洁。
3.7 本章习题
1. Java的数据类型分几种?举例说明。
2.如何实现显式数据类型转换?隐式数据类型转换适合哪些数据类型?
3.如何理解变量、常量二者的区别?
4.写出下列运算表达式的计算结果,其中x=20,y=12,z=false。
(1) 100==x + y; (2) x=y +10; (3) z||y+x; (4) x*=19; (5) (y/2>=x ? 3:6); (6) x%y; (7) y >=6&&y<=x; (8)!x*3<=x*4;
5.设int x=10,int y=5,z=7,分析下列表达式的计算结果。
(1) z=x++ * y++; (2) z=++x - y--; (3) z=x++*2/y--; (4) z=x++ - ++y;
注意:
不要使用过于复杂的运算符,如x++*=x--,最好分解成简单的计算步骤,如果需要可以通过增加“()”的方式确定计算的优先顺序。