小林coding面试题笔记(三)

怎么理解面向对象?简单说说封装、继承、多态

面向对象是一种编程范式,它通过将现实世界中的事物抽象为对象来构建程序。对象包含属性(字段)和行为(方法),通过对象之间的交互完成程序功能。面向对象编程的核心思想是以对象为中心,具有灵活性和可扩展性,能够更好地应对需求变化。

Java面向对象的三大特性包括:

  • 封装:将对象的属性和行为结合在一起,对外隐藏内部实现细节,仅通过公开的接口与外界交互。封装提高了安全性和代码的可维护性。

    • 示例:在银行账户类中,balance 属性被声明为私有,外部只能通过 getBalance()deposit() 方法访问和修改余额。
  • 继承:子类可以继承父类的属性和方法,从而实现代码复用。继承建立了类与类之间的层次关系,使代码结构更加清晰。

    • 示例:Animal 类定义了通用的 eat() 方法,Dog 类继承 Animal 并自动拥有 eat() 方法。
  • 多态:允许不同类的对象对同一消息作出响应。多态分为编译时多态(方法重载)和运行时多态(方法重写),使程序更加灵活和可扩展。

    • 示例:Shape 类有一个 draw() 方法,CircleRectangle 类分别实现自己的 draw() 方法。调用 shape.draw() 时,具体执行哪个方法取决于 shape 的实际类型。

多态体现在哪几个方面?

多态在面向对象编程中主要体现在以下几个方面:

  1. 方法重载

    • 同一类中可以定义多个同名方法,但参数列表必须不同(参数类型、数量或顺序不同)。编译器在编译时决定调用哪个方法。
    • 示例:
      1
      2
      3
      4
      5
      6
      7
      8
      class Calculator {
      int add(int a, int b) {
      return a + b;
      }
      double add(double a, double b) {
      return a + b;
      }
      }
  2. 方法重写

    • 子类可以重新定义父类中的方法,方法名、参数列表和返回类型必须一致。在运行时,JVM根据对象的实际类型决定调用哪个方法。
    • 示例:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      class Animal {
      void sound() {
      System.out.println("Animal makes a sound");
      }
      }
      class Dog extends Animal {
      @Override
      void sound() {
      System.out.println("Dog barks");
      }
      }
  3. 接口与实现

    • 不同类可以实现同一个接口,并通过接口类型的引用调用这些类的方法。
    • 示例:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      interface Animal {
      void makeSound();
      }
      class Dog implements Animal {
      public void makeSound() {
      System.out.println("Dog barks");
      }
      }
      class Cat implements Animal {
      public void makeSound() {
      System.out.println("Cat meows");
      }
      }
  4. 向上转型和向下转型

    • 向上转型:父类类型的引用指向子类对象。
      1
      2
      Animal animal = new Dog();
      animal.sound(); // 输出 "Dog barks"
    • 向下转型:将父类引用转回其子类类型,但需确保引用实际指向的对象类型以避免 ClassCastException
      1
      2
      3
      4
      if (animal instanceof Dog) {
      Dog dog = (Dog) animal;
      dog.sound();
      }

多态解决了什么问题?

多态通过允许子类替换父类,增强了代码的扩展性和复用性。它是许多设计模式和编程原则的基础,例如策略模式、依赖倒置原则和里氏替换原则。

  • 示例:在策略模式中,定义一个通用接口 PaymentStrategy,不同的支付方式(如信用卡支付、PayPal支付)实现该接口。通过多态,可以在运行时动态选择支付方式,而无需修改现有代码。

面向对象的设计原则有哪些?

面向对象编程中的六大设计原则:

  1. 单一职责原则(SRP):一个类应该只负责一项职责。

    • 示例:将员工管理和薪资计算分为两个独立的类。
  2. 开放封闭原则(OCP):软件实体应对扩展开放,对修改封闭。

    • 示例:通过接口定义图形类,不修改图形类本身即可扩展新图形。
  3. 里氏替换原则(LSP):子类对象应能替换所有父类对象。

    • 示例:正方形继承矩形时,修改矩形的行为不应破坏正方形的逻辑。
  4. 接口隔离原则(ISP):接口应小而专,客户端不应依赖不需要的接口。

    • 示例:通过接口抽象层实现模块解耦。
  5. 依赖倒置原则(DIP):高层模块不依赖低层模块,二者都依赖于抽象。

    • 示例:使用依赖注入减少模块间耦合。
  6. 最少知识原则(LoD):一个对象应只与其直接的朋友交互。

    • 示例:在一个电梯系统中,按钮类只与电梯控制器交互,而不直接操作电梯。

重载与重写的区别

  • 重载(Overloading)

    • 同一类中定义多个同名方法,参数列表不同。
    • 编译时决定调用哪个方法。
    • 示例:
      1
      2
      3
      4
      5
      6
      7
      8
      class Printer {
      void print(String text) {
      System.out.println(text);
      }
      void print(int number) {
      System.out.println(number);
      }
      }
  • 重写(Overriding)

    • 子类重新定义父类中的方法,方法名、参数列表和返回类型必须一致。
    • 运行时决定调用哪个方法。
    • 示例:
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      class Parent {
      void show() {
      System.out.println("Parent show");
      }
      }
      class Child extends Parent {
      @Override
      void show() {
      System.out.println("Child show");
      }
      }

抽象类和普通类的区别

  • 实例化:普通类可以直接实例化,抽象类不能被实例化。
  • 方法实现:普通类中的方法必须有实现,抽象类中的方法可以有实现也可以没有。
  • 继承:普通类可以被继承,抽象类只能作为基类被继承。

Java抽象类和接口的区别

  • 抽象类

    • 描述类的共同特性和行为。
    • 可以有成员变量、构造方法和具体方法。
  • 接口

    • 定义行为规范。
    • 只能有常量和抽象方法(Java 8 以后可有默认方法和静态方法)。

抽象类能加 final 修饰吗?

不能。final 修饰符禁止类被继承,而抽象类需要被继承,因此两者互斥。


接口可以包含哪些方法?

  • 抽象方法:默认 public abstract
  • 默认方法default 关键字实现。
  • 静态方法static 关键字实现。
  • 私有方法private 关键字实现,仅供接口内部使用。

抽象类可以被实例化吗?

抽象类不能直接实例化,但可以通过继承抽象类并实现所有抽象方法的子类来实例化。


接口可以包含构造函数吗?

不可以。接口不能被实例化,因此不需要构造函数。


解释 Java 中的静态变量和静态方法

  • 静态变量

    • 属于类本身,所有实例共享。
    • 在类加载时初始化。
  • 静态方法

    • 属于类本身,无需实例化即可调用。
    • 不能直接访问非静态成员。

非静态内部类和静态内部类的区别

  • 非静态内部类

    • 依赖于外部类实例。
    • 可以访问外部类的实例变量和方法。
  • 静态内部类

    • 不依赖于外部类实例。
    • 只能访问外部类的静态成员。

非静态内部类可以直接访问外部方法的原因

编译器在生成字节码时会为非静态内部类维护一个指向外部类实例的引用,从而实现直接访问外部方法的功能。