Java | Java 基础语法
三、类型转换
\quad
由于 Java
是一种强类型语言,所以在进行某些运算的时候,需要用到类型转换。所谓类型转换即将某一个数据的数据类型通过某种方式转换为另一种所需的数据类型。在运算中,不同类型的数据需要先转化为同一类型,然后才能进行运算。数据类型的转换可以分为隐式转换(自动类型转换
)和显式转换(强制类型转换
)两种。
3.1 隐式转换(自动类型转换)
\quad 在运算中,如果满足:(1)两种数据类型彼此兼容;(2)目标数据类型的取值范围大于源数据类型,那么将执行自动类型转换。自动类型转换主要表达数字表示范围小的数据类型可以自动转换成范围大的数据类型。
\quad 在运算过程中,由于不同的数据类型会转换成同一种数据类型,所以整型、浮点型以及字符型都可以参与混合运算。自动类型转换的规则是从低级类型数据转换成高级类型数据。转换规则如下:
- 数值型数据的转换:
byte
→short
→int
→long
→float
→double
; - 字符型转换为整型:
char
→int
。
注:
\quad 1.char
类型比较特殊,char
可以自动转换成int
、long
、float
和double
,但byte
和short
不能自动转换为char
,而且char
也不能自动转换为byte
或short
。
\quad 2.byte
,short
参与运算会自动转换为int
类型。
3.2 显式转换(强制类型转换)
\quad
当两种数据类型不兼容,或目标数据类型的取值范围小于源数据类型时,自动转换将无法进行,这时就需要进行强制类型转换。强制数据类型转换是手动将较大范围数据转换较小数据类型,格式是在需要进行类型转换的数据前加上“( )
”,然后在括号内加入需要转化的数据类型。有的数据经过转型运算后,精度会丢失,而有的会更加精确。
\quad 在强制类型转换中,如果是将浮点类型的值转换为整数,直接去掉小数点后边的所有数字;而如果是整数类型强制转换为浮点类型时,将在小数点后面补零。
\quad 注:不能对布尔值进行强制类型转换;转换过程中可能发生内存溢出或精度问题。
四、Java 变量
\quad
Java
程序在运行期间会产生一些临时数据,应用程序会将这些数据保存在一些内存单元中,每个内存单元都会使用一个标识符进行标识。这些内存单元被称为变量,进行标识的标识符就是变量名,内存单元中存储的数据就是变量的值。
\quad
Java
是一种强类型语言,每个变量都必须声明其数据类型,数据类型可以是基本数据类型,也可以是引用数据类型。Java
变量是程序中最基本的存储单元,其要素包括变量名,变量类型和作用域。在 Java
语言中,所有的变量在使用前必须声明。声明变量的基本格式:type identifier [= value][, identifier [= value] ...];
,其中 type
为 Java
数据类型,identifier
是变量名,同时可以使用逗号分割来声明多个同类型变量(不建议同一行声明多个变量
)。要注意变量声明是一条完整的语句,因此每一个声明都必须以分号结束。
\quad
Java
程序在声明变量的同时可以为变量赋值,也可以声明以后再赋值。例:int a=1; // 声明时赋值
、int a; a=1; // 声明后赋值
。没有赋值的变量,系统将按下列默认值进行初始化:
数据类型 | 初始值 | 数据类型 | 初始值 | 数据类型 | 初始值 |
---|---|---|---|---|---|
byte | 0 | short | 0 | int | 0 |
long | 0L | char | ‘\u0000’ | float | 0.0f |
double | 0.0 | boolean | false | 所有引用类型 | null(不引用任何对象) |
\quad
Java
变量作用域是指变量能生效的区域范围,变量只有在作用域范围内才能够被使用。声明在不同地方的变量具有不同的作用域,而决定作用域范围的就是花括号的位置。根据作用域的不同,一般将变量分为成员变量和局部变量。
4.1 全局变量
\quad
Java
的成员变量有两种,分别是成员变量(实例变量)和静态变量(类变量)。成员变量声明在一个类中,但在方法、构造方法和语句块之外,不属于任何一个方法,作用域是整个类。
成员变量类型 | 修饰 | 访问 | 生命周期 |
---|---|---|---|
成员变量(实例变量) | 无static 修饰 |
对象名. 变量名 |
在对象创建的时候创建,在对象被销毁的时候销毁。 |
静态变量(类变量) |
static 修饰 |
类名. 变量名
/
/
/ 对象名. 变量名 |
其生命周期取决于类的生命周期。在类第一次被访问时创建,在类被垃圾回收机制彻底回收时才会被销毁。 |
4.1.1 成员变量(实例变量)
- 实例变量声明在一个类中,但在方法、构造方法和语句块之外;
- 当一个对象被实例化之后,每个实例变量的值就跟着确定;
- 注:对象实例化过程:当一个对象被创建之后,虚拟机会为其分配内存,主要用来存放对象的实例变量及其从超类继承过来的实例变量,在为这些实例变量分配内存的同时,这些实例变量也会被赋予默认值。 即使我们没通过构造函数传递参数,或者
set
指令传递参数,那么实例变量也会被赋予默认初始值。
- 注:对象实例化过程:当一个对象被创建之后,虚拟机会为其分配内存,主要用来存放对象的实例变量及其从超类继承过来的实例变量,在为这些实例变量分配内存的同时,这些实例变量也会被赋予默认值。 即使我们没通过构造函数传递参数,或者
- 实例变量在对象创建的时候创建,在对象被销毁的时候销毁;
- 实例变量的值应该至少被一个方法、构造方法或者语句块引用,使得在类的外部能够通过这些方式获取实例变量信息;
- 实例变量的声明可以在使用前或者使用后;
- 因为实例变量伴随对象的创建而实例化,所以总是在第一步执行,无所谓写在前后。
- 访问修饰符(后续讲解)可以修饰实例变量;
- 实例变量对于类中的方法、构造方法或者语句块是可见的。一般情况下应该把实例变量设为私有(private,
只能在本类中访问到,在其他的类中无法进行访问
)。通过使用访问修饰符可以使实例变量对子类可见; - 实例变量具有默认值。数值型变量的默认值是
0
,布尔型变量的默认值是false
,引用类型变量的默认值是null
。变量的值可以在声明时指定,也可以在构造方法中指定; - 实例变量可以直接通过变量名访问。但在静态方法以及其他类中,就应该使用完全限定名:
ObejectReference.VariableName
,即对象名.
变量名。
4.1.2 静态变量(类变量)
- 类变量也称为静态变量,在类中以
static
关键字声明,但必须在方法之外。 - 无论一个类创建了多少个对象,类只拥有类变量的一份拷贝,即任意对象修改了静态变量,都会影响到所有对象。
- 静态变量会在对象实例化的时候在堆内存中单独创建一片固定的内存,即所有对象实例的静态变量值都指向这片区域。
- 静态变量除了被声明为常量外很少使用,静态常量是指声明为
public/private final static
类型的一种常量。静态常量初始化后不可改变。 - 静态变量储存在静态存储区。经常被声明为常量,很少单独使用
static
声明变量。 - 静态变量在第一次被访问时创建,在程序结束时销毁。
- 静态变量与实例变量具有相似的可见性。但为了对类的使用者可见,大多数静态变量声明为
public
类型。 - 静态变量默认值和实例变量相似。数值型变量默认值是
0
,布尔型默认值是false
,引用类型默认值是null
。变量的值可以在声明的时候指定,也可以在构造方法中指定。此外,静态变量还可以在静态语句块中初始化。 - 静态变量可以通过:
ClassName.VariableName
,即类名.
变量名的方式访问。 - 静态变量可以在自身所在的类内部被直接访问。
- 静态变量被声明为
public static final
类型时, 静态变量名称一般建议使用大写字母。如果静态变量不是public
和final
类型,其命名方式与实例变量以及局部变量的命名方式一致。
4.2 局部变量
- 局部变量声明在方法、构造方法或者语句块中;
- 局部变量在方法、构造方法、或者语句块被执行的时候创建,当它们执行完成后,变量将会被销毁;
-
访问修饰符不能用于局部变量,即不能被
public、private、protected
修饰; - 局部变量只在声明它的方法、构造方法或者语句块中可见;
- 局部变量是在栈上分配的。
- 局部变量没有默认值,所以局部变量被声明后,必须经过初始化,才可以使用。
- 局部变量是指在方法或者方法代码块中定义的变量,其作用域是其所在的代码块。可分为以下三种:
-
方法参数变量(
形参
):在整个方法内有效。 -
方法局部变量(
方法内定义
): 从定义这个变量开始到方法结束这一段时间内有效。 -
代码块局部变量(
代码块内定义
):从定义这个变量开始到代码块结束这一段时间内有效。
-
方法参数变量(
五、Java 常量
\quad
Java
常量是指在程序的整个运行过程中值保持不变的量,即常量在程序中只能被引用,而不能被重新赋值。常量需要初始化,即在声明常量的同时要赋予一个初始值。常量一旦初始化就不可以被修改。常量的语法格式和变量类型,只需要在变量的语法格式前面添加关键字 final
即可,其语法格式:final type CONST = value [, const = value ...];
。final
关键字表示最终的,修饰变量就变成了常量。final
关键字不仅可以用来修饰基本数据类型的常量,还可以用来修饰对象的引用或者方法。在 Java
编码规范中,要求常量名必须大写。Java
常量的使用一般有三种方式:
5.1 interface 中定义常量
\quad
Java interface
中声明的字段在编译时会自动加上 public static final
修饰符,即声明为常量。使用方法一般是“接口.
常量名”。
不推荐这种方式:
-
Java
中设计出接口这种语法,就是为了用来实现或者继承的,如果我们在实现类或者子类接口中定义了同名的常量,那么子类接口或者实现类引用同名的常量,就可能不一致。是不是很容易造成混乱。 - 与接口的定义不相符,接口是一种规范,一种协议规定,主要用来定义必须要实现的API。用接口来定义常量,与创造接口的目的不相符。
- 在
interface
中定义的常量属于编译型常量,每次更改常量值,都要重新编译所有引用到它的类。
5.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";
}
5.3 enum 定义常量
\quad
Java
枚举是一个特殊的类,一般表示一组常量。class
使用 enum
关键字来定义,各个常量之间使用逗号 ,
来分割。
5.4 通过配置文件来配置常量
\quad 后续详解
六、命名规范
- 所有变量、常量、方法和类名: 见 名 知 意 \pmb{见名知意} 见名知意见名知意见名知意
-
项目名:全部小写,多个单词用中划线 “
-
” 连接。 -
包名:全部小写,点分隔符之间有且仅有一个自然语义的英文单词或者多个单词自然连接(例:
springframework
,deepspace
不需要使用任何分割)。 -
类成员变量:首字母小写和驼峰原则。例:
monthSalary
-
局部变量:首字母小写和驼峰原则。例:
weekSalary
-
常量:大写字母和下划线。例:
MAX_VALUE
-
类名:首字母大写和驼峰原则。例:
HelloWorld
-
方法名:首字母小写和驼峰原则。例:
runProcedure()
七、运算符
\quad
Java
语言支持如下运算符:
- 算数运算符:
+
,-
,*
,/
,%(模运算,取余)
,++
,--
- 赋值运算符:
=
- 关系运算符:
>
,<
,>=
,<=
,==
,!=
,instanceof(后续详解)
,返回布尔值。 - 逻辑运算符:
&&
,||
,!
,返回布尔值。 - 位运算符:
&
,|
,^
,~
,>>
,<<
,>>>
- 条件运算符:
? :
- 复合赋值运算符:
+=
,-=
,*=
,/=
- 字符串连接符:
+
7.1 自增、自减
\quad
自增(++
)和自减(--
)运算符是对变量在原始值的基础上进行加 1
或减 1
的操作。它们都有前缀和后缀两种形式。前缀形式的运算规则可以概括为:”先自增(减),后引用
",而后缀形式的运算规则可以概括为:”先引用,后自增(减)
"。这里所说的 ”引用
",指的是使用变量的值。另外,我们还要强调一个细节:无论是前缀形式还是后缀形式,自增自减运算符的优先级要高于赋值运算符。
7.1.1 语句中仅有++或- -
\quad
代码:int a = 10; a++; System.out.println(a);
的输出结果是 3
,其中第 2
条语句中仅有一个后缀形式 ++
操作。
\quad
代码:int a = 10; ++a; System.out.println(a);
的输出结果同样是 3
,与之前的那段代码基本一样,只是第 2
条语句中,后缀形式的 ++
操作被换成了前缀形式。
\quad
这说明:当一条语句中仅有 ++
或 --
操作时,前缀形式与后缀形式的运算符没有任何区别。
7.1.2 ++ 或 - - 运算结果赋值给其他变量
\quad
代码 1
:int a = 2; int b = ++a; System.out.println(a); System.out.println(b);
的第 2
条语句对变量 a
进行了自增操作,并且把这个操作结果赋值给另一个变量 b
。语句中的变量 b
就是标题中所说的 ”其他变量”,是指没有进行自增自减操作的其他变量。为什么要强调 ”赋值给其他变量” 这个前提呢?就是因为如果把运算结果赋值给变量 a
自身,又会产生不同的效果,我们后面再去讲解赋值给自身的情况。现在先来分析程序,重点看第 2
条语句:变量 a
所进行的是前缀形式的自增操作,那么按照 ”先自增后引用” 的运算规则,a
的值首先变成 3
,然后赋值 b
。因此,给变量 b
赋值的是 3
,那么输出结果就是 3
和 3
。
\quad
代码 2
:int a = 2; int b = a++; System.out.println(a); System.out.println(b);
的第 2
行发生了变化,a
的自增操作变成了后缀形式。此时的程序输出结果是 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 在这里,请大家注意一个细节,那就是:代码中的自增操作虽然是后缀形式的,但这个自增动作却是在赋值之前完成的,这也解释了后缀形式的自增运算优先级高于赋值运算,而网上很多资料中所说的”先完成赋值再去做自增操作”是完全错误的。
\quad
那么,之前代码 1
中第2条语句是 ”b=++a;
”,会不会也把 a
的值存入操作数栈呢?答案是肯定的,只要是变量参了算术运算、赋值、被打印这些操作,都会取出变量的值存入操作数栈。因为语句中出现的是前缀形式的自增,所以在把值存入操作数栈之前就已经完成了自增操作。
7.1.3 ++ 或 - - 运算结果赋值给自身
\quad
代码 1
:int a = 2; a = ++a; System.out.println(a);
的第 2
条语句,对 a
进行了前缀形式的自增,然后又赋值给 a
自身,那么 a
的值是多少呢?因为 a
进行的是前缀形式的自增,所以运算规则是”先自增后引用
”,自增之后 a
的值变成了 3
,把 3
存入操作数栈,之后以 3
赋值给 a
,所以 a
的值还是 3
。
\quad
代码 2
:int a = 2; a = a++; System.out.println(a);
的第 2
条语句将 ++a
改成了 a++
。这种情况下,程序输出 a
的值为竟然为 2
,而不是 3
。说的直白一点,a
并没有按我们的想象实现自增。这是为什么呢?我们来分析一下整个运算的过程:计算机看到”=
”右边是后缀形式的自增,因此以 ”先引用后自增
” 的规则进行运算,先把 a
的值存入操作数栈,紧接着对 a
进行自增操作,a
的值变成了 3
,最后又用操作数栈中的 2
对 a
进行赋值,a
的值又变成了 2
。这样给我们造成了一种 ”a
没有进行自增” 的错觉。
\quad
接下来,我们再来研究一种更特殊的情况,请看代码 3
:int a = 2; a = a++; System.out.println(a);
这一次,语句中出现了复合赋值运算符。如果程序运行,输出 a
的值会是多少呢?我们首先可以推导出 ”+=
” 右边的运算结果是 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
。
7.2 逻辑运算符
\quad
逻辑与 “&&
” 的前后两个操作数必须都是 true
才返回 true
,否则返回 false
。逻辑或 “||
” 只要两个操作数中有一个是 true
,就返回 true
,否则返回 false
。逻辑与 “&&
” 和逻辑或 “||
” 被称作短路运算符。短路与(&&
)和短路或(||
)能够采用最优化的计算方式,从而提高效率。
\quad
“&&
” 运算符检查第一个表达式是否返回 “false
”,如果是 “false
” 则结果必为 “false
”,不再检查其他内容。
\quad
“||
” 运算符检查第一个表达式是否返回 “true
”,如果是 “true
” 则结果必为 “true
”,不再检查其他内容。
\quad
两种逻辑与(&&
和 &
)的运算规则基本相同,两种逻辑或(||
和 |
)的运算规则也基本相同。其区别是:&
和 |
运算是把逻辑表达式全部计算完,而 &&
和 ||
运算具有短路计算功能。
7.3 位运算符
-
按位与
&
:如果相对应位都是1
,则结果为1
,否则为0
。 -
按位或
|
:如果相对应位都是0
,则结果为0
,否则为1
。 -
按位异或
^
:如果相对应位值相同,则结果为0
,否则为1
。 -
按位取反
~
:按位取反运算符翻转操作数的每一位,即0
变成1
,1
变成0
。 -
按位左移
<<
:按位左移运算符。左操作数按位左移右操作数指定的位数,即丢弃左边指定位数,右边补0
。左操作数左移1
位相当于乘2
。- 当
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
)。左操作数右移1
位相当于除2
。- 和左移一样,
int
类型移位大于等于32
位时,long
类型大于等于64
位时,会先做求余处理再位移处理,byte
,short
移位前会先转换为int
类型(32
位)再进行移位。
- 和左移一样,
-
无符号右移
>>>
:无符号右移即按位右移补零操作符。左操作数的值按右操作数指定的位数右移,移动得到的左边空位以零填充。
7.4 条件运算符
\quad
Java
提供了一个特别的三元运算符(也叫三目运算符
)经常用于取代某个类型的 if-then-else
语句。条件运算符是 Java
唯一的三目运算符,表示为 “? :
”,使用该运算符时需要有三个操作数,因此称其为三目运算符。使用条件运算符的一般语法结构为:
result = <expression> ? <statement1> : <statement3>;
\quad
其中,expression
是一个布尔表达式。当 expression
为真时,执行 statement1
, 否则就执行 statement3
。此三元运算符要求返回一个结果,因此要实现简单的二分支程序,即可使用该条件运算符。
八、Java 包机制
\quad
为了更好地组织类,Java
提供了包机制,用于区别类名的命名空间。
8.1 包的作用
- 把功能相似或相关的类或接口组织在同一个包中,方便类的查找和使用。
- 如同文件夹一样,包也采用了树形目录的存储方式。同一个包中的类名字是不同的,不同的包中的类的名字是可以相同的,当同时调用两个不同包中相同类名的类时,应该加上包名加以区别。因此,包可以避免名字冲突。
- 包也限定了访问权限,拥有包访问权限的类才能访问某个包中的类。
8.2 创建包
- 包声明应该在源文件的第一行,每个源文件只能有一个包声明,这个文件中的每个类型都应用于它。包声明的语法格式为:
package pkg1[.pkg2[.pkg3...]];
- 创建包的时候,你需要为这个包取一个合适的名字(
一般利用公司域名倒置作为包名
)。之后,如果其他的一个源文件包含了这个包提供的类、接口、枚举或者注释类型的时候,都必须将这个包的声明放在这个源文件的开头。 - 如果一个源文件中没有使用包声明,那么其中的类,函数,枚举,注释等将被放在一个无名的包(
unnamed package
)中。
8.3 import 关键字
\quad
为了能够使用某一个包的成员,我们需要在 Java
程序中明确导入该包。使用 “import
” 语句可完成此功能。在 Java
源文件中 import
语句应位于 package
语句之后,所有类的定义之前,可以没有,也可以有多条,其语法格式为:
import package1[.package2 ...].(classname|*); // *表示导入包下所有的类
如果在一个包中,一个类想要使用本包中的另一个类,那么该包名可以省略。
九、JavaDoc 文档生成器
9.1 文档注释概览
\quad
“文档注释”(Java Doc Comments
)是专门为了用 javadoc
工具自动生成文档而写的注释,它是一种带有特殊功能的注释。
\quad
文档注释与一般注释的最大区别在于起始符号是 “/**
” 而不是 “/*
” 或 “//
”。
/**
* 这是文档注释
*/
/*
* 这是一般注释
*/
// 这是一般注释
\quad
在一些IDE
(比如 Eclipse
)中,文档注释会以不同于普通注释的颜色高亮显示。
\quad
此外,文档注释只负责描述类(class
)、接口(interface
)、方法(method)、构造器(constructor)、成员字段(field)。相应地,文档注释必须写在类、接口、方法、构造器、成员字段前面,而写在其他位置,比如函数内部,是无效的文档注释。
\quad
文档注释采用 HTML
语法规则书写,支持 HTML
标记(tag
),同时也有一些额外的辅助标记。需要注意的是,这些标记不是给人看的(通常他们的可读性也不好),他们的作用是为了 javadoc
工具更好地生成最终文档。所以,虽然有些标记写起来麻烦且看着不直观,还是要老老实实按规矩写滴。
9.2 文档注释的基本内容
\quad 一个文档注释由两部分组成:
/**
* 描述部分(description)
*
* 标记部分(block tags)
*/
9.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.
”。
9.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
:描述可能从此方法抛出的异常。
9.4 生成 JavaDoc 文档
9.4.1 控制台
- 用法:
javadoc [options] [packagenames] [sourcefiles] [@files]
9.4.2 Intellij IDEA
-
第一步:在工具栏中找到
Tools
,然后再子菜单中找到Generate JavaDoc...
。
-
第二步:在生成文档上进行配置。
参数介绍:-
public
:仅显示public
类和成员。 -
protected
:显示protected/public
类和成员(默认值)。 -
package
:显示package/protected/public
类和成员。 -
private
:显示所有类和成员。
-