Java程序基础基础
Java程序基础基础
注释
编写注释的原因
编写程序时总需要为程序添加一些注释,用以说明某段代码的作用,或者说明某个类的用途、某个方法的功能,以及该方法的参数和返回值的数据类型及意义等。
编写注释的原因及意义如下
为了更好的阅读自己编写的代码,建议添加这注释。自己写的代码,可能过一段时间回顾的时候,就变得不熟悉。这个时候,注释就起到了很好的帮助作用。
可读性第一,效率第二。一个软件一般都是一个团队协同作战开发出来的。因此,一个人写的代码,需要被整个团队的其他人所理解。
代码即文档。程序源代码是程序文档的重要组成部分。
注释的语法规则
编写Java中的注释不会出现在可执行程序中。因此,可以在源程序中根据需要添加任意多的注释,而不必担心可执行代码会膨胀。在 Java 中,有三种书写注释的方式。
-
单行注释——注释一行
以双斜杠“//”标识,只能注释一行内容,用在注释信息内容少的地方。

-
多行注释——注释一段
包含在“/* ”和“ */”之间,能注释很多行的内容。为了可读性比较好,一般首行和尾行不写注释信息(这样也比较美观好看)。

注意:多行注释可以嵌套单行注释,但是不能嵌套多行注释和文档注释。 -
文档注释
包含在“/** ”和“ */”之间,也能注释多行内容,一般用在类、方法和变量上面,用来描述其作用。注释后,鼠标放在类和变量上面会自动显示出我们注释的内容。

注意:文档注释能嵌套单行注释,不能嵌套多行注释和文档注释,一般首行和尾行也不写注释信息。
标识符和关键字
Java标识符定义
-
包名、类名、方法名、参数名、变量名等,这些符号被称为标识符。
-
标识符可以由字母、数字、下划线()和美元符号($)组成
-
标识符不能以数字开头,不能是java中的关键字。例如:
-
正确的标识符
Username、username123、user_name、_userName、$username -
不正确的标识符:
123username、class、87.2、Hello World、num*123
-
-
首字符之后可以是字母(AZ 或者 az),美元符($)、下划线()或数字的任何字符。
-
标识符是大小写敏感。
Java标识符规则
- 包名所有字母必须小写。例如:cn.itcast.test
- 类名和接口名每个单词的首字母都要大写。例如:ArrayList
- 常量名所有的字母都大写,单词之间用下划线连接。例如:DAY_OF_MONTH
- 变量名和方法名的第一个单词首字母小写,从第二个单词开始,每个单词首字母大写。例如:lineName、getLingNumber
- 在程序中,应该尽量使用有意义的英文单词来定义标识符,使得程序便于阅读。例如:使用userName表示用户名,password表示密码。
Java 关键字
- 下面列出了 Java 关键字。这些保留字不能用于常量、变量、和任何标识符的名称。
- 所有的关键字都是小写
- 程序中的标识符不能以关键字命名
- const和goto是保留字关键字,虽然在java中还没有任何意义,但在程序中不能用来作为自定义的标识符。
- true、false和null不属于关键字,它们是一个单独标识类型,不能直接使用
| 类别 | 关键字 | 说明 |
|---|---|---|
| 访问控制 | private | 私有的 |
| 访问控制 | protected | 受保护的 |
| 访问控制 | public | 公共的 |
| 类、方法和变量修饰符 | abstract | 声明抽象 |
| 类、方法和变量修饰符 | class | 类 |
| 类、方法和变量修饰符 | extends | 扩充,继承 |
| 类、方法和变量修饰符 | final | 最终值,不可改变的 |
| 类、方法和变量修饰符 | implements | 实现(接口) |
| 类、方法和变量修饰符 | interface | 接口 |
| 类、方法和变量修饰符 | native | 本地,原生方法(非 Java 实现) |
| 类、方法和变量修饰符 | new | 新,创建 |
| 类、方法和变量修饰符 | static | 静态 |
| 类、方法和变量修饰符 | strictfp | 严格,精准 |
| 类、方法和变量修饰符 | synchronized | 线程,同步 |
| 类、方法和变量修饰符 | transient | 短暂 |
| 类、方法和变量修饰符 | volatile | 易失 |
| 程序控制语句 | break | 跳出循环 |
| 程序控制语句 | case | 定义一个值以供 switch 选择 |
| 程序控制语句 | continue | 继续 |
| 程序控制语句 | default | 默认 |
| 程序控制语句 | do | 运行 |
| 程序控制语句 | else | 否则 |
| 程序控制语句 | for | 循环 |
| 程序控制语句 | if | 如果 |
| 程序控制语句 | instanceof | 实例 |
| 程序控制语句 | return | 返回 |
| 程序控制语句 | switch | 根据值选择执行 |
| 程序控制语句 | while | 循环 |
| 错误处理 | assert | 断言表达式是否为真 |
| 错误处理 | catch | 捕捉异常 |
| 错误处理 | finally | 有没有异常都执行 |
| 错误处理 | throw | 抛出一个异常对象 |
| 错误处理 | throws | 声明一个异常可能被抛出 |
| 错误处理 | try | 捕获异常 |
| 包相关 | import | 引入 |
| 包相关 | package | 包 |
| 基本类型 | boolean | 布尔型 |
| 基本类型 | byte | 字节型 |
| 基本类型 | char | 字符型 |
| 基本类型 | double | 双精度浮点 |
| 基本类型 | float | 单精度浮点 |
| 基本类型 | int | 整型 |
| 基本类型 | long | 长整型 |
| 基本类型 | short | 短整型 |
| 变量引用 | super | 父类,超类 |
| 变量引用 | this | 本类 |
| 变量引用 | void | 无返回值 |
| 保留关键字 | goto | 是关键字,但不能使用 |
| 保留关键字 | const | 是关键字,但不能使用 |
| 保留关键字 | null | 空 |
数据类型
强类型语言
强类型语言也称为强类型定义语言,是一种总是强制类型定义的语言,要求变量的使用要严格符合定义,所有变量都必须先定义后使用。
Java、.Net和C++等一些语言都是强制类型定义的,也就是说,一旦一个变量被指定了某个数据类型,如果不经过强制转换,那么它就永远是这个数据类型了。
例如你有一个整数,如果不显式地进行转换,你不能将其视为一个字符串。
弱类型语言
弱类型语言也称为弱类型定义语言,与强类型定义相反。像VB,PHP等一些语言就属于弱类型语言。
简单理解就是一种变量类型可以被忽略的语言。比如VBScript是弱类型定义的,在VBScript中就可以将字符串'12'和整数3进行连接得到字符串'123',然后可以把它看成整数123,而不用显示转换。但其实他们的类型没有改变,VB只是在判断出一个表达式含有不同类型的变量之后,自动在这些变量前加了一个clong()或(int)()这样的转换函数而已。能做到这一点其实是归功于VB的编译器的智能化而已,这并非是VB语言本身的长处或短处。
强类型语言和弱类型语言比较
强类型语言在速度上可能略逊色于弱类型语言,但是强类型语言带来的严谨性可以有效地帮助避免许多错误。
动态语言和静态语言
通常我们所说的动态语言、静态语言是指动态类型语言和静态类型语言。
动态类型语言:
动态类型语言是指在运行期间才去做数据类型检查的语言,也就是说,在用动态类型的语言编程时,永远也不用给任何变量指定数据类型,该语言会在你第一次赋值给变量时,在内部将数据类型记录下来。Python和Ruby就是一种典型的动态类型语言,其他的各种脚本语言如VBScript也多少属于动态类型语言。
静态类型语言:
静态类型语言与动态类型语言刚好相反,它的数据类型是在编译其间检查的,也就是说在写程序时要声明所有变量的数据类型,C/C++是静态类型语言的典型代表,其他的静态类型语言还有C#、JAVA等。
对于动态语言与静态语言的区分,套用一句流行的话就是:Static typing when possible, dynamic typing when needed。

