征服Java三大特性:封装×继承×多态+this/super高阶指南

一、封装

  • 本质理解:将数据属性和操作该数据的方法(行为)捆绑在中,通过访问控制修饰符限制外部对其内部细节的直接访问
  • 核心目的:
    1. 保护内部数据的完整性:防止外部代码随意修改导致状态不一致
    2. 隐藏实现细节:对外只暴露必要的接口,降低模块间的耦合度
    3. 简化使用复杂度:使用者只需关注接口,不必了解内部复杂逻辑
  • 通俗的讲,把该隐藏的隐藏起来,该暴露的暴露出来。这就是封装性的设计思想

访问控制修饰符的应用技巧

修饰符 本类 同包类 子类(不同包) 其他包非子类 常用场景
private 字段隐藏,内部方法封装
default 包级私有工具类与实现
protected 子类需访问的父类属性和方法
public 对外接口、常量的暴露

示例:封装的典型应用

java 复制代码
public class Person {
    // 私有属性(隐藏内部实现)
    private String name;
    private int age;

    // Getter/Setter方法(暴露受控接口)
    public String getName() {
        return name;
    }

    public void setName(String name) {
        if (name == null || name.trim().isEmpty()) { // 校验逻辑
            throw new IllegalArgumentException("姓名不能为空");
        }
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        if (age < 0 || age > 150) { // 年龄合理性校验
            throw new IllegalArgumentException("年龄不合法");
        }
        this.age = age;
    }
}
  • name和age被声明为private,外部无法直接修改
  • 通过setNamesetAge方法设置值时,会先进行合法性校验,避免无效数据破坏对象状态

二、关键字this

在 Java 中,this 关键字是一个指向当前对象实例的引用变量,主要用于解决变量命名冲突、调用其他构造方法或传递当前对象。下面介绍5种核心用法。

1、解决成员变量与局部变量命名冲突

  • 当成员变量(类属性)与局部变量(方法参数或内部变量)同名时,使用 this 明确指定成员变量
java 复制代码
public class Person {
    private String name; // 成员变量
    
    public void setName(String name) { // 局部变量
        this.name = name; // 用 this 指代成员变量
    }
}

