第3章 Java语言中的数据类型与运算符

本章主要介绍编程语言中最基础的部分:数据类型和运算符。这是所有编程语言都必须掌握的基础知识,也是整个程序代码不可缺少的重要部分。本章将通过大量的程序代码,来讲述如何操作这些数据类型和运算符。熟练地掌握此章对于Java开发有着非常重要的作用,并且还对以后学习其他开发语言有着重要的帮助。

本章重点:

□ Java语言中的数制。

□ 数据类型。

□ 变量和常量。

□ 各种常见运算符。

3.1 数制

在介绍数据之前,先了解数制的概念。数制可以说是纯粹数学上的内容,在计算机语言开发中使用得比较频繁,下面将详细讲述数制的有关知识。

3.1.1 基本概念

在使用计算机时,会遇到数值、文字、图像、声音等信息,计算机如何识别这些数据信息呢? 首先,这取决于计算机底层硬件是如何识别数据的。计算机底层硬件只能识别“0”和“1”,这种只有“0”和“1”两个数字符号的组合被称为二进制。例如计算机要处理数字“128”,那么计算机会将其转化成二进制“10000000”。一个这么简单的数字,要用这么长的数字符号来代替,在现实生活中稍显麻烦,所以后来又引进了十六进制和八进制。实际开发中使用最多的是十进制,后面会介绍各个数制的特征和使用。

3.1.2 Java语言中的数制表现形式

数制一般包括二进制、八进制、十六进制和十进制。

1.二进制

二进制的特征:

□ 由两个数字组成:“0”和“1”。

□ 运算时逢二进一。

例如:1100110011和10000001。

2.八进制

八进制的特征:

□ 由8个数字组成:“0”、“1”、“2”、“3”、“4”、“5”、“6”、“7”。

□ 运算时逢八进一。

例如:014、0726。

注意

八进制数据以0为前缀。它经常会与二进制产生混淆,所以在Java程序设计中,建议尽量不要使用八进制。

3.十六进制

十六进制的特征:

□ 由16个数字组成:“0”、“1”、“2”、“3”、“4”、“5”、“6”、“7”、“8”、“9”、“A”、“B”、“C”、“D”、“E”、“F”。

□ 运算时逢十六进一。

例如:0xB和0x12e。

注意

十六进制用A、B、C、D、E、F这6个字母分别表示10~15。字母不区分大小写。十六进制数据以0x为前缀。

4.十进制

十进制的特征:

□ 由10个数字组成:“0”、“1”、“2”、“3”、“4” 、“5”、“6”、“7”、“8”、“9”。

□ 运算时逢十进一。

例如:89、92、168。

3.2 数据类型

Java语言是一个强调数据类型的语言,在声明任何变量时,必须将该变量定义为一种数据类型。Java中的数据类型包括基本数据类型和对象类型(也称为引用数据类型)。对象类型不属于本章所讲述的内容,本章主要介绍数据的基本类型。Java程序中,总共有8大基本类型:其中包括4种整型、1种字符型、2种浮点型、1种布尔型。除了这几种基本类型外,其他都属于对象类型的数据。

3.2.1 整型

什么是整型呢?从字面上就可以知道,整型就是整数类型,也就是没有小数点的数字,可以是正数也可以是负数。在Java中,整型主要有4种:字节型(byte)、整数型(int)、短整型(short)、长整型(long)。

1.字节型

【实例3-1】byte用一个字节来表示整数值,它的范围介于-128~127之间。通常这种类型的整型数据拥有上节中提到的所有进制。但无论采用哪种进制,在输出控制台上,系统都会将其自动转化为十进制,从下列代码段可以得到证实。

01  public class Byte {
02       public static void main(String[] args) {
03            byte x = 22;                               //x是十进制数
04            byte y = 022;                              //y是八进制数
05            byte z = 0x22;                             //z是十六进制数
06            //输出相应十进制的值
07            System.out.println("转化成十进制,x=" + x);
08            System.out.println("转化成十进制,y=" + y);
09            System.out.println("转化成十进制,z=" + z);
10       }
11  }

【代码说明】第3~5行定义了3个byte型变量,分别代表不同的数制,第4行的变量值前缀是0,第5行的变量值前缀是0x。第7~9行并没有使用类型转换的函数,而是直接输出这3个变量。

【运行效果】在DOS窗口中,通过Javac编译该源代码,然后通过Java执行编译后的class文件。最终结果如下所示。

转化成十进制,x=22
转化成十进制,y=18
转化成十进制,z=34

2.短整型

【实例3-2】short用两个字节表示整数值,其整数值介于-32768~32767之间。它有八进制、十进制和十六进制3种表示方法,其表示方法与字节型是一样的,从下面的程序段可以证实。

01  public class Short
02  {
03      public static void main(String[] args)
04      {
05          short  x=22;                                  //十进制
06          short  y=022;                                 //八进制
07          short  z=0x22;                                //十六进制
08          System.out.println("转化成十进制,x="+ x);
09          System.out.println("转化成十进制,y="+ y);
10          System.out.println("转化成十进制,z="+ z);
11      }
12  }

【代码说明】第5~7行定义了3个short型变量,依然是用前缀0和0x代表八进制和十六进制。第8~10行没有使用数据类型转换的函数,直接输出结果。

【运行效果】

转化成十进制,x=22
转化成十进制,y=18
转化成十进制,z=34

可以看出,两个程序段运行结果都是一样的。其实,在实际编程过程中,开发者最常用的整型是int型。

3.整数型

【实例3-3】整数型又称作int型,用4个字节来表示整数值,其整数值介于-2147483648~2147483647之间。整数型拥有以上所说的各种进制,其表示方法与字节型也相同,从下面的程序段同样可以证实。

01  public class Int
02  {
03      public static void main(String[] args)
04      {
05          int  x=22;                                //十进制
06          int  y=022;                               //八进制
07          int  z=0x22;                              //十六进制
08          System.out.println("转化成十进制,x="+ x);
09          System.out.println("转化成十进制,y="+ y);
10          System.out.println("转化成十进制,z="+ z);
11      }
12  }

【代码说明】第5~7行同样定义了3种进制的变量,通过前缀0和0x来区别进制。第8~10行依然是直接输出变量的值,而没有显式地进行类型转换。

