除了转义字符,Java还有哪些需要注意的语法点?

发布于:2025-07-25 ⋅ 阅读:(12) ⋅ 点赞:(0)

Java 作为一门强类型、面向对象的编程语言,有许多语法细节容易被忽略或误用。除了转义字符,以下是一些需要重点注意的语法点:

1. 数据类型与类型转换

  • 强类型特性:Java 要求变量必须先声明类型再使用,且类型不可隐式转换为不兼容类型(如 int 不能直接转为 boolean)。
  • 自动类型转换限制:仅允许“小范围”类型向“大范围”类型转换(如 byte → short → int → long → float → double),反之必须显式强制转换,可能导致精度丢失:
    int a = 1000;
    byte b = (byte) a; // 强制转换,结果为 -24(因 byte 范围是 -128~127)
    
  • 包装类与基本类型:自动装箱(int → Integer)和拆箱(Integer → int)可能隐藏空指针风险:
    Integer num = null;
    int n = num; // 运行时抛 NullPointerException(拆箱时null无法转换)
    

2. 字符串(String)的特殊性

  • 不可变性String 对象创建后内容不可修改,所有拼接、替换操作都会生成新对象。频繁修改字符串应使用 StringBuilder(非线程安全)或 StringBuffer(线程安全):
    String s = "a";
    s += "b"; // 实际生成新的 String 对象,原对象被废弃
    
  • ==equals() 的区别== 比较对象地址(引用),equals() 比较内容(需注意 null 安全):
    String s1 = "abc";
    String s2 = new String("abc");
    System.out.println(s1 == s2); // false(地址不同)
    System.out.println(s1.equals(s2)); // true(内容相同)
    
  • 常量池缓存:直接赋值的字符串(如 String s = "abc")会被缓存到常量池,而 new String() 会创建新对象(除非调用 intern() 方法)。

3. 数组的语法细节

  • 初始化方式:数组必须初始化后才能使用,分为静态初始化(指定元素)和动态初始化(指定长度):
    int[] arr1 = {1, 2, 3}; // 静态初始化(长度由元素数决定)
    int[] arr2 = new int[3]; // 动态初始化(元素默认值为 0)
    
  • 长度固定:数组一旦创建,长度不可修改,访问超出范围的索引会抛 ArrayIndexOutOfBoundsException
  • 二维数组的不规则性:Java 二维数组的每行可以是不同长度(本质是“数组的数组”):
    int[][] arr = new int[2][];
    arr[0] = new int[3]; // 第一行长度 3
    arr[1] = new int[5]; // 第二行长度 5
    

4. 面向对象核心语法

  • 方法重写(Override)的限制
    • 子类方法的访问权限必须 不低于 父类(如父类 protected 方法,子类可改为 public,但不能改为 private)。
    • 返回值类型必须是父类方法返回值的 子类或相同类型(Java 5+ 支持协变返回)。
    • 不能重写 final 方法,不能重写父类的 private 方法(因子类不可见)。
  • thissuper 的区别
    • this 指代当前对象,用于访问本类成员或调用本类构造方法(this(...))。
    • super 指代父类对象,用于访问父类成员或调用父类构造方法(super(...)),且必须放在构造方法第一行。
  • 构造方法特性
    • 无返回值,名称与类名完全一致。
    • 若未显式定义构造方法,编译器会生成默认无参构造;若显式定义了构造方法,默认构造会被覆盖。
    • 子类构造方法会 默认调用父类无参构造,若父类没有无参构造,子类必须显式用 super(...) 调用父类有参构造,否则编译报错。

5. 异常处理的陷阱

  • finally 块的执行时机finally总会执行(除非 JVM 退出),即使 trycatch 中有 return
    public static int test() {
        try {
            return 1;
        } finally {
            return 2; // 最终返回 2(覆盖 try 中的 return)
        }
    }
    
  • 异常类型匹配catch 块按顺序匹配异常,子类异常必须放在父类异常之前,否则子类异常块会被屏蔽:
    try {
        // ...
    } catch (Exception e) { // 父类异常放前面,会屏蔽子类异常
        // ...
    } catch (NullPointerException e) { // 永远不会执行
        // ...
    }
    
  • checked 与 unchecked 异常
    • checked 异常(如 IOException)必须显式处理(try-catch)或声明抛出(throws)。
    • unchecked 异常(如 NullPointerExceptionIndexOutOfBoundsException)继承自 RuntimeException,可不必显式处理。

6. 泛型的限制

  • 类型擦除:泛型信息在编译后会被擦除,运行时无法获取泛型的具体类型:
    List<String> list = new ArrayList<>();
    System.out.println(list.getClass() == ArrayList.class); // true(擦除后都是 ArrayList)
    
  • 不能使用基本类型:泛型参数必须是引用类型,如 List<int> 错误,需用 List<Integer>
  • 通配符的区别<? extends T> 表示“T 及其子类”(上限),<? super T> 表示“T 及其父类”(下限),使用时需注意读写限制。

7. 关键字的特殊用法

  • final
    • 修饰变量:值不可修改(引用类型则引用不可变,对象内容可改)。
    • 修饰方法:不可被重写。
    • 修饰类:不可被继承(如 StringInteger 都是 final 类)。
  • static
    • 静态成员(变量/方法)属于类,而非实例,可通过 类名.成员 直接访问。
    • 静态方法中不能访问非静态成员(因无实例对象),也不能使用 thissuper
  • transient:修饰的变量不会被序列化(如敏感信息可标记为 transient)。
  • volatile:保证变量的可见性(多线程中一个线程修改后,其他线程能立即看到),但不保证原子性。

8. 流程控制的细节

  • switch 语句
    • Java 7+ 支持 String 作为 switch 表达式,但 case 标签必须是常量(字面量或 final 变量)。
    • 缺少 break 会导致“穿透现象”(执行当前 case 后继续执行下一个 case)。
  • for-each 循环:只能遍历元素,不能修改数组/集合的长度或通过索引访问,且对 null 数组/集合会抛 NullPointerException

9. 访问修饰符的范围

修饰符 本类 同包 子类(不同包) 其他包
private × × ×
默认(无修饰) × ×
protected ×
public
  • 注意:protected 修饰的成员在不同包子类中,只能通过子类实例访问,不能通过父类实例访问。

这些语法点是 Java 中最容易出错的地方,理解并掌握它们能有效减少编译错误和运行时异常,提升代码的健壮性。


网站公告

今日签到

点亮在社区的每一天
去签到