Java 基本数据类型
变量就是申请内存来存储值。也就是说,当创建变量的时候,需要在内存中申请空间。
内存管理系统根据变量的类型为变量分配存储空间,分配的空间只能用来储存该类型数据。

因此,通过定义不同类型的变量,可以在内存中储存整数、小数或者字符。
Java 的两大数据类型:
-
内置数据类型
-
引用数据类型

内置数据类型
Java语言提供了八种基本类型。六种数字类型(四个整数型,两个浮点型),一种字符类型,还有一种布尔型。
byte:
- byte 数据类型是8位、有符号的,以二进制补码表示的整数;
- 最小值是 -128(-2^7);
- 最大值是 127(2^7-1);
- 默认值是 0;
- byte 类型用在大型数组中节约空间,主要代替整数,因为 byte 变量占用的空间只有 int 类型的四分之一;
- 例子:byte a = 100,byte b = -50。
short:
- short 数据类型是 16 位、有符号的以二进制补码表示的整数
- 最小值是 -32768(-2^15);
- 最大值是 32767(2^15 - 1);
- Short 数据类型也可以像 byte 那样节省空间。一个short变量是int型变量所占空间的二分之一;
- 默认值是 0;
- 例子:short s = 1000,short r = -20000。
int:
- int 数据类型是32位、有符号的以二进制补码表示的整数;
- 最小值是 -2,147,483,648(-2^31);
- 最大值是 2,147,483,647(2^31 - 1);
一般地整型变量默认为 int 类型;- 默认值是 0 ;
- 例子:int a = 100000, int b = -200000。
long:
- long 数据类型是 64 位、有符号的以二进制补码表示的整数;
- 最小值是 -9,223,372,036,854,775,808(-2^63);
- 最大值是 9,223,372,036,854,775,807(2^63 -1);
- 这种类型主要使用在需要比较大整数的系统上;
- 默认值是 0L;
- 例子: long a = 100000L,Long b = -200000L。
"L"理论上不分大小写,但是若写成"l"容易与数字"1"混淆,不容易分辩。所以最好大写。
float:
- float 数据类型是单精度、32位、符合IEEE 754标准的浮点数;
- float 在储存大型浮点数组的时候可节省内存空间;
- 默认值是 0.0f;
- 浮点数不能用来表示精确的值,如货币;
- 例子:float f1 = 234.5f。
double:
- double 数据类型是双精度、64 位、符合IEEE 754标准的浮点数;
浮点数的默认类型为double类型;- double类型同样不能表示精确的值,如货币;
- 默认值是 0.0d;
- 例子:double d1 = 123.4。
boolean:
- boolean数据类型表示一位的信息;
- 只有两个取值:true 和 false;
- 这种类型只作为一种标志来记录 true/false 情况;
- 默认值是 false;
- 例子:boolean one = true。
char:
- char类型是一个单一的 16 位 Unicode 字符;
- 最小值是 \u0000(即为 0);
- 最大值是 \uffff(即为65535);
- char 数据类型可以储存任何字符;
- 例子:char letter = 'A';
类型默认值
下表列出了 Java 各个类型的默认值:
| 数据类型 | 默认值 |
|---|---|
| byte | 0 |
| short | 0 |
| int | 0 |
| long | 0L |
| float | 0.0f |
| double | 0.0d |
| char | 'u0000' |
| String (or any object) | null |
| boolean | false |
public class Test {
static boolean bool;
static byte by;
static char ch;
static double d;
static float f;
static int i;
static long l;
static short sh;
static String str;
public static void main(String[] args) {
System.out.println("Bool :" + bool);
System.out.println("Byte :" + by);
System.out.println("Character:" + ch);
System.out.println("Double :" + d);
System.out.println("Float :" + f);
System.out.println("Integer :" + i);
System.out.println("Long :" + l);
System.out.println("Short :" + sh);
System.out.println("String :" + str);
}
}