【运行效果】

转化成十进制,x=22
转化成十进制,y=18
转化成十进制,z=34

4.长整型

【实例3-4】长整型long用8个字节表示整数型,其数值介于-9223372036854775808~9223372036854775807之间。它的所有特性基本与前几种整型一样,唯一不同的是,长整型的数据后面有一个“L”字母,这个也是从表现形式上区别于其他整型的最大特征。可从下面的程序代码段中了解长整型与其他整型的区别。

01  public class Long
02  {
03      public static void main(String[] args)
04      {
05          long  x=22L;                      //十进制
06          long  y=022L;                     //八进制
07          long  z=0x22L;                    //十六进制
08          System.out.println("转化成十进制,x="+ x);
09          System.out.println("转化成十进制,y="+ y);
10          System.out.println("转化成十进制,z="+ z);
11      }
12  }

【代码说明】第5~7行定义了3个长整型变量,要注意的是结尾的标识“L”。第8~10行直接输出变量的值,会自动进行类型转换,输出的结果都是用十进制表示的。

【运行效果】

转化成十进制,x=22
转化成十进制,y=18
转化成十进制,z=34

从以上程序代码段中可以看出,4种不同的整型类型的数据,在程序段中所表现出来的运行结果几乎是一样的。不同的是每个类型数据的取值范围不一样,随着字节数增多,取值范围增大。虽然长整型的数据可以表示很大的数据,但如果超过了长整型的数据取值范围,该如何处理这些数据呢?在Java中,有一种大数值类型的数据,由于它属于对象类型数据,所以此处不做介绍。

3.2.2 字符型

字符型数据是平时程序设计中使用比较频繁的类型,其占两个字节。特别注意的是,它必须以单引号表示,例如'A'表示一个字符,这个字符就是A。"A"表示一个字符串,虽然只有一个字符,但因为使用双引号,所以它仍然表示字符串,而不是字符。

总之,字符数据类型只能表示单个字符,任何超过一个字符的内容,都不能被声明为字符型。字符的声明是用单引号,通过输出控制台,看到的是单引号内的字符数据。

【实例3-5】通过下面的程序代码,来看看字符型数据是如何输出的。

01  //声明了x,y,z,a 四个字符型数据变量
02  public class Char
03  {
04      public static void main(String[] args)
05      {
06          char x='';                       //声明一个字符型变量
07          char y='';                       //声明一个字符型变量
08          char z='';                       //声明一个字符型变量
09          char a='';                       //声明一个字符型变量
10          System.out.println("这些字符组合起来就是:"+x+y+z+a);
11      }
12  }

【代码说明】第6~9行定义了4个字符型变量,第10行输出变量的内容,将字符型数据连接在一起使用的运算符也是“+”。

【运行效果】

这些字符组合起来就是:美国人民

字符型数据和整型数据都是无小数点的数据,下面将要讲述的类型则是带小数点的数据,用专业术语来讲就是浮点型。

3.2.3 浮点型

浮点型数据表示有小数部分的数字,总共有两种类型:单精度浮点型(float)和双精度浮点型(double)。

1.单精度浮点型

单精度浮点型占4个字节,有效数字最长为7位,有效数字长度包括了整数部分和小数部分。一个单精度浮点型的数据定义如下所示。

float x=223.56F

注意

在每个单精度浮点型数据后面,都有一个标志性符号“F”或者“f”,有这个标志就代表是单精度浮点型数据。

【实例3-6】下面演示单精度浮点型数据在程序代码段中的使用情况。

01  //声明了x,y,z三个浮点型变量
02  public class Float
03  {
04      public static void main(String[] args)
05      {
06          float x=22.2f;                     //声明一个单精度类型变量
07          float y=42.2f;                     //声明一个单精度类型变量
08          float z=x*y;                       //实现相乘
09          System.out.println("x*y="+z);
10      }
11  }

【代码说明】第6~7行定义了两个浮点型数据,它们都以“f”标识结尾。第8行定义了变量z,其值是前面定义的变量x和y的乘积。第9行输出计算结果z。

【运行效果】

x*y=936.84

提示

如果在一个浮点数后面加上“F”或者“f”时,表示的就是单精度浮点型数据,否则,系统会认为是双精度浮点型数据。

2.双精度浮点型

双精度浮点型数据占据8个字节,有效数字最长为15位,后面带有标志性符号 “D”或“d”。系统默认不带标志性符号的浮点型数据是双精度浮点型数据。双精度浮点型数据的定义如下所示。

double x=33.5D

【实例3-7】下面是一个简单的程序代码段。

01  public class Double
02  {
03      public static void main(String[] args)
04      {
05          float x=23f;                             //声明一个单精度类型变量
06          double y=44;                             //声明一个双精度类型变量
07          System.out.println("x="+x);
08          System.out.println("y="+y);
09      }
10  }

【代码说明】第5行定义了一个单精度浮点型变量x,但没有带小数位。第6行定义了一个双精度浮点型变量y,也没有带小数位,也没有加标识“D”。第7~8行分别在控制台输出两个变量,注意输出的结果。

【运行效果】

x=23.0
y=44.0

从这段程序代码中可以看出,即使浮点型数据是一个只有整数位没有小数位的数据,在输出控制台上,其仍然是带小数点的浮点数据,系统会自动加上小数点,并且小数位全部置0。

3.2.4 布尔型

布尔型数据其实很简单,例如,如果有人问:去不去麦当劳?可以说“不去”。如果有人问:去不去看电影?可以说“去”。这里就隐藏着布尔型数据,布尔型数据就是“是”与“否”。在程序中使用“真”和“假”来代替“是”与“否”,即“true”和“false”。布尔类型的默认值是false,即如果定义了一个布尔变量但没有赋初值,默认该布尔变量值是false。

【实例3-8】仔细观察下面的程序代码。

01  public class Boolean {
02       public static void main(String[] args) {
03            int a = 30;                                   //声明一个整型变量a
04            int b = 59;                                   //声明一个整型变量b
05            boolean x, y, z;                              //声明三个布尔型变量x、y、z
06            x = (a > b);                                  //为变量x赋值
07            y = (a < b);                                  //为变量y赋值
08            z = ((a + b) == 50);                          //为变量z赋值
09            System.out.println("x=" + x);
10            System.out.println("y=" + y);
11            System.out.println("z=" + z);
12       }
13  }

