面向对象必学知识点

文章目录

一、类和对象

二、方法重载

三、方法重写

四、封装

五、继承

六、多态

七、抽象与接口

1、抽象类 (Abstract Class)

2、接口 (Interface)

3、接口和抽象的异同点总结

(1).相同点

(2).不同点

(3).使用场景


面向对象编程(Object-Oriented Programming,简称OOP)是一种编程范式,它使用“对象”来设计软件。这些对象是数据和可以操作这些数据的方法的封装体。面向对象编程的主要目标是提高代码的可重用性、模块化程度以及维护性。

一、类和对象

  • 类是对象的定义或蓝图。当你定义了一个类时,你实际上是在描述一类对象应该具有的属性和行为。
  • 对象是类的具体实例。当你根据类创建一个对象时,这个过程被称为实例化。例如,如果你有一个Car类,那么你可以创建多个Car对象,每个对象都是一辆具体的车。
  • 类提供了抽象的概念,它不包含任何实际的数据,只是一个模型或框架。
  • 对象是具体的实体,包含了具体的数据和行为。每个对象都有自己独特的属性值,但它们都遵循类中定义的结构和行为。
  • 由于继承的存在,一个类可以有多个子类,而这些子类可以有不同的实现。因此,即使是从同一个基类派生的对象,也可以表现出不同的行为,这就是多态性的体现。
  • 类通过封装将数据和方法绑定在一起,并通过访问控制保护内部状态。对象则是封装的结果,外部只能通过公共接口与其交互。

二、方法重载

方法重载(Method Overloading)是面向对象编程中的一个概念,指的是在同一个类中可以定义多个同名但参数列表不同的方法。这些方法具有相同的方法名,但它们的参数类型、参数数量或参数顺序不同。

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

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

// 调用
Calculator calc = new Calculator();
System.out.println(calc.add(2, 3));    // 调用 int 版本
System.out.println(calc.add(2.5, 3.5)); // 调用 double 版本

方法重载的特点

  1. 相同的方法名:重载的方法必须具有相同的方法名。
  2. 不同的参数列表:重载的方法必须有不同的参数列表。参数列表的不同可以体现在以下方面:
    • 参数的数量不同。
    • 参数的类型不同。
    • 参数的顺序不同。
  3. 返回类型可以不同:方法的返回类型可以不同,但这不是区分重载方法的必要条件。编译器主要通过方法名和参数列表来区分重载方法
  4. 访问修饰符可以不同:方法的访问修饰符(如 publicprivate 等)可以不同,但这不影响方法的重载

三、方法重写

方法重写(Method Overriding),也称为覆盖,是面向对象编程中的一个重要概念,特别是在继承关系中,当一个子类提供了与父类中某个方法具有相同名称、返回类型和参数列表的方法时,就发生了方法重写。方法重写允许子类提供特定于其自身行为的实现,从而改变或扩展父类的行为。通常使用@Override 注解来明确标识一个方法是重写的方法提高代码的可读性

// 父类
class Animal {
    // 定义一个方法,用于发出声音
    public void makeSound() {
        System.out.println("Some generic animal sound");
    }
}

// 子类
class Dog extends Animal {
    // 重写makeSound方法
    @Override
    public void makeSound() {
        System.out.println("Bark Bark");
    }
}

// 另一个子类
class Cat extends Animal {
    // 重写makeSound方法
    @Override
    public void makeSound() {
        System.out.println("Meow Meow");
    }
}

// 测试类
public class Main {
    public static void main(String[] args) {
        // 创建Animal对象
        Animal myAnimal = new Animal();
        myAnimal.makeSound();  // 输出: Some generic animal sound

        // 创建Dog对象
        Animal myDog = new Dog();
        myDog.makeSound();     // 输出: Bark Bark

        // 创建Cat对象
        Animal myCat = new Cat();
        myCat.makeSound();     // 输出: Meow Meow
    }
}

方法重写的特点

  1. 相同的方法签名:子类中重写的方法必须与父类中的方法具有相同的方法名、参数列表和返回类型。
  2. 访问权限:子类中重写的方法不能有比父类更严格的访问权限。例如,如果父类的方法是 public,那么子类中重写的方法也必须是 public
  3. 异常处理:子类中重写的方法抛出的异常不能比父类方法抛出的异常更宽泛。也就是说,子类方法可以抛出相同的异常或不抛出异常,但不能抛出父类方法没有声明的异常。

