Java面向对象基础词条


static代码块

在一个类中,我们可以定义若干个static代码块

public class StaticTest{
static int num=100;
static{
num+=100;
System.out.println(num);
}
static{
num+=500;
System.out.println(num);
}
}

JVM加载类时,会加载静态代码块,如有多个则按顺序加载,每个代码块只会被执行一次,静态代码块的加载和对象的创建没有关系

//在main函数中
StaticTest st1=new StaticTest();
StaticTest st1=new StaticTest(); //不会有输出,类通常只会被加载一次
System.out.println(num);
//输出:
//200
//700
//700

主动使用类的7种情况(会触发类加载和静态代码块)

public class TriggerConditions {
public static void main(String[] args) throws Exception {
System.out.println("=== 各种触发情况 ===");

// 1. 创建类的实例
new TriggerDemo();

// 2. 访问类的静态变量(非常量)
System.out.println(TriggerDemo.staticField);

// 3. 调用类的静态方法
TriggerDemo.staticMethod();

// 4. 使用反射
Class.forName("TriggerDemo");

// 5. 初始化子类(会先初始化父类)
new ChildClass();

// 6. 作为启动类(包含main方法的类)

// 7. 某些反射调用
}
}

class TriggerDemo {
static String staticField = "静态字段";

static {
System.out.println("TriggerDemo静态代码块执行");
}

public static void staticMethod() {
System.out.println("静态方法执行");
}
}

class ParentClass {
static {
System.out.println("ParentClass静态代码块执行");
}
}

class ChildClass extends ParentClass {
static {
System.out.println("ChildClass静态代码块执行");
}
}

实例代码块

实例代码块是没有static修饰的代码块,在每次创建对象时执行

public class MyClass {
// 实例代码块
{
System.out.println("实例代码块执行");
}

public MyClass() {
System.out.println("构造方法执行");
}
}
//实例化MyClass时输出:
//实例代码块执行
//构造方法执行

执行顺序:父类实例代码块 → 父类构造方法 → 子类实例代码块 → 子类构造方法


static修饰符

凡是被static修饰的变量、方法、代码块,这些都属于类本身,不属于对象,但对象可以访问这些static成员,所有对象共享一个static成员,修改这些static成员会影响所有对象

方面 static成员 实例成员
归属 属于类 属于对象
内存位置 方法区/元空间 堆内存
加载时机 类加载时 对象创建时
生命周期 程序运行期间 对象存在期间
访问方式 类名.成员 对象.成员
共享性 所有对象共享 每个对象独立
对象能否访问 可以(不推荐) 可以(必须)
类能否直接访问 可以 不可以

方法重载和方法重写

对比维度 方法重载 (Overload) 方法重写 (Override)
定义 同一个类中,多个方法名称相同但参数不同 子类中重新定义父类的同名同参数方法
位置关系 同一个类中,或父子类之间 只能在父子类之间(继承关系)
方法签名 必须不同(参数类型、个数、顺序至少一个不同) 必须完全相同(方法名和参数列表)
返回类型 可以不同,但仅返回类型不同不能构成重载 必须相同,或是父类返回类型的子类(协变返回)
访问修饰符 可以不同,没有限制 不能更严格(可以更宽松)
异常声明 可以不同,没有限制 不能抛出更宽泛的检查异常 可以抛出相同、更具体或不抛出异常
static/final 可以重载static/final方法 不能重写final方法 static方法只能隐藏,不是重写
private方法 可以重载private方法 不能重写(子类不可见)
绑定时机 编译时绑定(静态绑定、早期绑定) 运行时绑定(动态绑定、晚期绑定)
多态性 编译时多态(静态多态) 运行时多态(动态多态)
性能 编译时确定,性能较好 运行时确定,稍有性能开销
@Override注解 不需要,通常不使用 强烈建议使用,让编译器检查是否正确重写
调用机制 编译器根据参数列表决定调用哪个方法 JVM根据对象的实际类型决定调用哪个方法
设计目的 提供多种方式处理不同类型/数量的数据 修改/扩展父类行为,实现多态
应用场景 构造函数重载、工具方法多种形式 框架设计、模板方法模式、多态实现

super访问父类成员

super不可以访问父类的私有成员

若要用super访问父类构造,只能在子类构造中使用,且须出现在第1行

class Animal {
private String name;
private int age;

// 父类构造方法1
public Animal() {
System.out.println("Animal无参构造方法被调用");
this.name = "未知动物";
this.age = 0;
}
// 父类构造方法2
public Animal(String name, int age) {
System.out.println("Animal有参构造方法被调用");
this.name = name;
this.age = age;
}
}

class Dog extends Animal {
private String breed;

// 子类构造方法1:调用父类无参构造
public Dog() {
super(); // 调用父类Animal(),可以省略(编译器自动添加)
System.out.println("Dog无参构造方法被调用");
this.breed = "未知品种";
}
// 子类构造方法2:调用父类有参构造
public Dog(String name, int age, String breed) {
super(name, age); // 必须放在第一行!
System.out.println("Dog有参构造方法被调用");
this.breed = breed;
}
}

Object类

Object类是所有类的父类,任何类都默认继承自Object类,以下两个方法常被重写

toString方法:返回当前对象本身的有关信息,按字符串返回,内容为类的全名称和内存地址

equals方法:比较两个对象地址是否相同,是则返回true,相当于==

==在比较简单数据类型时直接比较值,比较引用类型时则判断两者是否为同一对象(地址)

一个字符串变量a在进行a.equals(“A”)运算的本质也是调用了equals方法,但这个方法在String类进行了改写,只比较内容是否相同


抽象类和抽象方法

abstract修饰的类或方法就是抽象类或抽象方法

