Skip to content

面向对象

三大特性

什么是面向对象?三大特性是什么?

答案: 面向对象(OOP)是一种编程思想,将现实世界的事物抽象为对象,通过对象之间的交互完成功能。

三大特性

  1. 封装(Encapsulation)
  2. 继承(Inheritance)
  3. 多态(Polymorphism)

封装

定义:将对象的属性和方法封装起来,隐藏内部实现细节,只暴露必要的接口。

优点

  • 提高安全性
  • 降低耦合度
  • 便于维护
java
public class Person {
    // 私有属性
    private String name;
    private int age;

    // 公共方法访问
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        if (age > 0 && age < 150) {  // 数据校验
            this.age = age;
        }
    }
}

继承

定义:子类继承父类的属性和方法,实现代码复用。

特点

  • Java只支持单继承
  • 子类可以重写父类方法
  • 使用extends关键字
java
// 父类
public class Animal {
    protected String name;

    public void eat() {
        System.out.println("动物吃东西");
    }
}

// 子类
public class Dog extends Animal {
    @Override
    public void eat() {
        System.out.println("狗吃骨头");
    }

    public void bark() {
        System.out.println("汪汪叫");
    }
}

多态

定义:同一个方法调用,不同对象有不同的行为。

实现方式

  1. 方法重载(编译时多态)
  2. 方法重写(运行时多态)

条件

  • 继承
  • 重写
  • 父类引用指向子类对象
java
public class PolymorphismDemo {
    public static void main(String[] args) {
        // 父类引用指向子类对象
        Animal animal1 = new Dog();
        Animal animal2 = new Cat();

        animal1.eat();  // 输出:狗吃骨头
        animal2.eat();  // 输出:猫吃鱼

        // 向下转型
        if (animal1 instanceof Dog) {
            Dog dog = (Dog) animal1;
            dog.bark();
        }
    }
}

class Cat extends Animal {
    @Override
    public void eat() {
        System.out.println("猫吃鱼");
    }
}

类与对象

类和对象的关系?

答案:

  • :对象的模板,描述对象的属性和行为
  • 对象:类的实例,具体的个体
java
// 类
public class Car {
    String brand;
    String color;

    void drive() {
        System.out.println("开车");
    }
}

// 对象
Car car1 = new Car();
car1.brand = "BMW";
car1.color = "黑色";
car1.drive();

构造方法的作用?

答案: 构造方法用于创建对象时初始化对象。

特点

  • 方法名与类名相同
  • 没有返回值类型
  • 创建对象时自动调用
  • 可以重载
java
public class Person {
    private String name;
    private int age;

    // 无参构造
    public Person() {
        this.name = "未知";
        this.age = 0;
    }

    // 有参构造
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // 构造方法重载
    public Person(String name) {
        this(name, 0);  // 调用其他构造方法
    }
}

this和super关键字的区别?

答案:

关键字作用
this引用当前对象
super引用父类对象
java
public class Parent {
    String name = "父类";

    public Parent() {
        System.out.println("父类构造方法");
    }

    public void show() {
        System.out.println("父类方法");
    }
}

public class Child extends Parent {
    String name = "子类";

    public Child() {
        super();  // 调用父类构造方法(必须在第一行)
        System.out.println("子类构造方法");
    }

    public void show() {
        System.out.println(this.name);   // 子类
        System.out.println(super.name);  // 父类
        super.show();  // 调用父类方法
    }
}

抽象类与接口

抽象类和接口的区别?

答案:

特性抽象类接口
关键字abstractinterface
继承单继承多实现
方法可以有具体实现默认抽象(JDK8+可有default方法)
变量可以有实例变量只能有常量(public static final)
构造方法可以有不能有
访问修饰符任意默认public
java
// 抽象类
public abstract class Animal {
    String name;  // 实例变量

    public Animal(String name) {  // 构造方法
        this.name = name;
    }

    abstract void sound();  // 抽象方法

    public void sleep() {  // 具体方法
        System.out.println("睡觉");
    }
}