【代码说明】

□ 当执行第6行代码“a>b”时,不等式不成立,所以x的结果是假。

□ 当执行第7行代码“a<b”时,不等式成立,所以y的结果是真。

□ 当执行第8行代码“a+b==50”时,结果不成立,所以z的结果是假。

说明

布尔型数据在只有两种选择,不可能出现第三种选择的情况下使用。

【运行效果】

x=false
y=true
z=false

在程序设计的过程中,如果一个参数只有正和反两方面,就可以将其设置为布尔型类型。

3.3 变量

在上一节详细介绍了Java语言中的数据类型及其功能。在具体实例代码中我们定义了很多变量,那么变量究竟是什么呢?本节将介绍变量的基本概念,以及如何操作变量。

3.3.1 变量的声明

变量就是在程序的运行中可以变化的量,变量是程序设计中一个非常重要和关键的概念。在Java程序设计中,每个声明过的变量都必须分配一个类型。声明一个变量时,应该先声明变量的类型,随后再声明变量的名字。下面演示了变量的声明方式。

01  //salary,age,op都是变量名字
02  //double,int,boolean则是变量的类型
03  double salary;
04  int age;
05  boolean op;

每一行的第一项是变量类型,第二项是变量名。行尾的分号是必需的,这是Java语句的结束符号。如果没有这个分号,程序不会认为这是一句完整的Java语句。

同一类型的不同变量,可以声明在一行,也可以声明在不同行,如果在同一行中声明,不同的变量之间用逗号分隔,如下面的例子。

int studentNumber,people;

3.3.2 变量的含义

在程序设计中,经常会听到变量这个名词,到底什么是变量呢?它又有什么意义呢?

在程序运行过程中,空间内的值是变化的,这个内存空间就称为变量。为了操作方便,给这个空间取名,称为变量名,内存空间内的值就是变量值。所以,申请了内存空间,变量不一定有值,要想变量有值,就必须要放入值。

例如:代码“int x”,定义了变量但没有赋值,即申请了内存空间,但没有放入值。如果“int x=5”,说明不但申请了内存空间而且还放入了值,值为5。

3.3.3 变量的分类

变量的分类方式多种多样,不可能单纯地将变量划分为几类,下面将以不同的分类方式来讨论变量的分类问题。

1.根据作用范围划分

根据作用范围来分,一般将变量分为全局变量和局部变量。从字面上理解很简单,全局变量就是在程序范围之内都有效的变量,而局部变量就是在程序中的部分区域有效的变量。

【实例3-9】从专业的角度来解释,全局变量就是在类的整个范围之内都有效的变量。而局部变量就是在类中某个方法函数或某个子类内有效的变量,下面将从实际程序代码中慢慢地体会。

01  //a,b都是全局变量
02  //c是局部变量
03  public class var
04  {
05      int a=10;                                               //定义全局变量a
06      int b=21;                                               //定义全局变量b
07      public static void main(String[] args)
08      {
09          var v=new var();
10          System.out.println("这个是全局变量a="+ v.a);
11          v.print();
12      }
13      void print()
14      {
15          int c=20;                                          //定义局部变量c
16          System.out.println("这个是局部变量c="+ c);
17      }
18  }

【代码说明】第5~6行定义了两个变量,它们在main()方法外。第15行在print()方法内定义了变量c,第16行在当前方法内输出此变量。

【运行效果】

这个是全局变量a=10
这个是局部变量c=20

【实例3-10】如果在main()方法中同样输出c的值,会出现什么样的结果呢?下面从实际代码段中仔细体会。

01  public class Math1
02  {
03      public static void main(String[] args)
04      {
05          Math1 v=new Math1();
06          System.out.println("这个是局部变量c="+ v.c);               //输出全局变量c
07      }
08      void print()
09      {
10          int c=20;                                                //定义局部变量c
11      }
12  }

【运行效果】以上代码在编译时,会出现错误,即找不到变量c。这说明变量c只在方法print()中起作用,在方法外就无法再调用。

【代码说明】从上述代码中可以看出,如果一个变量在类中定义,那么这个变量就是全局变量,例如下面的代码段。

01  public class var
02  {
03      int a=10;                                                          //定义全局变量a
04      int b=21;                                                          //定义全局变量b
05  }

这里的变量a、b都是全局变量,而在类的方法、函数中定义的变量就是局部变量,例如下面的代码段。

01  public class var
02  {
03      void print()
04      {
05          int c=20;                                                      //定义局部变量c
06      }
07  }

这里的变量c就是局部变量。因为它不是在类中直接定义的,而是在类的方法中定义的。

2.根据类型划分

如果根据类型划分,可以将变量分为基本类型变量和对象类型变量,而基本类型变量就是前面说的8种基本数据类型的变量,如整型、浮点型、字符型、布尔型等。

说明

对象类型将在后面的章节中介绍,这里暂时不做具体的说明。

3.根据所属范围划分

如果按所属范围来分,可以将变量分为类变量和成员变量,类变量就是用关键字“static”声明的全局变量,它是属于类的。而成员变量就是不用“static”声明的其他实例变量,它是属于对象本身的。

其实类变量就是在类中直接定义的,并且不随类产生的对象变化而变化。当在一个类中声明了一个类变量时,无论创造出多少个对象,使用对象引用这个变量,都不会发生变化。成员变量就不同了,它随着对象不同而变化。即针对同一个类,新创建一个对象,使用此对象引用这个变量,每次引用的值都不一样。

3.4 变量如何初始化

在C、C++、C#等语言中,都会提到变量的初始化,有关对象类型变量的初始化将在后面的章节详细讲述,这里将把基本类型变量的初始化作为本节的主要内容。

【实例3-11】基本类型变量的初始化工作,就是给变量赋值。为了能够更加清晰地看到变量如何初始化,以及初始化时需要注意的知识点,下面通过实例来演示。