对于数值类型的基本类型的取值范围,我们无需强制去记忆,因为它们的值都已经以常量的形式定义在对应的包装类中了。请看下面的例子:
//基本类型字节空间和取值范围测试
public class DataTypeSize {
public static void main(String[] args){
System.out.println("byte:"+Byte.SIZE/8+"字节("+Byte.MIN_VALUE+"~"+Byte.MAX_VALUE+")");
System.out.println("short:"+Short.SIZE/8+"字节("+Short.MIN_VALUE+"~"+Short.MAX_VALUE+")");
System.out.println("int:"+Integer.SIZE/8+"字节("+Integer.MIN_VALUE+"~"+Integer.MAX_VALUE+")");
System.out.println("long:"+Long.SIZE/8+"字节("+Long.MIN_VALUE+"~"+Long.MAX_VALUE+")");
System.out.println("float:"+Float.SIZE/8+"字节("+Float.MIN_VALUE+"~"+Float.MAX_VALUE+")");
System.out.println("double:"+Double.SIZE/8+"字节("+Double.MIN_VALUE+"~"+Double.MAX_VALUE+")");
System.out.println("char:"+Character.SIZE/8+"字节");
}
}
编译以上代码输出结果如下所示:
数据类型扩展
整数扩展
在JAVA里的进制中
- 0b开头二进制
- 0开头是八进制
- 0x开头是十六进制点数扩展
public static void main(String[] args) {
int i1 = 10;
int i2 = 0b10; //二进制10
int i3 = 010; //八进制10
int i4 = 0x10; //十六进制10 16进制: 0~9 A~F
// 全部输出查看结果
System.out.println(i1); //10
System.out.println(i2); //2
System.out.println(i3); //8
System.out.println(i4); //16
System.out.println("二进制输出" + Integer.toBinaryString(i1));
System.out.println("八进制输出" + Integer.toOctalString(i1));
System.out.printf("八进制输出" + "%010o\n", i1);
System.out.printf("十六进制输出" + "%010x\n", i1);
System.out.println("十六进制输出" + Integer.toHexString(i1));
}

