面向对象基础

面向对象与面向过程的主要区别

面向对象和面向过程的区别在于问题解决的方式:

  • 面向过程通过将问题拆分为一个个方法来解决,依靠方法的执行来完成任务。
  • 面向对象则是先抽象出对象,并通过对象来调用方法解决问题。

此外,面向对象的程序通常更易于维护、重用和扩展。

成员变量与局部变量的区别

  • 语法形式:成员变量属于类,而局部变量在方法或代码块中定义。成员变量可以被 public, private, static 等修饰符修饰,而局部变量不能被访问控制修饰符及 static 修饰。
  • 存储方式:成员变量存放在堆内存中,而局部变量存放在栈内存中。若成员变量被 static 修饰,则属于类,否则属于实例。
  • 生存时间:成员变量伴随对象的创建而存在,局部变量在方法调用时生成,方法结束时消失。
  • 默认值:若成员变量未赋值,则会自动赋类型的默认值(被 final 修饰的成员变量需显式赋值),而局部变量则不会自动赋值。

创建对象时使用的运算符及对象实体与对象引用的不同

创建对象时使用 new 运算符,它会在堆内存中创建对象实例,而对象引用则存放在栈内存中。一个对象引用可以指向一个对象,也可以不指向任何对象;反之,一个对象可以被多个引用指向。

对象相等与引用相等的区别

  • 对象相等主要比较内存中存放的内容是否一致。
  • 引用相等比较的是对象指向的内存地址是否相同。

类的构造方法的作用

构造方法是特殊的方法,主要用于完成对象的初始化工作。

类未声明构造方法的情况下程序是否能正常执行?

如果类没有声明构造方法,程序仍可正常执行,因为Java会默认提供一个无参数的构造方法。若自定义构造方法,默认的无参数构造方法将不再存在。因此,在重载构造方法时,建议还要写出无参数构造方法以避免潜在问题。

构造方法的特点及是否可被重写

构造方法的特点包括:

  • 名字与类名相同。
  • 没有返回值,且不能用 void 声明。
  • 对象创建时自动执行,无需显式调用。

构造方法不能被重写,但可以重载,因此一个类中可以包含多个构造函数。

面向对象的三大特征

封装

封装是将对象内部的状态信息隐藏,使外部无法直接访问。可以通过提供的访问方法来操作属性。例如,空调的内部零件是封装的,控制空调的遥控器相当于访问方法。

public class Student {
    private int id; // id属性私有化
    private String name; // name属性私有化

    // 获取id的方法
    public int getId() {
        return id;
    }

    // 设置id的方法
    public void setId(int id) {
        this.id = id;
    }

    // 获取name的方法
    public String getName() {
        return name;
    }

    // 设置name的方法
    public void setName(String name) {
        this.name = name;
    }
}

继承

继承允许新类基于现存类创建,并能增加新的特性或功能。例如,小明、小红和小李同学共享学生的特性(如班级、学号),同时每个学生也有其独特性。通过继承,能够快速创建新类,提高代码的重用性,增强程序的可维护性。

记住以下三点关于继承:

  1. 子类继承父类的所有属性和方法,但无法访问父类的私有属性和方法。
  2. 子类可以添加自己的属性和方法。
  3. 子类可以重写父类的方法。

多态

多态表示一个对象可以有多种状态,具体表现为父类的引用指向子类的实例。

多态的特点:

  • 对象类型和引用类型之间存在继承或实现关系。
  • 方法调用的具体实现必须在程序运行期间确定。
  • 多态不能调用仅存在于子类中的方法。
  • 若子类重写了父类的方法,实际执行的是子类的实现。

接口与抽象类的共同点和区别

共同点:

  • 均不能被实例化。
  • 均可以包含抽象方法。
  • Java 8 之后,均可以定义默认实现的方法。

区别:

  • 接口主要用于约束行为,抽象类强调代码复用。
  • 一个类只能继承一个抽象类,但可以实现多个接口。
  • 接口的成员变量只能是 public static final,而抽象类的成员变量可以具有不同的访问修饰符。

深拷贝与浅拷贝的区别及引用拷贝的概念

  • 浅拷贝:在堆上创建新对象,若原对象内部属性为引用类型,则仅复制引用地址,导致拷贝对象与原对象共享同一内部对象。
  • 深拷贝:完全复制整个对象,包括内部对象。

引用拷贝:不同的引用指向同一个对象。

示例代码片段展示了如何实现浅拷贝和深拷贝。

浅拷贝示例

public class Address implements Cloneable {
    private String name;

    @Override
    public Address clone() {
        try {
            return (Address) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }
}

public class Person implements Cloneable {
    private Address address;

    @Override
    public Person clone() {
        try {
            return (Person) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }
}

深拷贝示例

@Override
public Person clone() {
    try {
        Person person = (Person) super.clone();
        person.setAddress(person.getAddress().clone());
        return person;
    } catch (CloneNotSupportedException e) {
        throw new AssertionError();
    }
}

Java 常见类

Object 类的常见方法

Object 类是所有类的父类,提供了11个基本方法,如 getClass(), hashCode(), equals(), clone(), 和 toString()

==equals() 方法的区别

  • 对于基本数据类型,== 比较值。
  • 对于引用数据类型,== 比较内存地址。
  • equals() 方法用于判断两个对象的内容是否相等。

hashCode() 方法的作用

hashCode() 获取一个对象的哈希码,常用于哈希表的索引位置。为了提高效率,hashCode()equals() 一起使用,以更快地判断对象是否相等。

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

因为相等的对象必须具有相同的哈希码,若只重写 equals() 而不重写 hashCode() 可能导致哈希冲突,影响对象在集合中的存储和检索。

StringStringBufferStringBuilder 的区别

  • 可变性String 不可变,StringBufferStringBuilder 可变。
  • 线程安全性StringBuffer 是线程安全的,StringBuilder 不安全。
  • 性能StringBuilder 性能优于 StringBuffer

字符串不可变的原因

String 的数组为 final,并且没有修改方法。String 类被声明为 final,不允许被继承。

字符串拼接时使用 + 还是 StringBuilder

在循环中使用 + 进行字符串拼接会创建多个对象,推荐使用 StringBuilder 以提高性能。

字符串常量池的作用

字符串常量池用于避免字符串的重复创建,提高性能和减少内存消耗。