Java入门首周精要:从基础语法到面向对象核心解析

文章目录

  • Java入门首周精要:从基础语法到面向对象核心解析
    • 1.Java类名与文件名的一致性规则
    • 2.Java和C语言中char类型的区别
    • 3.Java中的注释
    • 4.Java中的‘’+‘’运算符
    • 5.Java的输入输出
    • 6.方法(重载&重写)
      • 方法的重载
      • 方法的重写
    • 7.面向对象(OOP) VS 面向过程(POP)
    • 8.类(Class)和对象(Object)
    • 9.成员变量 VS 局部变量
    • 10.构造方法(构造器)
    • 11.JavaBean是什么?
    • 12.static关键字
    • 13.继承
    • 14.this和super关键字
    • 15.抽象类 VS 接口
    • 16.多态
    • 17.权限修饰符
    • 18.final关键字
    • 19.构造代码块 VS 静态代码块
    • 20.内部类
    • 21.异常
    • 22.为什么String类可以不用new创建对象?
    • 23.增强for

Java入门首周精要:从基础语法到面向对象核心解析

1.Java类名与文件名的一致性规则

规则

  • 如果一个类被声明为 public,那么文件名必须与该类的名称完全一致(包括大小写)
  • 一个 .java 文件中只能有一个 public,但可以有多个非 public 类。
  • 如果没有 public 类,文件名可以任意(但通常与主类名一致)。

Java的设计逻辑

  • Java强制要求 public 类名与文件名一致,是为了让编译器能快速定位类。Java的每个类在编译后都会生成独立的 .class 文件(如 Car.classEngine.class)。
  • public 类可以被其他包中的代码访问,因此需要明确的文件名与类名对应关系,确保JVM在加载类时能找到正确的文件。

建议

  • 始终让文件名与 public 类名一致:避免编译错误。
  • 一个文件只放一个类:提高代码可读性(即使允许放多个类)。
  • 注意大小写:Java区分大小写,Car.javacar.java 是不同的文件!

2.Java和C语言中char类型的区别

C语言

  • C语言中char类型通常为8位(1字节),采用ASCII或本地扩展编码,取值范围通常是 0 − 255 0-255 0255(无符号char)或 − 128 − 127 -128-127 128127(有符号char),符号默认由编译器决定,可显式声明有无符号。
    • 仅能表示256种可能的值,无法直接处理Unicode字符。
    • 多字节字符(如中文)需依赖外部库(如wchar_t或UTF-8编码的函数)。

Java

  • 而Java中的char类型固定为16位(2字节),采用Unicode编码(UTF-16)始终是无符号的,取值范围是\u0000(0)~\uffff(65535)

    • \u 用于表示 Unicode 字符的转义序列,格式为 \uXXXX,其中 XXXX4 位 16 进制数,对应 Unicode 码点的值(C 语言默认不支持 \u 作为字符转义序列,但在 C11 及之后的标准 中,引入了 \u 作为 Unicode 字符转义)。

    • 码点是什么?

      码点(Code Point) 是 Unicode 字符集中的 唯一数字标识符,用来表示一个字符。Unicode 码点的范围是 U+0000U+10FFFF,其中:

      • U+ 表示 Unicode 码点的前缀。
      • 0000 ~ 10FFFF十六进制 数,表示 Unicode 字符的索引。
    • UTF-16编码是一种变长编码,大部分常用字符(BMP平面,即U+0000U+FFFF)用1个char表示。超出BMP的字符(如部分Emoji,U+10000U+10FFFF)需要用2个char(代理对)表示。

      • 什么是BMP?

        BMP,全称为 Basic Multilingual Plane,中文意为基本多文种平面。Unicode 共有17个平面,BMP 是 Unicode 的0号平面,即[U+0000, U+FFFF]区间。BMP 包含了几乎所有的现代语言的文字和大量的符号。

总结

  • C的char
    面向底层,直接操作字节,符号性灵活,适合处理原始数据或ASCII文本,但需手动处理编码和多字节字符。
  • Java的char
    为国际化设计,强制使用UTF-16编码,无符号且类型安全,适合处理多语言文本,但灵活性较低。

3.Java中的注释

单行注释//

多行注释/* ... */

文档注释/** ... */

  • 在Java中,文档注释(Javadoc) 是一种特殊的注释格式,用于生成标准的API文档(HTML格式),方便开发者理解类、方法、字段的功能和使用方式。

  • 语法格式

    /**
     * 描述内容
     * @标签名 标签内容
     */
    
    • /** 开头,*/ 结尾。
    • 每行以 * 开头(非强制,但建议对齐以增强可读性)。
  • 核心Javadoc标签:

    标签 用途 示例
    @param 描述方法的参数(仅用于方法) @param num1 第一个整数
    @return 描述方法的返回值(非void方法必须使用) @return 两个整数的和
    @throws / @exception 描述方法可能抛出的异常 @throws IllegalArgumentException 参数为负数时抛出
    @deprecated 标记方法或类已过时,建议使用替代方案 @deprecated 使用 {@link #newMethod()} 代替
    @see 添加相关类、方法或资源的参考链接 @see java.util.ArrayList
    @since 指定引入该功能的版本 @since 1.8
    @author 标注作者(通常用于类或接口) @author John Doe
    @version 指定版本号(通常用于类或接口) @version 1.0.0
    {@link} 内联链接到其他类或方法 使用 {@link #calculateSum(int, int)} 计算和
    {@code} 将内容格式化为代码样式(不解析HTML) {@code int x = 5;}
  • 用法示例

    • 类注释

      /**
       * 表示一个二维坐标系中的点。
       * 
       * @author Jane Smith
       * @version 1.2
       * @since 1.0
       */
      public class Point {
          private int x;
          private int y;
      
          // ...
      }
      
    • 方法注释

      /**
       * 计算两个整数的和。
       * 
       * @param num1 第一个加数(必须为非负数)
       * @param num2 第二个加数
       * @return 两个参数的和
       * @throws IllegalArgumentException 如果num1为负数
       * @see 算法参考文档
       */
      public int add(int num1, int num2) {
          if (num1 < 0) {
              throw new IllegalArgumentException("num1不能为负数");
          }
          return num1 + num2;
      }
      
  • 生成Javadoc文档

    • 命令行生成

      javadoc -d docs -encoding UTF-8 -charset UTF-8 MyClass.java
      
      • -d docs:指定输出目录为docs
      • -encoding UTF-8:指定源文件编码。
      • -charset UTF-8:指定生成文档的字符集。
    • IDE生成

      ToolsGenerate JavaDoc → 配置输出目录和选项 → 点击生成。

  • 核心价值

    生成标准化API文档,提升代码可维护性和团队协作效率。

4.Java中的‘’+‘’运算符

在Java中,+ 运算符是一个多功能操作符,主要用于算术加法字符串连接

  • 算术加法

    • 适用场景:操作数均为数值类型byte, short, int, long, float, double, char)。

    • 规则

      • 若操作数类型不同,会进行隐式类型提升(向更高精度的类型转换)。
      • 运算结果类型与提升后的类型一致。
    • 示例

      int a = 5 + 3;       // 8(int + int → int)
      double b = 5 + 3.0;  // 8.0(int + double → double)
      char c = 'A' + 1;    // 'B'(char提升为int,计算后转回char)
      
  • 字符串拼接

    • 适用场景:任一操作数为字符串String)类型。

    • 规则

      • 非字符串操作数会自动转换为字符串(调用toString()方法)。
      • 运算结果为新的字符串对象。
    • 示例

      String s1 = "Hello" + " World";     // "Hello World"
      String s2 = "Age: " + 25;          // "Age: 25"(int转String)
      String s3 = 10 + 20 + "30";        // "3030"(先计算10+20=30,再连接"30")
      String s4 = "Sum: " + (10 + 20);   // "Sum: 30"(括号改变优先级)
      
    • 在 Java 中,字符串是不可变的,每次使用 + 连接字符串时,实际上是创建了一个新的 String 对象。为了提高性能,Java 提供了 StringBuilderStringBuffer 来优化字符串的连接操作。

      StringBuilder sb = new StringBuilder();
      sb.append("Hello").append(" ").append("World");
      String result = sb.toString(); // "Hello World"
      