浮点数扩展
-
浮点数是一个离散的数,表示的值不精确且存在误差,不适合进行数值比较
-
浮点数在计算机中是以一种类似于科学计数法的方式(IEEE 754)来存储和运算的(计算机组成原理)。
-
浮点数的计算是不精确的,它存在舍入误差和表示大小溢出的问题。浮点数强制转型成整数时,会舍弃掉小数,显示整数的最大值
-
浮点数的存储也是不精确的,它是采用二进制的方式存储在计算机中,超出精度后溢出的部分就不会被存储,所以会出现误差。
public static void main(String[] args) { float f1=0.1F; double f2=0.1; System.out.println(f1==f2);//false float f3=888888888888888f; float f4=f3+1; System.out.println(f3==f4);//ture }
那么问题也来了,既然我们在程序中使用的浮点数来表示小数的时候,会出现误差,我们在表示银行业务的时候,应该用什么来表示账户余额才能使其不出现问题呢?
我们可以采用Java中提供的数学工具类BigDecimal类来对其进行表示。
注意:在Java中,最好完全避免使用浮点数来进行比较。
字符扩展
在Java中,我们通常使用''将字符包裹起来用于字符的表示,但实际上,计算机对字符的存储是一个二进制的整数,每个正整数都对应了一个字符,这一一的对应就是Unicode编码,Unicode编码就是一张表,一列表示为数字,一列表示为字符,正是通过这张表将其一一对应。
char ch1 = 'A';
char ch2 = 'a';
System.out.println(ch1);//A
System.out.println((int)ch1);//65
System.out.println(ch2);//a
System.out.println((int)ch2);//97
这里就不得不说到字符中的转义字符,我们在表示字符的时候,也可以表示成下面的这种形式,其范围在\u0000~\uFFFF:
char ch3 = '\u0061';
System.out.println(ch3);//a
这里的\u就表示Unicode编码。转义字符也有很多,常见的有:
\n 回车
\r 换行
\'单引号
\"双引号
\t水平制表符
\b空格
\\ 反斜杠
布尔值扩展
boolean flag = true;
if (flag == true) {}
if (flag) {}
两个if判断所表示的意识完全一致,但是后者比前者更为简洁,者一般就是新手程序员喜欢写的,而后者一般为老程序员的习惯,我们写代码的时候,不仅是要完成业务逻辑的需要,我们写出的代码也应该简洁精炼,在这里就不得不提到Jquery的一句话,write less,do more,写的更少,做的更多。
类型转换
自动类型转换
数据类型自动转换:将取值范围小的类型自动转换为取值范围大的类型。例如:
一个 int 类型变量和一个 byte 类型变量进行相加,运算的结果是:变量的类型将是 int 类型。
同样道理,当一个 int 类型变量和一个 double 变量运算时, int 类型将会自动提升为 double 类型进行运算。

转换规则:
范围小的类型向范围大的类型提升:byte、short、char 运算时候直接提升为 int。
byte、short、char‐‐>int‐‐>long‐‐>float‐‐>double。
强制类型转换
强制类型转换:将取值存储位数多的的类型强制转换成储存位数少的的类型 。
比较而言,自动转换是 Java 自动执行的,而强制转换需要我们自己手动执行。

将 1.5 赋值到 int 类型变量:产生编译失败,无法赋值。
要修改为:
int i = (int)1.5; //但是这样会导致 1.5 变成 1。
int d=(int)2.5; // double类型数据强制转成int类型,直接去掉小数点,保留整数。
public static void main(String[] args) {
/**
* int a=1.5; 编译失败,无法赋值
*/
int i = (int)1.5;
double d=2.5;
//int类型和double类型运算,结果是double类型
//int类型会提升为double类型
double e = d + i;
System.out.println(e);
}
分析以下程序:
public static void main(String[] args) {
System.out.println(4/3);
System.out.println(4/3.0);
System.out.println(4.0/3);
System.out.println((float)(4/3.0));
System.out.println(4*3);
System.out.println(4*3.0);
System.out.println(4.0*3);
System.out.println(4+3);
System.out.println(4+3.0);
System.out.println(4.0+3);
}

变量、常量、作用域
变量
变量,见名知义,就是可以变化的量。
因为Java是一种强类型的语言,每一个变量都必须声明它们的类型,它是程序中最基本的存储单元,其中包括变量名、变量类型和作用域。语法如下:
数据类型 变量名 [= 值] [{,变量名 [ = 值]}...];
//一行中声明多个同类型的变量 都未赋初始值
int a, b, c;
//一行中声明多个变量 都赋值
int d = 1, e = 2;
//一行中声明多个变量 有赋值 有未赋值
int f, g = 3;
//一行中声明一个变量
int age = 20;
//声明一个引用型变量
String name = "Ara_Hu";
我们在声明变量时,为了提高程序的可阅读性,我们最好不要在一行中声明多个变量,我们可以一行声明一个变量,在某些关键的变量之前加上注释,标明这个变量的用途,这样可以大大提高我们程序的可阅读性。
注意:
- 每个变量都需要有类型,类型可以是基本类型,也可以是引用类型
- 变量名必须是合法的标识符
- 变量声明是一条完整的语句,必须以分号结尾
- 变量在声明后必须要赋值后才能进行使用
常量
常量,可以理解为一种特殊的变量,它的值初始化之后就不能再进行修改,它是不会变动的值,常量名一般采用大写字符。
//声明一个常量
final double PI = 3.14;
我们声明常量的时候需要使用final关键字来对变量进行修饰,在修饰前它是一个变量,但是在通过final是修饰后,它就成为了一个常量。
**注意:常量的值,是不能进行修改的**
作用域
作用域(scope),程序设计概念,通常来说,一段程序代码中所用到的名字并不总是有效/可用的,而限定这个名字的可用性的代码范围就是这个名字的作用域。
变量又分为类变量、实例变量和局部变量,三种不同类型的变量就对应着不同的作用范围,就是它们存在着不同的作用域。
- 类变量:在类中但是独立于方法之外的变量,用
static修饰。 - 实例变量:在类中但是独立于方法之外的变量,不过没有
static修饰。 - 局部变量:类的方法中的变量。
类变量在本类中任何地方都可直接使用,在其他类中用类名.变量名可使用;
实例变量不论在什么地方都需要先实例化本类,在通过实例化的本类的引用(new的该对象的变量名)来进行使用;
局部变量仅在声明此变量的那段代码中可用,一般都是在声明此变量到最接近声明此变量之后的}段中有效(比如下面这个图,变量i仅在两个红框之间有效);

