Java | Java 基础语法
III、类型转换
\quad
由于 Java 是一种强类型语言,所以在进行某些运算的时候,需要用到类型转换。所谓类型转换即将某一个数据的数据类型通过某种方式转换为另一种所需的数据类型。在运算中,不同类型的数据需要先转化为同一类型,然后才能进行运算。数据类型的转换可以分为隐式转换(自动类型转换)和显式转换(强制类型转换)两种。
3.1 隐式转换(自动类型转换)
\quad 在运算中,如果满足:(1)两种数据类型彼此兼容;(2)目标数据类型的取值范围大于源数据类型,那么将执行自动类型转换,转换为取值范围大的数据类型。自动类型转换主要表达数字表示范围小的数据类型可以自动转换成范围大的数据类型。
\quad 在运算过程中,由于不同的数据类型会转换成同一种数据类型(取值范围大的数据类型),所以整型、浮点型以及字符型都可以参与混合运算。自动类型转换的规则是从低级类型数据转换成高级类型数据。转换规则如下:
boolean类型不参与自动类型转换,值只能为true或false。- 数值型数据的转换:
byte,short→int→long→float→double;byte,short参与运算时(包含同种类型运算),会自动转换为int类型,然后参与运算,最终结果为int类型。- 当对
long类型变量进行赋值时,如果忘记在数值后加“L”,会产生两种情况:
- 数值在
int取值范围内不报错,因为会将数值先定义为int类型,然后在运算过程中自动类型转换为long类型。 - 数值超出
int取值范围会报错,因为无法将数值定义为int类型。
- 字符型转换为整型:
char→int。char类型比较特殊,char可以自动转换成int,long,float和double,但byte和short不能自动转换为char,而且char也不能自动转换为byte或short。char与byte和short做运算时(包含同种类型运算),三者会自动转换为int类型,然后参与运算,最终结果为int类型。
3.2 显式转换(强制类型转换)
\quad
当两种数据类型不兼容,或目标数据类型的取值范围小于源数据类型时,自动转换将无法进行,这时就需要进行强制类型转换。强制数据类型转换是手动将较大范围数据转换较小数据类型,格式是在需要进行类型转换的数据前加上“(数据类型)”,在括号内加入需要转化的数据类型。有的数据经过转型运算后,精度会丢失,而有的会更加精确。
\quad 在强制类型转换中,如果是将浮点类型的值转换为整数,直接截断小数点后边的所有数字(损失精度);而如果是整数类型强制转换为浮点类型时,将在小数点后面补零。
\quad 注:不能对布尔值进行强制类型转换;转换过程中可能发生内存溢出或精度问题。
IV、String
String不是基本数据类型,属于引用数据类型。- 声明
String类型变量时,必须使用一对双引号" "或强制类型转换。8种基本数据类型不会自动转换为String类型。 - 使用方式与基本数据类型一致。例:
String str = “Hello world!”。 String类型变量可以是空字符串。例:String str = “”。- 一个字符串可以拼接另一个字符串,也可以使用“
+”与8种基本数据类型(包含boolean类型)进行拼接,最终得到的结果都是字符串类型的数据。 - “
+”两侧出现字符串类型数据,执行的是拼接运算而不是加法运算。 String类型不能强制类型转换成基本数据类型,会报错。- 练习:
char+char执行加法运算,char+String或String+char执行拼接运算。System.out.println('*'+'\t'+'*');// 93 System.out.println('*'+"\t"+'*');// * * System.out.println('*'+'\t'+"*");// 51*
V、Java 变量
\quad
Java 程序在运行期间会产生一些临时数据,应用程序会将这些数据保存在一些内存单元中,每个内存单元都会使用一个标识符进行标识。这些内存单元被称为变量,对内存单元进行标识的标识符就是变量名,内存单元中存储的数据就是变量的值。
-
Java变量作用:用于在内存中保存数据。 -
Java是一种强类型语言,每个变量都必须声明其数据类型,数据类型可以是基本数据类型,也可以是引用数据类型。 -
Java变量是程序中最基本的存储单元,其要素包括变量名,变量类型和作用域。 -
在
Java语言中,所有的变量在使用前必须声明。 -
声明变量的基本格式:
type identifier [= value][, identifier [= value] ...];,其中type为Java数据类型,identifier是变量名。- 可以使用逗号分割来同时声明多个同类型变量(
不建议同一行声明多个变量)。 - 要注意变量声明是一条完整的语句,因此每一个声明都必须以分号“
;”结束。
- 可以使用逗号分割来同时声明多个同类型变量(
-
Java程序在声明变量的同时可以为变量赋值,也可以声明以后再赋值,需要使用变量名来访问这块区域的数据。例:- 声明时赋值:
int a = 1; - 声明后赋值:
int a; a = 1;
- 声明时赋值:
\quad
Java变量作用域是指变量能生效的区域范围,变量只有在作用域范围内才能够被使用。声明在不同地方的变量具有不同的作用域,而决定作用域范围的就是花括号{}的位置。同一个作用域内,不能定义重名的变量。根据作用域的不同,一般将变量分为成员变量(全局变量)和局部变量。
5.1 全局变量
\quad
Java 的成员变量有两种,分别是成员变量(实例变量)和静态变量(类变量)。成员变量声明在一个类中,但在方法、构造方法和语句块之外,不属于任何一个方法,作用域是整个类。
| 成员变量类型 | 修饰 | 访问 | 生命周期 |
|---|---|---|---|
| 成员变量(实例变量) | 无static修饰 | 对象名.变量名 | 在对象创建的时候创建,在对象被销毁的时候销毁。 |
| 静态变量(类变量) | static修饰 | 类名.变量名
/
/
/ 对象名.变量名 | 其生命周期取决于类的生命周期。在类第一次被访问时创建,在类被垃圾回收机制彻底回收时才会被销毁。 |
没有赋值的全局变量,系统将按下列默认值进行初始化:
| 数据类型 | 初始值 | 数据类型 | 初始值 | 数据类型 | 初始值 |
|---|---|---|---|---|---|
| byte | 0 | short | 0 | int | 0 |
| long | 0L | char | ‘\u0000’ | float | 0.0f |
| double | 0.0 | boolean | false | 所有引用类型 | null(不引用任何对象) |
5.1.1 成员变量(实例变量)
- 实例变量声明在一个类中,但在方法、构造方法和语句块之外;
- 当一个对象被实例化之后,每个实例变量的值就跟着确定;
- 注:对象实例化过程:当一个对象被创建之后,虚拟机会为其分配内存,主要用来存放对象的实例变量及其从超类继承过来的实例变量,在为这些实例变量分配内存的同时,这些实例变量也会被赋予默认值。 即使我们没通过构造函数传递参数,或者
set指令传递参数,那么实例变量也会被赋予默认初始值。
- 注:对象实例化过程:当一个对象被创建之后,虚拟机会为其分配内存,主要用来存放对象的实例变量及其从超类继承过来的实例变量,在为这些实例变量分配内存的同时,这些实例变量也会被赋予默认值。 即使我们没通过构造函数传递参数,或者
- 实例变量在对象创建的时候创建,在对象被销毁的时候销毁;
- 实例变量的值应该至少被一个方法、构造方法或者语句块引用,使得在类的外部能够通过这些方式获取实例变量信息;
- 实例变量的声明可以在使用前或者使用后;
- 因为实例变量伴随对象的创建而实例化,所以总是在第一步执行,无所谓写在前后。
- 访问修饰符(后续讲解)可以修饰实例变量;
- 实例变量对于类中的方法、构造方法或者语句块是可见的。一般情况下应该把实例变量设为私有(private,
只能在本类中访问到,在其他的类中无法进行访问)。通过使用访问修饰符可以使实例变量对子类可见; - 实例变量具有默认值。数值型变量的默认值是
0,布尔型变量的默认值是false,引用类型变量的默认值是null。变量的值可以在声明时指定,也可以在构造方法中指定; - 实例变量可以直接通过变量名访问。但在静态方法以及其他类中,就应该使用完全限定名:
ObejectReference.VariableName,即对象名.变量名。
5.1.2 静态变量(类变量)
- 类变量也称为静态变量,在类中以
static关键字声明,但必须在方法之外。 - 无论一个类创建了多少个对象,类只拥有类变量的一份拷贝,即任意对象修改了静态变量,都会影响到所有对象。
- 静态变量会在对象实例化的时候在堆内存中单独创建一片固定的内存,即所有对象实例的静态变量值都指向这片区域。
- 静态变量除了被声明为常量外很少使用,静态常量是指声明为
public/private final static类型的一种常量。静态常量初始化后不可改变。 - 静态变量储存在静态存储区。经常被声明为常量,很少单独使用
static声明变量。 - 静态变量在第一次被访问时创建,在程序结束时销毁。
- 静态变量与实例变量具有相似的可见性。但为了对类的使用者可见,大多数静态变量声明为
public类型。 - 静态变量默认值和实例变量相似。数值型变量默认值是
0,布尔型默认值是false,引用类型默认值是null。变量的值可以在声明的时候指定,也可以在构造方法中指定。此外,静态变量还可以在静态语句块中初始化。 - 静态变量可以通过:
ClassName.VariableName,即类名.变量名的方式访问。 - 静态变量可以在自身所在的类内部被直接访问。
- 静态变量被声明为
public static final类型时, 静态变量名称一般建议使用大写字母。如果静态变量不是public和final类型,其命名方式与实例变量以及局部变量的命名方式一致。
5.2 局部变量
- 局部变量声明在方法、构造方法或者语句块中;
- 局部变量在方法、构造方法、或者语句块被执行的时候创建,当它们执行完成后,变量将会被销毁;
- 访问修饰符不能用于局部变量,即不能被
public、private、protected修饰; - 局部变量只在声明它的方法、构造方法或者语句块中可见;
- 局部变量是在栈上分配的。
- 局部变量没有默认值,所以局部变量被声明后,必须经过初始化,才可以使用。
- 局部变量是指在方法或者方法代码块中定义的变量,其作用域是其所在的代码块。可分为以下三种:
- 方法参数变量(
形参):在整个方法内有效。 - 方法局部变量(
方法内定义): 从定义这个变量开始到方法结束这一段时间内有效。 - 代码块局部变量(
代码块内定义):从定义这个变量开始到代码块结束这一段时间内有效。
- 方法参数变量(
VI、Java 常量
\quad
Java 常量是指在程序的整个运行过程中值保持不变的量,即常量在程序中只能被引用,而不能被重新赋值。常量需要初始化,即在声明常量的同时要赋予一个初始值。常量一旦初始化就不可以被修改。常量的语法格式和变量类型,只需要在变量的语法格式前面添加关键字 final 即可,其语法格式:final type CONST = value [, const = value ...];。final 关键字表示最终的,修饰变量就变成了常量。final 关键字不仅可以用来修饰基本数据类型的常量,还可以用来修饰对象的引用或者方法。在 Java 编码规范中,要求常量名必须大写。Java 常量的使用一般有三种方式:
6.1 interface 中定义常量
\quad
Java interface 中声明的字段在编译时会自动加上 public static final 修饰符,即声明为常量。使用方法一般是“接口.常量名”。
不推荐这种方式:
Java中设计出接口这种语法,就是为了用来实现或者继承的,如果我们在实现类或者子类接口中定义了同名的常量,那么子类接口或者实现类引用同名的常量,就可能不一致。是不是很容易造成混乱。- 与接口的定义不相符,接口是一种规范,一种协议规定,主要用来定义必须要实现的API。用接口来定义常量,与创造接口的目的不相符。
- 在
interface中定义的常量属于编译型常量,每次更改常量值,都要重新编译所有引用到它的类。
6.2 Class 中定义常量
public class ConstantClassField {
public static final String SUNDAY = "SUNDAY";
public static final String MONDAY = "MONDAY";
public static final String TUESDAY = "TUESDAY";
public static final String WEDNESDAY = "WEDNESDAY";
public static final String THURSDAY = "THURSDAY";
public static final String FRIDAY = "FRIDAY";
public static final String SATURDAY = "SATURDAY";
}
6.3 enum 定义常量
\quad
Java 枚举是一个特殊的类,一般表示一组常量。class 使用 enum 关键字来定义,各个常量之间使用逗号 , 来分割。
6.4 通过配置文件来配置常量
\quad 后续详解
VII、命名规范
- 所有变量、常量、方法和类名: 见 名 知 意 \pmb{见名知意} 见名知意见名知意见名知意
- 项目名:全部小写,多个单词用中划线 “
-” 连接。 - 包名:全部小写,点分隔符之间有且仅有一个自然语义的英文单词或者多个单词自然连接(例:
springframework,deepspace不需要使用任何分割)。 - 类成员变量:首字母小写和驼峰原则。例:
monthSalary - 局部变量:首字母小写和驼峰原则。例:
weekSalary - 常量:大写字母和下划线。例:
MAX_VALUE - 类名:首字母大写和驼峰原则。例:
HelloWorld - 方法名:首字母小写和驼峰原则。例:
runProcedure()
VIII、运算符
\quad
Java 语言支持如下运算符:
-
算数运算符:
+,-,*,/,%(模运算,取余),++,--+:正号、加法运算、字符串拼接。++:(前)自增、(后)自增。--:(前)自减、(后)自减。/:整型(byte,short,int)数值相除之后为int类型,赋给float/double类型变量也只是在整数末尾加.0。%:运算结果的符号与被模数的符号相同。例:-12 % 5的结果为-2。
-
赋值运算符:
=,当=两侧数据类型不一致时,可以自动类型转换或使用强制类型转换。支持连续赋值。 -
比较运算符(关系运算符):
>,<,>=,<=,==,!=,instanceof(后续多态部分详解)。返回布尔值。==和!=不仅可以使用在数值类型数据之间,还可以使用在其他引用类型变量之间。
-
逻辑运算符:逻辑与
&, 短路与&&、逻辑或|, 短路或||、逻辑非!、逻辑异或^。返回布尔值。 -
位运算符:
&,|,^,~,>>,<<,>>> -
条件运算符:
? : -
复合赋值运算符:
+=,-=,*=,/=,%=,不会改变变量本身的数据类型,即不会发生自动类型转换。- 示例:
int i = 10; i *= 1.234; System.out.println(i);//编译通过,正常运行,输出12
- 示例:
-
字符串连接符:
+
8.1 自增、自减
\quad
自增(++)和自减(--)运算符是对变量在原始值的基础上进行加 1 或减 1 的操作,且不会改变变量原有的数据类型。它们都有前缀和后缀两种形式。前缀形式的运算规则可以概括为:”先自增(减),后引用",而后缀形式的运算规则可以概括为:”先引用,后自增(减)“。这里所说的 ”引用”,指的是使用变量的值。另外,我们还要强调一个细节:无论是前缀形式还是后缀形式,自增自减运算符的优先级要高于赋值运算符。
8.1.1 语句中仅有++或- -
- 代码
1:输出结果是11,其中第2条语句中仅有一个后缀形式++操作。int a = 10; a++; System.out.println(a); - 代码
2:输出结果同样是11,与之前的那段代码基本一样,只是第2条语句中,后缀形式的++操作被换成了前缀形式。int a = 10; ++a; System.out.println(a);
\quad
这说明:当一条语句中仅有++或--操作时,前缀形式与后缀形式的运算符没有任何区别。
8.1.2 ++或- -运算结果赋值给其他变量
-
代码
1:第2条语句对变量a进行了自增操作,并且把这个操作结果赋值给另一个变量b。int a = 2; int b = ++a; System.out.println(a); System.out.println(b);\quad 语句中的变量
b就是标题中所说的 “其他变量”,是指没有进行自增自减操作的其他变量。为什么要强调 ”赋值给其他变量” 这个前提呢?就是因为如果把运算结果赋值给变量a自身,又会产生不同的效果,我们后面再去讲解赋值给自身的情况。现在先来分析程序,重点看第2条语句:变量a所进行的是前缀形式的自增操作,那么按照 ”先自增后引用” 的运算规则,a的值首先变成3,然后赋值b。因此,给变量b赋值的是3,那么输出结果就是3和3。
\quad -
代码
2:第2条语句发生了变化,变量a的自增操作变成了后缀形式。int a = 2; int b = a++; System.out.println(a); System.out.println(b);\quad 此时的程序输出结果是
3和2。为什么会是这样的运行结果呢?网上有很多资料对此的解释是:因为表达式中出现的是后缀形式的自增操作,因此,计算机会先使用a的值给b赋值,a的值是2,所以b被赋值为2,a在完成给b赋值的操作之后,才会完成自增变为3,所以程序的输出结果为3和2。这种解释看似非常合理,但其实是错误的!\quad 按照这种解释,后缀形式的自增是在赋值之后才完成的,由此可以推出后缀形式的自增自减运算的优先级比赋值运算的优先级更低。而我们之前已经特意强调过:无论是前缀形式还是后缀形式,自增自减运算符的优先级都比赋值运算符要高。接下来问题来了:既然
++和--的运算优先级高于赋值运算符,那么为什么赋值之前a的值没有自增为3呢?\quad 其实这是个错觉!
a在赋值给变量b之前,就已经完成了自增。为了讲解清楚真实情况,我们必须科普一个小常识,那就是:程序中如果用到某个变量的值,都会先把这个变量存入一个临时的空间,专业上把这个临时的空间称之为 “操作数栈”。我们之前所说的 ”先引用后自增” 中所说的这个 ”引用” 操作,其实就是指 ”把变量的值存入操作数栈” 这个动作。当程序中需要用到变量的值,计算机是从 ”操作数栈” 中取出值进行运算,并不是我们想象的直接从变量所在的内存单元中取出数值。但是,如果语句中仅有++或--,并不会把变量的值存入操作数栈,而是直接对变量进行自增或自减的操作,这也是为什么我们把语句中仅有++或--单独作为一种情况讲解的原因。\quad 科普完这个小常识之后,我们来解释刚才的代码为什么会输出
3和2。代码中出现用a的值给变量b赋值的语句,并且a的后面出现了++,说明要对a进行后缀形式的自增操作。按照我们刚才科普的小常识,a参与了赋值运算,那么就会把a的值存入操作数栈。因为a的自增是后缀形式的,所以要遵循 “先引用后自增” 的运算规则,因此,计算机会首先取出a的值2存入操作数栈,然后再把a的值增加到3。做完自增操作之后,接下来会对变量b进行赋值操作。那么,是用哪个值给变量b赋值呢?就是用刚才存到操作数栈中的那个2对变量b进行赋值,所以b最终得到的值是2,因此输出结果是3和2。\quad 在这里,请大家注意一个细节,那就是:代码中的自增操作虽然是后缀形式的,但这个自增动作却是在赋值之前完成的,这也解释了后缀形式的自增运算优先级高于赋值运算,而网上很多资料中所说的”先完成赋值再去做自增操作”是完全错误的。
-
代码
1中第2条语句是 “b=++a;”,会不会也把a的值存入操作数栈呢?
\quad 答案是肯定的,只要是变量参了算术运算、赋值、被打印这些操作,都会取出变量的值存入操作数栈。因为语句中出现的是前缀形式的自增,所以在把值存入操作数栈之前就已经完成了自增操作。
8.1.3 ++ 或 - - 运算结果赋值给自身
-
代码
1:第2条语句,对a进行了前缀形式的自增,然后又赋值给a自身。int a = 2; a = ++a; System.out.println(a);\quad 因为
a进行的是前缀形式的自增,所以运算规则是 “先自增后引用”,自增之后a的值变成了3,把3存入操作数栈,之后以3赋值给a,所以a的值还是3。 -
代码
2:第2条语句将++a改成了a++。这种情况下,程序输出a的值为2,而不是3。int a = 2; a = a++; System.out.println(a);\quad
a并没有按我们的想象实现自增,这是为什么呢?我们来分析一下整个运算的过程:计算机看到 ”=” 右边是后缀形式的自增,因此以 ”先引用后自增” 的规则进行运算,先把a的值存入操作数栈,紧接着对a进行自增操作,a的值变成了3,最后又用操作数栈中的2对a进行赋值,a的值又变成了2。这样给我们造成了一种a没有进行自增的错觉。 -
代码
3:第2条语句对a进行了两次自增操作,最终输出a的值是6。int a = 2; a = ++a + a++; System.out.println(a);\quad
+加号左边运算规则是 “先自增后引用”,自增之后a的值变成了3,把3存入操作数栈1,+加号右边以 ”先引用后自增” 的规则进行运算,先把a的值存入操作数栈2,这时放入操作数栈2的值是3,紧接着对a进行自增操作,,然后对操作数栈1和操作数栈2的数值进行加法运算,即3+3,然后执行赋值运算,将6赋给a。 -
接下来,我们再来研究一种更特殊的情况,请看代码
4:这一次,语句中出现了复合赋值运算符。int a = 2; a += ++a + ++a; System.out.println(a);\quad 我们首先可以推导出 ”
+=” 右边的运算结果是7。我们还知道,复合赋值运算符在完成运算的时候,要把右边当作整体。那么现在关键的问题就只剩一个了,那就是:”+=” 左边的a到底是多少?很多人认为 ”+=” 左边a的值应该是4,原因是++的运算优先级高于+=,所以要先完成2次自增,完成了2次自增以后,a的值已经变成了4,由此推得 ”+=” 左边a的值应该是4,而最终的运算结果是11(4+7的和)。但实际运行程序的话,可以看到输出a的值为9而非11。这是为什么呢?就是因为+=的优先级虽然低于++,但是计算机在实际完成+=运算的时候会分为好几个步骤进行。我们可以大致把+=运算分解为四大步骤:- 把
+=左边的变量值存入操作数栈1。 - 计算
+=右边的表达式,并把计算结果存入操作数栈2(此步骤其实是由多个具体步骤组成的)。 - 把
操作数栈1和操作数栈2中的数值相加得到运算结果。 - 把运算结果存入变量
a当中。
\quad 现在最关键的问题是步骤
1和2哪一个先被执行。如果先执行步骤1,那么存入操作数栈1的是变量a自增之前的值,也就是2;反之,如果先执行步骤2,那么存入操作数栈1的是变量a自增之后的值,也就是4。真实的情况是先执行步骤1,也就是把变量a自增之前的值存入操作数栈1。这是一个普遍适用的规律,所以大家一定要记住:当语句中以复合赋值运算符给变量赋值的时候,计算机会先把复合赋值运算符左边变量的值存入操作数栈。因此,这段程序运行的结果是9。 - 把
-
开发中实现
int类型变量+2的方式:- 方式
1:num=num+2; - 方式
2:num+=2;(推荐)
- 方式
-
开发中实现
int类型变量+1的方式:- 方式
1:num=num+1; - 方式
2:num+=1; - 方式
3:num++;(推荐)
- 方式
8.2 逻辑运算符
- 逻辑运算符操作的都是
boolean类型的变量,返回的结果也是boolean类型的值。 - 短路与 “
&&” 的前后两个操作数必须都是true才返回true,否则返回false。 - 短路或 “
||” 只要两个操作数中有一个是true,就返回true,否则返回false。 - 短路与“
&&”和短路或“||”被称作短路运算符。短路与(&&)和短路或(||)能够采用最优化的计算方式,从而提高效率。 - 短路与 “
&&” 检查第一个表达式是否返回 “false”,如果是 “false” 则结果必为 “false”,不再检查其他内容。 - 短路或 “
||” 检查第一个表达式是否返回 “true”,如果是 “true” 则结果必为 “true”,不再检查其他内容。 - 两种与(
&&和&)的运算规则基本相同,两种或(||和|)的运算规则也基本相同。其区别是:&和|运算是把逻辑表达式全部计算完,而&&和||运算具有短路计算功能。开发中推荐使用&&和||。&与&&的运算结果相同。当符号左边是true时,二者都会执行符号右边的运算;当符号左边是false时,&继续执行符号右边的运算,&&不再执行符号右边的运算。|与||的运算结果相同。当符号左边是false时,二者都会执行符号右边的运算;当符号左边是true时,|继续执行符号右边的运算,||不再执行符号右边的运算。- 例题:程序输出结果
public class Operator{ public static void main(String[] args){ boolean x = true; boolean y = false; short z = 40; if ((z++ == 40) && (y = true)){ z++; } if ((x = false) || (++z == 43)){ z++; } System.out.println("z = " + z); // z = 44 } }
- 逻辑非 “
!“ 就是指本来值的反值。 - 逻辑异或 “
^“ 的前后两个操作数的值不相同,则异或结果为true,反之异或结果为false。
| a | b | a & b | a && b | a | b | a || b | !a | a ^ b |
|---|---|---|---|---|---|---|---|
| true | true | true | true | true | true | false | false |
| true | false | false | false | true | true | false | true |
| false | true | false | false | true | true | true | true |
| false | false | false | false | false | false | true | false |
8.3 位运算符
-
位运算是直接对整数的二进制进行的运算,即位运算符操作的都是整型的数据。
-
按位与
&:如果相对应二进制位都是1,则结果为1,否则为0。 -
按位或
|:如果相对应二进制位都是0,则结果为0,否则为1。 -
按位异或
^:如果相对应二进制位值相同,则结果为0,否则为1。a==(a^b)^a,可用于交换两个变量的值,示例如下:int num1; int num2; num1 = num1 ^ num2; num2 = num1 ^ num2;// (num1 ^ num2) ^ num2得到num1,赋值给num2 num1 = num1 ^ num2;// (num1 ^ num2) ^ num2,这里的num2已经替换为num1,所以左式得到原有num2,赋值给num1
-
按位取反
~:按位取反运算符翻转二进制操作数的每一位(包含符号位),即0变成1,1变成0。负数操作的是补码。 -
按位左移
<<:按位左移运算符。左操作数按位左移右操作数指定的位数,即丢弃左边指定位数,右边补0。在一定范围内(不影响数字原二进制位),左操作数左移n位相当于乘2的n次幂( 2 n 2^n 2n)。- 当
int类型进行左移操作时,左移位数大于等于32位操作时,会先求余(%)后再进行左移操作。也就是说左移32位相当于不进行移位操作,左移40位相当于左移8位(40 % 32 = 8)。当long类型进行左移操作时,long类型在二进制中的体现是64位的,因此求余操作的基数也变成了64,也就是说左移64位相当于没有移位,左移72位相当于左移8位(72 % 64 = 8)。 - 由于
double,float在二进制中的表现比较特殊,因此不能来进行移位操作。 - 其它几种整型
byte,short移位前会先转换为int类型(32位)再进行移位。
- 当
-
按位右移
>>:按位右移运算符。左操作数按位右移右操作数指定的位数,即丢弃右边指定位数,左边补上符号位(正数符号位0,负数符号位1)。正数左操作数右移n位相当于除2的n次幂( 2 n 2^n 2n),然后取整。负数操作的是补码。- 和左移一样,
int类型移位大于等于32位时,long类型大于等于64位时,会先做求余处理再位移处理,byte,short移位前会先转换为int类型(32位)再进行移位。
- 和左移一样,
-
无符号右移
>>>:无符号右移即按位右移补零操作符。左操作数的值按右操作数指定的位数右移,移动得到的左边空位以零填充。
8.4 条件运算符
Java提供了一个特别的三元运算符(也叫三目运算符)经常用于取代某个类型的if-then-else语句。- 条件运算符是
Java唯一的三目运算符,表示为 “? :”,使用该运算符时需要有三个操作数,因此称其为三目运算符。使用条件运算符的一般语法结构为:result = <expression> ? <statement1> : <statement2>;- 其中,
expression是一个布尔表达式。当expression为真时,执行statement1, 否则就执行statement2。statement1和statement2返回的值的数据类型要能够统一,可以被result接收。
- 其中,
- 三元运算符可以进行嵌套,结构为:
result = <expression1> ? <statement1> : <expression1> ? <statement2> : <statement3>; - 凡是可以使用三元运算符的地方,都可以改写为
if-else,反之不成立。 - 如果程序既可以使用三元运算符,又可以使用
if-else结构,那么优先选择三元运算符。原因:简介、执行效率高。
IX、Java 包机制
\quad
为了更好地组织类,Java 提供了包机制,用于区别类名的命名空间。
9.1 包的作用
- 把功能相似或相关的类或接口组织在同一个包中,方便类的查找和使用。
- 如同文件夹一样,包也采用了树形目录的存储方式。同一个包中的类名字是不同的,不同的包中的类的名字是可以相同的,当同时调用两个不同包中相同类名的类时,应该加上包名加以区别。因此,包可以避免名字冲突。
- 包也限定了访问权限,拥有包访问权限的类才能访问某个包中的类。
9.2 创建包
- 包声明应该在源文件的第一行,每个源文件只能有一个包声明,这个文件中的每个类型都应用于它。包声明的语法格式为:
package pkg1[.pkg2[.pkg3...]]; - 创建包的时候,你需要为这个包取一个合适的名字(
一般利用公司域名倒置作为包名)。之后,如果其他的一个源文件包含了这个包提供的类、接口、枚举或者注释类型的时候,都必须将这个包的声明放在这个源文件的开头。 - 如果一个源文件中没有使用包声明,那么其中的类,函数,枚举,注释等将被放在一个无名的包(
unnamed package)中。
9.3 import 关键字
\quad
为了能够使用某一个包的成员,我们需要在 Java 程序中明确导入该包。使用 “import” 语句可完成此功能。在 Java 源文件中 import 语句应位于 package 语句之后,所有类的定义之前,可以没有,也可以有多条,其语法格式为:
import package1[.package2 ...].(classname|*); // *表示导入包下所有的类
如果在一个包中,一个类想要使用本包中的另一个类,那么该包名可以省略。
X、JavaDoc 文档生成器
10.1 文档注释概览
\quad
“文档注释”(Java Doc Comments)是专门为了用 javadoc 工具自动生成文档而写的注释,它是一种带有特殊功能的注释。
\quad
文档注释与一般注释的最大区别在于起始符号是 “/**” 而不是 “/*” 或 “//”。
/**
* 这是文档注释
*/
/*
* 这是一般注释
*/
// 这是一般注释
\quad
在一些IDE(比如 Eclipse)中,文档注释会以不同于普通注释的颜色高亮显示。
\quad
此外,文档注释只负责描述类(class)、接口(interface)、方法(method)、构造器(constructor)、成员字段(field)。相应地,文档注释必须写在类、接口、方法、构造器、成员字段前面,而写在其他位置,比如函数内部,是无效的文档注释。
\quad
文档注释采用 HTML 语法规则书写,支持 HTML 标记(tag),同时也有一些额外的辅助标记。需要注意的是,这些标记不是给人看的(通常他们的可读性也不好),他们的作用是为了 javadoc 工具更好地生成最终文档。所以,虽然有些标记写起来麻烦且看着不直观,还是要老老实实按规矩写滴。
10.2 文档注释的基本内容
\quad 一个文档注释由两部分组成:
/**
* 描述部分(description)
*
* 标记部分(block tags)
*/
10.2 描述部分(Description)
\quad
描述部分的第一行应该是一句对类、接口、方法等的简单描述,这句话最后会被 javadoc 工具提取并放在索引目录中。
\quad
怎么界定第一句话到哪结束了呢?答案是跟在第一个句号(英文标点)之后的 tab、空行或行终结符规定了第一句的结尾。
\quad
除了普通的文本之外,描述部分可以使用:
\qquad
1. HTML语法标签,例: <b>xxx</b>
\qquad
2. javadoc 规定的特殊标签,例:{@link xxx}。标签的语法规则是:{@标签名 标签内容}。
需要注意的地方:
- 特殊标签在由
javadoc工具生成文档时会转化成特殊的内容,例:{@link URL}会被转化成指向URL类的超链接。 - 如果注释包含多段内容,段与段之间需要用
<p>分隔,空行是没用的。 - 最后结尾行 “
*/” 和起始行不同,这里只有一个星号。 - 为了避免一行过长影响阅读效果,务必将每行的长度限制在
80个字符以内。 - 善用
javadoc工具的复制机制避免不必要的注释: 如果一个方法覆盖了父类的方法或实现了接口的方法,那么javadoc工具会在该注释里添加指向原始方法的链接,此外如果新方法没有注释,那么javadoc会把原始方法的注释复制一份作为其注释,但是如果新方法有注释了,就不会复制了。
注释风格:
- 使用
<code>关键字</code>来强调关键字,建议强调的内容有:java关键字、包名、类名、方法名、接口名、字段名、参数名等。 - 控制
{@link xxx}的数量,太多的链接会使文档的可读性很差,因为读者总是跳来跳去;不要出现相同的链接,同样的链接只保留第一个;不要为java自带的内容或是常识性的内容提供链接。 - 描述一个方法时,应当只保留方法名字,不要附带方法的参数。比如有个方法是
add(Object obj),那么用add指代该方法即可,而不是add(Object obj)。 - 英文注释可以是短语也可以是句子。如果是句子,首字母要大写;如果是短语,首字母小写。
- 英文注释使用第三人称,而不是第二人称。比如:
/**
* Gets the label(建议)
*/
/**
* Get the label(不建议)
*/
- 方法的注释应该以动词或动词词组开头,因为方法是一个动作。比如:
/**
* Gets the label of this button(建议)
*/
/**
* This method gets the label(不建议)
*/
- 当描述类、接口、方法这类的概念时,可以不用指名 “类”、“接口”、“方法” 这些词语,比如:
/**
* A button label(建议)
*/
/**
* This field is a button label(不建议)
*/
- 英文使用
this而不是the指代当前类,比如:
/**
* Gets the toolkit for this component (建议)
*/
/**
* Gets the toolkit for the component (不建议)
*/
API名应该是能够简单自我说明的,如果文档注释只是简单重复API的名称还不如没有文档,所以文档注释应该至少提供一些额外信息,否则干脆不要注释。- 英文注释避免拉丁风格的缩写。比如使用 “
also knwon as” 而不是 “aka”,使用 “that is” 或 “to be specific” 而不是 “i.e.”,使用 “for example” 而不是 “e.g.”,使用 “in other words” 或 “namely” 而不是 “viz.”。
10.3 标记部分(Tag)
\quad 标记部分跟在描述部分之后,且前面必须有一个空行间隔。
常见标记:
@author:标记作者。如果一个文件有多个作者来维护就标记多个@author,@author后面可以跟作者姓名(也可以附带邮箱地址)、组织名称(也可以附带组织官网地址)。
/**
* @author Rod Johnson // 纯文本作者
* @author Igor Hersht, igorh@ca.ibm.com // 纯文本作者,邮件
* @author <a href="mailto:ovidiu@cup.hp.com">Ovidiu Predescu</a> // 超链接邮件 纯文本作者
* @author shane_curcuru@us.ibm.com // 纯文本邮件
* @author Apache Software Foundation // 纯文本 组织
* @author <a href="https://jakarta.apache.org/turbine"> Apache Jakarta Turbine</a> // 超链接组织地址 纯文本组织
*/
@version:用于标记当前版本,默认为1.0。
/**
* @version 1.0
*/
@since:描述此功能首次存在的时间或版本号。
/**
* @since 1.8
* @since 16 April 2021
*/
@param:说明一个方法的参数,后面跟参数名,再跟参数描述。
/**
* @param str the {@code CharSequence} to check (may be {@code null})
*/
@return:返回值描述,如果方法没有返回值就不要写@return。
/**
* @return {@code true} if the {@code String} is not {@code null}
*/
public static boolean hasText(@Nullable String str){}
{@code}:将文本标记为code,{@code text}会被解析成<code> text </code>。@deprecated:表示此方法已废弃、暂时可用,但以后此类或方法都不会再更新、后期可能会删除,建议后来人不要调用此方法。通常在给定此注解后,应该在文档注释中告诉用户这个API被哪个新方法替代了,随后用@see标记或{@link}标记指向新API。
/**
* @deprecated As of JDK 1.1, replaced by {@link #setBounds(int,int,int,int)}
*/
@see:插入一个指向其他类或者方法的链接。既可以用来类上也可以用在方法上,表示可以参考的类或者方法。@see标记必须在一行的开头书写。
/**
* @see java.net.URLDecoder#decode(String, String)
*/
public static String uriDecode(String source, Charset charset){}
{@link}:插入一个指向其他类或者方法的链接。使用语法:{@link 包名.类名#方法名(参数类型)},其中当包名在当前类中已经导入了包名可以省略,可以只是一个类名,也可以是仅仅是一个方法名,也可以是类名#方法名。使用此文档标记的类或者方法,可用通过按住 “Ctrl键+单击” 快速跳到相应的类或者方法上。@link标记可以写在一行中的任意位置。
{@link java.lang.Character} // 完全限定的类名
{@link String} // 省略包名
{@link #length()} // 省略类名,表示指向当前的某个方法
{@link java.lang.String#charAt(int)} // 包名.类名.方法名(参数类型)
{@linkplain}:与{@link}相同,除了链接的标签以纯文本显示,而不是以代码字体显示,当标签是纯文本时很有用。{@docRoot}:指定当前文档的根目录路径。{@value}:显示常量的值,该常量必须是static属性。
/**
* 默认数量 {@value}
* */
private static final Integer QUANTITY = 1;
@exception、@throws:描述可能从此方法抛出的异常。
10.4 生成 JavaDoc 文档
10.4.1 控制台
- 用法:
javadoc [options] [packagenames] [sourcefiles] [@files]
10.4.2 Intellij IDEA
- 第一步:在工具栏中找到
Tools,然后再子菜单中找到Generate JavaDoc...。

- 第二步:在生成文档上进行配置。

参数介绍:public:仅显示public类和成员。protected:显示protected/public类和成员(默认值)。package:显示package/protected/public类和成员。private:显示所有类和成员。

被折叠的 条评论
为什么被折叠?



