JVM 内存空间
包括栈,堆,方法区
栈是线程私有的,其生命周期与线程相同。其中存储的内容包括:
- 局部变量表:局部变量必须初始化,作用域限制在声明的方法或代码块。
- 操作数栈:用于方法执行的计算工作
- 动态链接:指向运行时常量池的方法引用
- 方法出口:存储方法返回的地址
堆是线程共享的,存储了几乎所有的对象实例和数组,也是垃圾收集器管理的主要区域。
方法区也是线程共享的,主要用于存储:
- 类型信息:类的完整有效名、类的直接父类的完整有效名、类的修饰符等
- 运行时常量池
- 静态变量:
static修饰的变量(e.g., 类变量,属于类,该类下所有实例共享,可通过类名访问) - 即时编译器编译后的代码缓存
输入输出处理
输入
Scanner scanner = new Scanner(System.in);
String word = scanner.next(); // 读取单个单词(直到空格)
String line = scanner.nextLine(); // 读取一行(直到换行符)
int num = scanner.nextInt(); // 读取整数
double d = scanner.nextDouble(); // 读取浮点数
boolean b = scanner.nextBoolean(); // 读取bool类型
while(scanner.hasNext()); // 检查是否有下一个输入
if(scanner.hasNextInt()); // 检查下一个输入是否为整数
scanner.close()
- 使用
nextInt()后,再使用nextLine()之前要额外调用一次nextLine()来清空缓冲区,否则会读到缓冲区里的换行符 - 使用scanner完毕后要调用
close()来释放资源
输出
System.out.printf("%s", "Your name"); // 字符串
System.out.printf("%d", age); // 整数
System.out.printf("%.2f", balance); // 浮点数(四舍五入)
System.out.printf("%c", 'Y'); // 字符
System.out.printf("%b", true); // 布尔
System.out.printf("%x", 255); // 十六进制
System.out.printf("%o", 16); // 八进制
System.out.printf("%e", 10000000); // 科学计数法
System.out.printf("整数: %d\n", age); // 整数:25
System.out.printf("带符号: %+d\n", age); // 带符号:+25
System.out.printf("补零: %05d\n", age); // 补零:00025
System.out.printf("左对齐: %-10s|\n", name);
System.out.printf("右对齐: %10s|\n", name);
// 输出:
// 左对齐: 琪琪 |
// 右对齐: 琪琪|
类型转换
// 其他类型转换为字符串
int num = 123;
String str1 = String.valueOf(num); // "123"
String str2 = Integer.toString(num); // "123"
String str3 = num + ""; // "123"
// 字符串转换为其他类型
String numStr = "456";
int intValue = Integer.parseInt(numStr);
double doubleValue = Double.parseDouble("3.14");
boolean boolValue = Boolean.parseBoolean("true");
按照byte - short - int - long - float - double的顺序属于向上转化,可以自动转化,不会损失精度(除了long - float)。但是向下转化时,需要去显式指定,并且会存在精度损失。
对于包装类(e.g., Interger, Character),自动装箱是指将基本类型转化为包装类,自动拆箱是指将包装类转化为基本类型。
Interger是java中自动缓存的$[-128,127]$之间的整数对象。
char类型会自动按照Unicode编码值转化为相应的int,但int不能自动转为char,因为有可能超出范围。
关于Switch语句
与golang不同,java的switch语句和c/c++中的类似,存在fall-through的机制,因此必须加入break语句来避免。
- switch语句不支持
long,float,double等浮点类型 - 对于
enum类型,不需要case color.RED:,只需case RED:即可
// Java - 默认会穿透
int day = 2;
switch (day) {
case 1:
System.out.println("Monday");
// 注意:没有break,会继续执行下一个case
case 2:
System.out.println("Tuesday");
case 3:
System.out.println("Wednesday");
break;
default:
System.out.println("Other day");
}
// 输出:
// Tuesday
// Wednesday
在golang中,执行完一个case后会自动结束,要触发fall-through则需要手动添加关键字。
// golang
// 如果需要穿透,必须显式使用 fallthrough 关键字
day := 1
switch day {
case 1:
fmt.Println("Monday")
fallthrough // 显式穿透
case 2:
fmt.Println("Tuesday")
// 这里不会自动穿透到case 3
case 3:
fmt.Println("Wednesday")
}
// 输出:
// Monday
// Tuesday
Java 14引入了switch表达式,它是switch语句的增强版本,可以返回值,语法更加简洁,并且避免了fall-through的问题。
// java 14 switch表达式
String dayType = switch (dayOfWeek) {
case 1, 2, 3, 4, 5 -> "工作日";
case 6, 7 -> "周末";
default -> "无效";
};
数组与多维数组
java中的多维数组实际上是“数组的数组”,每个子数组是独立的对象,可能分布在堆内存的不同位置。且每个子数组的长度可以不同,即锯齿状数组。
// 创建不规则数组
int[][] jaggedArray = new int[4][];
// 为每一行分配不同长度的数组
jaggedArray[0] = new int[1]; // 第1行:1个元素
jaggedArray[1] = new int[2]; // 第2行:2个元素
jaggedArray[2] = new int[3]; // 第3行:3个元素
jaggedArray[3] = new int[4]; // 第4行:4个元素
数组的操作
// 数组复制
int[] copyArr = Arrays.copyOf(array, array.length); // 常规需求
System.arraycopy(src, 0, dst, 0, length); // 大数组
copyArr = array.clone();
// 数组排序
Arrays.sort(array);
// 数组二分搜索
int index = Arrays.binarySearch(sortedArray, target);