01  //通过不同类型的数据的输出来查看变量如何初始化
02  //所有的变量都是全局变量
03  public class var0
04  {
05      byte x;                                           //定义全局变量x
06      short y;                                          //定义全局变量y
07      int z;                                            //定义全局变量z
08      long a;                                           //定义全局变量a
09      float b;                                          //定义全局变量b
10      double c;                                         //定义全局变量c
11      char d;                                           //定义全局变量d
12      boolean e;                                        //定义全局变量e
13      public static void main(String[] args)
14      {
15          var0 m=new var0();                            //创建对象m
16          System.out.println(" 打印数据x="+m.x);
17          System.out.println(" 打印数据y="+m.y);
18          System.out.println(" 打印数据z="+m.z);
19          System.out.println(" 打印数据a="+m.a);
20          System.out.println(" 打印数据b="+m.b);
21          System.out.println(" 打印数据c="+m.c);
22          System.out.println(" 打印数据d="+m.d);
23          System.out.println(" 打印数据e="+m.e);
24      }
25  }

【代码说明】第5~12行定义了8个变量,它们分别对应8种数据类型。我们并没有为其设置初始值,第13~23行直接在控制台输出这些变量,读者可以在下面的运行效果中发现有的变量具备默认值,但有的变量什么也不输出。

【运行效果】

打印数据x=0
打印数据y=0
打印数据z=0
打印数据a=0
打印数据b=0.0
打印数据c=0.0
打印数据d=
打印数据e=false

【实例3-12】从以上例子可以看出,作为全局变量,无须初始化,系统自动给变量赋值。除了字符型数据被赋值为空,布尔型数据被赋值为false,其他一律赋值为0,下面再看一段程序代码。

01  //通过不同类型的数据的输出来查看变量如何初始化
02  //所有的变量都是局部变量
03  public class var1
04  {
05      void printnumber()                               //定义了一个名为printnumber的方法
06      {
07          byte x;                                      //定义局部变量x
08          short y;                                     //定义局部变量y
09          int z;                                       //定义局部变量z
10          long a;                                      //定义局部变量a
11          float b;                                     //定义局部变量b
12          double c;                                    //定义局部变量c
13          char d;                                      //定义局部变量d
14          boolean e;                                   //定义局部变量e
15      }
16      public static void main(String[] args)
17      {
18          var1 m=new var1();                           //创建对象m
19          System.out.println(" 打印数据x="+m.x);
20          System.out.println(" 打印数据y="+m.y);
21          System.out.println(" 打印数据z="+m.z);
22          System.out.println(" 打印数据a="+m.a);
23          System.out.println(" 打印数据b="+m.b);
24          System.out.println(" 打印数据c="+m.c);
25          System.out.println(" 打印数据d="+m.d);
26          System.out.println(" 打印数据e="+m.e);
27      }
28  )

【代码说明】第7~14行定义了8个变量,但其被定义在printnumber()方法中,属于局部变量。第19~26行在没有初始化这些变量的时候,在控制台输出这些变量,其实是不正确的。

【运行效果】这个程序段编译时就会报错,原因是所有局部变量都没有初始化。

从以上两段程序代码得出一个结果:全局变量可以不用进行初始化赋值工作,而局部变量必须要进行初始化赋值工作。

3.5 常量

前两节详细介绍了关于Java语言中变量的定义和初始化功能,通过学习我们了解到变量主要用来存储数值,该数值可以变化,即变量在程序运行期间是可以变化的。从程序开始运行到结束为止,肯定有保持不变的量,它们由谁来存储呢?这就涉及Java语言中的常量。

在Java程序设计中,使用关键字“final”来声明一个常量。常量表示在程序开始运行到结束期间都不变的量。

【实例3-13】例如下面的程序代码。

01  //这里的X是一个常量,由于是不在某个方法内的常量,也可以称为成员常量(作者给它取的名字)
02  public class var2
03  {
04      final int X=20;                                             //定义了一个常量X
05      public static void main(String[] args)
06      {
07          var2 m=new var2();
08          System.out.println(" 打印数据X="+m.X);                  //输出常量X的值
09      }
10  }

【代码说明】第4行通过关键字final定义了一个常量X。第8行输出这个常量的值。

注意

常量名一般都定义为大写字母。

【运行效果】

打印数据X=20

【实例3-14】如果要声明一个类常量,就需要使用关键字static和final的组合,例如下面的例子。

01  //这里的X是类常量,所以无论是哪个对象的引用,它的值始终不变
02  public class var3
03  {
04      static final int X=20;                                    //定义了一个类常量X
05      public static void main(String[] args)
06      {
07          System.out.println(" 打印数据X="+X);                 //输出类常量X的值
08      }
09  }

【代码说明】第4行使用关键字static和final的组合,定义了类常量X。第7行在没有实例化对象的情况下,直接在控制台输出X的值。

【运行效果】

打印数据X=20

从上面的例子可以看出,如果这个常量是类常量,那么无须再构造对象,可以直接引用这个常量。前一个例子声明的常量是一般常量,不是类常量,所以一定要构造对象,通过对象来引用这个常量,所以切记类常量和一般常量的区别所在。

3.6 运算符

运算符就是在用变量或常量进行运算时,经常需要用到的运算符号,目前常用的总共有10种:算术运算符、关系运算符、逻辑运算符、位运算符、移位运算符、赋值运算符、三元运算符、逗号运算符、字符串运算符(将在第6章介绍)、转型运算符。下面将会对每种运算符结合实例进行详细的讲解。

3.6.1 算术运算符

在小学阶段就学过“加”、“减”、“乘”、“除”、“余”,其实这也是Java中的算术运算符(也被称为数学运算符)。下面来看一种情况,当一个浮点型数据加上一个整型数据,其结果是什么类型的数据呢?这涉及数字精度问题,在不同类型的数据之间进行运算时,为了使结果更加精确,系统会将结果自动转化为精度更高的数据类型。

【实例3-15】以上所述的定义有点复杂,通过下面的例子进行说明。

01  public class var4
02  {
03      public static void main(String[] args)
04      {
05          int a=10;                                           //这里的a是一个整型数据
06          float b=10f;                                        //这里的b是一个浮点型数据
07          System.out.println("a+b="+(a+b));                   //相加后为一个浮点型数据
08      }
09  }

【代码说明】第5行定义了整型变量a。第6行定义了浮点型变量b。第7行在控制台输出两个变量进行“加”运算后的结果。

【运行效果】

a+b=20.0