抽象类中可以有非抽象方法

抽象方法必须在抽象类中

抽象方法没有方法体

抽象方法必须在子类中被实现,除非子类是抽象类

// 抽象类:动物
abstract class Animal {
// 属性(可以有非抽象的属性和方法)
protected String name;
protected int age;

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

// 抽象方法:动物发出声音(没有方法体)
public abstract void makeSound();

// 抽象方法:动物如何进食
public abstract void eat();

// 具体方法:所有动物都会睡觉
public void sleep() {
System.out.println(name + "正在睡觉...");
}

// 具体方法:获取信息
public void displayInfo() {
System.out.println("动物: " + name + ", 年龄: " + age + "岁");
}
}

// 具体子类:狗
class Dog extends Animal {
private String breed; // 特有属性:品种

public Dog(String name, int age, String breed) {
super(name, age); // 调用父类构造方法
this.breed = breed;
}

// 必须实现父类的抽象方法
@Override
public void makeSound() {
System.out.println(name + "汪汪叫!");
}

@Override
public void eat() {
System.out.println(name + "正在吃狗粮");
}

// 可以添加特有方法
public void wagTail() {
System.out.println(name + "开心地摇尾巴");
}
}

final修饰符

final修饰的类不能有子类

final修饰的方法不能被重写

final修饰的变量会变成常量


向上转型和向下转型

向上转型

自动隐式转换,总是安全

语法Parent p = new Child();

class Animal {
void eat() {
System.out.println("动物吃东西");
}
}

class Dog extends Animal {
void bark() {
System.out.println("汪汪叫");
}
}

public class WhyCasting {
public static void main(String[] args) {
Animal animal = new Dog(); // 只知道是Animal,不知道具体类型
animal.eat(); // 可以
animal.bark(); // 编译错误!编译器只知道它是Animal
}
}

向下转型

需要调用子类特有方法,而引用是父类类型时,需要显式强制转换,可能不安全

语法Child c = (Child) p;

class Animal {
void eat() {
System.out.println("动物吃东西");
}
}

class Dog extends Animal {
void bark() {
System.out.println("汪汪叫");
}
}

public class WhyCasting {
public static void main(String[] args) {
Dog dog2 = (Dog) animal; // 强制转换:我确定animal是Dog
dog2.eat(); // 可以
dog2.bark(); // 可以
}
}

for增强循环(for-each循环)

基本语法

for (元素类型 元素变量 : 集合或数组) {
// 循环体
}

接口(Interface)

接口是类似于一种合约,接口内部定义了一些列的抽象方法,当实现类采用了某一个接口就必须实现该接口中的所有方法

// 支付接口,包含默认方法
public interface PaymentProcessor {

// 抽象方法:处理支付(必须由实现类实现)
boolean processPayment(double amount);

// 抽象方法:获取支付方式名称
String getPaymentMethod();

// 默认方法:生成交易ID(所有支付方式通用的逻辑)
default String generateTransactionId() {
String id = "TXN" + System.currentTimeMillis() + (int)(Math.random() * 1000);
System.out.println("生成的交易ID: " + id);
return id;
}

// 静态方法:获取支持的货币
static String[] getSupportedCurrencies() {
return new String[]{"CNY", "USD", "EUR", "JPY"};
}
}

Java8以后,接口中可以有默认方法,默认方法可以具有方法体

接口数组

接口数组可以容纳调用了该接口的实现类的实例化对象


异常处理

异常是程序中可能中断程序运行的事件,异常处理可以预先设置好异常的处理方法,异常发生时程序可以正常运行

try:执行可能产生异常的代码
catch:捕获异常
finally:无论是否发生异常,代码总能执行
throws:声明方法可能要抛出的异常,要求调用者处理
throw:手动抛出异常

异常对象(Exception)

Throwable (所有异常/错误的超类)
├── Error (严重错误,程序无法处理)
│ ├── OutOfMemoryError
│ ├── StackOverflowError
│ └── ...

└── Exception (程序可以处理的异常)
├── RuntimeException (运行时异常,非受检异常)
│ ├── NullPointerException
│ ├── IndexOutOfBoundsException
│ ├── ArithmeticException
│ └── ...

└── 其他Exception (受检异常,编译报错)
├── IOException
├── SQLException
└── ...

异常的本质也是一种对象

异常对象 描述
InputMismatchException 类型异常
ArithmeticException 算数异常
Exception 其他异常

异常也有自己的方法

异常方法 描述
void printStackTrace() 输出异常的堆栈信息
String getMessage() 返回异常信息的描述字符串,是printStackTrace()输出的一部分

try-catch-finally结构

try中无异常,直接执行finally,若try中有异常,再到catch内对异常进行处理,最后执行finallyfinally通常都会执行

public class SimplestTryCatch {
public static void main(String[] args) {

// 场景:计算两个数的除法
calculateDivision(10, 2); // 正常
calculateDivision(10, 0); // 除以零
calculateDivision(10, "abc"); // 类型错误
}

static void calculateDivision(int a, Object b) {
System.out.println("\n计算 " + a + " / " + b);

try {
// 尝试转换为数字
int divisor = Integer.parseInt(b.toString());

// 计算除法
int result = a / divisor;
System.out.println("结果:" + result);

} catch (ArithmeticException e) {
System.out.println("错误:除数不能为0");

} catch (NumberFormatException e) {
System.out.println("错误:" + b + " 不是有效的数字");

} catch (Exception e) {
System.out.println("未知错误:" + e.getMessage());

} finally {
System.out.println("计算完成");
}
}
}

多个catch并列排开称为多路异常捕获,这样可以全面精准地捕获异常,通常捕获的异常从子类到父类,只执行第一个与异常匹配的catch语句