// 接口
public interface Flyable {
    int MAX_SPEED = 100;  // 常量

    void fly();  // 抽象方法

    default void land() {  // 默认方法(JDK8+)
        System.out.println("降落");
    }

    static void info() {  // 静态方法(JDK8+)
        System.out.println("飞行接口");
    }
}

// 实现
public class Bird extends Animal implements Flyable {
    public Bird(String name) {
        super(name);
    }

    @Override
    void sound() {
        System.out.println("鸟叫");
    }

    @Override
    public void fly() {
        System.out.println("鸟飞");
    }
}

什么时候用抽象类,什么时候用接口?

答案:

使用抽象类

  • 有共同的属性和方法
  • 需要构造方法
  • 需要非public成员

使用接口

  • 定义行为规范
  • 需要多继承
  • 不同类有相同行为
java
// 抽象类:描述"是什么"
abstract class Vehicle {
    String brand;
    abstract void start();
}

// 接口:描述"能做什么"
interface Drivable {
    void drive();
}

interface Flyable {
    void fly();
}

// 汽车:是交通工具,能驾驶
class Car extends Vehicle implements Drivable {
    void start() {}
    public void drive() {}
}

// 飞机:是交通工具,能驾驶,能飞
class Plane extends Vehicle implements Drivable, Flyable {
    void start() {}
    public void drive() {}
    public void fly() {}
}

内部类

内部类有哪些?

答案:

1. 成员内部类

java
public class Outer {
    private String name = "外部类";

    class Inner {  // 成员内部类
        public void show() {
            System.out.println(name);  // 可以访问外部类成员
        }
    }

    public void test() {
        Inner inner = new Inner();
        inner.show();
    }
}

// 使用
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();

2. 静态内部类

java
public class Outer {
    private static String name = "外部类";

    static class Inner {  // 静态内部类
        public void show() {
            System.out.println(name);  // 只能访问静态成员
        }
    }
}

// 使用
Outer.Inner inner = new Outer.Inner();

3. 局部内部类

java
public class Outer {
    public void test() {
        class Inner {  // 局部内部类
            public void show() {
                System.out.println("局部内部类");
            }
        }

        Inner inner = new Inner();
        inner.show();
    }
}

4. 匿名内部类

java
public class AnonymousDemo {
    public static void main(String[] args) {
        // 匿名内部类实现接口
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("匿名内部类");
            }
        };

        // Lambda表达式(JDK8+)
        Runnable runnable2 = () -> System.out.println("Lambda");
    }
}

Object类

Object类的常用方法?

答案:

java
public class Object {
    // 1. equals() - 比较对象是否相等
    public boolean equals(Object obj) {
        return (this == obj);
    }

    // 2. hashCode() - 返回对象的哈希码
    public native int hashCode();

    // 3. toString() - 返回对象的字符串表示
    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

    // 4. clone() - 克隆对象
    protected native Object clone() throws CloneNotSupportedException;

    // 5. getClass() - 获取对象的Class对象
    public final native Class<?> getClass();

    // 6. wait() / notify() / notifyAll() - 线程通信
    public final native void wait(long timeout) throws InterruptedException;
    public final native void notify();
    public final native void notifyAll();

    // 7. finalize() - 垃圾回收前调用(已废弃)
    protected void finalize() throws Throwable { }
}

为什么重写equals()必须重写hashCode()?

答案: 保证equals()相等的对象,hashCode()也相等,否则在HashMap等集合中会出现问题。

java
public class Person {
    private String name;
    private int age;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age && Objects.equals(name, person.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

问题示例

java
Person p1 = new Person("Tom", 20);
Person p2 = new Person("Tom", 20);

System.out.println(p1.equals(p2));  // true

Map<Person, String> map = new HashMap<>();
map.put(p1, "value1");
System.out.println(map.get(p2));  // 如果没重写hashCode(),返回null

练习题

  1. 什么是向上转型和向下转型?
  2. 为什么Java不支持多继承?
  3. 内部类有什么优缺点?
  4. 如何实现对象的深拷贝?

Released under the MIT License.