5.Java的输入输出

  • 控制台输出

    System.out:标准输出流,默认输出到控制台。

    • System.out.print():输出不换行。
    • System.out.println():输出并换行。
    • System.out.printf():格式化输出(类似C语言)。
    System.out.print("Hello");       // Hello
    System.out.println(" World");    //  World(换行)
    System.out.printf("PI: %.2f", Math.PI); // PI: 3.14
    
  • 控制台输入

    Scanner:最常用的输入工具,可解析基本类型和字符串。

    import java.util.Scanner;    // 导包
      
    public class ConsoleInput {
        public static void main(String[] args) {
            Scanner scanner = new Scanner(System.in);
            System.out.print("请输入姓名: ");
            String name = scanner.nextLine();  // 读取整行输入
            System.out.print("请输入年龄: ");
            int age = scanner.nextInt();       // 读取整数
            System.out.printf("姓名: %s, 年龄: %d", name, age);
        }
    }
    
    • 常用方法
      • nextInt()nextDouble():读取数值。
      • nextLine():读取整行(包括空格)。
      • next():读取单词(以空格分隔)。

6.方法(重载&重写)

定义

方法是类或对象中用于执行特定任务的代码块。它接受输入参数(可选),执行操作,并可能返回结果。方法封装了功能,提高了代码的可重用性和模块化。

语法

访问修饰符 返回类型 方法名(参数列表) {
    // 方法体
}
  • 访问修饰符:如 publicprivateprotected,控制方法的可见性。
  • 返回类型:方法返回值的数据类型,无返回值时用 void
  • 参数列表:传递给方法的参数,可为空。
  • 方法体:实现具体逻辑的代码块。

示例

public int add(int a, int b) {
    return a + b;
}

方法的重载

定义

在同一个类中定义多个同名方法,但它们的参数列表不同(参数类型、个数或顺序不同)。返回类型可以不同,但仅返回类型不同不足以构成重载。

  • 方法名必须相同。
  • 参数列表必须不同(类型、数量或顺序)。
  • 返回类型、访问修饰符和异常声明可以不同。

作用

提高代码可读性,允许用同一方法名处理不同类型或数量的参数。

示例

public class Calculator {
    // 重载1:两个int参数
    public int add(int a, int b) {
        return a + b;
    }

    // 重载2:三个int参数
    public int add(int a, int b, int c) {
        return a + b + c;
    }

    // 重载3:double类型参数
    public double add(double a, double b) {
        return a + b;
    }
}
calc.add(2, 3);      // 调用重载1
calc.add(1, 2, 3);   // 调用重载2
calc.add(1.5, 2.5);  // 调用重载3

方法的重写

定义

子类重新定义父类中已有的方法,以提供特定实现。重写是实现运行时多态的关键。

  • 方法名、参数列表和返回类型必须与父类方法一致(Java 5+ 允许返回子类类型)。
  • 访问修饰符不能比父类更严格(如父类是 protected,子类可为 public,但不能为 private)。
  • 子类方法抛出的异常不能比父类更宽泛。
  • 不能重写 staticfinalprivate 方法。

作用

允许子类自定义行为,实现多态性。

示例

class Animal {
    public void sound() {
        System.out.println("动物发出声音");
    }
}

class Dog extends Animal {
    @Override
    public void sound() {
        System.out.println("汪汪");
    }
}

class Cat extends Animal {
    @Override
    public void sound() {
        System.out.println("喵喵");
    }
}
Animal myDog = new Dog();
myDog.sound(); // 输出 "汪汪"(多态)

@Override 是 Java 中的一个注解(Annotation),用于明确标识某个方法是重写(Override)了父类或接口中的方法。它的核心作用是让编译器帮助你检查方法重写的正确性,避免因拼写错误、参数不匹配等问题导致重写失败。

作用

  • 编译时检查
    如果使用了 @Override 注解,编译器会强制检查该方法是否:

    • 确实重写了父类或接口中的方法。
    • 方法名、参数列表、返回类型与父类完全一致。
    • 访问权限符不低于父类方法(例如,父类方法是 protected,子类不能是 private)。

    如果不符合重写规则,编译器会直接报错,而不是默默接受错误代码。

  • 提高代码可读性
    明确告诉其他开发者这是一个重写方法,增强代码的可维护性。

7.面向对象(OOP) VS 面向过程(POP)

之前学过的C语言是典型的面向过程编程语言,而现在正在学的Java面向对象编程语言的代表。

什么是面向过程编程?

  • 定义

    面向过程编程是一种以**过程(函数)**为核心的编程范式,通过将程序分解为一系列线性步骤(函数调用),逐步操作数据以完成任务。其核心思想是“怎么做”(How to do)。

  • 核心特征

    • 函数为中心:程序由函数构成,每个函数实现特定功能。
    • 数据与行为分离:数据(变量)独立于函数,函数通过参数接收数据并处理。
    • 线性流程:代码按预定义顺序执行,强调算法和逻辑步骤。
    • 典型语言:C、Pascal、Fortran。
  • 优缺点

    优点 缺点
    简单直观,适合小型程序 代码复用性差
    执行效率高 难以管理复杂系统
    适合算法密集型任务 数据与行为分离易导致耦合度高

什么是面向对象编程?

  • 定义

    面向对象编程是一种以对象为核心的编程范式,将现实世界的实体抽象为包含数据(属性)和行为(方法)的对象,并通过封装、继承、多态、抽象四大特性构建程序。其核心思想是“谁来做”(Who does what)。

  • 核心特征

    • 对象为中心:程序由对象组成,对象是类的实例。
    • 封装性:将数据和方法绑定,隐藏内部实现细节。
    • 继承性:子类复用父类的属性和方法。
    • 多态性:同一接口在不同对象中有不同实现。
    • 典型语言:Java、C++、Python。
  • 优缺点

    优点 缺点
    代码模块化,易维护扩展 学习曲线陡峭
    支持代码复用(继承/组合) 性能略低于面向过程
    适合复杂系统开发 过度设计可能导致冗余

