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加载类时,会加载静态代码块,如有多个则按顺序加载,每个代码块只会被执行一次,静态代码块的加载和对象的创建没有关系
StaticTest st1=new StaticTest(); StaticTest st1=new StaticTest(); System.out.println(num);
|
主动使用类的7种情况(会触发类加载和静态代码块)
public class TriggerConditions { public static void main(String[] args) throws Exception { System.out.println("=== 各种触发情况 ===");
new TriggerDemo();
System.out.println(TriggerDemo.staticField);
TriggerDemo.staticMethod();
Class.forName("TriggerDemo");
new ChildClass();
} }
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("构造方法执行"); } }
|
执行顺序:父类实例代码块 → 父类构造方法 → 子类实例代码块 → 子类构造方法
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; public Animal() { System.out.println("Animal无参构造方法被调用"); this.name = "未知动物"; this.age = 0; } public Animal(String name, int age) { System.out.println("Animal有参构造方法被调用"); this.name = name; this.age = age; } }
class Dog extends Animal { private String breed; public Dog() { super(); System.out.println("Dog无参构造方法被调用"); this.breed = "未知品种"; } 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.eat(); animal.bark(); } }
|
向下转型
需要调用子类特有方法,而引用是父类类型时,需要显式强制转换,可能不安全
语法: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; dog2.eat(); dog2.bark(); } }
|
for增强循环(for-each循环)
基本语法
for (元素类型 元素变量 : 集合或数组) { }
|
接口(Interface)
接口是类似于一种合约,接口内部定义了一些列的抽象方法,当实现类采用了某一个接口就必须实现该接口中的所有方法
public interface PaymentProcessor { boolean processPayment(double amount); String getPaymentMethod(); 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内对异常进行处理,最后执行finally,finally通常都会执行
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语句