1. 其他基础
-
Java 区分大小写;Java 中每个句子必须用分号结束;使用括号表示代码块;使用(.)来调用类的方法等;双引号定字符串。
-
源文件的文件名必须与公共类的名字相同(包括大小写),并用
.java
作为扩展名 -
Java 虚拟机总是从指定类中的 main 方法的代码开始执行。根据 Java 语言规范,main 方法必须声明为 public。并且在 Java 1.4 及之后版本中强制 main 方法必须是 public。
之外 main 方法还必须是 static 的。
main 方法使用 void,表示没有返回,main 方法没有给操作系统返回 “退出码”。如果 main 方法正常退出,那么 Java 应用程序的退出码是 0,表示成功地运行了程序。如果希望在终止程序时返回其他的退出码,使用
System.exit
方法。 -
Java 的类和 C++ 的类很相似,但是还是存在一些差异。比如 Java 中的所有函数都是某个类的方法(标准术语将其称为方法,而不是成员函数)。
-
java main 中的 args 中并没有存储程序名。
java Message -g cruel world
中 args[0] 为 “-g”。public static void main(String[] args) { }
-
java.lang 中的包不需要显示导入
2. 注释
Java 中的注释不会出现在可执行程序中。因此,在源代码中添加任意多的注释,也不用担心可执行代码会膨胀。Java 有三种标记注释的方式。
//行注释
/*块注释*/
/**
*这种注释可以用来自动地生成文档
*/
不能简单地把代码用
/*
和*/
括起来作为注释,因为这段代码本身可能也包含一个*/
界定符。
3. 标识符
由 字母、下划线、数字、美元符号 等组成。开头不能是数字,也不能是关键字(如 true、false、null)等。
Java 使用 unicode 字符集,该字符集包含了世界上大部分语言的“字母表”。
4. 数据类型
Java 是一种强类型语言,也就是必须为每一个变量声明一种类型。Java 一共有 8 种基本类型:4 种整型、2 种浮点类型、1 种字符类型 char(用于表示 Unicode 编码的代码单元)、一种表示逻辑的 boolean 类型。
4.1. 逻辑类型---boolean
true、false 两个值,用来判断逻辑条件。整型值和布尔值之间不能进行相互转换。
在 C++ 中数值甚至指针类型可以代替 boolean 值,值 0 相当于 false,非 0 值相当于 true。
4.2. 整型
关键字 | 常量表示 | 字节数 |
---|---|---|
int | 123(这个常量是十进制数值)、077(这个常量是八进制数值)、0xABC/0XABC(这个常量是十六进制数值)、0b1001/0B1001(这个常量是二进制数值,表示 9 这个 10 进制数值)、1_000_000(也可以在数字字面量中加下划线,这些下划线只是为了易读,Java 编译器会去掉这些下划线的) | 4 |
byte | (byte)-12、(byte)28---强制转换为 byte 型,但是可能会发生截断等行为,所以记得请把一定范围内的 int 型常量或者 int 型的值赋值给 byte | 1 |
short | (short)-12、(short)28---同 byte | 2 |
long | 108L(这个常量是 long 型),用后缀 L 来表示 | 8 |
Java 中没有任务无符号形式的 int、long、short 或 byte 类型,比如不存在
unsigned int m
这种的。Java 中,所有的数值类型所占用的字节数与平台无关,在不同的平台上数据类型的取值范围是固定的,这就解决从一个平台移植到另一个平台带来的诸多问题。而 C 或者 C++ 会针对不同的处理器选择最为高效的整型,这就造成了一个在 32 位处理器上运行很好的 C/C++ 程序在 16 位的系统上运行时会发生整数溢出。
4.3. 字符类型
- 2 字节,但是最高位不是符号位,不存在负数
- 单引号表示的是字符类型常量,如
'A'
- 转移字符表示的也是字符类型,如
'\n'
、'\b'
、'\t'
、'\r'
、'\''
、'\"'
,'\\'
。这种方式可以出现在字符串中,如"Hello\n"
。 - 字符在 unicode 字符表中排序位置的十六进制表示也可以表示字符类型,如
'\u0041'
。这种方式可以出现在字符串中,也可以出现在字符串外,如main(String \u005B\u005D args)
。这么理解就好,因为字符串外的那些也是文本,也会被处理成 unicode。
char 类型原本用于表示单个字符。但是,如今有些 Unicode 字符可以用一个 char 值表示,另外一些 Unicode 字符需要两个 char 值。
4.4. 浮点数
关键字 | 常量表示 | 字节数 |
---|---|---|
float | 后缀 f 或者 F,如 3.14f | 4,(6~7 位有效数字) |
double | 后缀 d 或者 D,如 3.14D(默认情况下为 double,也就是 3.14 double 类型的) | 8,(16 位有效数字) |
所有的浮点数值计算都遵循 IEEE 754 规范,使用下面三个特殊的浮点数值表示溢出和出错情况:
-
正无穷大,比如正整数除以 0
-
负无穷大
-
NaN(不是一个数值),比如 0/0 或者负数的平方根
Double.POSITIVE_INFINITY 和 Double.NEGATIVE_INFINITY 和 Double.NaN 表示这三个特殊的值,Float 也是一样的。另外在比较一个值是否等于 Double.NaN 的时候,要使用 Double.isNaN() 方法来判断,而不能使用 x == Double.NaN
来判断。
浮点数还可以使用十六进制表示,比如 0.125 = 2^(-3),那么可以表示为 0x1.0p-3。在这种表示方法中,1.0 的部分是尾数使用十六进制,-3 部分是指数(p 后面),使用十进制。指数的基数是 2,而不是 10。
4.5. 基础类型转换
Java 中使用 (类型名)要转换的值
的方式进行类型转换。级别低常量/变量赋值给级别高的变量时,系统自动进行转换,如 float x = 100
;相反的赋值需要用上述方式进行显示转换。
假如两个进行运算的数中有一个是 double 型,那么则将另一个数也转化为 double 型,也就是往级别高的转。
级别从低到高(其实就是按照字节数及表示的值范围进行排序):byte、short、char、int、long、float、double
需要注意的时:级别高常量/变量赋值给级别低的变量时,需要注意截断问题。所以最好不要超过级别低的变量所能表示的范围。若超过了,则级别低的值需要根据截断的字节情况算出来。比如
byte a = (byte) 128
,实际 a 的值为 -128。
5. 字符串
Java 没有内置的字符串类型,而是在标准的 Java 类库中提供一个预定义类,叫做 String,每个用双引号括起来的字符串都是 String 类的一个实例。那么,Java 字符串就是 Unicode 字符序列。
String 类对象是不可变的,也就是不能修改 Java 字符串中的某个字符。不可变字符串有一个优点:那就是编译器可以让这些字符串共享。
Java 字符串大致类似于 char* 指针,而不是字符数组。
char * greet = "Hello"; // 是这个 char greet[] = "Hello"; // 而不是这个
5.1. 拼接
Java 也使用 +号来拼接两个字符串。当一个字符串与另一个非字符串的值进行拼接时,后者会转换为字符串。
String expletive = "expletive";
String PG13 = "deleted";
String message = expletive + PG13;
int age = 13;
String rating = "PG" + age;
可以使用 String.join()
将多个字符串放在一起,中间用一个界定符号分隔。
String all = String.join(" / ", "S", "M", "L", "XL");
// S / M / L / XL
5.2. 字符串是否相等
Java 中不能使用 ==
来检测两个字符串是否相等,这个 == 只能判断这两个字符串是否存放在同一个位置。那么你可能会有疑问了,既然 Java 字符串是共享的,那么当同一个位置的时候,那么不就代表两个字符串相等嘛?假如,虚拟机使用将相同的字符串共享,那么其实使用 == 也是可以的。但是,完全有可能将内容相同的多个字符串副本放在不同的位置上。并且,实际上只有字符串字面常量是共享的,而 + 或者 substring 操作得到的字符串并不共享。所以请不要使用 ==
运算符来测试字符串的相等性。
要想比较两个字符串是否相等,请使用 equals()
方法,如 s.equals(t)
。如果字符串 s 和字符串 t 相等,则返回 true。
C++ 的 string 类,重载了 == 运算符,但是 Java 没有。Java 字符串看起来像是数值,当表现得又类似于指针。
5.3. 空串和 null
空串是长度为 0 的字符串,如 ""
。它是一个 Java 对象,有种自己的串长度和内容。
null 表示该变量没有与任何对象实例相关联。
5.4. 子串
使用 string 的 substring()
方法可以从一个字符串中提取一个子串。
String greeting = "Hello";
String sub = greeting.substring(0, 3); // Hel
5.5. 字符串长度
Java 字符串由 char 值序列组成,char 数值类型是一个采用 UTF-16 编码表示 Unicode 码点的代码单元。最常用的 Unicode 字符使用一个代码单元就可以表示,但是有些辅助字符可能需要一对代码单元表示。
length()
方法返回的是代码单元数量,charAt(n)
将返回位置 n 的代码单元。当字符串中的字符都是用一个代码单元的时候,那么 length()
返回则是字符数量,charAt()
返回是第 n 个字符,但是当有一个字符是用两个代码单元时候,那么此时就有点问题了。这个时候可以使用码点单元的方式来实现了。
String greeting = "Hello";
int count = greeting.codePointCount(0, greeting.length()); // 码点数量
// 获得第 i 个码点
int index = greeting.offsetByCodePoints(0, i);
int cp = greeting.codePointAt(index);
// 查看每个码点
int cp = sentence.codePointAt(i);
if (Character.isSupplementaryCodePoint(cp)) {
i += 2;
} else {
i++;
}
虚拟机不一定要把字符串实现为代码单元序列,在 Java 9 中,只包含单字节代码单元的字符串可以使用 byte 数组实现。
6. 数组
Java 的数组是一种数据结构,用来存储同一类型值的集合。通过一个整型下标可以访问数组中的每一个值。
6.1. 声明、创建、初始化一维、多维数组
// 指出数组类型,后面紧跟 [],然后后头紧跟数组变量的名字。但是,这里仅仅声明了变量 name,并没有创建数组,name 就可以当成一个引用而已。
int name[];
int[] name; // 后者更常用
// 声明二维数组,一个二维数组由若干个一维数组构成,相当于数组的数组
int name[][];
int[][] name;
// 一次性声明多个数组
float[] a, b; // 等价于 float a[], b[]
float[] a, b[]; // 等价于 float a[], b[][]
// 数组名 = new 数组元素的类型[大小],创建数组之后就不能改变数组的长度了。
// new int[4] 相当于在 Java 的堆区分配了内存,并且会返回该内存区的首地址。之后将该地址赋值给 name 变量,Java 中不使用指针这一概念,而是使用引用这一概念。因此 name 只是一个引用指向了堆区的这个位置
int[] name;
name = new int[4];
// 同理
int[] name = new int[4];
// Java 允许使用 int 型变量来指定数组的大小
int size = 10;
int[] name = new int[size];
// Java 中构成二维数组的一维数组不必有相同的长度
int[][] a = new int[3][];
a[0] = new int[6];
a[1] = new int[12];
a[2] = new int[8];
// 声明数组的时候,可以直接将一些初始化值付给数组变量;
// 个人认为其实,{1, 2, 3, 4} 也是一种创建数组的方式;
int[] data1 = {1, 2, 3, 4};
int[] data2 = new int[]{1, 2, 3, 4};
// int[] data2 = new int[4]{1, 2, 3, 4}; 这种方式是会出错的
// 初始化一个二维数组,组成二维数组的一维数组的长度可以不相同。
int[][] a = {{1}, {1, 1}, {1, 2, 1}, {1, 2, 3, 1}};
-
与 C 不同的是 Java 不允许
int a[12]
或者int[12] a
-
创建数组的时候没有指定分配的元素的值的话,那么 Java 会使用默认值填充,如 float 是 0.0;整型是 0;boolean 是 false;对象数组则初始化为 null,也就是对象数组中的每一个引用其实都是 null,并没有实际指向。
-
Java 数组与堆栈上的 C++ 上的数组有很大不同,但是基本上与堆上分配的数组指针一样。也就是说 Java 的
int[] a = new int[100]
等同于 C++ 的int* a = new int[100]
,而不同等同于int a[100]
。 -
上面阐述了多维数组,但实际上 Java 没有多维数组。多维数组被解释为 “数组的数组”。Java 的多维数组主要是在底层实现上存在一些细微的差异。
double[][] balances = new double[10][6];
double 实际上一个包含 10 个元素的数组,只是这 10 个元素又是一个由 6 个浮点数组成的数组,你可以把这 10 个元素理解成 double[] 类型的引用变量。那么,
balances[i]
实际上引用的是第 i 个子数组,它本身也指向一个数组,所以balances[i][j]
实际上是访问第 i 个数组的 j 个元素。同时,我们可以单独交换某两个子数组。double[] temp = balances[i]; balances[i] = balances[j]; balances[j] = temp;
因此,我们可以构建出一个不规则的数组,也就是每个子数组的长度是不一样的。
Java 中的
double[][] balances = new double[10][6]
不同于 C++ 中的double balances[10][6]
,也不同于 C++ 中的double (*balances)[6] = new double[10][6]
。而等同于double ** balances = new double*[10]; for (int i = 0; i < 10; i++) { balances[i] = new double[6]; }
6.2. 数组操作
-
使用索引值进行访问,下标从 0 开始。比如一个包含 100 个元素的数组,下标只能是 0-99,如果超过 99,那么就会报错。
Java 中的
[]
运算符被预定义为会完成越界检查,而且没有指针运算,既不能通过 +1 得到数组中的下一个元素。 -
数组名.length
(一维数组是数组中元素的个数;二维数组是一维数组的个数)。 -
数组排序可以使用
Arrays.sort()
方法,这个方法使用了优化的快速排序算法。 -
使用
System.out.println()
输出的时候,char 型的数组输出的是数组全部元素的值,要想输出 char 型数组的运用值使用System.out.println("" + a)
;int 型的数组输出则为引用值。要想打印输出 int 型的数组可以使用
Arrays.toString(array)
方法获得一个包含数组元素的字符串。二维数组的话,可以使用Arrays.deepToString(array)
。
6.3. 数组拷贝
数组拷贝分为两种:一种是浅拷贝,另一种是深拷贝。
// 浅拷贝
int[] smallPrimes = {2, 3, 5, 7, 11, 12};
int[] luckyNumbers = smallPrimes;
// 深拷贝,将一个数组的值拷贝到另一个新的数组中去。
// copyOf 的方法,第二个参数是新数组的长度,可以比原数组要长,数组中多出的位置的元素将被赋值为默认值;如果比原数组要短,那么只拷贝前面的值。
int[] copiedLuckyNumbers = Arrays.copyOf(luckyNumbers, luckyNumbers.length);
7. 变量和常量
变量是用来存储值的,常量是值不变的变量。变量和常量相当给内存的一段区域取了一个名字。
7.1. 声明变量
先指定变量的类型,然后是变量名。变量名必须以一个字母、下划线或者 $ 开头,并且由字母、下划线、$ 和数字构成序列(不能是 Java 保留的关键字),同时变量名是大小写敏感的,在长度上基本没有限制。
Java 中可以将声明放在代码中的任何地方,因此变量的声明最好尽可能靠近变量第一次使用的地方。
double salary;
int vacationDays;
boolean done;
int i, j; // 这种方式不建议
相比其他语言,Java 变量名中的字母范围会更大,从 A-Z、a-z、_、$ 以及在任何语言中表示字母的任何 Unicode 字符,数字也是类似的,某种语言中表示数字的任何 Unicode 字符。但是,建议使用常规的英文字母和 0-9 。比如 $ 只用在 Java 编译器或者其他工具生成的名字中。
7.2. 变量初始化
声明一个变量之后,必须要用赋值语句对变量进行显示初始化。
int vationDays = 12;
// 等同于
int vationDays;
vationDays = 12;
从 Java 10 开始,对于局部变量,可以从变量的初始化推断出它的类型,因此可以不用再声明类型了。
var vationDays = 12; // int var greeting = "Hello"; // String
C++ 中区分变量的声明和定义,比如
extern int i
是一个声明,int i =10
是一个定义,而在 Java 中不区分声明和定义。
7.3. 常量
使用关键值 final 来表示,final 表示的变量只能被赋值一次,也就是被赋值之后就不能再改变了,习惯上常量名使用大写。
类常量在 final 关键字的基础之上,新增了一个 static 关键字,并且定义在 main 方法的外面,同一类中的其他方法也可以使用这个类常量。假如是 public 的话,那么其他类中的方法也可以使用类型直接访问这个类常量。
public class demo { public static final double CM_PER_INCH = 2.54; public static void main(String[] args) { ... } }
const 虽然被 Java 作为保留的关键字,但是并没有使用。
7.4. 枚举类型
枚举类型包含有限个命名的值,假如一个变量的取值仅在几个值中选择的话,那么可以使用枚举类型。
enum Size {SMALL, MEDIUM, LARGE, EXTRA_LARGE};
Size s = Size.MEDIUM;
8. 运算符
运算符类型 | 表示 |
---|---|
算术运算符 | +、-、*、/(运算的两个数都是整数的话,那就是整数除法;否则表示浮点除法,15/2 等于 7,15.0/2 等于 7.5。除以 0 获得无穷大或者 NaN,会产生异常 )、% |
自增、自减 | 前++、--(先++、--,再使用该变量的值);后++、--(先用变量,再 ++、--) |
关系运算符 | >、<、>=、<=、==、!= |
逻辑运算符 | &&、||、!(操作元必须是 boolean 型数据;&& 左边为 false 时,右边的值不会再判断;|| 左边为 true 时,右边则不会再判断了,也就是按照短路方式来求值) |
赋值运算符 | =(左边的操作元必须是变量,不能是常量或者表达式),+=、-= 等等 |
三元运算符 | condition ? expression1 : expression2 ,如果 condition 为 true 则去第一个值,否则第二个值 |
位运算符 | &、|、~、^(异或:相同为 0,不相同为 1),应用在 boolean 值上的时候 &、| 也会得到一个布尔值,但是后者不采用 “短路” 方式来求值。 >>、<< 是将位右移和左移,>>> 会用 0 填充高位,而 >> 是用符号位填充高位(不存在 <<< 运算符);另外移位运算符的右操作数要完成模 32 的运算(32 是针对整型,long 则是 64),1 << 35 等同于 1<<3。 |
instanceof | instanceof (左边的操作元是一个对象,右边是一个类,当左边的对象是右边的类或者子类创建的对象时,为 true) |
算数混合运算的精度是:double、float、long、int 的顺序来,也就是说假如同时存在 double 和 int 类型的,那么则按照 double 类型的来。
Java 不适用逗号运算符,但是 for 语句中的第一部分和第三部分可以使用逗号来分割表达式。
优先级从高到低:
9. 控制流程
Java 与其他程序设计语言一样,使用条件语句和循环结构确定控制流程。
在深入了解控制结构之间,先了解块(block)的概念。块(即复合语句)是由若干条 Java 语句组成的语句,并用一对大括号括起来。块确定了变量的作用域。一个块可以嵌套在另一个块中,但是不能在嵌套的块中声明被嵌套的块中的同名变量。
// 这种一个块 A 嵌套在另一个块 B 中,块 A 不能再声明和块 B 相同的变量。
// 下面这段代码是无法通过的
public static void main(String[] args) {
int n;
...
{
int k;
int n;
...
}
}
在 C++ 中,可以在嵌套的块中重定义一个变量,在内层定义的变量会覆盖在外层定义的变量。
Java 中没有 goto 语句。
9.1. 条件分支语句
- if
- if ... else ...
- if ... else if ... else
if (condition) {
.....
}
if (condition) {
......
} else {
......
}
if (condition) {
......
} else if (condition) {
......
} else {
......
}
- else 子句会与最邻近的 if 组成一组
9.2. 选择语句
-
switch 语句将从与选项匹配的 case 标签开始执行,直到遇到 break 语句,或者执行到 switch 语句的结束为止。如果没有相匹配的 case 标签,而有 default 子句,那么会执行 default 子句。
如果在 case 分支语句的末尾没有 break 语句,那么就会接着执行下一个 case 分支语句。
-
case 标签可以是①类型为 char、byte、short 或 int 的常量表达式;②枚举常量,在使用枚举常量时不需要指明枚举名;③还可以是字符串字面量(Java 7 开始)
switch (expression) {
case constant1:
......;
break;
case constant2:
......;
break;
case ......:
......;
break;
default :
......;
}
enum Size {SMALL, MEDIUM, LARGE, EXTRA_LARGE};
Size s = Size.MEDIUM;
switch (s) {
case SMALL:
...
break;
...
}
- switch 中 expression 的值可以是 byte、short、int、char,不可以是 long;
9.3. 循环语句
-
while,先检测条件在执行
-
do ... while,先执行语句,再检测条件,如果 true 则重复执行
-
for,有三个部分:第一部分通常是初始化;第二部分是要判断的条件;第三部分是更新计数器。第一和第三可以使用逗号。Java 允许 for 循环的各个部分放置任何表达式,但是有一条不成文的规定,那就是 for 语句的三个部分应该对同一个计数器变量进行初始化、检测和更新。
另外 for 语句第一部分声明的一个变量,它的作用域是到 for 循环体的末尾。
-
for ... each
// for 语句
for (int i = 0; i < 10; i++) {
......
}
// while 语句
while (condition) {
......
}
// do ... while 语句
do {
} while(i < 10);
for (int i = 1; i < 10; i++) {
System.out.println(i);
}
// for each 语句,遍历数组中的每个元素,而不是下标值
// for (声明循环变量:数组的名字){}
int[] a = {1, 2, 3, 4, 5};
for (int i:a){
System.out.println(i);
}
// 声明循环变量,必须变量声明的形式,而不能拿已声明过的变量来,如下是错误的
int i = 0;
for (i:a) {
......
}
for...each 的方式除了遍历数组之外,还可以遍历任何一个实现了 Iterable 接口的类对象。
break 和 continue 同样可以应用在循环语句中,当然 break 还可以用于 switch 语句。
9.4. 中断流程控制的语句
-
break,相比其他语言的 break,Java 的 break 可以带标签,用于跳出多重嵌套的循环语句。break 除了用在循环和 switch 的情况,还可以用于 if 的情况,用来跳出语句块。
-
continue,同样可以带标签
-
return
read_data:
while (...) {
for (...) {
if (n < 0) {
break read_data;
}
}
}
label:
{
if (condition) {
break label;
}
}
Java 的设计者将 goto 作为保留字,但是没有在语言中使用它。
10. 输入与输出
10.1. 标准输入
想要通过控制台进行输入,首先需要构建一个与“标准输入流” System.in 关联的 Scanner 对象。
Scanner in = new Scanner(System.in);
String name = in.nextLine(); // 将读取一行输入,可含空格
String firstName = in.next(); // 读取一个单词,以空白符作为分隔符
int age = in.nextInt(); // 读取一个整数
boolean b = reader.nextBoolean();
byte b = reader.nextByte();
short s = reader.nextShort();
long l = reader.nextLong();
float f = reader.nextFloat();
double d = reader.nextDouble();
// 上述的输入是可见的,不适用于从控制台读取密码等。Java 引入了 java.io.Console 类来实现这个目的,返回的密码存放在一个字符数组中,对密码处理完成之后,马上用一个填充值覆盖数组元素。Console 对象必须每次读取一行输入。
Console cons = System.console();
String username = cons.readLine("User name: ");
char[] passwd = cons.readPassword("Password: ");
Scanner 类定义在 java.util 包中
10.2. 标准输出
// 允许使用 “+” 将变量、表达式、一个常数值或一个字符串进行合并输出
System.out.println(); // 输出之后自动换行
System.out.print(); // 不换行
// 格式化输出
System.out.printf("格式化控制部分", 表达式1, 表达式2, ..., 表达式n);
System.out.printf("%8.2f", 10000.0 / 3.0); // 以一个字段宽度(8)打印 x,并且精度为小数点后 2 个字符。也就是打印一个前导的空格和 7 个字符 3333.33
-
格式化控制部分中的 % 字符开始的格式说明符都会用相应的参数替换掉,格式说明符尾部的转换符指示要格式化的数值的类型,f 表示浮点数,s 表示字符串等。注意可以使用 s 转换符格式化任意的对象,那么对于实现了 Formattable 接口的任意对象会调用这个对象的 formatTo 方法,否则调用 toString 方法。
-
指定控制格式化输出外观的各种标志
10.3. 文件输入与输出
// 读取 myfile.txt 这个的文件,假如有反斜杠的话需要再加一个额外的反斜杠(\)
// 以 UTF-8 的字符编码方式读取,如果省略则使用运行这个 Java 程序的机器的默认编码
Scanner in = new Scanner(Path.of("myfile.txt"), StandardCharsets.UTF_8);
Scanner in = new Scanner("myfile.txt"); // 将 myfile.txt 这个字符串作为输入
// 创建一个输出对象,提供文件名和字符编码方式。如果文件名不存在则创建该文件。可以使用 print、printf、println 方法
PrintWriter out = new PrintWriter("myfile.txt", StandardCharsets.UTF_8);
当指定一个相对文件名时,例如
../myfile.txt
,那么文件是相对于 Java 虚拟机启动目录的位置。
11. 巨人的肩膀
- 《Java 核心技术卷1-原书第11版》