核心对比

面向过程 面向对象
核心单位 函数 对象
设计目标 步骤分解 职责划分
数据与行为 分离 绑定(封装)
扩展性 低(需修改函数) 高(继承、多态)
适用场景 小型工具、算法、嵌入式开发 大型系统、GUI、企业级应用

叽里咕噜的说什么呢,想象你要做一道菜(比如番茄炒蛋),面向过程就是:你亲自动手,严格按照步骤一步一步来:

1. 洗番茄 → 2. 切番茄 → 3. 打鸡蛋 → 4. 开火 → 5. 炒菜 → 6. 出锅

关注的是步骤(先干什么,后干什么),所有事情都要自己干,没有分工,假如你还要做另一道菜(比如青椒肉丝),你需要重写一遍所有步骤。

想象你开了一家餐厅,面向对象就是:你把任务分给不同的人(对象),比如:

- 厨师对象:负责做菜  
- 服务员对象:负责端菜  
- 收银员对象:负责收钱

关注的是谁来做(每个对象只负责自己的任务),不同对象之间互相配合(比如服务员告诉厨师做菜),如果要新增一道菜,只需要让厨师学新菜谱,其他岗位不用变。

  • 面向过程“自己做所有事”(关注步骤)。
  • 面向对象“让别人帮你做事”(关注分工)。

为什么要有面向对象?

想象你开的是大型连锁餐厅

  • 如果每道菜都自己从头做到尾(面向过程),会累死,而且难以管理。
  • 如果用面向对象的方式,每个岗位各司其职,系统更灵活,容易扩展(比如新增一个配送员送外卖)。

8.类(Class)和对象(Object)

类(Class)

  • 定义

    • 是一个模板或蓝图,用于描述一类对象的属性(成员变量)行为(成员方法)
    • 类定义了对象的类型,但本身不占用内存空间,只有通过 new 关键字创建对象时才会分配内存。
  • 语法

    public class 类名 {
        // 成员变量(属性)
        数据类型 变量名;
    
        // 构造方法(初始化对象)
        public 类名(参数列表) {
            // 初始化代码
        }
    
        // 普通方法(行为)
        返回类型 方法名(参数列表) {
            // 方法体
        }
    }
    
  • 示例

    public class Car {
        // 成员变量(属性)
        String color;
        String brand;
      
        // 构造方法
        public Car(String color, String brand) {
            this.color = color;
            this.brand = brand;
        }
      
        // 方法(行为)
        public void start() {
            System.out.println(brand + "汽车启动了!");
        }
    }
    

对象(Object)

  • 定义

    • 对象 是类的一个具体实例,通过 new 关键字创建。
    • 每个对象在内存中独立存在,拥有自己的成员变量值(数据)和方法实现(行为)。
  • 如何创建对象?

    • 使用 new 关键字:

      • 分配内存:在堆内存中为对象分配空间。
      • 调用构造方法:初始化对象的成员变量。
      • 返回引用:将对象的内存地址赋给变量(引用)。
    • 语法

      类名 对象名 = new 类名(参数列表);
      
    • 示例

      public class Main {
          public static void main(String[] args) {
              // 创建 Car 类的对象
              Car myCar = new Car("红色", "Toyota");
              Car yourCar = new Car("蓝色", "BMW");
          
              // 调用对象的方法
              myCar.start(); // 输出:Toyota汽车启动了!
              yourCar.start(); // 输出:BMW汽车启动了!
          }
      }
      

9.成员变量 VS 局部变量

成员变量

  • 定义

    • 定义在类内部,但在方法、构造方法或代码块之外
    • 包括两种类型:
      • 实例变量(非静态成员变量)
      • 类变量(静态成员变量,用 static 修饰)
  • 特点

    • 作用域
      • 整个类内部均可访问。
      • 实例变量通过对象访问(obj.variable),静态变量通过类名访问(Class.variable)。
    • 生命周期
      • 实例变量:随对象创建而存在,对象被垃圾回收时销毁。
      • 静态变量:随类加载而存在,程序结束时销毁。
    • 默认值
      • 成员变量有默认初始值(如 int 默认为 0,对象类型默认为 null)。
    • 存储位置
      • 实例变量:存储在堆内存(对象内部)。
      • 静态变量:存储在方法区(类元数据区)。
  • 示例

    public class Car {
        // 实例变量(成员变量)
        private String brand;  // 默认值为 null
        private int speed;     // 默认值为 0
          
        // 静态变量(类变量)
        public static int wheels = 4;  // 显式初始化
      
        public void accelerate() {
            speed += 10;  // 可以直接访问成员变量
        }
    }
    

局部变量

  • 定义

    定义在方法、构造方法、代码块内部形参列表中。

  • 特点

    • 作用域
      • 仅在定义它的方法、代码块内部有效。
      • 超出作用域后无法访问。
    • 生命周期
      • 随方法/代码块的执行而创建,执行结束后销毁。
    • 默认值
      • 没有默认值,必须显式初始化后才能使用。
    • 存储位置
      • 存储在栈内存(方法调用栈帧中)。
  • 示例

    public class Calculator {
        public int add(int a, int b) {  // 形参 a、b 是局部变量
            int result = a + b;         // 局部变量 result
            return result;              // result 的作用域仅在 add 方法内
        }
      
        public void printSum() {
            int x = 5;                 // 局部变量 x
            int y = 10;
            System.out.println(x + y);  // 正确
            System.out.println(result); // 错误!result 在此不可见
        }
    }
    

核心对比