2、在构造方法中调用其他构造方法

  • 使用 this(...) 在构造方法中调用同类其他构造方法(必须放在第一行
java 复制代码
public class Rectangle {
    private int width, height;
    
    public Rectangle() {
        this(10, 10); // 调用带参构造方法
    }
    
    public Rectangle(int width, int height) {
        this.width = width;
        this.height = height;
    }
}

3、作为参数传递当前对象

  • 当前对象传递给其他方法
java 复制代码
public class Printer {
    public void print() {
        System.out.println("Printing...");
    }
}

public class Document {
    private String content;
    
    public void sendToPrinter(Printer printer) {
        printer.print(this.content); // 传递内容
    }
    
    public void process() {
        new Printer().printDocument(this); // 传递当前 Document 对象
    }
}

4、返回当前对象(链式调用)

  • 在方法中返回 this 实现链式调用
java 复制代码
public class Calculator {
    private int value;
    
    public Calculator add(int num) {
        this.value += num;
        return this; // 返回当前对象
    }
    
    public Calculator subtract(int num) {
        this.value -= num;
        return this;
    }
}

// 链式调用示例
Calculator calc = new Calculator();
calc.add(5).subtract(3).add(10); // 连续操作

5、内部类中访问外部类对象

  • 当内部类与外部类有同名变量时,用 外部类名.this 明确指定
java 复制代码
public class Outer {
    private String name = "Outer";
    
    class Inner {
        private String name = "Inner";
        
        public void printNames() {
            System.out.println(name);         // "Inner" (内部类变量)
            System.out.println(Outer.this.name); // "Outer" (外部类变量)
        }
    }
}

三、继承

  • 本质理解:子类(派生类)继承父类(基类)的非私有属性和方法,实现代码重用并扩展新功能或重写特定行为
  • 核心目的:
    1. 代码复用:减少冗余代码,提升开发效率
    2. 建立类层级关系:通过 is-a 关系建模(如 Manager is an Employee
    3. 实现多态基础:子类可替代父类对象使用(里氏替换原则)
  • 深度特性解析:
    1. extends 关键字实现单继承(Java不支持多继承)
    2. 父类构造方法通过 super() 调用
    3. @Override 注解确保正确重写方法
    4. 所有类隐式继承 Object 类(如toString()equals()方法)

示例:公司员工体系建模

java 复制代码
class Employee {
    private String name;
    private double baseSalary;
    
    public Employee(String name, double salary) {
        this.name = name;
        this.baseSalary = salary;
    }
    
    public double calculatePay() {
        return baseSalary; // 普通员工直接返回基础薪资
    }
}

class Manager extends Employee {
    private double bonus; // 经理特有属性-奖金
    
    public Manager(String name, double salary, double bonus) {
        super(name, salary); // 调用父类构造
        this.bonus = bonus;
    }
    
    @Override
    public double calculatePay() {
        // 扩展行为:经理工资 = 基础工资 + 奖金
        return super.calculatePay() + bonus; 
    }
    
    public void conductReview() { 
        // 经理特有方法
        System.out.println("Conducting performance review...");
    }
}

四、关键字super

在 Java 中,super是一个特殊的关键字,主要用于访问父类(超类)的成员(属性方法构造器)。它在继承关系中发挥着关键作用。

1、调用父类的构造器

  • 在子类构造器中用 super(...) 调用父类构造器
  • 必须是子类构造器的第一行语句this()super() 不能共存)
  • 若未显式调用,编译器会自动插入 super()(调用父类无参构造器)
java 复制代码
 class Parent {
     Parent(int value) {
         System.out.println("Parent构造器: " + value);
     }
 }

 class Child extends Parent {
     Child() {
         super(10); // 显式调用父类有参构造器
         System.out.println("Child构造器");
     }
 }

2、访问父类的成员变量

  • 当子类变量与父类同名时,用 super.变量名 区分父类变量
java 复制代码
class Parent {
    String name = "父类";
}

class Child extends Parent {
    String name = "子类";
    
    void print() {
        System.out.println(super.name); // 输出: 父类
        System.out.println(this.name);   // 输出: 子类
    }
}

3、调用父类被重写的方法

  • 子类重写父类方法后,用 super.方法名() 调用父类原始方法:
java 复制代码
class Parent {
    void show() {
        System.out.println("Parent的方法");
    }
}

class Child extends Parent {
    @Override
    void show() {
        super.show(); // 先调用父类方法
        System.out.println("Child的方法");
    }
}

this关键字(或者this和super都不写)查找变量或方法时,优先查找当前类,找不到则向父类逐层查找

五、多态

  • 本质理解:同一个方法调用,在不同对象上表现出不同的行为
  • Java实现的两大途径:
    1. 编译时多态(静态/方法重载):相同方法名,不同参数列表
    2. 运行时多态(动态绑定/方法重写):父类引用指向子类对象,调用被重写方法

运行机制揭秘:

sequenceDiagram Main->>Employee: employee.calculatePay() Employee-->>Manager: JVM查实际对象类型 Manager-->>Manager: 执行Manager的calculatePay() Manager->>Main: 返回结果

1、方法内局部变量的赋值体现多态

java 复制代码
public class TestPet {
    public static void main(String[] args) {
        //多态引用
        Pet pet = new Dog();
        pet.setNickname("小白");

        //多态的表现形式
        /*
        编译时看父类:只能调用父类声明的方法,不能调用子类扩展的方法;
        运行时,看"子类",如果子类重写了方法,一定是执行子类重写的方法体;
         */
        pet.eat();//运行时执行子类Dog重写的方法
//      pet.watchHouse();//不能调用Dog子类独有的扩展方法

        pet = new Cat();
        pet.setNickname("雪球");
        pet.eat();//运行时执行子类Cat重写的方法
    }
}

2、方法的形参声明体现多态

java 复制代码
public class Person{
    private Pet pet;
    public void adopt(Pet pet) {//形参是父类类型,实参是子类对象
        this.pet = pet;
    }
    public void feed(){
        pet.eat();//pet实际引用的对象类型不同,执行的eat方法也不同
    }
}

3、方法返回值类型体现多态

java 复制代码
public class PetShop {
    //返回值类型是父类类型,实际返回的是子类对象
    public Pet sale(String type){
        switch (type){
            case "Dog":
                return new Dog();
            case "Cat":
                return new Cat();
        }
        return null;
    }
}

4、成员变量没有多态性

  • 即使子类定义了与父类同名的实例变量,该变量也不会覆盖父类的变量
  • 访问变量时是基于引用的编译时类型决定的,而非对象的实际类型
java 复制代码
public class TestVariable {
    public static void main(String[] args) {
        Base b = new Sub();
        System.out.println(b.a);  // 1
        System.out.println(((Sub)b).a);  // 2

        Sub s = new Sub();
        System.out.println(s.a); // 2
        System.out.println(((Base)s).a);  // 1
    }
}
class Base{
    int a = 1;
}
class Sub extends Base{
    int a = 2;
}

5、向上转型和向下转型

向上转型

  • 定义:将子类对象引用转换为父类类型
  • 特点:
    • 自动发生(隐式转换)
    • 绝对安全(子类"是"父类的一种)
    • 只能访问父类中定义的成员(方法或变量),不能访问子类独有成员
java 复制代码
class Animal {
    void eat() {
        System.out.println("Animal eats");
    }
}

class Dog extends Animal {
    @Override
    void eat() {
        System.out.println("Dog eats bones");
    }
    
    void bark() {
        System.out.println("Dog barks");
    }
}

public class Main {
    public static void main(String[] args) {
        // 向上转型:Dog -> Animal
        Animal animal = new Dog();  // 隐式转换
        
        animal.eat();  // 输出:"Dog eats bones"(多态:调用子类重写方法)
        // animal.bark(); // 编译错误!父类引用无法访问子类特有方法
    }
}

向下转型

  • 定义:将父类对象引用强制转换回子类类型

  • 特点:

    • 需显式强制转换(可能抛出ClassCastException
    • 不安全(必须确保父类引用实际指向目标子类对象)
    • 转换成功后,可访问子类特有成员
  • 安全做法:先用instanceof检查类型

    java 复制代码
    if (父类引用 instanceof 目标子类) {
        目标子类 引用 = (目标子类) 父类引用;
    }
java 复制代码
public class Main {
    public static void main(String[] args) {
        Animal animal = new Dog();  // 向上转型
        
        // 向下转型(安全方式)
        if (animal instanceof Dog) {
            Dog dog = (Dog) animal;  // 强制转换
            dog.eat();   // 输出:"Dog eats bones"
            dog.bark();  // 输出:"Dog barks"(访问子类特有方法)
        }
        
        // 错误示例(导致ClassCastException)
        Animal cat = new Cat();
        // Dog invalidDog = (Dog) cat; // 运行时异常:cat不是Dog对象
    }
}
相关推荐
神奇小汤圆5 小时前
浅析二叉树、B树、B+树和MySQL索引底层原理
后端
文艺理科生5 小时前
Nginx 路径映射深度解析:从本地开发到生产交付的底层哲学
前端·后端·架构
千寻girling5 小时前
主管:”人家 Node 框架都用 Nest.js 了 , 你怎么还在用 Express ?“
前端·后端·面试
南极企鹅5 小时前
springBoot项目有几个端口
java·spring boot·后端
Luke君607975 小时前
Spring Flux方法总结
后端
define95275 小时前
高版本 MySQL 驱动的 DNS 陷阱
后端
忧郁的Mr.Li5 小时前
SpringBoot中实现多数据源配置
java·spring boot·后端
暮色妖娆丶6 小时前
SpringBoot 启动流程源码分析 ~ 它其实不复杂
spring boot·后端·spring
Coder_Boy_6 小时前
Deeplearning4j+ Spring Boot 电商用户复购预测案例中相关概念
java·人工智能·spring boot·后端·spring
Java后端的Ai之路6 小时前
【Spring全家桶】-一文弄懂Spring Cloud Gateway
java·后端·spring cloud·gateway