Java核心技术卷I (11版)第三章学习(三)
读取输入
- 使用
nextLine
方法,是因为在输入行中有可能包含空格。 - 要想读取一个单词(以空白符作为分隔符),可以使用
in.next()
方法。 - 读取一个整数,调用
nextInt()
方法。 - 读取下一个浮点数,调用
nextDouble()
方法。
import java.util.*;
// 创建与“标准输入流”System.in关联的Scanner对象
Scanner in = new Scanner(System.in);
// 使用in的方法nextLine()读取一行输入
System.out.println("名字:");
String name = in.nextLine();
// nextLine()以回车作为结束,空格作为字符也会被读取
// 想读取一个单词(以空白符作为分隔符)
String name = in.next();
// 读取一个整数
Int age = in.nextInt();
import java.util.Scanner;
// 创建与“标准输入流” System.in关联的Scanner对象
Scanner in = new Scanner(System.in);
// 使用nextLine方法读取一行输入
System.out.println("你叫啥?");
String name = in.nextLine();
// 读取一个单词
String firstName = in.next();
// 读取一个整数
System.out.println("多大了?");
int age = in.nextInt();
// 读取一个浮点数使用in.nextDouble()
Scanner
类定义在Java.util
包中,在文件的最前面加上代码import Java.uti.*;
即可。boolean hasNext()
:检测输入中是否还有其他单词。boolean hasNextInt()
:检测输入中是否还有下一个表示整数的字符序列。boolean hasNextDouble()
:检测输入中是否还有下一个表示浮点数的字符序列。
由于输入可见,所以Scanner
类不适用于从控制台读取密码,Java6引入了Console
类来实现密码读取。
Console cons = System.console();
String username = cons.readLine("User name: ");
char[] passwd = cons.readPassword("Password: ");
- 使用
System.console()
来获取一个与当前Java虚拟机关联的控制台对象,然后使用该控制台对象的方法来读取输入。 - 采用
Console
对象来获取输入,必须每次读取一行输入,没有读取单个单词或数值的方法。 public static Console console()
:如果可以交互,就返回一个Console
对象通过控制台窗口与用户交互,否则返回null
。- 在图形用户界面(GUI)环境中,
System.console()
可能会返回null
,因为GUI通常不提供与控制台相对应的输入/输出流。
public class ConsoleExample{
public static void main(String[] args){
// 获取控制台对象
Console cons = System.console();
if (console == null){
System.out.println("No console available.");
return;
}
// 读取用户名
String name = cons.readLine("Your name: ");
// 读取密码(不会显示内容)
char[] password = cons.readPassword("Password: ");
// 出于安全考虑,处理完密码后清楚密码数组内容
java.util.Arrays.fill(password, ' ');
}
}
Console
对象读取的密码会存储在字符数组中保存,而不是字符串中。
格式化输出
// Java5沿用了C语言函数库的printf()方法
System.out.printf("%8.2f", x); // 总共8哥字符,小数点后2个字符
System.out.printf("Hello, %s, Next year, you'll be %d", name, age);
// 创建格式化的字符串
String message = String.format("Hello, %s. Next year, you'll be %d", name, age);
// 打印当前的日期和时间
System.out.printf("%tc", new Date());
f
表示浮点数,s
表示字符串,d
表示十进制整数,b
是布尔,c
是字符。
文件输入与输出
import java.io.IOException; // 使用try catch需要导入的包
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.Scanner;
public class ScannerFileExample {
public static void main(String[] args) {
String filePath = "myfile.txt";
// 读取文件
try (Scanner scanner = new Scanner(Path.of(filePath), StandardCharsets.UTF_8)) {
while (scanner.hasNextLine()) { // 如果存在下一行内容,则执行下述内容
String line = scanner.nextLine();
// 在这里处理读取到的每一行内容
System.out.println(line); //打印到控制台
}
} catch (IOException e1) {
e1.printStackTrace();
}
try (PrintWriter writer = new PrintWriter(filePath, StandardCharsets.UTF_8)){
writer.println("写入内容!");
}catch(IOException e2) {
e2.printStackTrace();
}
}
}
- 在创建
Scanner
对象时,不能写成Scanner in = new Scanner("filePath.txt");
,这样会导致Scanner
把字符串解释为数据,而不是文件名,所以需要使用Path.of("filePath.txt")
。 StandardCharsets.UTF_8
用于指定字符编码。- 为什么要使用
try-catch
包围?try-catch
语句块石一种处理运行时错误或异常的机制。这种机制允许程序在检测到错误时执行特定的代码块(例如与该错误匹配的catch
块),而不是立即崩溃或者停止。
控制流程
import java.util.*;
import java.io.*;
public class Example_3_5{
public static void main(String[] args) {
// 在嵌套的块中无法重复定义变量
int n=1;
{
//int n; //报错
int k;
}
// if语句
if(2>n) {
System.out.println("2大于n");
}else if(2==n){
System.out.println("2等于n");
}else {
System.out.println("2小于n");
}
// 循环语句
while(n<5) {
System.out.println(n);
n++;
} // while语句中,若条件为True,则执行下述内容,条件判定先于内容运行
do {
System.out.println(n);
n++;
}while(n<10); // do while语句中,先执行内容,再进行条件判定
for (int i=1; i<15; i++) {
System.out.println(n);
}
for ( ; n<20; n++) { //变量n已经定义,不需要再定义(此循环前n=10)
System.out.println(n);
}
int choice = 2;
switch(choice) {
case 1:
System.out.println("choice值为1。");
break;
case 2:
System.out.println("choice值为2。");
break;
default:
System.out.println("choice值超出范围。");
break;
}
}
}
块作用域
- 块一般以大括号为界定符,例如上述代码就是由三个块嵌套起来的
- java在嵌套的块中不能重定义一个变量。但是在C++中,可以在嵌套的块中重新定义这个变量。
break语句
break
语句通常用于提前终止当前循环(如for
、while
或do-while
循环)。然而,当存在多层嵌套循环时,直接使用break
只能跳出最内层的循环。若想跳出多层嵌套循环,需要用到带标签的break。- 在下述例子中,当
i * j
等于9时,程序会跳出标签为outerLoop
的循环,即使它位于内层循环内部。注意,标签必须放在它要标记的循环之前,并且在该循环的范围内是可见的。
outerLoop: for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
if (i * j == 9) {
break outerLoop; // 当i和j的乘积等于9时,跳出外层循环
}
System.out.println("i = " + i + ", j = " + j);
}
}
continue语句
continue
语句在编程中用于控制循环的流程。当在循环体内遇到continue
语句时,程序会跳过当前循环迭代中continue
之后的所有剩余语句,并立即开始下一次迭代。换句话说,continue
语句用于结束当前循环迭代,而不是终止整个循环。- 在
for
循环中,continue
语句会导致程序流程直接跳到循环头部的更新表达式,并开始下一次迭代。在while
循环中,continue
语句会导致程序流程直接回到循环的条件判断部分。
#include <stdio.h>
int main() {
for (int i = 0; i < 10; i++) {
if (i == 5) {
continue; // 当i等于5时,跳过本次循环的剩余部分
}
printf("%d ", i); // 输出除了5以外的数字
}
return 0;
}
// 输出结果为:0 1 2 3 4 6 7 8 9
- 同样地,在
while
循环中也可以使用continue
语句。 - 下述代码的输出结果与上面的
for
循环示例相同。需要注意的是,在while
循环中,通常建议在continue
语句之前更新循环变量(在这个例子中是i
),以确保循环能够正常进行到下一次迭代。
#include <stdio.h>
int main() {
int i = 0;
while (i < 10) {
i++;
if (i == 5) {
continue; // 当i等于5时,跳过本次循环的剩余部分
}
printf("%d ", i); // 输出除了5以外的数字
}
return 0;
}
大数
java.math
包中有两个类BigInteger
和BigDecimal
,可以处理包含任意长度数字序列的数值。前者实现任意精度的证书运算,后者实现任意精度的浮点数运算。
BigInteger
- 构造器:可以通过字符串或字节数组创建
BigInteger
实例。
BigInteger bigIntFromString = new BigInteger("12345678901234567890");
BigInteger bigIntFromByteArray = new BigInteger(byteArray);
- 算术操作:支持加、减、乘、除等基本算术操作。
BigInteger a = new BigInteger("1234567890");
BigInteger b = new BigInteger("9876543210");
BigInteger sum = a.add(b);
BigInteger difference = a.subtract(b);
BigInteger product = a.multiply(b);
BigInteger quotient = a.divide(b); // 注意:可能会抛出 ArithmeticException 如果 a 不能被 b 整除
BigInteger remainder = a.mod(b);
- 比较:可以使用
compareTo
方法比较两个BigInteger
的大小。
int comparisonResult = a.compareTo(b);
if (comparisonResult < 0) {
System.out.println("a is less than b");
} else if (comparisonResult > 0) {
System.out.println("a is greater than b");
} else {
System.out.println("a is equal to b");
}
- 位移操作:可以左移或右移指定的位数。
BigInteger shiftedLeft = a.shiftLeft(3); // 等同于 a * 2^3
BigInteger shiftedRight = a.shiftRight(2); // 无符号右移
- 转换为基本类型:可以将
BigInteger
转换为基本类型,但这在数值非常大时会导致溢出。
BigInteger bigInt = new BigInteger("31623189273812783101747180471274");
long longValue = bigInt.longValue(); // 注意:如果 bigInt 太大,这里会发生溢出
- 位操作:支持 AND、OR、XOR 和 NOT 等位操作。
- 其他操作:还有很多其他有用的方法,如计算幂、计算最大公约数(GCD)、求模逆元等。
BigDecimal
java.math.BigDecimal
类提供了用于任意精度的十进制算术运算的不可变对象。与 BigInteger
类似,BigDecimal
允许你对非常大的或非常小的浮点数进行精确计算,而不会遇到浮点数运算中常见的舍入误差。
- 构造器:可以通过字符串、数字或
BigInteger
创建BigDecimal
实例。
BigDecimal(double)
存在精度损失风险,在精确计算或值比较的场景中可能会导致业务逻辑异常,如:BigDecimal g= new BigDecimal(0.1F);
实际的存储值为0.10000000149。- 优先推荐入参为
String
的构造方法,或使用BigDecimal
的valueOf
方法,此方法内部其实执行了Double
的toString
,而Double
的toString
按double
的实际能表达的精度对尾数进行了截断。
BigDecimal bdFromString = new BigDecimal("123.456");
BigDecimal bdFromDouble = new BigDecimal(123.456); // 不推荐,因为 double 不精确
BigDecimal bdFromBigInteger = new BigDecimal(BigInteger.valueOf(123456), 3); // 123.456,第二个参数是小数点后的位数
- 算术操作:支持加、减、乘、除等基本算术操作。
BigDecimal a = new BigDecimal("10.0");
BigDecimal b = new BigDecimal("3.0");
BigDecimal sum = a.add(b); // 加法
BigDecimal difference = a.subtract(b); // 减法
BigDecimal product = a.multiply(b); // 乘法
BigDecimal quotient = a.divide(b, 2, RoundingMode.HALF_UP); // 除法,指定小数位数和舍入模式
- 比较:可以使用
compareTo
方法比较两个BigDecimal
的大小。
int comparisonResult = a.compareTo(b);
if (comparisonResult < 0) {
System.out.println("a is less than b");
} else if (comparisonResult > 0) {
System.out.println("a is greater than b");
} else {
System.out.println("a is equal to b");
}
- 数值操作:可以设置小数点后的位数、进行舍入、求绝对值等。
BigDecimal rounded = a.setScale(2, RoundingMode.HALF_UP); // 设置小数点后位数并进行舍入
BigDecimal absolute = a.abs(); // 求绝对值
- 转换为基本类型:可以将
BigDecimal
转换为基本类型,但这在数值非常大或非常小时可能会导致溢出或丢失精度。
double doubleValue = bd.doubleValue(); // 转换为 double,可能丢失精度
BigDecimal
在金融计算、科学计算、税务计算等需要高精度小数运算的领域非常有用。在这些场景中,使用 float
或 double
类型可能会导致不可接受的误差。
笔记目录
Java核心技术卷I (11版)第二章学习
Java核心技术卷I (11版)第三章学习(一)
Java核心技术卷I (11版)第三章学习(二)
Java核心技术卷I (11版)第三章学习(三)