特性 成员变量 局部变量
定义位置 类内部,方法外 方法、代码块内部或形参列表
作用域 整个类内部 仅方法/代码块内部
生命周期 对象或类存在期间 方法/代码块执行期间
默认值 有默认值(如 0/null 必须显式初始化
存储位置 堆(实例变量)或方法区(静态)
访问权限 可添加访问修饰符(如 public 不能使用访问修饰符
内存分配 自动分配 需要手动初始化

局部变量和成员变量同名时怎么办?

局部变量会覆盖成员变量(就近原则),通过 this 关键字访问成员变量。

public class Test {
    private int value = 10;
    
    public void setValue(int value) {
        this.value = value;  // this.value 是成员变量,value 是局部变量
    }
}

10.构造方法(构造器)

在 Java 中,构造方法(Constructor)是类中用于初始化对象的特殊方法,在对象创建时自动调用。

核心特性

  • 命名与类名相同
    构造方法必须与类名完全一致(包括大小写)。

  • 无返回类型
    构造方法没有返回值(连 void 也不写),例如:

    public class Student {
        // 构造方法(无返回类型)
        public Student() { ... }
    }
    
  • 自动触发
    通过 new 关键字创建对象时,构造方法自动执行。

  • 可重载
    一个类可以有多个构造方法(参数不同),提供多种初始化方式。

分类

  • 默认构造方法(无参构造方法)

    • 如果类中未显式定义任何构造方法,Java 编译器会自动生成一个默认的无参构造方法

    • 示例:

      public class Student {
          // 编译器自动生成默认构造方法:public Student() {}
      }
      
  • 自定义构造方法(带参数)

    • 开发者显式定义,用于灵活初始化对象属性。

    • 示例:

      public class Student {
          private String name;
          private int age;
          
          // 自定义构造方法
          public Student(String name, int age) {
              this.name = name;
              this.age = age;
          }
      }
      

基本用法

public class Student {
    private String name;
    private int age;

    // 无参构造方法(显式定义)
    public Student() {
        
    }

    // 有参构造方法
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public static void main(String[] args) {
        Student stu1 = new Student();            // 调用无参构造方法
        Student stu2 = new Student("张三", 18);  // 调用有参构造方法
    }
}

构造方法的重载

public class Car {
    private String brand;
    private String color;

    // 构造方法1:仅初始化品牌
    public Car(String brand) {
        this.brand = brand;
        this.color = "黑色";
    }

    // 构造方法2:初始化品牌和颜色
    public Car(String brand, String color) {
        this.brand = brand;
        this.color = color;
    }
}

构造方法(构造器) 是对象创建的入口,用于确保对象初始状态合法。通过重载构造方法,可以提供多种初始化方式。

11.JavaBean是什么?

定义JavaBean 是一种符合特定编码规范的 Java 类,主要用于封装数据提供标准化操作

核心规范

  • 公共的无参构造方法

    • 类必须提供无参数构造方法,以便通过反射机制实例化对象。

    • 示例:

      public class User {
          public User() {
                
          } // 必须有无参构造方法
      }
      
  • 属性私有化

    • 所有属性(字段)必须声明为 private,禁止直接暴露给外部。

      private String name;
      private int age;
      
  • 通过公共方法访问属性

    • 提供 getXxx()setXxx() 方法操作属性(称为 Getter 和 Setter)。

    • 布尔类型属性可使用 isXxx() 作为 Getter。

      public String getName() {
          return name;
      }
      public void setName(String name) {
          this.name = name;
      }
      

示例

public class User {
    // 属性私有化
    private String name;
    private int age;
    private boolean active;

    // 无参构造方法(必须)
    public User() {
        
    }

    // 带参构造方法(可选)
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // Getter 和 Setter(必须)
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    // 布尔类型可使用 isXxx()
    public boolean isActive() {
        return active;
    }

    public void setActive(boolean active) {
        this.active = active;
    }
}

12.static关键字

static 是 Java 中用于修饰类成员(变量、方法、代码块、内部类)的关键字,表示该成员属于类本身而非类的实例。其主要目的是实现与类直接关联的功能或数据共享,避免重复创建实例的消耗。

应用场景

  • 静态变量(类变量)

    • 定义:用 static 修饰的成员变量,属于类而非实例。

    • 特点

      • 所有实例共享同一份静态变量。
      • 内存中仅在类加载时分配一次(存储在方法区/元空间)。
      • 可通过 类名.变量名 直接访问,无需实例化对象。
    • 示例

      public class Counter {
          static int count = 0;  // 静态变量
        
          public Counter() {
              count++;  // 所有实例共享 count
          }
        
          public static void main(String[] args) {
              new Counter();
              new Counter();
              System.out.println(Counter.count); // 输出 2
          }
      }
      
  • 静态方法

    • 定义:用 static 修饰的方法,属于类而非实例。

    • 特点

      • 只能直接访问静态成员(变量/方法),不能访问实例成员。
      • 可通过 类名.方法名() 直接调用。
      • 常用作工具方法(如 Math.sqrt())。
    • 示例

      public class MathUtils {
          public static int add(int a, int b) { // 静态方法
              return a + b;
          }
        
          public static void main(String[] args) {
              int result = MathUtils.add(3, 5); // 直接调用
              System.out.println(result); // 输出 8
          }
      }
      
  • 静态代码块

    • 定义:用 static { ... } 定义的代码块,在类加载时执行一次。

    • 用途

      • 初始化静态变量。
      • 加载静态资源(如配置文件)。
    • 示例

      public class DatabaseConfig {
          static String url;
          static String username;
        
          static {  // 静态代码块
              url = "jdbc:mysql://localhost:3306/mydb";
              username = "root";
              System.out.println("数据库配置已加载");
          }
      }
      
  • 静态内部类

    • 定义:用 static 修饰的内部类,不依赖外部类实例。

    • 特点

      • 可直接创建实例:new Outer.Inner()
      • 不能访问外部类的非静态成员。
      • 常用于工具类或单例模式。
    • 示例

      public class Outer {
          static class Inner {  // 静态内部类
              void print() {
                  System.out.println("静态内部类");
              }
          }
          
          public static void main(String[] args) {
              Outer.Inner inner = new Outer.Inner();  // 直接创建
              inner.print();
          }
      }
      

底层原理

  • 内存分配
    • 静态变量存储在方法区(Method Area)(Java 8 后为元空间 MetaSpace)。
    • 类加载时初始化,生命周期与类相同。
  • 类加载机制
    • JVM 首次使用类时加载类信息,静态代码块和静态变量在此阶段初始化。
    • 类卸载时静态资源才会释放(通常由 JVM 管理)。

注意事项

场景 规则
访问权限 静态方法只能访问静态成员,非静态方法可访问静态和非静态成员。
多线程安全 静态变量是共享资源,需通过同步机制(如 synchronized)保证线程安全。
继承与重写 静态方法不能被子类重写(但可以隐藏),且不支持 @Override
设计模式 过度使用 static 会导致代码耦合度高,违反面向对象设计原则。

为什么静态方法不能访问非静态成员?

非静态成员依赖对象实例存在,而静态方法在类加载时即可调用,此时对象可能尚未创建。

静态变量与实例变量的区别?

维度 静态变量 实例变量
归属 对象实例
内存分配 类加载时分配(方法区) 对象创建时分配(堆内存)
生命周期 与类共存亡 与对象共存亡

13.继承

定义

继承 是面向对象编程(OOP)的三大特性之一(封装、继承、多态),允许一个类(子类/派生类)基于另一个类(父类/基类)构建,复用父类的属性和方法,并可以扩展或修改其功能。

核心作用

  • 代码复用:避免重复编写公共代码。
  • 层次化设计:建立类之间的层次关系(如动物 → 猫 → 布偶猫)。
  • 多态支持:通过父类引用操作子类对象。

语法:使用 extends 关键字实现继承

class 父类 {
    // 父类属性和方法
}

class 子类 extends 父类 {
    // 子类特有的属性和方法
}

继承的成员范围

  • 子类可继承的成员
    • publicprotected 修饰的属性和方法。
    • 默认访问权限(无修饰符)的成员(若子类与父类在同一包中)。
  • 不可继承
    • private 成员。
    • 父类的构造方法(但可通过 super() 调用)。

子类可重写父类的方法,以提供特定实现。

class Animal {
    public void makeSound() {
        System.out.println("动物发出声音");
    }
}

class Cat extends Animal {
    @Override
    public void makeSound() {
        System.out.println("喵喵");
    }
}

构造方法与继承

  • 创建子类对象时,父类构造方法优先执行。

    class Parent {
        Parent() {
            System.out.println("父类构造方法");
        }
    }
    
    class Child extends Parent {
        Child() {
            super(); // 默认隐含调用父类无参构造
            System.out.println("子类构造方法");
        }
    }
    
    // 输出:
    // 父类构造方法
    // 子类构造方法
    
  • 若父类只有带参构造方法,子类必须显式调用 super(参数)

    class Parent {
        Parent(int value) { ... }
    }
      
    class Child extends Parent {
        Child() {
            super(10); // 必须显式调用
        }
    }
    

单继承与多继承

Java只支持单继承(一个子类只能有一个直接父类),但可以通过接口(implements)实现多继承的效果

14.this和super关键字

this关键字

  • 作用

    • 指向当前对象的引用,用于在类的内部访问当前对象的成员(属性、方法、构造方法)。
    • 主要解决变量名冲突(如局部变量与成员变量同名)。
  • 使用场景

    • 区分成员变量和局部变量

      public class Person {
          private String name;  // 成员变量
          
          public void setName(String name) {  // 参数(局部变量)
              this.name = name;  // 用 this 区分同名变量
          }
      }
      
    • 调用当前对象的其他构造方法(必须在构造方法的第一行使用)

      public class Person {
          private String name;
          private int age;
          
          // 无参构造方法调用有参构造方法
          public Person() {
              this("Unknown", 0);  // 调用下面的构造方法
          }
          
          public Person(String name, int age) {
              this.name = name;
              this.age = age;
          }
      }
      
    • 返回当前对象本身

      public class Counter {
          private int count;
            
          public Counter increment() {
              count++;
              return this;  // 返回当前对象,支持链式调用
          }
      }
        
      // 使用示例
      Counter counter = new Counter().increment().increment();
      
  • 注意事项

    • 不能用于静态方法static 方法属于类,不依赖对象)。
    • 不能单独使用(必须指向具体的成员)。

super关键字

  • 作用

    • 指向父类对象的引用,用于在子类中访问父类的成员(属性、方法、构造方法)。
    • 主要解决继承中的成员覆盖问题
  • 使用场景

    • 调用父类的构造方法

      • 必须在子类构造方法的第一行使用。

      • 如果父类没有无参构造方法,必须显式调用 super(...)

        class Animal {
            private String type;
              
            public Animal(String type) {
                this.type = type;
            }
        }
          
        class Dog extends Animal {
            public Dog() {
                super("犬科");  // 显式调用父类有参构造方法
            }
        }
        
    • 访问父类的成员变量或方法

      • 当子类覆盖父类方法时,用 super 调用父类原始方法。

        class Animal {
            public void eat() {
                System.out.println("动物在吃东西");
            }
        }
          
        class Dog extends Animal {
            @Override
            public void eat() {
                super.eat();  // 先调用父类的 eat()
                System.out.println("狗在啃骨头");
            }
        }
        
    • 访问父类被隐藏的成员变量

      class Parent {
          String name = "Parent";
      }
        
      class Child extends Parent {
          String name = "Child";
            
          public void printNames() {
              System.out.println(super.name);  // 输出 Parent
              System.out.println(this.name);   // 输出 Child
          }
      }
      
  • 注意事项

    • 不能用于静态上下文(静态方法或静态块)。
    • 不能单独使用(必须指向具体的父类成员)。

注意

  • 在构造方法中,this()super() 不能共存,必须放在第一行且只能调用一次。
  • super 只能直接访问直接父类,无法跨级访问祖父类。

示例

class Vehicle {
    String type;
    
    public Vehicle(String type) {
        this.type = type;
    }
    
    public void start() {
        System.out.println(type + "启动");
    }
}

class Car extends Vehicle {
    String brand;
    
    public Car(String brand) {
        super("汽车");  // 调用父类构造方法
        this.brand = brand;
    }
    
    @Override
    public void start() {
        super.start();  // 调用父类的 start()
        System.out.println(brand + "汽车正在行驶");
    }
}

public class Main {
    public static void main(String[] args) {
        Car car = new Car("丰田");
        car.start();
    }
}

// 输出:
// 汽车启动
// 丰田汽车正在行驶

15.抽象类 VS 接口

什么是抽象?

抽象(Abstraction)是面向对象编程的核心思想之一,指提取对象的本质特征,忽略具体实现细节。通过抽象,可以定义统一的规范或模板,让具体实现由子类或实现类完成。

抽象类(Abstract Class)

  • 定义:用 abstract 修饰的类,称为抽象类。它可以包含抽象方法(无具体实现)和具体方法(有实现),用于定义部分实现的模板

  • 核心特性

    • 不能被实例化:只能通过子类继承后使用。
    • 可以包含抽象方法:用 abstract 修饰的方法,无方法体。
    • 可以包含具体方法:普通方法的实现。
    • 可以定义成员变量:与普通类相同。
    • 可以有构造方法:用于子类初始化。
  • 示例

    public abstract class Animal {
        // 抽象方法(无实现)
        public abstract void makeSound();
    
        // 具体方法(有实现)
        public void eat() {
            System.out.println("动物进食");
        }
    }
    
  • 使用场景:

    • 定义模板方法模式:父类定义算法骨架,子类实现具体步骤。
    • 部分方法需复用:抽象类中既有通用实现,又预留扩展点。
    • 需要状态管理:抽象类可定义成员变量保存状态。

接口(Interface)

  • 定义:用 interface 定义的类型,用于描述对象的行为规范。接口中的方法默认是抽象的(Java 8 前),支持多继承。

  • 核心特性

    • 默认方法(Default Method):用 default 修饰,提供默认实现。
    • 静态方法(Static Method):用 static 修饰,直接通过接口调用。
    • 私有方法(Java 9+):用 private 修饰,辅助默认方法。
    • 成员变量默认是 public static final:即常量。
    • 不能定义构造方法:无法实例化。
  • 示例

    public interface Flyable {
        // 抽象方法(默认 public abstract)
        void fly();
    
        // 默认方法(Java 8+)
        default void glide() {
            System.out.println("滑翔中");
        }
    
        // 静态方法(Java 8+)
        static int getMaxSpeed() {
            return 1000;
        }
    }
    
  • 使用场景

    • 定义行为规范:如 Runnable 接口定义线程任务。
    • 实现多继承:一个类可实现多个接口。
    • 解耦组件:通过接口隔离实现与调用方。

对比

维度 抽象类(Abstract Class) 接口(Interface)
关键字 abstract class interface
实例化 不能直接实例化 不能直接实例化
成员变量 普通变量(可非 final) 默认 public static final(常量)
构造方法 可以有 不能有
继承关系 单继承(一个子类只能继承一个抽象类) 多继承(一个类可实现多个接口)
设计目的 代码复用,定义部分实现 定义行为规范,解耦设计

如何选择

  • 优先使用接口:避免继承带来的强耦合。
  • 抽象类用于代码复用:当多个类有共同逻辑时。

16.多态

定义多态(Polymorphism) 是面向对象编程(OOP)的核心特性之一,指同一操作作用于不同对象时,可以有不同的行为。它允许使用统一的接口处理不同类型的对象,从而提高代码的灵活性和可扩展性。

Java 中的多态分为两类:

类型 实现方式 特点
编译时多态 方法重载(Overloading) 根据参数列表在编译时确定调用的方法。
运行时多态 方法重写(Overriding) + 继承 根据对象实际类型在运行时确定调用的方法。

运行时多态(动态绑定)

  • 核心机制:JVM 在运行时根据对象的实际类型(而非引用类型)动态绑定要执行的方法。

  • 条件

    • 继承关系:存在父类与子类。
    • 方法重写:子类重写父类的方法。
    • 向上转型:父类引用指向子类对象。
  • 示例

    class Animal {
        public void sound() {
            System.out.println("动物发出声音");
        }
    }
      
    class Dog extends Animal {
        @Override
        public void sound() {
            System.out.println("汪汪");
        }
    }
      
    class Cat extends Animal {
        @Override
        public void sound() {
            System.out.println("喵喵");
        }
    }
      
    public class Main {
        public static void main(String[] args) {
            Animal a1 = new Dog();  // 向上转型
            Animal a2 = new Cat();
      
            a1.sound();  // 输出 "汪汪"(动态绑定)
            a2.sound();  // 输出 "喵喵"
        }
    }
    

应用场景

  • 统一接口处理不同对象。
  • 结合工厂模式,通过多态创建不同子类对象。

多态与类型转换

  • 向上转型

    • 子类对象赋值给父类引用(自动完成)。

    • 特点:安全,但只能访问父类声明的方法。

      Animal animal = new Dog();  // 向上转型
      
  • 向下转型

    • 父类引用强制转换为子类类型(需显式转换)。

    • 风险:可能抛出 ClassCastException

    • 安全做法:使用 instanceof 检查。

      if (animal instanceof Dog) {
          Dog dog = (Dog) animal;  // 安全向下转型
          dog.bark();
      }
      

注意事项

  • 静态方法不支持重写,无多态性(通过类名调用)。
  • 私有方法不可被重写,无多态性。
  • final 方法禁止重写,无多态性。
  • 成员变量无多态性,访问时取决于引用类型。
  • 构造方法不可被重写,无多态性。

17.权限修饰符

权限修饰符总览

修饰符 类内部 同包内 子类(不同包) 其他包
public
protected
默认(不写)
private

  • 默认权限:不使用任何修饰符,称为包级私有(package-private)。
  • 子类权限protected 允许不同包的子类访问父类成员。
  • 类本身的修饰符:外部类只能使用 public 或默认权限(不能是 protectedprivate)。

public(公开)

  • 作用范围:任何地方均可访问。

  • 典型场景

    • 开放给外部调用的方法(如工具类方法)。
    • 常量(public static final)。
    • 主类(包含 main 方法的类必须用 public)。
  • 示例

    public class Calculator {
        public static final double PI = 3.14159;  // 公开常量
          
        public int add(int a, int b) {  // 公开方法
            return a + b;
        }
    }
    

protected(受保护)

  • 作用范围:本类、同包类、不同包的子类。

  • 典型场景

    • 父类中希望被子类继承或重写的方法或属性。
    • 框架设计中需要被子类扩展的成员。
  • 示例

    public class Animal {
        protected void breathe() {  // 子类可继承并调用
            System.out.println("呼吸");
        }
    }
      
    class Dog extends Animal {
        public void showBreathe() {
            breathe();  // 子类可直接调用父类的 protected 方法
        }
    }
    

默认权限(包级私有)

  • 作用范围:本类、同包类。

  • 典型场景

    • 内部工具方法或属性,不希望被其他包访问。
    • 模块化设计中,包内协作的类。
  • 示例

    class Logger {  // 默认权限,只能在同包内使用
        void log(String message) {  // 默认权限方法
            System.out.println("[LOG] " + message);
        }
    }
    

private(私有)

  • 作用范围:仅本类内部。

  • 典型场景

    • 隐藏类的内部实现细节(如成员变量)。
    • 防止外部直接修改数据,通过公共方法(getter/setter)控制访问。
  • 示例

    public class BankAccount {
        private double balance;  // 私有属性,外部无法直接访问
          
        public void deposit(double amount) {  // 公开方法控制存取
            if (amount > 0) {
                balance += amount;
            }
        }
          
        public double getBalance() {  // 提供公共读取接口
            return balance;
        }
    }
    

注意事项

  • 类的修饰符
    • 外部类只能使用 public 或默认权限。
    • 内部类可以是 privateprotected
  • 方法重写时的权限
    • 子类重写父类方法时,权限不能缩小(如父类方法是 public,子类不能改为 protected)。
  • 构造方法的权限
    • 构造方法可以是任意权限(如 private 用于单例模式)。

建议

  • 最小化访问原则
    • 优先使用最严格的权限(如 private),仅在必要时放宽。
    • 减少代码耦合,提高安全性。
  • 封装数据
    • 成员变量尽量设为 private,通过公共方法(getter/setter)控制访问。
    • 避免直接暴露内部状态。
  • 模块化设计
    • 使用默认权限隐藏包内实现细节。
    • 通过 public 类或方法对外提供接口。

18.final关键字

定义final 是 Java 中用于限制类、方法或变量的修改或继承的关键字,其核心目标是增强代码的安全性、不可变性和设计约束

作用

修饰对象 作用
变量 变量值(或引用)不可变(常量)。
方法 方法不能被子类重写。
类不能被继承。
  • 修饰变量

    • 基本数据类型变量

      • 变量值一旦初始化,不可修改。

        final int MAX_VALUE = 100;
        // MAX_VALUE = 200;  // 编译错误:无法为final变量赋值
        
    • 引用类型变量

      • 引用不可变(即不能指向其他对象),但对象内部状态可修改。

        final List<String> list = new ArrayList<>();
        list.add("Java");   // 允许:修改对象内容
        // list = new LinkedList<>(); // 编译错误:不能改变引用
        
    • 成员变量与局部变量

      • 成员变量:必须显式初始化(可在声明时、构造方法或初始化块中赋值)。

      • 局部变量:只需在使用前初始化一次。

        class Example {
            final int a = 10;        // 声明时初始化
            final int b;
            
            Example() {
                b = 20;              // 构造方法中初始化
            }
        }
        
  • 修饰方法

    • 禁止子类重写

      • 确保父类方法逻辑不被修改。

        class Parent {
            public final void show() {
                System.out.println("父类方法");
            }
        }
          
        class Child extends Parent {
            // 编译错误:无法重写final方法
            // public void show() { ... }
        }
        
    • private 的隐式 final

      • 若方法为 private,则隐式具有 final 效果(子类无法访问,更无法重写)。

        class Parent {
            private void method() {} // 隐式final
        }
            
        class Child extends Parent {
            // 合法:实际是子类的新方法,非重写
            private void method() {}
        }
        
  • 修饰类

    • 禁止继承

      • 保证类不可被扩展(如 StringInteger 等核心类均为 final)。

        final class ImmutableClass { ... }
            
        // 编译错误:无法继承final类
        // class SubClass extends ImmutableClass { ... }
        
    • 设计不可变类

      • 若类为 final,且所有字段为 final,则对象创建后状态不可变。

        public final class ImmutablePoint {
            private final int x;
            private final int y;
              
            public ImmutablePoint(int x, int y) {
                this.x = x;
                this.y = y;
            }
            // 仅提供getter,无setter
        }
        

19.构造代码块 VS 静态代码块

在 Java 中,代码块(Code Block)是一段被大括号 {} 包围的代码,用于控制变量的作用域或定义代码的执行逻辑。

实例代码块(构造代码块)

  • 定义:直接写在类中,无修饰符,每次创建对象时执行(在构造方法之前)。

  • 作用:初始化实例变量或执行对象共有的初始化逻辑。

  • 语法

    {
        // 初始化实例变量或通用逻辑
    }
    
  • 示例

    public class Car {
        String brand;
        int speed;
          
        // 实例代码块:每次 new 对象时执行
        {
            speed = 0;  // 所有 Car 对象初始速度为 0
            System.out.println("实例代码块执行:车辆已启动");
        }
          
        public Car(String brand) {
            this.brand = brand;
        }
    }
    

静态代码块

  • 定义:用 static 修饰,属于类级别,在类加载时执行一次

  • 作用:初始化静态变量或加载静态资源(如配置文件)。

  • 语法

    static {
        // 初始化静态资源
    }
    
  • 示例

    public class DatabaseConfig {
        static String url;
        static String username;
          
        // 静态代码块:类加载时读取配置
        static {
            url = "jdbc:mysql://localhost:3306/mydb";
            username = "admin";
            System.out.println("静态代码块执行:数据库配置已加载");
        }
    }
    

实例代码块 vs 静态代码块

  • 实例代码块:每次 new 对象时执行(在构造方法之前)。
  • 静态代码块:类加载时执行一次(仅一次)。

示例

public class ExecutionOrder {
    static {
        System.out.println("静态代码块执行");
    }
    
    {
        System.out.println("实例代码块执行");
    }
    
    public ExecutionOrder() {
        System.out.println("构造方法执行");
    }
    
    public static void main(String[] args) {
        new ExecutionOrder();
        new ExecutionOrder();
    }
}

// 输出:
// 静态代码块执行
// 实例代码块执行
// 构造方法执行
// 实例代码块执行
// 构造方法执行

使用场景与建议

  • 实例代码块
    • 场景:多个构造方法共有的初始化逻辑(如设置默认值)。
    • 建议:可将通用逻辑提取到私有方法中,由构造方法调用。
  • 静态代码块
    • 场景:初始化全局配置(如数据库连接)、加载静态资源(如图片、文件)。
    • 建议:避免在静态代码块中执行耗时操作(影响类加载速度)。

总结

  • 静态代码块处理类级初始化,实例代码块处理对象级初始化。
  • 优先选择构造方法或方法封装逻辑,避免滥用代码块!

20.内部类

在 Java 中,内部类(Inner Class) 是定义在另一个类内部的类,它可以访问外部类的成员,并实现更灵活的代码封装。

成员内部类

  • 定义:直接定义在外部类的成员位置(与属性、方法同级)。

  • 特点

    • 可以访问外部类的所有成员(包括 private)。
    • 依赖外部类实例存在(不能有静态成员,除非用 static final 常量)。
  • 示例

    public class Outer {
        private int outerField = 10;
          
        // 成员内部类
        class Inner {
            void print() {
                System.out.println("访问外部类字段:" + outerField);  // 直接访问外部类成员
            }
        }
    }
    
    Outer outer = new Outer();
    Outer.Inner inner = outer.new Inner();  // 必须通过外部类实例创建
    

静态内部类

  • 定义:用 static 修饰的内部类。

  • 特点

    • 不依赖外部类实例(可直接创建)。
    • 不能直接访问外部类的非静态成员(只能访问静态成员)。
  • 示例

    public class Outer {
        private static int staticField = 20;
        private int instanceField = 30;
          
        static class StaticInner {
            void print() {
                System.out.println(staticField);  // 合法
                // System.out.println(instanceField);  // 错误!无法访问非静态成员
            }
        }
    }
    
    Outer.StaticInner inner = new Outer.StaticInner();  // 直接创建
    

局部内部类

  • 定义:定义在方法或代码块内部的类。

  • 特点

    • 作用域仅限于所在方法或代码块。
    • 可以访问外部类的成员,但只能访问所在方法的 final 或 effectively final 局部变量
  • 示例

    public class Outer {
        private int outerField = 40;
          
        public void method() {
            int localVar = 50;  // 必须为 final 或 effectively final
              
            class LocalInner {
                void print() {
                    System.out.println(outerField);  // 合法
                    System.out.println(localVar);   // 合法(localVar 不可修改)
                }
            }
              
            LocalInner inner = new LocalInner();
            inner.print();
        }
    }
    

匿名内部类

  • 定义:没有名字的局部内部类,通常用于简化代码(如事件监听、线程实现)。

  • 特点

    • 直接继承父类或实现接口。
    • 只能使用一次(无法复用)。
  • 示例

    public interface ClickListener {
        void onClick();
    }
      
    public class Button {
        public void setOnClickListener(ClickListener listener) {
            listener.onClick();
        }
    }
      
    // 使用匿名内部类
    Button button = new Button();
    button.setOnClickListener(new ClickListener() {
        @Override
        public void onClick() {
            System.out.println("按钮被点击!");
        }
    });
    

应用场景

  • 封装性增强

    • 场景:将仅用于某个外部类的辅助逻辑封装在内部(如链表节点 Node)。

      public class LinkedList {
          private Node head;
            
          // 成员内部类:外部无需关心 Node 的实现
          private class Node {
              int data;
              Node next;
          }
      }
      
  • 回调与事件处理

    • 场景:匿名内部类简化事件监听(如 Android 点击事件)。

      // Java Swing 示例
      JButton button = new JButton("提交");
      button.addActionListener(new ActionListener() {
          @Override
          public void actionPerformed(ActionEvent e) {
              System.out.println("提交表单");
          }
      });
      
  • 访问外部类私有成员

    • 场景:内部类直接操作外部类私有属性。

      public class Outer {
          private int secret = 100;
            
          class Inner {
              void revealSecret() {
                  System.out.println("秘密是:" + secret);  // 直接访问 private 成员
              }
          }
      }
      
  • 静态工具类

    • 场景:静态内部类实现与外部类相关的工具逻辑。

      public class MathUtils {
          // 静态内部类:提供特定算法
          public static class Calculator {
              public static int add(int a, int b) {
                  return a + b;
              }
          }
      }
          
      // 使用
      int sum = MathUtils.Calculator.add(2, 3);
      

内部类是实现高内聚、低耦合的重要手段,但需根据场景选择合适类型,避免滥用!

21.异常

定义异常(Exception) 是程序执行过程中发生的非正常事件,它会中断程序的正常流程。Java 通过异常机制提供了一种结构化的错误处理方式。

分类

Java 异常类均继承自 Throwable,分为三大类:

类型 特点 示例
Error JVM 严重错误,程序无法恢复(通常不处理) OutOfMemoryError
检查型异常(Checked Exception) 编译时强制要求处理(继承自 Exception 但非 RuntimeException 的子类) IOException, SQLException
非检查型异常(Unchecked Exception) 运行时异常,通常由程序逻辑错误引起(RuntimeException 及其子类) NullPointerException, ArrayIndexOutOfBoundsException

异常处理

  • 使用try-catch-finally块捕获并处理异常

    • 核心思想:在代码中直接捕获异常并处理,避免程序终止。

    • 适用场景:当前方法明确知道如何处理异常时(如重试、回滚、日志记录等)。

    • 语法

      try {
          // 可能抛出异常的代码
      } catch (ExceptionType1 e1) {
          // 处理特定异常
      } catch (ExceptionType2 e2) {
          // 处理其他异常
      } finally {
          // 无论是否异常,最终执行的代码(如释放资源)
      }
      
    • 示例

      try {
          FileInputStream fis = new FileInputStream("file.txt");
      } catch (FileNotFoundException e) {
          System.out.println("文件未找到");
      } finally {
          if (fis != null) fis.close(); // 需处理IOException
      }
      
  • 使用throws声明抛出异常

    • 核心思想:将异常抛给调用者处理,当前方法不直接处理。

    • 适用场景:当前方法无法处理异常(如工具类方法、底层逻辑),需由上层调用者决定如何应对。

    • 示例

      public void readFile() throws FileNotFoundException, IOException {
          FileReader file = new FileReader("test.txt");
          // 其他可能抛出 IOException 的操作
      }
      
  • throw抛出异常

    • 手动抛出异常对象,可以是自定义异常。

    • 示例

      if (age < 0) {
          throw new IllegalArgumentException("年龄不能为负数");
      }
      

自定义异常

  • 步骤

    • 继承 Exception(检查型异常)或 RuntimeException(非检查型异常)。
    • 提供构造方法(通常调用父类构造)。
  • 示例

    public class InsufficientFundsException extends RuntimeException {
        public InsufficientFundsException(String message) {
            super(message);
        }
    }
    
    // 使用
    if (balance < amount) {
        throw new InsufficientFundsException("余额不足");
    }
    

Error 和 Exception 的区别?

  • Error:JVM 严重问题(如内存溢出),程序无法恢复。
  • Exception:可捕获处理的异常(检查型需处理,非检查型通常不强制)。

22.为什么String类可以不用new创建对象?

在Java中,String类可以不用new关键字创建对象,这是由**字符串常量池(String Pool)**机制和Java对字符串字面量的特殊处理共同实现的。

字符串常量池(String Pool)

  • 什么是字符串常量池?

    字符串常量池是Java堆内存中的一块特殊存储区域,用于缓存字符串字面量。当通过字面量(如"abc")创建字符串时,JVM会先在字符串常量池中检查是否存在相同内容的字符串:

    • 如果存在:直接返回池中已有字符串的引用,不创建新对象
    • 如果不存在:在池中创建该字符串对象,并返回引用。
  • 目的:减少重复字符串的内存占用,提升性能。

  • 示例

    String s1 = "abc";  // 第一次创建,池中没有,故在池中新建对象
    String s2 = "abc";  // 直接复用池中的"abc",s1和s2指向同一对象
    System.out.println(s1 == s2); // 输出 true(引用相同)
    

new String()与字面量的区别

  • 使用new关键字

    每次都会在堆中强制创建新对象,即使内容相同。

    String s3 = new String("abc"); // 在堆中新建对象,不检查常量池
    String s4 = new String("abc"); // 再新建一个对象
    System.out.println(s3 == s4); // 输出 false(引用不同)
    
  • 使用字面量

    直接利用常量池,避免重复构建。

    String s5 = "abc"; // 从常量池获取
    String s6 = "abc"; // 同上
    System.out.println(s5 == s6); // 输出 true(引用相同)
    

字符串的不可变性

  • String类被设计为不可变(所有修改操作都返回新对象),这使得常量池可以安全地缓存字符串:
    • 多个引用共享同一字符串时,无需担心内容被意外修改。
    • 缓存字符串的哈希值,提升性能(如HashMap的键)。

编译时优化

  • 字面量的处理

    在编译阶段,字符串字面量会被直接写入类的常量池。当类加载时,JVM会将这些字面量预先加载到字符串常量池中。

    String s7 = "hello" + " world"; // 编译期优化为"hello world"
    // 等价于 String s7 = "hello world";
    

intern方法

手动将字符串添加到常量池:调用intern()方法时,如果池中已有相同内容的字符串,返回其引用;否则将当前字符串添加到池中。

String s8 = new String("xyz").intern();
String s9 = "xyz";
System.out.println(s8 == s9); // 输出 true

总结

Java通过字符串常量池和字面量处理机制,使得直接赋值(如"abc")能够复用已有对象,无需显式使用new。这种处理能够节省内存、提升性能,同时依赖字符串的不可变性保证安全性。

23.增强for

增强 for 循环(也称为 for-each 循环)是 Java 5 引入的语法糖,旨在简化集合和数组的遍历操作。它隐藏了迭代器或索引的细节,使代码更简洁易读。

基本语法

for (元素类型 变量名 : 集合或数组) {
 // 操作变量
}

示例

List<String> list = Arrays.asList("A", "B", "C");
for (String s : list) {
 System.out.println(s);  // 输出 A, B, C
}

int[] array = {1, 2, 3};
for (int num : array) {
 System.out.println(num);  // 输出 1, 2, 3
}

原理

  • 遍历数组:编译器会将其转换为传统索引循环

    // 源码:for (int num : array) { ... }
    for (int i = 0; i < array.length; i++) {
        int num = array[i];
        // 操作 num
    }
    
  • 遍历集合:编译器会调用集合的 iterator() 方法,使用迭代器遍历

    // 源码:for (String s : list) { ... }
    Iterator<String> iterator = list.iterator();
    while (iterator.hasNext()) {
        String s = iterator.next();
        // 操作 s
    }
    

注意事项

  • 无法直接访问索引

    若需索引,需改用传统 for 循环或额外计数变量。

    int index = 0;
    for (String s : list) {
        System.out.println(index + ": " + s);
        index++;
    }
    
  • 不能修改集合结构

    遍历时直接调用集合的 add()remove() 会抛出 ConcurrentModificationException

你可能感兴趣的:(java,学习,笔记)