Java笔记_5
概述和环境
源码文件
- 确保安装了
JDK, 并且配置了环境变量 jdk/lib目录中可以找到src.zip, 这个压缩包包含了所有的公共类库的源码- 使用
jar xvf jdk/lib/src.zip可以将src.zip解压缩到当前目录下 - 如果是别的源码, 比如编译器, 原生方法, 虚拟机, 私有辅助类, 需要从
openjdk那边拿到
控制台运行Java
- 一个包含
Main函数的Java类, 使用javac xxx.java可以将xxx.java文件编译为.class文件 - 然后再使用
java xxx, 不需要加.class后缀即可直接运行这个编译后的文件
JShell
JDK 9引入了另一种使用Java的方法,就是"读取-评估-打印循环" (Read-Evaluate-Print Loop,REPL)- 输入一个表达式,
JShell会评估输入, 打印结果, 并等待下一个输入. 直接在控制台输入jshell即可开始使用- 输入
"java".length()就会返回字符个数$1 ==> 4 - 然后再输入
4 * $1 + 1, 会返回$2 ==> 17 - 并不需要手动输入
System.out.println()也可以直接返回结果, 并自动存储变量 - 也可以手动指定变量名, 例如
int ans = 42, 会返回结果ans ==> 42
- 输入
Java规范
- 类名需要驼峰: 首字母大写, 后面每个单词的首字母均大写
- 文件名必须和文件内的公共类相同
main函数一定需要在public类中, 虚拟机从main函数开始执行.main方法总是静态的main函数返回值为0, 如果需要以其他的返回值返回, 需要使用System.exit(nums)- 变量的声明尽可能靠近使用变量的地方
JDK 10开始, 对于局部变量, 可以使用var关键字声明, 这样可以从变量的初始值推断出他的类型var d = 12; // d is an intvar s = "12"; // s is a String
Java中不区分变量的声明和定义
类型
Java具有8种基本类型, 其中4种整型, 2种浮点类型, 1种字符类型char(用于表示Unicode编码), 1种boolean类型- 同时
Java具有一个表示任意精度的算数包, 大数big number是一个Java对象, 而不是基本Java类型
整型
Java的int无论在什么环境下都是4 Bytes, 可以表示- 可以给数字加上下划线, 在编译时会自动去除, 方便源码阅读:
12_000_000_000 Java中没有无符号类型, 如果确实需要无符号数, 比如short表示范围需要在0~255之间的话, 进行计算的时候可以使用Byte.toUnsignedInt(b)来得到一个无符号整数
浮点型
- 一般都使用
8 Bytes的double类型, 而不是4 Bytes的float类型 - 可以使用十六进制表示浮点数字面量, 例如可以写成
0x1.0p-3- 这里
0x表示十六进制,p表示指数. 不是e是因为e在十六进制中了,-3表示十进制的-3次方, 基数为2
- 这里
- 浮点数溢出的三种情况:正溢出,负溢出和
NaN,如果正数/0就是正溢出,反之负溢出;如果就是NaN Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY, Double.NaN表示三个特殊的值, 基本不用if (x == Double.NaN)永远为假, 因为NaN是永远不会和其他值相同, (Double.NaN也不会和Double.NaN相同)- 如果需要判断
x是否为NaN, 可以使用if (Double.isNaN(x))
- 如果需要判断
- 如果需要精确表示浮点数的话, 需要使用
BigDecimal类 - 整数除
0产生异常, 浮点数除0得到结果NaN
字符类型
char类型可以表示十六进制值, 范围从\u0000~\uFFFF- 需要注意注释中尽量不要出现
Unicode字符, 例如// \u000A is a newline.这句话会产生一个语法错误, 因为读程序时会将\u000A转换为一个换行符// look inside c:\user这里也是会报错的, 因为\u后面没有跟着4位十六进制数
- 原本
Unicode字符不超过65536个, 所以Java设置了char类型只有16位 - 后来字符放不下了,
Java设置码点来解决问题, 指一个编码表中某个字符对应的代码值 - 码点采用十六进制编写, 加上前缀
U+, 比如U+0041就是A的码点 Unicode的码点可以分为17个代码平面, 第一个代码平面被称为基本多语言平面, 包括了码点范围是U+0000~U+FFFF- 其余
16个平面的码点范围是U+10000~U+10FFFF - 不要在
Java代码中使用char类型, 一般将字符串作为抽象数据类型处理
布尔类型
Java的整型和布尔值不能相互转换
常量
- 使用
final关键字定义的, 只能被赋值一次, 不能再更改, 一般用全大写命名 - 可能需要创建一个常量在类中的多个方法中使用, 称为类常量, 可以使用
static final设置一个类常量 const是Java保留的一个关键字, 但是目前并没有使用
1 | public class A { |
枚举类型
1 | enum Size {SMALL, MEDIUM, LARGE, EXTRA_LARGE}; |
循环移位
>>表示左移, 使用符号位填充;>>>表示左移, 永远使用0填充<<表示右移; 不存在<<<- 所有移位的右操作数都需要对32取模, 如果左操作数是
Long类型, 则右操作数需要对64取模1
2
3
4
5int i = 1;
long j = 1;
1 << 35; // 与 1 << 3等价, 结果为8
i << 35; // 与 1 << 3等价, 结果为8
j << 35; // 与 1 << 35等价, 结果为34359738368
字符串
Java中字符串就是Unicode序列, 比如"Java\u2122"由五个Unicode字符组成Java没有内置的字符串类型, 标准Java类库中提供了一个预定义类- 任何
Java对象都可以转换为字符串, 所以"PG" + 12 = "PG12" - 如果多个字符串使用界定符分割的话, 可以使用静态
join方法1
String all = String.join("/", "S", "M", "I"); // all = "S/M/I"
JDK 11中提供了repeat方法1
String rep = "J".repeat(3); // rep = "JJJ"
Java中字符串不可变, 如果需要修改一个字符串的一部分, 需要先使用substring提取字符串不需要修改的部分, 然后加上其他修改后的结果字符串
- 字符串不可变每次都需要生成新的字符串, 会降低效率, 但是编译器底层可以实现字符串共享
- 开发者认为字符串共享带来的收益比修改字符串带来的收益明显, 因为往往需要比对字符串是否相同, 修改频率较少
- 只会共享字符串字面量, 由
+或者substring得到的字符串无法共享 - 因此比较两个字符串相等的时候务必使用
"A".equals("A"), 如果使用==, 则会比较两个字符串引用是否相等 - 如果要检查字符串不是空串也不是
null的话, 需要先检查null:if (s != null && s.length() != 0), 否则如果字符串为null的话, 调用这个字符串的length()函数会报错
-
Java中最常用的Unicode由一个代码单元表示, 但是辅助字符需要两个代码单元, 所以如果使用charAt(), 会返回指定索引的代码单元- 如果正好某个索引需要两个代码单元, 但是使用
charAt()指定了前一个索引, 就会出现问题. 因此,charAt()一般不要用 - 为了测试, 需要提前设置
cmd窗口内编码格式为Unicode, 在cmd内输入chcp 65001将当前窗口的编码格式切换为Unicode1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20public static void main(String[] args) {
String s = "123\uD835\uDD46";
System.out.println("当前字符串是: " + s);
System.out.println("s.length() = " + s.length());
System.out.println("使用s.charAt()打印每一个元素: ");
for (int i = 0; i < s.length(); i ++) {
System.out.print(s.charAt(i) + " ");
}
System.out.println();
System.out.print("s的实际长度(码点个数)为: ");
int trueLen = s.codePointCount(0, s.length());
System.out.println(trueLen);
System.out.println("使用s.codePointAt()打印每一个元素: ");
for (int i = 0; i < trueLen; i ++) {
System.out.print(s.codePointAt(s.offsetByCodePoints(0, i)) + " ");
}
}- 上面代码的输出结果为:
- 上面代码的输出结果为:
- 如果正好某个索引需要两个代码单元, 但是使用
-
如果需要拼接多个较短的字符串, 可以使用
StringBuilderStringBuffer效率不如StringBuilder, 不过StringBuffer可以支持多线程添加删除字符- 如果所有操作都在单线程, 则使用
StringBuilder
-
JDK 15中存在文本块, 以三个引号开头结尾, 可以更加方便的写换行, 例如1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16String a =
"""
Hello
World
""";
String b = "Hello\nWorld\n";
// a == b
// 文本块最适合放SQL语句或者HTML语句, 但是如果所有的反斜线都需要转义
// 如果不转义就会变成不换行
String c =
"""
Hello\
World
""";
String d = "HelloWorld\n";
// c == d
输入输出
- 如果是密码相关的内容, 建议不要使用
println(), 可以使用Console.readPassword() printf("%+f", d)可以打印d的正负号printf(%,f, d)可以对d增加三个数一组的分割符printf("%1$d,%1$x", d)分别以十进制和十六进制打印第一个参数dprintf("%d%<x", d)分别以十进制和十六进制打印同一个数- 使用
String.format方法可以格式化字符串, 不打印输出1
2
3String message = String.format("Hello, %s. Next year, you'll be %d", name, age + 1);
// JDK 15版本以后可以使用下面这种更加简单的方法
String message = "Hello, %s. Next year, you'll be %d".formatted(name, age + 1); - 写入文件使用
1
2
3
4
5PrintWriter out = new PrintWriter("tmp.txt", StandardCharsets.UTF_8);
out.write("werwr");
out.flush();
// 如果tmp.txt不存在, 则会自动创建一个
// 写入以后需要使用flush才能保存
控制流程
Java中两个嵌套的块不能重复定义相同的变量- 在
for循环中不能检测了两个浮点数是否相等, 因为误差的存在, 可能会导致死循环
switch-case
1 | // 第一种 |
- 不能混用
:和->, 同时->也不存在直通的行为 yield也会终止switch语句, 但是还会生成一个值switch表达式的关键是生成一个值, 或者抛出异常, 不允许使用return跳出
1 | int ans = switch(s) { |
1 | int nums = switch(s) { |
大数
1 | // 使用valueOf()静态方法可以将一个普通的数转换为大数 |
- 对于
BigDecimal类, 总应该使用带有字符串参数的构造器生成. 尽管有BigDecimal(double), 但是传入的时候会产生浮点数精度误差 Java不能通过编程实现运算符的重载, 所以只能使用add(), multiply()等方法实现加减乘除Java设计者只重载了+来实现字符串拼接
数组
Java中允许长度为0的数组存在- 数组创建时, 数字数组初始化为
0, 对象数组初始化为null,boolean数组初始化为false - 如果想要打印数组a, 可以直接写
Arrays.toString(a), 这个返回值包含了数组中所有元素的字符串 - 如果想要快速打印一个二维数组, 可以使用
Arrays.deepToString(a)
1 | int[] a = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; |
数组拷贝
1 | int[] a = {1, 2, 3}; |
main函数的参数, 是String[] args, 接受一个字符串数组, 也就是命令行指定的参数java Message -h hello这里面args[0]=-hargs[1] = hello
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Sangs Blog!
