11. Java面向对象进阶笔记
目录
一、static关键字
1.1 核心概念
static 是Java中的静态修饰符,可修饰成员变量和成员方法。
| 修饰目标 | 名称 | 特点 |
|---|---|---|
| 成员变量 | 静态变量(类变量) | 被该类所有对象共享 |
| 成员方法 | 静态方法(类方法) | 属于类,不属于对象 |
1.2 静态变量特点
- 被该类所有对象共享
- 不属于对象,属于类
- 随类加载而加载,优先于对象创建
- 存储在静态区(方法区/元空间)
1.3 调用方式
// 推荐:类名调用
Student.teacherName = "阿玮老师";
// 不推荐:对象名调用(语法允许,但语义易混淆)
Student s1 = new Student();
s1.teacherName = "阿玮老师";
1.4 静态变量 vs 成员变量
| 对比项 | 静态变量(static) | 成员变量(非static) |
|---|---|---|
| 归属 | 属于类 | 属于对象 |
| 共享 | 所有对象共享 | 每个对象独有 |
| 加载时机 | 类加载时 | 对象创建时 |
| 调用方式 | 类名/对象名 | 只能对象名 |
1.5 使用场景
- 静态变量:描述类级别的共享数据(如班级老师、全局计数器)
- 静态方法:工具类方法(如
Math.sqrt()),无需创建对象即可调用
二、工具类设计
2.1 核心设计规范
| 原则 | 含义 | 代码体现 |
|---|---|---|
| 类名见名知意 | 一眼看出类的功能 | ArrayUtil(数组工具) |
| 私有化构造方法 | 禁止外部创建工具类实例 | private 类名(){} |
| 方法定义为静态 | 直接通过类名调用 | public static 返回类型 方法名(参数) |
2.2 数组工具类 ArrayUtil
package a01staticdemo1;
public class ArrayUtil {
/**
* 私有构造方法:防止外部实例化工具类
*/
private ArrayUtil() {}
/**
* 将 int 数组格式化为 [元素1,元素2,...] 字符串
*/
public static String printAll(int[] arr){
StringBuilder sb = new StringBuilder();
sb.append("[");
for(int i = 0; i < arr.length; i++){
if(i == arr.length - 1){
sb.append(arr[i]).append("]");
} else {
sb.append(arr[i]).append(",");
}
}
return sb.toString();
}
/**
* 计算 double 数组的平均值
*/
public static double getAv(double[] arr){
double sum = 0;
for(int i = 0; i < arr.length; i++){
sum += arr[i];
}
return sum / arr.length;
}
}
2.3 学生工具类 StudentUtil
package a01staticdemo1;
import java.util.ArrayList;
public class StudentUtil {
private StudentUtil() {}
/**
* 从学生集合中获取年龄最大的学生的年龄值
*/
public static int getMaxAge(ArrayList<Student> list){
int maxAge = list.get(0).getAge();
for (Student student : list) {
if(student.getAge() > maxAge){
maxAge = student.getAge();
}
}
return maxAge;
}
}
2.4 工具类 vs JavaBean类
| 类型 | 作用 | 示例 |
|---|---|---|
| JavaBean类 | 描述一类事物(属性+行为) | Student(描述学生) |
| 工具类 | 提供通用功能,不描述事物 | ArrayUtil(操作数组) |
| 测试类 | 程序入口,验证其他类功能 | Test(包含main方法) |
三、static方法访问权限
3.1 核心结论
| 方法类型 | 能访问什么 | 不能访问什么 | 有没有this |
|---|---|---|---|
| 静态方法 | 只能访问静态变量、静态方法 | 不能访问非静态变量、非静态方法 | 没有this |
| 非静态方法 | 可以访问所有(静态+非静态) | 无限制 | 有this |
3.2 内存层面理解
- 静态内容:存在方法区(元空间),随类加载而加载
- 非静态内容:存在堆内存,必须new出对象后才存在
- 关键矛盾:类加载时,静态内容就有了,但还没有任何对象
3.3 代码示例
public class Student {
String name; // 堆内存,属于对象
static String teacherName; // 方法区,属于类
// 静态方法
public static void method() {
// System.out.println(name); // 错误!不能访问非静态变量
System.out.println(teacherName); // 正确!可以访问静态变量
}
// 非静态方法
public void show() {
System.out.println(name); // 正确!可以访问非静态变量
System.out.println(teacherName); // 正确!可以访问静态变量
}
}
3.4 this关键字
this 代表当前调用方法的那个对象的地址,JVM自动赋值。
public void show1(Student this) { // 隐藏的this参数
System.out.println(this.name);
}
静态方法里没有this,因为静态方法不需要对象。
四、封装(Encapsulation)
4.1 核心思想
对象代表什么,就得封装对应的数据,并提供数据对应的行为。
- 把对象的属性和行为打包在一个类里
- 用访问权限修饰符隐藏内部细节,只对外提供安全的访问方式
4.2 代码示例
public class Student {
// 私有化属性
private String name;
private int 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; }
// 封装行为
public void sleep() {
System.out.println(name + "在睡觉");
}
}
4.3 封装的好处
- 数据安全:防止外部直接修改属性
- 代码复用:行为封装在类里,所有对象都能复用
- 易于维护:内部实现修改不影响外部调用
五、继承(Inheritance)
5.1 核心思想
让一个类继承另一个类,从而复用父类的代码,并在其基础上扩展新功能。
5.2 基本格式
public class 子类名 extends 父类名 {
// 子类新增的属性和方法
}
5.3 继承规则
| 规则 | 说明 |
|---|---|
| 单继承 | 一个子类只能继承一个父类 |
| 不支持多继承 | 避免方法冲突 |
| 支持多层继承 | 子类→父类→Object |
5.4 继承的好处
- 代码复用:把多个子类重复的代码抽取到父类中
- 功能扩展:子类可以在父类基础上,新增自己的属性和方法
- 便于维护:修改父类代码,所有子类自动生效
5.5 什么时候用继承
- 存在共性:多个类有相同的属性和行为
- is-a关系:子类是父类的一种
5.6 Object类
Java中每一个类都直接或间接继承于Object类,因此都拥有Object的方法(如toString()、equals()、hashCode()等)。
六、继承中成员访问特点
6.1 成员变量的访问特点
就近原则:方法内局部变量 > 子类成员变量 > 父类成员变量
class Fu { String name = "Fu"; }
class Zi extends Fu {
String name = "Zi";
public void ziShow() {
String name = "ziShow";
System.out.println(name); // 输出 ziShow
System.out.println(this.name); // 输出 Zi
System.out.println(super.name);// 输出 Fu
}
}
6.2 成员方法的访问特点
- 子类调用方法时,优先在本类中查找,若不存在则向上查找父类继承的方法
this.方法名():优先调用子类方法super.方法名():强制调用父类的方法
七、方法重写(Override)
7.1 重写的定义
当父类方法无法满足子类需求时,子类可定义与父类方法声明完全一致的方法,覆盖父类实现。
7.2 重写规范
| 规范 | 说明 |
|---|---|
| 声明一致 | 方法名、参数列表必须与父类完全相同 |
| 权限放大 | 子类方法访问权限 ≥ 父类方法访问权限 |
| 返回值类型 | 子类返回值类型 ≤ 父类返回值类型(协变返回类型) |
| 注解 | 推荐添加@Override注解,编译器会自动校验 |
7.3 代码示例
class Person {
public void eat() { System.out.println("吃米饭,吃菜"); }
}
class Student extends Person {
@Override
public void eat() {
System.out.println("吃食堂套餐"); // 子类重写实现
}
}
7.4 重写的本质
子类重写父类方法时,会覆盖父类方法在虚方法表中的位置,运行时根据对象实际类型调用对应实现(多态核心)。
八、继承中构造方法的特点
8.1 核心规则
- 先父类,后子类:创建子类对象时,会先执行父类的构造方法
- 子类构造方法中默认隐藏
super(),会自动调用父类的无参构造方法 - 若父类没有无参构造,子类必须显式调用父类有参构造
super(参数列表)
8.2 代码示例
class Fu {
public Fu() {
System.out.println("父类无参构造执行");
}
}
class Zi extends Fu {
public Zi() {
super(); // 隐藏的super()
System.out.println("子类无参构造执行");
}
}
8.3 注意事项
- 构造方法不能被继承
super()必须放在构造方法第一行this()和super()不能同时出现
九、this与super对比
| 关键字 | 代表对象 | 核心用途 | 位置要求 |
|---|---|---|---|
| this | 当前对象 | 访问本类成员、调用本类构造 | 构造方法第一行 |
| super | 父类对象 | 访问父类成员、调用父类构造 | 构造方法第一行 |
十、多态(Polymorphism)
10.1 什么是多态
同类型的对象,表现出不同的行为形态。同一个方法调用,在不同子类对象上,会执行不同的逻辑。
10.2 多态的核心表现形式
// 父类类型 变量名 = 子类对象;
Animal a1 = new Dog();
Animal a2 = new Cat();
这种写法也叫向上转型。
10.3 多态的三个前提条件
- 有继承/实现关系
- 有方法重写
- 父类引用指向子类对象
10.4 多态调用成员的特点
| 成员类型 | 编译时规则 | 运行时规则 | 口诀 |
|---|---|---|---|
| 成员变量 | 看「左边」(父类) | 看「左边」(父类) | 编译看左,运行也看左 |
| 成员方法 | 看「左边」(父类) | 看「右边」(子类) | 编译看左,运行看右 |
10.5 多态的好处
- 扩展性强:新增子类无需修改原有代码
- 代码复用:用父类作为参数,可以接收所有子类对象
- 灵活性高:统一接口调用,不用关心具体子类类型
十一、引用类型转换
11.1 自动类型转换(向上转型)
- 场景:子类 → 父类
- 特点:自动完成,安全可靠,但会丢失子类特有功能
Person p = new Student();
11.2 强制类型转换(向下转型)
- 场景:父类 → 子类
- 目的:恢复子类类型,调用子类特有功能
- 风险:若转换类型与真实对象类型不一致,会抛出
ClassCastException
Person p = new Student();
Student s = (Student) p; // 强转后可调用Student特有方法
11.3 instanceof关键字
用于校验对象真实类型,配合向下转型保证安全。
Animal a = new Dog();
if (a instanceof Dog d) {
d.lookHome(); // 直接使用d,无需额外强转
}
十二、包(Package)
12.1 包的本质
包就是文件夹,用于分类管理不同功能的Java类。
12.2 包名命名规则
- 格式:公司域名反写 + 包的作用
- 要求:全部英文小写,见名知意
- 示例:
com.itheima.domain
12.3 导包规则
| 场景 | 是否需要导包 | 说明 |
|---|---|---|
| 同一个包中的类 | 不需要 | 直接使用类名 |
| java.lang包中的类 | 不需要 | 如String、System、Math等 |
| 其他包中的类 | 需要 | 用import 全类名;导入 |
| 两个包存在同名类 | 必须用全类名 | 避免歧义 |
十三、final关键字
13.1 final修饰的三种目标
| 修饰目标 | 含义 | 核心限制 |
|---|---|---|
| 方法 | 最终方法 | 不能被子类重写 |
| 类 | 最终类 | 不能被子类继承 |
| 变量 | 常量 | 只能被赋值一次 |
13.2 常量命名规范
- 单个单词:全部大写,如
AGE - 多个单词:全部大写 + 下划线分隔,如
MAX_VALUE
13.3 关键细节
- 基本类型变量:存储的数据值不可改变
- 引用类型变量:存储的地址值不可改变,但对象内部属性可以修改
final int NUM = 10;
// NUM = 20; // 编译报错
final Dog d = new Dog();
// d = new Dog(); // 编译报错
d.setAge(3); // 可以修改对象内部属性
十四、权限修饰符
14.1 权限修饰符对比
| 修饰符 | 本类 | 同一个包 | 不同包子类 | 不同包无关类 |
|---|---|---|---|---|
| private | ✓ | ✗ | ✗ | ✗ |
| 默认(空着不写) | ✓ | ✓ | ✗ | ✗ |
| protected | ✓ | ✓ | ✓ | ✗ |
| public | ✓ | ✓ | ✓ | ✓ |
14.2 核心作用
- private:最高封装,仅类内部使用
- 默认权限:包级封装,适合仅在模块内部共享的成员
- protected:继承友好,允许子类复用父类功能
- public:对外接口,提供稳定的功能入口
十五、代码块
15.1 局部代码块
用于限制变量的作用域,让变量在代码块执行结束后立即被回收。
public class LocalBlockDemo {
public static void main(String[] args) {
{
int temp = 100; // 变量仅在块内有效
System.out.println("局部代码块内: temp = " + temp);
}
// System.out.println(temp); // 编译报错:temp已超出作用域
}
}
15.2 构造代码块
在每次创建对象时、构造方法执行前自动调用,用于抽取多个构造方法中的重复代码。
public class Student {
private String name;
// 构造代码块
{
System.out.println("构造代码块执行:初始化公共资源");
}
public Student() {
System.out.println("无参构造执行");
}
}
15.3 静态代码块
随类的第一次加载而执行,且整个程序生命周期仅执行一次,用于完成类级别的数据初始化。
public class StaticBlockDemo {
public static String config;
// 静态代码块
static {
System.out.println("静态代码块执行:加载配置文件");
config = "读取自配置文件的参数";
}
}
15.4 执行顺序
- 静态代码块 → 类加载时执行(仅一次)
- 构造代码块 → 每次创建对象时执行(在构造方法前)
- 构造方法 → 完成对象的最终初始化
- 局部代码块 → 方法执行到该块时才执行
十六、抽象类与抽象方法
16.1 核心概念
抽象方法:只有方法声明,没有方法体,用abstract关键字修饰。
抽象类:包含抽象方法的类必须被定义为抽象类。
16.2 定义格式
// 抽象类
public abstract class Animal {
// 普通成员方法
public void drink() {
System.out.println("动物喝水");
}
// 抽象方法
public abstract void eat();
}
16.3 核心注意事项
- 抽象类不能实例化
- 抽象类中不一定有抽象方法,但包含抽象方法的类必须是抽象类
- 抽象类可以有构造方法
- 子类继承抽象类必须重写所有抽象方法,或者也声明为抽象类
16.4 抽象类 vs 接口
| 维度 | 抽象类 | 接口 |
|---|---|---|
| 继承/实现关系 | 类用extends继承,单继承 | 类用implements实现,多实现 |
| 构造方法 | 有构造方法 | 无构造方法 |
| 成员变量 | 可以有普通成员变量 | 只能有public static final常量 |
| 方法类型 | 可包含抽象方法、普通方法等 | 可包含抽象方法、默认方法、静态方法 |
十七、接口(Interface)
17.1 为什么需要接口
抽象类是单继承,且父类的行为会强制所有子类继承。接口将非体系共性、可插拔的行为抽成接口,作为一种"规则",打破单继承限制。
17.2 接口的定义与使用
// 定义接口
public interface Swimable {
void swim(); // 抽象方法
}
// 类实现接口
public class Frog extends Animal implements Swimable {
@Override
public void swim() {
System.out.println("青蛙在水里游");
}
}
// 多实现
public class Student extends Person implements Swimable, Runnable {
@Override
public void swim() { ... }
@Override
public void run() { ... }
}
17.3 接口成员的特点
| 成员类型 | 特点 |
|---|---|
| 成员变量 | 默认public static final(必须初始化,本质是常量) |
| 抽象方法 | 默认public abstract(可省略修饰符) |
| 默认方法(Java 8+) | 用default修饰,有方法体 |
| 静态方法(Java 8+) | 用static修饰,有方法体 |
17.4 JDK8+接口新增方法
| JDK版本 | 新增方法类型 | 修饰关键字 | 调用方式 |
|---|---|---|---|
| JDK8 | 默认方法 | default | 实现类对象调用 |
| JDK8 | 静态方法 | static | 接口名调用 |
| JDK9+ | 私有方法 | private | 仅接口内部调用 |
17.5 接口的核心意义
- 解耦:将行为与类的继承体系分离
- 规范:定义统一的行为规则
- 多实现:打破单继承局限
- 跨体系:接口可以被任意体系的类实现
十八、内部类
18.1 什么是内部类
在一个类的内部,再定义一个完整的类,这个被嵌套在内部的类就叫做内部类。
适用场景:当一个类(B)表示的事物是另一个类(A)的组成部分,且B类单独存在没有任何意义时。
18.2 内部类分类
| 内部类类型 | 定义位置 | 学习要求 |
|---|---|---|
| 成员内部类 | 外部类成员位置 | 了解 |
| 静态内部类 | 外部类成员位置(static修饰) | 了解 |
| 局部内部类 | 外部类的方法/代码块内部 | 了解 |
| 匿名内部类 | 方法/代码块内部,无类名 | 掌握(重点) |
18.3 成员内部类
定义在外部类的成员位置,属于外部类的成员。
public class Car {
String carName;
// 成员内部类
class Engine {
String engineName;
}
}
创建对象格式:
Car.Engine engine = new Car().new Engine();
18.4 静态内部类
用static修饰的内部类,属于外部类的静态成员,不依赖外部类对象。
public class Car {
public static String brand = "BMW";
static class Engine {
public void show() {
System.out.println("品牌:" + brand);
}
}
}
创建对象格式:
Car.Engine engine = new Car.Engine();
18.5 局部内部类
定义在外部类的方法/代码块内部,作用域仅限于当前方法/代码块。
public class Outer {
public void show() {
int a = 10; // JDK8+ 要求effectively final
class Inner {
public void method1() {
System.out.println(a);
}
}
Inner inner = new Inner();
inner.method1();
}
}
18.6 匿名内部类(重点)
本质上是一个没有名字的局部内部类,适用于某个类/接口只需要使用一次的场景。
标准格式:
new 类名/接口名() {
// 重写方法
@Override
public 返回值 方法名(参数) {
// 方法实现
}
};
代码示例:
interface Swim {
void swim();
}
public class Test {
public static void main(String[] args) {
// 匿名内部类:实现Swim接口
Swim s = new Swim() {
@Override
public void swim() {
System.out.println("重写之后游泳方法");
}
};
s.swim();
}
}
核心规则:
- 访问权限:可直接访问外部类的所有成员(包括private)
- 不能有构造器:因为没有类名
- 单继承/单实现限制:只能继承一个类或实现一个接口
- 分号问题:匿名内部类的大括号
}后面必须加分号;
十九、适配器设计模式
19.1 适用场景
当一个接口包含大量抽象方法,但实现类只需要使用其中部分方法时,通过适配器模式简化实现。
19.2 核心思想
用一个抽象适配器类实现接口,对所有抽象方法做空实现;再让业务实现类继承适配器类,仅重写需要的方法。
19.3 代码示例
// 原接口
interface 原接口 {
void 方法1();
void 方法2();
void 方法3();
}
// 适配器类
abstract class XXXAdapter implements 原接口 {
@Override
public void 方法1() {}
@Override
public void 方法2() {}
@Override
public void 方法3() {}
}
// 业务实现类
class 业务类 extends XXXAdapter {
@Override
public void 方法1() {
// 只重写需要的方法
}
}
二十、总结
20.1 面向对象三大核心特征
| 特征 | 核心思想 | 实现手段 |
|---|---|---|
| 封装 | 隐藏内部细节,只暴露必要接口 | private + getter/setter |
| 继承 | 复用代码并扩展功能 | extends |
| 多态 | 同一行为在不同子类上表现不同 | 父类引用指向子类对象 |
更多推荐
所有评论(0)