以上的程序代码中,变量a是整型,变量b是单精度浮点型,运算的结果是单精度浮点型。以上的实例说明了一点:为了保证经过算术运算后结果的数据精度,尽量让结果与运算数据中精度较高的类型相同。这个例子就是让结果与a、b中精度较高的单精度浮点型b变量的数据类型相同,所以结果类型就是单精度浮点数据类型。

如何将结果进行转换?转化有什么规律吗?笔者根据经验总结了以下几点。

□ 当使用运算符把两个操作数结合到一起时,首先会将两个操作数转化成相同类型的数据。

□ 两个操作数中如有一个是double型,那么另一个操作数一定先转化成double型,再进行运算。

□ 两个操作数中如有一个是float型,那么另一个操作数一定先转化成float型,再进行运算。

□ 两个操作数中如有一个是long型,那么另一个操作数一定会先转化成long型,再进行运算。

□ 其他任何两个基本类型数据操作,两个操作数都会自动转化成int型。

明白了数据精度的问题,再回到算术运算符的应用。算术运算符的使用可参考表3-1。

表3-1 算术运算符

说明

表3-1中的++、--运算符的作用说明中的“=”是赋值运算符,把右边的值赋予左边的变量,左边原来的变量值被覆盖。

【实例3-16】下面通过程序段来熟悉这些运算符的用法。

01  //两个整型变量a、b通过算术运算符得出的结果
02  public class data1
03  {
04      public static void main(String[] args)
05      {
06          int a=10;                                      //这里的a是一个整型数据
07          int b=3;                                      //这里的b是一个整型数据
08          System.out.println("a+b="+(a+b));
09          System.out.println("a-b="+(a-b));
10          System.out.println("a*b="+(a*b));
11          System.out.println("a/b="+(a/b));
12          System.out.println("a%b="+(a%b));
13      }
14  }

【代码说明】第6~7行先定义两个整型变量。然后通过第8~12行的加、减、乘、除和求余运算,在控制台输出计算结果。

【运行效果】

a+b=13
a-b=7
a*b=30
a/b=3
a%b=1

下面重点讨论自加和自减运算符的用法,它可以使一个变量自动加1和自动减1,得到的值再赋给这个变量。自加运算符又分为两种:一种是前自加,一种是后自加。

【实例3-17】下面通过一个程序段看看什么是前自加和后自加。

01  public class data2
02  {
03      public static void main(String[] args)
04      {
05          int a=10;
06          System.out.println("a="+(a++));            //输出整型变量a后自加结果
07      }
08  }

【代码说明】上面的程序段介绍了后自加,其意义就是:先把a的值赋给a,然后,将a的值加1,存储到内存空间。于是,a输出的值就是10,而存储在内存中的a的值为11。

【运行效果】

a=10

【实例3-18】下面的程序代码演示了前自加功能。

01  public class data3
02  {
03      public static void main(String[] args)
04      {
05          int a=10;
06          System.out.println("a="+(++a));            //输出整型变量a前自加结果
07      }
08  }

【代码说明】上面的程序段演示了前自加,其意义就是:先让a的值加1,然后再将这个加之后的值赋给a。于是,a的输出值当然就是11。

【运行效果】

a=11

【实例3-19】下面来看一个综合的实例。

01  public class data4
02  {
03      public static void main(String[] args)
04      {
05          int a=10;                                      //这里的a是一个整型数据
06          System.out.println("a="+(a++));                //输出整型变量a后自加结果
07          System.out.println("a="+(++a));                //输出整型变量a前自加结果
08      }
09  }

【代码说明】这个程序段首先将a的值赋值给a,然后再将a加1放到内存中,内存中的值为11,那么第一个打印语句的结果就是a=10,接下来,将内存中a的值加1再赋给a,那么a的值为12。

【运行效果】

a=10
a=12

同样自减运算符也有两种:一种是前自减,一种是后自减。

【实例3-20】先看下面的程序段。

01  public class data5
02  {
03      public static void main(String[] args)
04      {
05          int a=10;
06          System.out.println("a="+(--a));            //输出整型变量a前自减结果
07      }
08  }

【代码说明】这个程序段介绍的是前自减,其意义是:先将a的值减1,然后赋值给a,于是a的结果就是9。

【运行效果】

a=9

【实例3-21】再来看看下面的程序段。

01  public class data6
02  {
03      public static void main(String[] args)
04      {
05          int a=10;
06          System.out.println("a="+(a--));            //输出整型变量a后自减结果
07      }
08  }

【代码说明】这个程序段介绍的是后自减,其意义是:将a的值赋给a后,再将a的值自动减1,于是输出a是10。

【运行效果】

a=10

【实例3-22】下面再看一个综合实例。

01  public class data7
02  {
03      public static void main(String[] args)
04      {
05          int a=10;                                           //这里的a是一个整型数据
06          System.out.println("a="+(a--));                     //输出整型变量a后自减结果
07          System.out.println("a="+(--a));                     //输出整型变量a前自减结果
08      }
09  }

【代码说明】这个程序段首先将a的值赋值给a,然后再将a减1放到内存中,内存中的值为9。那么第一个打印语句的结果就是a=10。接下来,将内存中a的值减1再赋给a,那么a的值为8。

【运行效果】

a=10
a=8

【实例3-23】在现实的编程中,可能会遇到更加复杂的程序段,下面继续看一个综合实例。

01  public class data8
02  {
03      public static void main(String[] args)
04      {
05          int a=10;                                            //这里的a是一个整型数据
06          System.out.println("a="+(a--));                      //输出整型变量a后自减结果
07          System.out.println("a="+(--a));                      //输出整型变量a前自减结果
08          System.out.println("a="+(a++));                      //输出整型变量a后自加结果
09          System.out.println("a="+(++a));                      //输出整型变量a前自加结果
10      }
11  }

【代码说明】首先将a的值赋给a,然后将a自动减1放到内存中,此时内存中值为9,所以第一个打印语句输出的值为10。接着,将内存的值先减1再赋给a,a就为8。随后,将a的值先赋给a,再将a的值加1放到内存中,所以内存中a为9。最后将内存中的值加1再赋给a,a的值为10。

【运行效果】

a=10
a=8
a=8
a=10

为了能方便地记忆自加和自减运算符的用法,总结如下:

□ ++x:因为++在前,所以可以记忆为先加后用。

□ x++:因为++在后,所以可以记忆为先用后加。

□--x:因为--在前,所以可以记忆为先减后用。

□ x--:因为--在后,所以可以记忆为先用后减。

3.6.2 关系运算符

关系运算符就是指两个操作数之间的关系,它包括了“>”、“<”等。算术运算符的结果都是数字,而关系运算符的结果则是布尔型数据,这一点一定要注意。关系运算符的使用可参考表3-2。

表3-2 关系运算符

注意

1)区别关系运算符“= =”和赋值运算符“=”,前者是比较符号左右两边的数据是否相等,而后者是把符号右边的数据赋予左边的变量。

2)“= =”、“!=”可以用于对象的比较,而对象的比较通常不是很简单的通过对象名字比较或对象类型比较,而是有自己的equal()函数,有些情况下两个对象是否相等的函数需要程序员自己编写,这里读者需要知道该知识点,在深入学习了面向对象技术后会有切身的理解。

在实际开发中,经常使用关系运算符来作为判断的条件:如果条件是真,会如何处理;如果条件是假,又该如何处理。

【实例3-24】下面看一个简单的例子,看看关系运算符的输出是什么样子。

01  //关系运算符的应用
02  public class data9
03  {
04      public static void main(String[] args)
05      {
06          int a=10;
07          int b=21;
08          System.out.println("说a>b,对吗?"+(a>b));                  //>运算符使用
09          System.out.println("说a>=b,对吗?"+(a>=b));                //>=运算符使用
10          System.out.println("说a<b,对吗?"+(a<b));                  //<运算符使用
11          System.out.println("说a<=b,对吗?"+(a<=b));                //<=运算符使用
12          System.out.println("说a==b,对吗?"+(a==b));                //==运算符使用
13          System.out.println("说a!=b,对吗?"+(a!=b));                //!=运算符使用
14      }
15  }

【代码说明】第6~7行首先定义了两个变量a和b。第8~13行通过比较两个变量的大小,来输出关系运算的结果。

【运行效果】

说a>b,对吗?false
说a>=b,对吗?false
说a<b,对吗?true
说a<=b,对吗?true
说a==b,对吗?false
说a!=b,对吗?true

说明

从以上的程序段可以看出,关系运算符的结果是布尔型数据。

3.6.3 逻辑运算符

常用的逻辑运算符有3种:“非”、“和”、“或”。逻辑运算符一般与关系运算符结合起来使用,下面将详细地介绍这3个逻辑运算符。

1.NOT运算符

NOT运算符就是一个否定的意思,因为英文中“NOT”就是“不”的意思,在Java中,NOT用符号“!”表示。

【实例3-25】下面看一个简单的例子。

01  //非逻辑运算符的应用
02  public class data10
03  {
04      public static void main(String[] args)
05      {
06          int a=10;
07          int b=21;
08          System.out.println("a>b,对吗?"+!(a>b));                  //!运算符使用
09      }

10 }

【代码说明】在程序中,“a>b”是假的,即“false”,但是前面有个“!”否定运算符,将“false”变成了“true”。

【运行效果】

说a>b,对吗?true

2.AND运算符

根据英文“AND”的意思,就知道此运算符是“与”的意思。使用它必须要满足AND前后两个条件。AND运算符在Java中用符号“&&”表示。

【实例3-26】下面看一个简单的实例。

01  //与逻辑运算符的应用
02  public class data11
03  {
04      public static void main(String[] args)
05      {
06          int a=10;
07          int b=21;
08          System.out.println("认为既a>b又a<b,对吗?"+((a>b)&&(a<b)));  //&&运算符使用
09      }
10  }

【代码说明】“a<b”这个关系运算得出的结果是“true”,而“a>b”这个关系运算得出的结果是“false”。两者用AND这个逻辑运算符连接后,得出的结果是“false”。

【运行效果】

认为既a>b又a<b,对吗?false

为什么会这样呢?下面是AND运算符的原理:两个操作数只要有一个是“false”,那么结果就是“false”,如果两个操作数都是“true”,那么结果才是“true”。

3.OR运算符

根据英文“OR”的意思,就知道此运算符是“或”的意思,使用它只要满足OR前后两个条件中任意一个条件。OR运算符在Java中用符号“||”表示,其原理如下:

两个操作数只要有一个是“true”,那么结果就是“true”,否则结果就是“false”。

【实例3-27】下面看一个简单的例子。

01  public class data12
02  {
03      public static void main(String[] args)
04      {
05          int a=10;
06          int b=21;
07          int c=10;
08          System.out.println("认为既a>ba<b,对吗?"+((a>=b)||(a==b)));  //||运算符使用
09          System.out.println("认为既a>ba=c,对吗?"+((a<b)||(a==c)));   //||运算符使用
10      }
11  }

【代码说明】上面的程序段中,“a>=b”和“a==b”两个操作数的结果都是“false”,所以“或”的结果就是“false”;而“a<b”的结果是“true”,“a==c”的结果是“true”,所以结果就是“true”。

【运行效果】

认为既a>b又a<b,对吗?false
认为既a>b又a=c,对吗?true

逻辑运算符主要用于判断条件,例如后面要讲解的判断语句、循环语句的条件判断等。

表3-3给出了所有逻辑运算符。

表3-3 逻辑运算符

说明

读者或许发现“&&”与“&”,“|”与“||”的计算结果相同,但是二者之间还是有区别的,对于“&&”和“||”只要计算完左边的值可以确定整个表达式的值,则不必再进行计算,但是对于“&”和“|”必须把左右两边的结果都计算完后才可以计算结果值。

3.6.4 位运算符

位运算符主要针对二进制进行运算,它包括了“与”、“非”、“或”、“异或”。从表面上看似乎有点像逻辑运算符,但逻辑运算符是针对两个关系表达式来进行逻辑运算,而位运算符主要针对两个二进制数的位进行运算。下面详细介绍每个位运算符。

1.与运算符

与运算符用符号“&”表示,其使用规律如下:两个操作数中位都为1的情况下,结果才为1,否则结果为0。

【实例3-28】例如下面的程序段。

