项目开发笔记-2

基础知识

Posted by Sirin on October 30, 2025

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);