命名规范
代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束。
正例:name / sex / age
反例:*name / _sex / $age
代码中的命名严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式。
正确的英文拼写和语法可以让阅读者易于理解,避免歧义。注意,即使纯拼音命名方式
也要避免采用。
正例:alibaba / taobao / youku / hangzhou 等国际通用的名称,可视同英文。
反例:DaZhePromotion [打折] / getPingfenByName() [评分] / int 某变量 = 3
类名使用 UpperCamelCase风格。
但以下情形例外:BO/DTO/VO/AO/PO/UID等。
正例:MarcoPolo / UserDO / XmlService / TcpUdpDeal / TaPromotion
反例:macroPolo / UserDo / XMLService / TCPUDPDeal / TAPromotion
方法名、参数名、成员变量、局部变量都统一使用 lowerCamelCase 风格,必须遵从驼峰形式。
正例: localValue / getHttpMessage / inputUserId
反例:local_value / localvalue
常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长。
正例:UNIVERSITY_OF_CODE
反例:UNI_OF_CODE
抽象类命名使用 Abstract 或 Base 开头;异常类命名使用 Exception 结尾;测试类命名以它要测试的类的名称开始,以 Test 结尾。
正例:BaseService / NameEmptyException / UserServiceTest
类型与中括号紧挨相连来表示数组。
正例:定义整形数组 int[] arrayDemo;
反例:在 main 参数中,使用 String args[]来定义。
POJO 类中布尔类型的变量,都不要加 is 前缀,否则部分框架解析会引起序列化错误。
正例:success
反例:is_success
包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词。包名统一使用单数形式,但是类名如果有复数含义,类名可以使用复数形式。
正例:应用工具类包名为 com.alibaba.ai.util、类名为 MessageUtils(此规则参考 Spring 的框架结构)
杜绝完全不规范的缩写,避免望文不知义。
正例:BaseService / NameEmptyException / UserServiceTest
反例:BaseS / NEException / USTest
注释
行内注释
// 单行注释
多行注释
/*
这是多行注释
*/
类注释
类注释必须放在 import 语句之后,类定义之前。
标记由 @开始, 如 @author或 @param。
package test;
import java.util.Random;
/**
* @author XUEW
*/
public class RandomTest {
}
方法注释
每一个方法注释必须放在所描述的方法之前。
/**
* 这是hello方法
* @param name 名字
* @return 打招呼
*/
public String hello(String name) {
return "Hello" + name;
}
标记
类注释和方法注释上均可以使用标记进行文档的详细描述,之后方便生成 Java Doc。
通用标记:既可以声明在类注释,也可以声明在方法注释。
标记 | 描述 |
---|---|
@author | 作者,这个标记将产生一个 author(作者)条目。可以使用多个 @author 标记,每个标记对应一个作者。 |
@version | 版本,这个标记将产生一个 version(版本)条目。这里的文本可以是对当前版本的任何描述。 |
@since | 始于,这个标记将产生一个 since(始于)条目。这里的 text 可以是对引入特性的版本描述。例如:@since version 1.7.10 |
@deprecated | 过时,这个标记将对类、方法或变量添加一个不再使用的注释。文本中给出了取代的建议。 |
@see | 超链接,这个标记将在 see 后面部分增加一个超级链接。它可以用于类中,也可以用于方法中。例如:@see world.xuewei.entity.Employee#raiseSalary(double) |
@date | 创建日期,例如:@date 2022-01-01 12:30 |
方法标记:声明在方法注释上
标记 | 描述 |
---|---|
@param | 变量描述,这个标记将对当前方法的 param(参数)部分添加一个条目。这个描述可以占据多行,并可以使用 HTML标记。一个方法的所有 @param标记必须放在一起。 |
@return | 返回,这个标记将对当前方法添加 return(返回)部分。这个描述可以跨越多行,并可以使用 HTML 标记。 |
@throws | 异常,这个标记将添加一个注释,用于表示这个方法有可能抛出异常。 |
基本数据类型
Java 是一种强类型语言,定义一个变量就需要声明一个类型。
Java有一个能够表示任意精度的算术包,通常称为“大数值”(big number)。虽然被称为大数值,但它并不是一种新的 Java 类型,而是一个 Java 对象。
1个字节(Byte)= 8 比特位(Bit)
4 种整型
由于 Java 程序必须保证在所有机器上都能够得到相同的运行结果, 所以各种数据类型的取值范围必须固定。
在通常情况下,int 类型最常用。但如果表示星球上的居住人数,就需要使用 long 类型了。byte 和 short类型主要用于特定的应用场合,例如,底层的文件处理或者需要控制占用存储空间量的大数组。
长整型数值有一个后缀L或l,如4000000000L。
2 种浮点型
除了 float 和 double,还有三个特殊的浮点数值,用来表示溢出和出错的情况。
- 正无穷大 Double.POSITIVE_INFINITY
- 负无穷大 Double.NEGATIVE_INFINITY
- NaN(不是一个数字)Double.NaN
例如:正数 / 0 = 正无穷大,0 / 0 = NaN
判断数字是否为无穷大小 d.isInfinite()
判断数字是否为数字 d.isNaN()
绝大部分应用程序都采用 double 类型,但是浮点数值不适用于无法接受舍入误差的金融计算中。2.0 - 1.1 = 0.89999999999…,这种舍入误差原因是浮点数系统使用二进制来表示,但是二进制系统无法精确的表示分数。比如三分之一就没法用二进制表示。
1 种字符型
**char 类型的字面量值要用单引号括起来,只需要 1 个字节。**例如:‘A’ 是编码值为 65 所对应的字符常量。它与 "A"不同,"A"是包含一个字符 A 的字符串。
char 类型的值可以表示为十六进制值。其范围从 \u0000 到 \uffff,特殊字符的转义序列如下。
注意:当心注释中的\u片段,可能会报语法错误
比如下面的注释就会报错
public static void main(String[] args) {
// 目标地址 c:\user\home
System.out.println("Hello World");
}
1 种真值型
boolean(布尔)类型有两个值:false 和 true,用来判定逻辑条件。
关于布尔类型的存储字节数,网上大概有三种说法:
1个比特
理由是 boolean 类型的值只有 true 和 false 两种逻辑值,在编译后会使用 1 和 0 来表示,这两个数在内存中只需要 1 位(bit)即可存储,位是计算机最小的存储单位,八分之一个字节。
1个字节
理由是虽然编译后 1 和 0 只需占用 1 位空间,但计算机处理数据的最小单位是 1 个字节,1 个字节等于 8 位,实际存储的空间是:用 1 个字节的最低位存储,其他 7 位用 0 填补,如果值是 true 的话则存储的二进制为:0000 0001,如果是 false 的话则存储的二进制为:0000 0000。
4个字节
理由来源是《Java虚拟机规范》一书中的描述:“虽然定义了 boolean 这种数据类型,但是只对它提供了非常有限的支持。在 Java 虚拟机中没有任何供 boolean 值专用的字节码指令,Java 语言表达式所操作的 boolean 值,在编译之后都使用 Java 虚拟机中的 int 数据类型来代替,而 boolean 数组将会被编码成 Java 虚拟机的 byte 数组,每个元素 boolean 元素占 8 位”。这样我们可以得出 boolean 类型单独使用是 4 个字节,在数组中又是 1 个字节。
变量转换
隐式转换
两个数值进行二元操作时(例如 n + f,n 是整数,f 是浮点数),先要将两个操作数转换为同一种类型,然后再进行计算。
- 如果两个操作数中有一个是 double 类型,另一个操作数就会转换为 double 类型。
- 如果其中一个操作数是 float 类型,另一个操作数将会转换为 float 类型。
- 如果其中一个操作数是 long 类型,另一个操作数将会转换为 long 类型。
- 否则,两个操作数都将被转换为 int 类型。
强制转换
在必要的时候,int 类型的值将会自动地转换为 double 类型。但另一方面,有时也需要将 double 转换成 int。在 Java 中,允许进行这种数值之间的类型转换。
当然,有可能会丢失一些信息。在这种情况下,需要通过强制类型转换(cast)实现这个操作。
强制类型转换的语法格式是在圆括号中给出想要转换的目标类型,后面紧跟待转换的变量名。
double x = 9.997;
int nx = (int)x;
上面这种情况会丢失精度,所以注意不要将大范围类型的数据转为小范围类型的数据。
字符串
从概念上讲,Java 字符串就是 Unicode字符序列。 例如, 串 Java\u2122
由 5 个Unicode 字符 J
、a
、v
、a
和™
。
Java 没有内置的字符串类型, 而是在标准 Java 类库中提供了一个预定义类,很自然地叫做 String。
每个用双引号括起来的字符串都是 String 类的一个实例:
String e = ""; // an empty string
String greeting = "Hello";
代码单元和码点
每一个字符通常被称为代码单元(code unit),char 类型描述了 UTF-16 编码中的一个代码单元。
码点(code point)是指与一个编码表中的某个字符对应的代码值。在 Unicode 标准中,码点采用十六进制书写,并加上前缀 U+,例如 U+0041 就是拉丁字母 A 的码点。
字符串的拼接和截取
字符串可以直接使用 + 进行拼接,但是这种方式效率较低,后面会介绍 StringBuilder。
当将一个字符串与一个非字符串的值进行拼接时,后者被转换成字符串。
如果需要把多个字符串放在一起,用一个定界符分隔,可以使用静态join方法:String s = String.join("*", "1", "2", "3");
字符串的截取操作可以调用substring方法。
不可变字符串
底层使用的是 final char[] 用来存储每个字符,并且 String 类没有提供用于修改字符串的方法。
private final char value[];
String 类被 final 修饰,表示这个类不可以被继承,意味着创建一个 String 的时候,创建完成后字符串本身的长度和内容就固定了,不能二次修改。此时在内存中指向了一块内存地址,当二次赋值的时候,并不是在原有基础上修改,而是重新指定一块新的内存地址,原始地址的数据将会被 JVM 的自动垃圾回收机制回收。
比较相等使用 equals 方法
s.equal(t)
如果字符串 s 与字符串 t 相等,则返回 true;否则,返回 false。
检测两个字符串是否相等,而不区分大小写,可以使用 equalsIgnoreCase 方法。
一定不要使用 == 运算符检测两个字符串是否相等!这个运算符只能够确定两个字串是否放置在同一个内存位置上。
常量池
String str1="abc";
String str2="abc";
String str3=new String("abc");
String str4=new String("abc");
String str5=str4.intern();
str1==str2 //第一行,true
str1==str3 //第二行,false
str1==str5 //第三行,true
str3==str4 //第四行,false
str3==str5 //第五行,false
str1.equals(str2) //第六行,true
str1.equals(str3) //第七行,true
str1.equals(str5) //第八行,true
str3.equals(str4) //第九行,true
str3.equals(str5) //第十行,true
第一行:str1 和 str2 在赋值时,使用的是字符串常量。**Java 虚拟机有一个常量池机制,它会直接把字符串常量放入常量池中,从而实现复用。**因此 str1 和 str2 指向的是常量池中的同一个内存地址,所以返回值是 true。
第二行:str3 是用 new 关键字创建的,在 Java 中,new 关键字代表创建一个新对象。因此 str3 指向的是一个全新的内存地址。而 str1 指向的是一个常量池中的旧地址,因此 str1 和 str3 肯定是不同的,所以返回值是 false。
第三行:str5 是用 String 类的 intern() 方法创建的。JDK 文档中对 intern 方法是这样描述的:“返回一个常量池中的固定对象。当 intern 方法被调用时,如果常量池中已经包含了这个 String 对象(用 equals 方法判断包含与否),那么直接返回这个对象。否则,就向常量中添加这个对象,并返回对它的引用”。
第四行:str3 和 str4 使用 new 关键字分别创建了新的对象,所以返回值是 false。
第五行:str3 是指向新创建的内存地址,而 str5 指向常量池中的对象地址,两者是不可能相等的,因此返回值是 false。
对于第六、七、八、九、十行,它们全部使用 eqauls() 方法进行比较。关于 String 类的equals方法,JDK文档是这样说的:“equals 方法返回 true 当且仅当它的入参不为空并且它们代表相同的字符串内容”。
空串和Null串
空串"" 是长度为 0 的字符串。空串是一个 Java 对象,有自己的串长度( 0 ) 和内容(空)。
if (str.length() = 0) 或 if (str.equals (""))
String 变量还可以存放一个特殊的值,名为 null,这表示目前没有任何对象与该变量关联。
if (str = = null )
String 类常用基础API
StringBuilder
使用 + 进行字符串拼接的时候效率很低,每次连接字符串都会创建一个新的 String 对象,即耗时又浪费空间。使用 StringBuilder 类就可以避免这个问题的发生。
首先创建一个空的字符串构造器,然后不断的调用 append 方法,当构建完成的时候调用 toString() 得到 String 对象类型的字符串结果。
StringBuilder builder = new StringBuilder();
builder.append("str");
...
String result = builder.toString();
注意,StringBuilder并不是线程安全的,但是效率高,它的前身是StringBuffer,它才是线程安全的,但是效率较低。这两个类的API都是相同的。
输入和输出
键盘读取输入
Scanner
要想通过控制台进行输人,首先需要构造一个 Scanner 对象,并与“标准输入流” System.in 关联。
Scanner in = new Scanner(System.in);
Console
因为输入是可见的,所以 Scanner 类不适用于从控制台读取密码。 Java SE 6 特别引入了 Console 类实现这个目的。要想读取一个密码, 可以采用下列代码:
Console cons = System.console();
String username = cons.readLine("User name: ");
char[] passwd = cons.readPassword("Password: ");
为了安全起见,返回的密码存放在一维字符数组中, 而不是字符串中。
输出
System.out.print( )
普通输出
System.out.println( )
换行输出
System.out.printf( )
格式化输出
System.out.printf("Hello, %s. Next year, you'll be %d" , name, age) ;
每一个以 %字符开始的格式说明符都用相应的参数替换。格式说明符尾部的转换符将指示被格式化的数值类型:f 表示浮点数,s 表示字符串,d 表示十进制整数。
流程控制
块作用域
块(即复合语句)是指由一对大括号括起来的若干条简单的 Java 语句。块确定了变量的作用域。一个块可以嵌套在另一个块中。不能在嵌套的两个块中声明同名的变量。
条件控制
if (age >= 0 && age < 1) {
System.out.println("婴儿");
} else if (age >= 1 && age < 5) {
System.out.println("幼儿");
} else if (age >= 5 && age < 11) {
System.out.println("儿童");
} else if (age >= 11 && age < 18) {
System.out.println("少年");
} else if (age >= 18 && age < 35) {
System.out.println("青年");
} else if (age >= 35 && age < 60) {
System.out.println("中年");
} else {
System.out.println("老年");
}
循环控制
在循环中可以使用 break 跳出循环,continue 跳过本次循环。
for 循环
for 循环语句是支持迭代的一种通用结构,利用每次迭代之后更新的计数器或类似的变量来控制迭代次数。
在循环中,检测两个浮点数是否相等需要格外小心。由于舍入的误差, 最终可能得不到精确值,无法达成终止条件。
for (int i = 0; i < 10; i++) {
// 注意这里是从 0 开始的,0~9
System.out.println("第" + i + "次循环");
}
for each 循环
JDK 5 引入增强 for 循环,for each 循环语句的循环变量将会遍历数组中的每个元素,而不需要使用下标值。
int[] array = new int[]{1, 2, 3, 4, 5};
for (int num : array) {
System.out.println(num);
}
while 循环
while 循环语句首先检测循环条件。因此,循环体中的代码有可能不被执行。
int i = 0;
while (i < 10) {
// 循环内容
System.out.println(i);
// 更新循环条件
i++;
}
do/while 循环
至少执行一次
int i = 10;
do {
// 循环内容
System.out.println(i);
// 更新循环条件
i++;
}
while (i < 10);
多重选择:switch 语句
在处理多个选项时,使用 if/else 结构显得有些笨拙。
switch 语句将从与选项值相匹配的 case 标签处开始执行直到遇到 break 语句,或者执行到 switch 语句的结束处为止。如果没有相匹配的 case 标签,而有 default 子句,就执行这个子句。
case 标签可以是:
- 类型为 char、 byte、 short 或 int 的常量表达式。
- 枚举常量。
- 从 Java SE 7 开始,case 标签还可以是字符串字面量。
**有可能触发多个 case 分支。如果在 case 分支语句的末尾没 break 语句,那么就会接着执行下一个 case 分支语句。**这种情况相当危险,常常会引发错误。
int i = 3;
switch (i) {
case 1:
System.out.println("这是1");
break;
case 2:
System.out.println("这是2");
break;
case 3:
System.out.println("这是3");
break;
default:
System.out.println("这是默认");
break;
}
大数值
如果基本的整数和浮点数精度不能够满足需求,那么可以使用 java.math 包中的两个很有用的类:Biglnteger 和 BigDecimal。这两个类可以处理包含任意长度数字序列的数值。Biglnteger 类实现了任意精度的整数运算,BigDecimal 实现了任意精度的浮点数运算。
注意的是,不能使用+ - * / 来处理运算,而是需要使用它们提供的API。
Biglnteger
BigDecimal
使用 BigDecimal 的 divide 方法进行除法运算的时候,需要指定小数位的取舍方式( rounding mode )。
RoundingMode.HALF_UP 是在学校中学习的四舍五入方式。它适用于常规的计算。
默认保留一位小数,如果想指定保留小数位,可以参考下面
BigDecimal value = d2.divide(d1, 2, RoundingMode.HALF_UP);
提供的其他的取舍方式还有:
方式 | 描述 |
---|---|
RoundingMode.UP | 向远离 0 的方向舍入。保留两位小数为例: 1.371 -> 1.38 -1.371 -> -1.38 |
RoundingMode.DOWN | 向靠近 0 的方向舍入。保留两位小数为例: 1.371 -> 1.37 -1.371 -> -1.37 |
RoundingMode.CEILING | 全向右靠,向大靠拢。保留两位小数为例: 1.371 -> 1.38 -1.371 -> -1.37 |
RoundingMode.FLOOR | 跟 CEILING 相反,全向左靠,向小靠拢。保留两位小数为例: 1.371 -> 1.37 -1.371 -> -1.38 |
RoundingMode.HALF_UP | 四舍五入。保留两位小数为例: 1.375 -> 1.38 -1.371 -> -1.37 |
RoundingMode.HALF_DOWN | 五舍六入。保留两位小数为例: 1.375 -> 1.37 1.376 -> 1.38 |
RoundingMode.HALF_EVEN | - |
RoundingMode.UNNECESSARY | - |
数组
数组是一种数据结构,用来存储同一类型值的集合。**通过一个整型下标可以访问数组中的每一个值。**例如,如果 a 是一个整型数组,a[i] 就是数组中下标为 i 的整数。
int[] a = new int[100];
创建了一个可以存储 100 个整数的数组。索引从 0-99,试图访问元素 a[100](或任何在 0~99之外的下标),程序就会引发 “array index out of bounds” 数组下标越界异常而终止执行。
要想获得数组中的元素个数,可以使用 array.length
有个更加简单的方式打印数组中的所有值,即利用 Arrays 类的 toString 方法。调用 Arrays.toString(a)
返回一个包含数组元素的字符串,这些元素被放置在括号内,并用逗号分隔,例如,“[2,3,5,7,11,13]”
声明数组有两种方式,int[] a
和 int a[]
,但是推荐使用第一种!
- 数字数组,所有元素都初始化为 0
- boolean数组的元素会初始化为false
- 对象数组的元素则初始化为一个特殊值null,这表示这些元素未存放任何对象。
数组的初始化以及匿名数组
int[] array = { 2 , 3, 5 , 7, 11, 13 };
int[] array = new int[] { 17, 19, 23, 29, 31, 37 }
在 Java 中,允许数组长度为 0。
数组的复制
在 Java 中,允许将一个数组变量拷贝给另一个数组变量。这时,两个变量将引用同一个数组。
int[] array1 = {1,2,3,4,5}
int[] array2 = array1
array2[0] = -1
由于两个引用指向了同一个变量,那么 array1[0] 的值也就发生的变化。
如果想实现完全的复制出单独一份数组的话,需要使用 Arrays.copyOf 方法:
int[] copiedLuckyNumbers = Arrays.copyOf(luckyNumbers, luckyNumbers.length);
第 2 个参数是新数组的长度。这个方法通常用来增加数组的大小。
数组的排序
要想对数值型数组进行排序,可以使用 Arrays 类中的 sort 方法,这个方法使用了优化的快速排序算法。
int[] a = new int[10000];
Arrays.sort(a);
多维数组
double[][] balances = new double[5][5];
int[][] magicSquare =
{
{1, 2, 3, 4},
{1, 2, 3, 4, 5, 6},
{1, 2, 3, 4, 5, 6, 7},
{1, 2, 3, 4},
};
一但数组被初始化,就可以利用两个方括号访问每个元素,例如,balances[i][j]。
Java 还可以方便地构造一个“不规则“数组,即数组的每一行有不同的长度。
常用API
笔记大部分摘录自《Java核心技术卷I》,含有少数本人修改补充痕迹。