01  public class data13
02  {
03      public static void main(String[] args)
04      {
05          int a=129;
06          int b=128;
07          System.out.println("ab与的结果是:"+(a&b));        //&运算符使用
08      }
09  }

【代码说明】a的值是129,转换成二进制就是10000001,而b的值是128,转换成二进制就是10000000。根据与运算符的运算规律,只有两个位都是1,结果才是1,可以知道上述结果就是10000000,即128。

【运行效果】

a和b与的结果是:128

2.或运算符

或运算符用符号“|”表示,其运算规律如下:两个位只要有一个为1,那么结果就是1,否则就为0。

【实例3-29】下面看一个简单的例子。

01  public class data14
02  {
03      public static void main(String[] args)
04      {
05          int a=129;
06          int b=128;
07          System.out.println("ab或的结果是:"+(a|b));             //|运算符使用
08      }
09  }

【代码说明】a的值是129,转换成二进制就是10000001,而b的值是128,转换成二进制就是10000000,根据或运算符的运算规律,两个位中有一个是1,结果就是1,可以知道上述结果就是10000001,即129。

【运行效果】

a和b或的结果是:129

3.非运算符

非运算符用符号“~”表示,其运算规律如下:如果位为0,结果是1;如果位为1,结果是0。

【实例3-30】下面看一个简单例子。

01  public class data15
02  {
03      public static void main(String[] args)
04      {
05          int a=2;
06          System.out.println("a非的结果是:"+(~a));                  //~运算符使用
07      }
08  }

【代码说明】a的值是2,转换成二进制就是0010,因为非运算就是取相反的位值,而且取反是针对二进制的所有位而言,因为二进制中最高位是1表示负数,所以最终转换为十进制就是-3。

注意

本章中所计算的一些二进制,计算方法都没有那么复杂,如int类型占4字节,每个字节是8位,则对于数值2来说,标准的二进制应该是00000000000000000000000000000010,一共是32位。但为了简化说法,我们并不把多余的零写出来。上述二进制进行非运算后是11111111111111111111111111111101。对于计算机基础学得好的读者,可能还会知道负数的二进制,其实是它绝对值的二进制取反再加1。上述非运算后的结果是00000000000000000000000000000011这个二进制取反加1的结果,而这个值转换为十进制就是3,因为最高位是1表示负数,所以结果为-3。如果读者还不明白这些内容,可以参考一些数据结构和汇编语言的专业书籍。

【运行效果】

a非的结果是:-3

4.异或运算符

异或运算符是用符号“^”表示的,其运算规律是:两个操作数的位中,相同则结果为0,不同则结果为1。

【实例3-31】下面看一个简单的例子。

01  public class data16
02  {
03      public static void main(String[] args)
04      {
05          int a=15;
06          int b=2;
07          System.out.println("a b异或的结果是:"+(a^b));              //^运算符使用
08      }
09  }

【代码说明】a的值是15,转换成二进制为1111,而b的值是2,转换成二进制为0010,根据异或的运算规律,可以得出其结果为1101,即13。

【运行效果】

a与 b异或的结果是:13

3.6.5 移位运算符

移位运算符也是针对二进制的“位”,它主要包括:左移运算符(<<)、右移运算符(>>)、无符号右移运算符(>>>)。

1.左移运算符

左移运算符用“<<”表示,是将运算符左边的对象向左移动运算符右边指定的位数,并且在低位补0。其实,向左移n位,就相当于乘上2的n次方。

【实例3-32】例如下面的例子。

01  public class data17
02  {
03      public static void main(String[] args)
04      {
05      int a=2;
06      int b=2;
07      System.out.println("a移位的结果是:"+(a<<b));              //<<运算符使用
08      }
09  }

【代码说明】首先从本质上分析,2的二进制是00000010,它向左移动2位,就变成了00001000,即8。如果从另一个角度来分析,它向左移动2位,其实就是乘上2的2次方,结果还是8。

【运行效果】

a移位的结果是:8

2.带符号右移运算符

带符号右移运算符用符号“>>”表示,是将运算符左边的运算对象向右移动运算符右边指定的位数。如果是正数,在高位补0,如果是负数,则在高位补1。

【实例3-33】先看下面一个简单的例子。

01  public class data19
02  {
03      public static void main(String[] args)
04      {
05          int a=16;
06          int c=-16;
07          int b=2;
08          int d=2;
09          System.out.println("a的移位结果:"+(a>>b));                //>>运算符使用
10          System.out.println("c的移位结果:"+(c>>d));                //>>运算符使用
11      }
12  }

【代码说明】a的值是16,转换成二进制是00010000,让它右移两位就变成00000100,即4。c的值是-16,转换成二进制是11101111,让它右移一位是11111011,即-4。

【运行效果】

a的移位结果:4
c的移位结果:-4

3.无符号右移运算符

无符号右移运算符用符号“>>>”表示,是将运算符左边的对象向右移动运算符右边指定的位数,并且在高位补0。其实右移n位,就相当于除上2的n次方。

【实例3-34】来看下面的例子。

01  public class data18
02  {
03      public static void main(String[] args)
04      {
05          int a=16;
06          int b=2;
07          System.out.println("a移位的结果是:"+(a>>>b));        //>>>运算符使用
08      }
09  }

【代码说明】从本质上来分析,16的二进制是00010000,它向右移动2位,就变成了00000100,即4。如果从另一个角度来分析,它向右移动2位,其实就是除以2的2次方,结果还是4。

【运行效果】

a移位的结果是:4

现在再来总结一下移位运算符的运算规则:首先把运算对象转化成二进制位,如把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次方。如前面计算规则中的例子,20<<2 = 20×22 = 80,20>>>2 = 20/22= 5。

表3-4描述了移位运算符的用法。

表3-4 移位运算符

3.6.6 赋值运算符

赋值就是将数值赋给变量,而赋值运算符就充当了这个赋值的任务,其实最简单的赋值运算符就是“=”。

当然除了“=”外,还有很多其他赋值运算符,有“+=”、“-=”、“*=”、“/=”、“%=”、“>>=”、“>>>=”、“<<=”、“&=”、“|=”、“^=”。

【实例3-35】下面给出一个简单的例子。