四、封装

通常认为封装是把数据和操作数据的方法绑定起来,对数据的访问 只能通过已定义的接口。面向对象的本质就是将现实世界描绘成一系列完全自治、封闭的对象。我们在类中编写的方法就是对实现细节的一种封装;我们编写 一个类就是对数据和数据操作的封装。可以说,封装就是隐藏一切可隐藏的东西, 只向外界提供最简单的编程接口(可以想想普通洗衣机和全自动洗衣机的差别, 明显全自动洗衣机封装更好因此操作起来更简单;我们现在使用的智能手机也是封装得足够好的,因为几个按键就搞定了所有的事情)。

五、继承

继承是从已有类得到继承信息创建新类的过程。提供继承信息的类被称为父类(超类、基类);得到继承信息的类被称为子类(派生类)。继承让变化中的软件系统有了一定的延续性,同时继承也是封装程序中可变因素的重要手段

六、多态

多态性是指允许不同子类型的对象对同一消息作出不同的响应。 简单的说就是用同样的对象引用调用同样的方法但是做了不同的事情。多态性分为编译时的多态性和运行时的多态性。如果将对象的方法视为对象向外界提供的服务,那么运行时的多态性可以解释为:当A系统访问B系统提供的服务时,B 系统有多种提供服务的方式,但一切对A系统来说都是透明的(就像电动剃须刀是A系统,它的供电系统是B系统,B系统可以使用电池供电或者用交流电, 甚至还有可能是太阳能,A系统只会通过B类对象调用供电的方法,但并不知道供电系统的底层实现是什么,究竟通过何种方式获得了动力)。

class Animal {
    public void makeSound() {
        System.out.println("Some generic animal sound");
    }
}

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

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

// 测试
public class Main {
    public static void main(String[] args) {
        Animal myAnimal = new Animal();
        myAnimal.makeSound();  // 输出: Some generic animal sound

        Animal myDog = new Dog();
        myDog.makeSound();     // 输出: Bark Bark

        Animal myCat = new Cat();
        myCat.makeSound();     // 输出: Meow Meow
    }
}

方法重载 (overload)实现的是编译时的多态性(也称为前绑定)。

而方法重写(override) 实现的是运行时的多态性(也称为后绑定)。运行时的多态是面向对象最精髓的东西,运行时系统根据实际对象的类型来决定调用哪个方法。要实现多态需要做两件事:

1). 方法重写(子类继承父类并重写父类中已 有的或抽象的方法);

2). 对象造型(用父类型引用引用子类型对象,这样同样 的引用调用同样的方法就会根据子类对象的不同而表现出不同的行为)。

  • 多态性:通过使用父类引用,你可以在运行时决定具体调用哪个子类的方法。这使得代码更加灵活,可以处理不同类型的子类对象。
  • 接口统一:使用父类引用可以确保所有子类对象都遵循相同的接口。
  • 降低耦合:使用父类引用可以降低代码之间的耦合度。如果你的代码只依赖于父类接口,那么将来添加新的子类或修改现有子类时,不需要修改使用这些对象的代码。
Animal myDog = new Dog();
Animal myCat = new Cat();

// 多态性
makeSound(myDog);  // 输出: Bark Bark
makeSound(myCat);  // 输出: Meow Meow

public static void makeSound(Animal animal) {
    animal.makeSound();
}



List<Animal> animals = new ArrayList<>();
animals.add(new Dog());
animals.add(new Cat());

for (Animal animal : animals) {
    animal.makeSound();  // 根据实际对象类型调用相应的方法
}

七、抽象与接口

1、抽象类 (Abstract Class)

抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面。抽象只关注对象有哪些属性和行为,并不关注这些行为的细节是什么

数据抽象是指隐藏具体的实现细节,只向外界暴露必要的信息。通过这种方式,用户只需要知道如何使用一个类或接口,而不必了解其内部的具体工作方式