类变量:
- 在本类中可以直接使用,在其他类中可直接通过类名调用(ClassName.VariableName)
- 类变量通常是一个常量,为了方便调用,一般在类变量前面添加
public和final关键字,声明为公开的常量 - 类变量在第一次被访问时创建,在程序结束时销毁
实例变量:
- 实例变量在对象创建的时候创建,在对象被销毁的时候销毁
- 实例变量可以通过变量名访问(就是new出来的对象的变量名)
- 一般实例变量都会构建为
private私有的,然后类中提供对应的getter和setter方法来进行访问和赋值
局部变量:
- 仅在声明它的方法或者代码块中有效
- 当包含局部变量的方法或者代码块执行时,变量创建,结束时销毁
- 局部变量没有默认值,所以局部变量在被声明后,必须初始化才能使用
public class Demo {
//类变量 在此类加载时就会初始化
static String name = "Ara_Hu";
//实例变量 在此类实例化时才会初始化
int age = 20;
public static void main(String[] args) {
//局部变量 在此方法运行时初始化
int i = 0;
}
}
基本运算符
计算机的最基本用途之一就是执行数学运算,作为一门计算机语言,Java也提供了一套丰富的运算符来操纵变量。我们可以把运算符分成以下几组:
- 算术运算符
- 关系运算符
- 位运算符
- 逻辑运算符
- 赋值运算符
- 其他运算符
1. 算术运算符
算术运算符在数学表达式中的使用方式与在代数中使用的方式相同。下表列出了算术运算符的使用示例 -
假设整数类型变量A的值为:10,变量B的值为:20,则 -
| 运算符 | 描述 | 示例 |
|---|---|---|
+ |
加法运算符,第一个操作数加上第二个数操作数 | A + B结果为:30 |
- |
减法运算符,从第一个操作数减去第二个操作数 | A - B结果为:-10 |
* |
两个操作数相乘 | A * B结果为:200 |
/ |
左操作数除以右操作数返回模值 | B / A结果为:2 |
% |
左操作数除以右操作数返回余数 | B % A结果为:0 |
++ |
将操作数的值增加1 |
A++,则A的值为:11 |
-- |
将操作数的值减1 |
A--,则A的值为:9 |
public class Test {
public static void main(String[] args) {
int a = 10;
int b = 20;
int c = 25;
int d = 25;
System.out.println("a + b = " + (a + b) );
System.out.println("a - b = " + (a - b) );
System.out.println("a * b = " + (a * b) );
System.out.println("b / a = " + (b / a) );
System.out.println("b % a = " + (b % a) );
System.out.println("c % a = " + (c % a) );
System.out.println("a++ = " + (a++) );
System.out.println("a-- = " + (a--) );
// 查看 d++ 与 ++d 的不同
System.out.println("d++ = " + (d++) );
System.out.println("++d = " + (++d) );
}
}
注意:Java中两个整数相除,只会输出结果的整数部分,要输出较为准确的结果,把其中一个变为浮点数
两个整数之间的运算结果是整数
int和float运算的结果是float类型
public static void main(String[] args) {
System.out.println(4/3);
System.out.println(4/3.0);
System.out.println((float)(4/3.0));
System.out.println((4/((float)3.0)));
}