01  public class data20
02  {
03      public static void main(String[] args)
04      {
05          int a=5;
06          int b=2;
07          System.out.println("a+=b的值:"+(a+=b));            //+=运算符使用
08          System.out.println("a-=b的值:"+(a-=b));            //-=运算符使用
09          System.out.println("a*=b的值:"+(a*=b));            //*=运算符使用
10          System.out.println("a/=b的值:"+(a/=b));            ///=运算符使用
11          System.out.println("a%=b的值:"+(a%=b));            //%=运算符使用
12          System.out.println("a>>=b的值:"+(a>>=b));          //>>=运算符使用
13          System.out.println("a>>>=b的值:"+(a>>>=b));        //>>>=运算符使用
14          System.out.println("a<<=b的值:"+(a<<=b));          //<<=运算符使用
15          System.out.println("a&=b的值:"+(a&=b));            //&=运算符使用
16          System.out.println("a|=b的值:"+(a|=b));            //|=运算符使用
17          System.out.println("a^=b的值:"+(a^=b));            //^=运算符使用
18      }
19  }

【代码说明】第7行的“a+=b”就是a=a+b。第8行的“a-=b”就是a=a-b。第17行的“a^=b”就是a=a^b。

【运行效果】

a+=b的值:7
a-=b的值:5
a*=b的值:10
a/=b的值:5
a%=b的值:1
a>>=b的值:0
a>>>=b的值:0
a<<=b的值:0
a&=b的值:0
a|=b的值:2
a^=b的值:0

3.6.7 三元运算符

三元运算符一般用得很少,因为它在程序段中的可读性很差,所以不建议经常使用三元运算符,但 很少使用并不代表不使用,所以还是要掌握它的用法。三元运算符的表达形式如下:

布尔表达式?值0:值1

它的运算过程是:如果布尔表达式的结果是true,就返回值0;如果布尔表达式的结果是false,就返回值1。

【实例3-36】例如下面的程序段。

01  public class data21
02  {
03      public static void main(String[] args)
04      {
05          int a=10;
06          int b=20;
07          System.out.println("此三元运算式结果是:"+((a>b)?'A':'B'));  //三元运算符应用
08      }
09  }

【代码说明】因为“a”小于“b”,所以“a>b”这个关系运算符的结果是“false”,既然是“false”,那么选择值1,即这个三元运算符的结果是“B”。

【运行效果】

此三元运算式结果是:B

3.6.8 逗号运算符

在Java程序设计中,逗号运算符一般是用来将几个数值彼此分开,例如数组中的每个元素都是使用逗号与其他元素分开的。这个运算符太简单了,这里不再给出实例。

3.6.9 转型运算符

转型运算符的用处是将一种类型的对象或数据,经过强制转换而转变为另一种类型的数据。它的格式是在需要转型的数据前加上“()”,然后在括号内加入需要转化的数据类型。

【实例3-37】有的数据经过转型运算后,精度会丢失,而有的会更加精确,下面的例子可以说明这个问题。

01  public class data22
02  {
03      public static void main(String[] args)
04      {
05          int x;
06          double y;
07          x=(int)34.56+(int)11.2;                                //强制转换
08          y=(double)x+(double)11;                                //强制转换
09          System.out.println("x="+x);
10          System.out.println("y="+y);
11      }
12  }

【代码说明】第7行中,由于在34.56前有一个int的强制类型转化,所以34.56就变成了34。同样,11.2就变成了11,所以x的结果就是45。第8行中,在x前有一个double类型的强制转换,所以x的值变为45.0,而数值11也被强制转换成double类型,所以也变成11.0,所以最后y的值变为56.0。

【运行效果】

x=45
y=56.0

3.6.10 运算符的优先级别

当多个运算符出现在一个表达式中,谁先谁后呢?这就涉及运算符的优先级别。在一个多运算符的表达式中,运算符优先级不同会导致最后的结果差别甚大,例如(1+3)+(3+2)*2,这个表达式如果按加号最优先计算,答案就是18,如果按照乘号最优先计算,答案则是14。

下面将详细介绍在Java程序设计中,各个运算符的优先级别,如表3-5所示。

如表3-5 运算符的优先级别

3.7 常见疑难解答

3.7.1 如何将十进制转换成二进制

如何将十进制转换成二进制?作者有一个方法就是先熟练记忆2的n次方的结果,一般来说记到2的7次方就可以了。

下面将举例讲解这个方法:首先记住20=1、21=2、22=4、23=8、24=16、25=32、26=64、27=128。现在要把十进制155转换成二进制,因为155是大于128的,所以第8位上肯定是1。用155-128=27,因为27是大于16小于32的,所以第7位、第6位都为0,而第5位就是1。再用27-16=11,11大于8,所以第4位是1。再用11-8=3,3小于4,所以第3位为0。由于3大于2,所以第2位为1,而3-2=1正好等于第1位,所以第1位为1,综合起来就是:10011011。

3.7.2 转型运算符会引起精度问题,为什么还要使用它

其实不仅基本类型数据会使用转型运算符,对象类型的数据也要使用转型运算符。在使用基本数据转型时,一般都要从低精度往高精度转,但是在某些特定的情况下,或者说在用户特殊要求下,会从高精度转向低精度。例如有的数字希望能够去掉小数位,那么就只能从高精度往低精度转型。

3.8 小结

本章是Java程序设计的基础,程序中的数据离不开变量和常量,而程序的运算离不开这些数据类型和运算符。本章给出了37个小实例,主要是希望读者能自己动手多加练习,并找到一些运算的技巧和原理。

3.9 习题

一、填空题

1.整型主要有4种:__________、__________、__________和__________。

2.Java中的数据类型包括__________ 和__________两种。

3.Java语句的结束符号是__________。

4.在Java程序设计中,使用关键字__________来声明一个常量,常量表示在程序开始运行到结束期间都不变的量。

二、上机实践

1.创建一个实现输出数值的各位值的类,即定义一个整型变量,然后通过算术运算符输出该变量每位的值。

【提示】关键代码如下:

     int num = 8461;
     int gewei = num % 10;
     int shiwei = num / 10 % 10;
     int baiwei = num / 100 % 10;
     int qianwei = num / 1000;

2.创建一个实现大小写字母转换的类,即定义一个字符变量,然后通过算术运算符输出该变量转换后的字母。

【提示】关键代码如下:

     char a = 'a';
     char b = (char)(a-32);