行为抽象是指定义了一组操作或功能,但不指定具体如何执行这些操作。这通常通过抽象方法或者接口来实现。行为抽象使得不同的子类可以根据自己的需求以不同的方式实现相同的功能。

  • 语法:使用abstract关键字来定义抽象类或抽象方法。
  • 特点
    • 可以包含普通方法和静态方法。
    • 可以有构造器、成员变量等。
    • 子类继承抽象类时,必须提供抽象方法的具体实现,除非子类也是抽象类。
    • 一个类只能继承一个抽象类(因为Java不支持多重继承)。
  • 用途:当你想要创建一组相关的类,并且这些类共享一些共同的行为,但是也各自有一些特定的行为时,可以考虑使用抽象类。
public 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 void sleep() {
        System.out.println(name + " is sleeping...");
    }

    // 获取信息的方法
    public String getInfo() {
        return "Name: " + name + ", Age: " + age;
    }
}

2、接口 (Interface)

  • 定义:接口是完全抽象的类,它只定义了行为的规范,而不提供具体的实现。
  • 语法:使用interface关键字来定义接口。
  • 特点
    • 接口中所有的方法默认是公共的和抽象的(从Java 8开始,接口也可以包含默认方法和静态方法)。
    • 可以包含常量(即用final修饰的变量)。
    • 一个类可以实现多个接口。
    • Java 8及之后版本允许接口包含默认方法(default methods)和静态方法(static methods),这使得接口可以在不破坏现有实现的情况下添加新的功能。
  • 用途:当需要定义一种协议或标准,让不同的类去实现时,或者需要实现多继承的效果时,可以使用接口。
public interface Listable {
    // 原有的方法
    void add(Object element);
    
    // 新增的默认方法
    default void sort() {
        System.out.println("Sorting elements...");
        // 实现排序逻辑
    }
}

// 现有实现类
public class MyList implements Listable {
    private List<Object> list = new ArrayList<>();

    @Override
    public void add(Object element) {
        list.add(element);
    }

    // 不需要重写sort方法,除非想提供特定的实现
}

在Java 8中引入的接口默认方法(default methods)提供了一种向现有接口添加新功能而不破坏已实现该接口的类的方法。当你想要给一个现有的接口增加新的方法时,如果直接在接口中添加抽象方法,则所有实现了该接口的类都需要去实现这个新的方法,否则编译会失败。

使用默认方法可以避免这个问题,因为它提供了默认实现,所以不需要改变现有的实现类。这解决了以前版本中接口一旦发布就很难修改的问题,因为修改接口可能会影响到所有已经实现了该接口的类。

3、接口和抽象的异同点总结

(1).相同点

  • 两者都可以定义static方法
  • 两者都支持抽象方法的定义,即没有方法体的方法。
  • 都不能被实例化,只能通过子类或实现类来创建对象。
  • 无论是接口还是抽象类,都可以用于实现多态,允许一个变量引用不同类型的对象。

(2).不同点

  • 抽象类是通过extends关键字被继承的,而接口是通过implements关键字被实现的。
  • 一个类可以继承多个接口,但只能继承一个抽象类(因为Java不支持多重继承)。
  • 接口中的变量默认且只能是public static final的,即常量。抽象类可以有普通成员变量,包括静态变量和实例变量。
  • 接口中所有的方法默认是public abstract的,在Java 8及以后版本中还包含default方法。抽象类中可以有抽象方法和具体方法(非抽象方法),也可以有privateprotectedpublic等访问级别的方法。
  • 接口不能有构造器。抽象类可以有构造器,用于初始化成员变量等。
  • 接口主要用于定义行为规范,通常不涉及状态信息。抽象类不仅可以定义行为,还可以包含状态信息(成员变量)。

(3).使用场景

  • 当需要定义一组通用行为且这些行为可能由多个不相关的类实现时,应使用接口。
  • 当有一组紧密相关的类并且希望它们共享一些共同的数据和方法时,应使用抽象类。
  • 如果需要在未来添加新的方法而不影响现有的实现者,接口提供了更好的灵活性,特别是通过默认方法。
  • 如果需要为某些方法提供默认实现,并且希望子类能够继承这些实现,那么应该选择抽象类。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

java_学习爱好者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值