自增自减运算符
1、自增(++)自减(--)运算符是一种特殊的算术运算符,在算术运算符中需要两个操作数来进行运算,而自增自减运算符是一个操作数。
public class selfAddMinus{
public static void main(String[] args){
int a = 3;//定义一个变量;
int b = ++a;//自增运算
int c = 3;
int d = --c;//自减运算
System.out.println("进行自增运算后的值等于"+b);
System.out.println("进行自减运算后的值等于"+d);
}
}
运行结果为:
进行自增运算后的值等于4
进行自减运算后的值等于2
解析:
- int b = ++a; 拆分运算过程为: a=a+1=4; b=a=4, 最后结果为b=4,a=4
- int d = --c; 拆分运算过程为: c=c-1=2; d=c=2, 最后结果为d=2,c=2
2、前缀自增自减法(++a,--a): 先进行自增或者自减运算,再进行表达式运算。
3、后缀自增自减法(a++,a--): 先进行表达式运算,再进行自增或者自减运算 实例:
public class selfAddMinus{
public static void main(String[] args){
int a = 5;//定义一个变量;
int b = 5;
int x = 2*++a;
int y = 2*b++;
System.out.println("自增运算符前缀运算后a="+a+",x="+x);
System.out.println("自增运算符后缀运算后b="+b+",y="+y);
}
}
运行结果为:
自增运算符前缀运算后a=6,x=12
自增运算符后缀运算后b=6,y=10
如何理解?b=++a就是先执行a=a+1,在执行b=a;
b=*--a是先执行b=a;在执行a=a+1;
对于以下,有int x = 5, y = 6, z; 题目1:z = ++x + y++; 题目2:z = ++x + x++; 题目3:x = ++x + x++; 对于上面的三道题目,我们下面一一解答。使用的技巧就是:把原始计算式转化成多个、有先后计算顺序的、小的计算式,然后带入变量的值,进行求解。记住:同一优先级的运算符的计算顺序是从右往左。 Q1:z = ++x + y++; 可以转化为: x = x +1; z = x + y; y = y + 1; 带入x = 5, y = 6,可得x = 6; z = 12; y = 7; Q2:z = ++x + x++; 可以转化为: x = x +1; z = x + x; x = x + 1; 带入x = 5,可得x = 6; z = 6+6=12; x = 7; 故x=7,z=12; Q3:x = ++x + x++; 可以转化为: x = x +1; x = x + x; x = x + 1; 带入x = 5,可得x = 5+1=6; x = 6+6=12; x = 12+1=13; 故x=13。
溢出
整数溢出
要特别注意,整数由于存在范围限制,如果计算结果超出了范围,就会产生溢出,而溢出不会出错,却会得到一个奇怪的结果:
public class Main {
public static void main(String[] args) {
int x = 2147483640;
int y = 15;
int sum = x + y;
System.out.println(sum); // -2147483641
}
}
要解释上述结果,我们把整数2147483640和15换成二进制做加法:
0111 1111 1111 1111 1111 1111 1111 1000 + 0000 0000 0000 0000 0000 0000 0000 1111 ----------------------------------------- 1000 0000 0000 0000 0000 0000 0000 0111
由于最高位计算结果为1,因此,加法结果变成了一个负数。
要解决上面的问题,可以把int换成long类型,由于long可表示的整型范围更大,所以结果就不会溢出:
long x = 2147483640;
long y = 15;
long sum = x + y;
System.out.println(sum); // 2147483655
浮点数溢出
整数运算在除数为0时会报错,而浮点数运算在除数为0时,不会报错,但会返回几个特殊值:
NaN表示Not a NumberInfinity表示无穷大-Infinity表示负无穷大
例如:
double d1 = 0.0 / 0; // NaN
double d2 = 1.0 / 0; // Infinity
double d3 = -1.0 / 0; // -Infinity
这三种特殊值在实际运算中很少碰到,我们只需要了解即可。
2. 关系运算符
Java语言支持以下关系运算符。假设变量A的值是10,变量B的值是20,则 -
| 运算符 | 描述 | 示例 |
|---|---|---|
== |
等于运算符,检查两个操作数的值是否相等,如果相等,则条件变为真。 | A==B结果为假。 |
!= |
不等于运算符,检查两个操作数的值是否不相等,如果不相等,则条件变为真。 | A!=B结果为真。 |
> |
大于运算符,检查左操作数的值是否大于右操作数的值,如果大于,则条件变为真。 | A>B结果为假。 |
< |
小于运算符,检查左操作数的值是否小于右操作数的值,如果小于,则条件变为真。 | A<B结果为真。 |
>= |
大于或等于运算符,检查左操作数的值是否大于等于右操作数的值,如果大于或等于,则条件变为真。 | A>=B结果为假。 |
<= |
小于或等于运算符,检查左操作数的值是否小于或等于右操作数的值,如果小于或等于,则条件变为真。 | A<=B结果为真。 |
public class Test {
public static void main(String[] args) {
int a = 10;
int b = 20;
System.out.println("a == b = " + (a == b) );
System.out.println("a != b = " + (a != b) );
System.out.println("a > b = " + (a > b) );
System.out.println("a < b = " + (a < b) );
System.out.println("b >= a = " + (b >= a) );
System.out.println("b <= a = " + (b <= a) );
}
}
a == b = false
a != b = true
a > b = false
a < b = true
b >= a = true
b <= a = false
3. 按位运算符
移位运算
在计算机中,整数总是以二进制的形式表示。例如,int类型的整数7使用4字节表示的二进制如下:
00000000 0000000 0000000 00000111
可以对整数进行移位运算。对整数7左移1位将得到整数14,左移两位将得到整数28:
int n = 7; // 00000000 00000000 00000000 00000111 = 7
int a = n << 1; // 00000000 00000000 00000000 00001110 = 14
int b = n << 2; // 00000000 00000000 00000000 00011100 = 28
int c = n << 28; // 01110000 00000000 00000000 00000000 = 1879048192
int d = n << 29; // 11100000 00000000 00000000 00000000 = -536870912
左移29位时,由于最高位变成1,因此结果变成了负数。
类似的,对整数28进行右移,结果如下:
int n = 7; // 00000000 00000000 00000000 00000111 = 7
int a = n >> 1; // 00000000 00000000 00000000 00000011 = 3
int b = n >> 2; // 00000000 00000000 00000000 00000001 = 1
int c = n >> 3; // 00000000 00000000 00000000 00000000 = 0
如果对一个负数进行右移,最高位的1不动,结果仍然是一个负数:
int n = -536870912;
int a = n >> 1; // 11110000 00000000 00000000 00000000 = -268435456
int b = n >> 2; // 11111000 00000000 00000000 00000000 = -134217728
int c = n >> 28; // 11111111 11111111 11111111 11111110 = -2
int d = n >> 29; // 11111111 11111111 11111111 11111111 = -1
还有一种无符号的右移运算,使用>>>,它的特点是不管符号位,右移后高位总是补0,因此,对一个负数进行>>>右移,它会变成正数,原因是最高位的1变成了0:
int n = -536870912;
int a = n >>> 1; // 01110000 00000000 00000000 00000000 = 1879048192
int b = n >>> 2; // 00111000 00000000 00000000 00000000 = 939524096
int c = n >>> 29; // 00000000 00000000 00000000 00000111 = 7
int d = n >>> 31; // 00000000 00000000 00000000 00000001 = 1
对byte和short类型进行移位时,会首先转换为int再进行位移。
仔细观察可发现,左移实际上就是不断地×2,右移实际上就是不断地÷2。
位运算
位运算是按位进行与、或、非和异或的运算。
与运算的规则是,必须两个数同时为1,结果才为1:
n = 0 & 0; // 0
n = 0 & 1; // 0
n = 1 & 0; // 0
n = 1 & 1; // 1
或运算的规则是,只要任意一个为1,结果就为1:
n = 0 | 0; // 0
n = 0 | 1; // 1
n = 1 | 0; // 1
n = 1 | 1; // 1
非运算的规则是,0和1互换:
n = ~0; // 1
n = ~1; // 0
异或运算的规则是,如果两个数不同,结果为1,否则为0:
n = 0 ^ 0; // 0
n = 0 ^ 1; // 1
n = 1 ^ 0; // 1
n = 1 ^ 1; // 0
对两个整数进行位运算,实际上就是按位对齐,然后依次对每一位进行运算。例如:
// 位运算
public class Main {
public static void main(String[] args) {
int i = 167776589; // 00001010 00000000 00010001 01001101
int n = 167776512; // 00001010 00000000 00010001 00000000
System.out.println(i & n); // 167776512
}
}
a = 0011 1100
b = 0000 1101
-----------------
a&b = 0000 1100
a|b = 0011 1101
a^b = 0011 0001
~a = 1100 0011
下面的表中列出了按位运算符,假设整数变量A=60,变量B=13,那么 -
| 运算符 | 描述 | 示例 |
|---|---|---|
& |
二进制AND运算符,如果存在于两个操作数中,则它会将结果复制到结果中。 | A & B的结果为:12,也就是:0000 1100 |
| ` | ` | 二进制OR运算符,如果存在于任一操作数中,则复制一位。 |
^ |
二进制异或运算符,如果在一个操作数中设置但不在两个操作数中设置,则复制该位。 | A ^ B的结果为:49,也就是:0011 0001 |
~ |
二元一元补充运算符是一元的,具有“翻转”位的效果。 | ~A的结果为:-61,也就是:1100 0011 |
<< |
二进制左移运算符,左操作数值向左移动右操作数指定的位数。 | A << 2的结果为:240,也就是:1111 0000 |
>> |
二进制右移运算符,左操作数值向右移动右操作数指定的位数。 | A >> 2的结果为:15,也就是:1111 |
>>> |
右移零填充运算符。 左操作数值向右移动右操作数指定的位数,移位值用零填充。 | A >>>2的结果为:15,也就是:0000 1111 |
4. 逻辑运算符
下表列出了逻辑运算符 -
假设布尔变量A的值为:true,变量B 的值为:false,则 -
| 运算符 | 描述 | 示例 |
|---|---|---|
&& |
逻辑AND运算符。 如果两个操作数都不为零,则条件成立。 | (A && B)结果为:false |
| ` | ` | |
! |
逻辑非运算符。用于反转其操作数的逻辑状态。 如果条件为真,则口逻辑NOT运算符将为false。 |
!(A && B)结果为:true |
短路运算
布尔运算的一个重要特点是短路运算。如果一个布尔运算的表达式能提前确定结果,则后续的计算不再执行,直接返回结果。
因为false && x的结果总是false,无论x是true还是false,因此,与运算在确定第一个值为false后,不再继续计算,而是直接返回false。
public class Main {
public static void main(String[] args) {
boolean b = 5 < 3;
boolean result = b && (5 / 0 > 0);
System.out.println(result);
}
}
如果没有短路运算,&&后面的表达式会由于除数为0而报错,但实际上该语句并未报错,原因在于与运算是短路运算符,提前计算出了结果false。
如果变量b的值为true,则表达式变为true && (5 / 0 > 0)。因为无法进行短路运算,该表达式必定会由于除数为0而报错,可以自行测试。
类似的,对于||运算,只要能确定第一个值为true,后续计算也不再进行,而是直接返回true:
boolean result = true || (5 / 0 > 0); // true
5. 赋值运算符
以下是Java语言支持的赋值运算符 -
| 运算符 | 描述 | 示例 |
|---|---|---|
= |
简单赋值运算符。 将右侧操作数的值分配给左侧操作数。 | C = A + B将A + B的值分配给C。 |
+= |
相加与赋值运算符。 它将右操作数相加到左操作数并将结果分配给左操作数。 | C += A等于C = C + A。 |
-= |
减去与赋值运算符。 它从左操作数中减去右操作数,并将结果赋给左操作数。 | C -= A等于C = C - A。 |
*= |
乘以与赋值运算符。 它将右操作数与左操作数相乘,并将结果赋给左操作数。 | C *= A等于C = C * A。 |
/= |
除以与赋值运算符。 它将左操作数除以右操作数,并将结果赋给左操作数。 | C /= A等于C = C / A。 |
%= |
模数与赋值运算符。 它使用两个操作数来计算获取模数,并将结果赋给左操作数。 | C %= A等于C = C % A。 |
<<= |
左移与赋值运算符。 | C <<= 2与C = C << 2相同 |
>>= |
右移与赋值运算符。 | C >>= 2与C = C >> 2相同 |
&= |
按位与赋值运算符。 | C &= 2与C = C & 2相同 |
^= |
按位异或和赋值运算符。 | C ^= 2与C = C ^ 2相同 |
| ` | =` | 按位包含或与赋值运算符。 |
6. 其它运算符
Java语言支持的其他运算符很少。
条件运算符(?😃
条件运算符也称为三元运算符。 此运算符由三个操作数组成,用于计算布尔表达式。 运算符的目标是确定应将哪个值赋给变量。 运算符写成 -
variable x = (expression) ? value if true : value if false
下面是一段示例代码:
public class Test {
public static void main(String args[]) {
int a, b;
a = 10;
b = (a == 1) ? 20: 30;
System.out.println( "Value of b is : " + b );
b = (a == 10) ? 20: 30;
System.out.println( "Value of b is : " + b );
}
}
执行上面示例代码,得到以下结果 -

6.2. instanceof运算符
此运算符仅用于对象引用变量。 运算符检查对象是否属于特定类型(类类型或接口类型)。 instanceof运算符写成 -
( Object reference variable ) instanceof (class/interface type)
如果操作符左侧的变量引用的对象是右侧的类/接口类型,则结果为真。 以下是一个例子 -
public class Test {
public static void main(String args[]) {
String name = "haha";
// 当 name 的类型是 String 时,则返回为:true
boolean result = name instanceof String;
System.out.println( result );
}
}
执行上面示例代码,得到以下结果:
true
如果要比较的对象与右侧类型兼容,则此运算符仍将返回true。 以下是另一个例子 -
class Vehicle {}
public class Car extends Vehicle {
public static void main(String args[]) {
Vehicle a = new Car();
boolean result = a instanceof Car;
System.out.println( result );
}
}
执行上在示例代码,得到以下结果:
true
7.Java运算符优先级
在Java的计算表达式中,运算优先级从高到低依次是:
()!~++--*/%+-<<>>>>>&|+=-=*=/=
记不住也没关系,只需要加括号就可以保证运算的优先级正确。
下表中具有最高优先级的运算符在的表的最上面,最低优先级的在表的底部。
| 类别 | 操作符 | 关联性 |
|---|---|---|
| 后缀 | () [] . (点操作符) | 左到右 |
| 一元 | expr++ expr-- | 从左到右 |
| 一元 | ++expr --expr + - ~ ! | 从右到左 |
| 乘性 | * /% | 左到右 |
| 加性 | + - | 左到右 |
| 移位 | >> >>> << | 左到右 |
| 关系 | > >= < <= | 左到右 |
| 相等 | == != | 左到右 |
| 按位与 | & | 左到右 |
| 按位异或 | ^ | 左到右 |
| 按位或 | | | 左到右 |
| 逻辑与 | && | 左到右 |
| 逻辑或 | | | | 左到右 |
| 条件 | ?: | 从右到左 |
| 赋值 | = + = - = * = / =%= >> = << =&= ^ = | = | 从右到左 |
| 逗号 | , | 左到右 |


浙公网安备 33010602011771号