java基础面试题(5)
接口和抽象类有什么共同点和区别
共同点
都不能被实例化
两者都属于抽象类型,无法直接创建对象,只能作为父类(抽象类)或实现类(接口)的模板。都可以包含抽象方法
都能声明没有具体实现的方法(抽象方法),强制子类 / 实现类去实现这些方法,体现 “约定大于实现” 的思想。都可以被继承 / 实现
抽象类通过extends
被子类继承,接口通过implements
被类实现,且子类 / 实现类都必须实现所有抽象方法(除非自身也是抽象类)。都用于代码设计和复用
都可以定义规范(方法签名),供多个类遵循,实现代码的抽象和解耦,便于扩展和维护。
核心区别
特性 | 抽象类(Abstract Class) | 接口(Interface) |
---|---|---|
关键字 | 使用abstract class 定义 | 使用interface 定义 |
继承 / 实现方式 | 子类通过extends 单继承(Java 中类只能单继承) | 类通过implements 多实现(一个类可实现多个接口) |
方法实现 | 可以包含普通方法(有方法体)和抽象方法(无方法体) | Java 8 之前:只能有抽象方法; Java 8 之后:可包含默认方法( default )和静态方法(static )和private,但抽象方法仍需实现类完成 |
成员变量 | 可以有各种类型的成员变量(普通变量、静态变量等) | 只能是常量(默认被public static final 修饰,必须初始化) |
构造方法 | 有构造方法(供子类初始化时调用) | 没有构造方法 |
访问修饰符 | 类和方法可以用public 、protected 、default 修饰 | 接口本身和方法默认是public ,变量默认是public static final |
设计意图 | 体现 “is-a” 关系(强调继承和共性),用于抽取子类的共同属性和行为 | 体现 “has-a” 关系(强调功能扩展),用于定义类的额外能力或行为规范 |
深拷贝、浅拷贝、引用拷贝
1.浅拷贝(Shallow Copy)
浅拷贝会创建一个新对象,新对象的基本类型成员会直接复制原对象的值,但引用类型成员只会复制引用地址(即新对象和原对象的引用类型成员指向同一块内存地址)。
实现方式:
- 让类实现
Cloneable
接口(标记接口,无实际方法)。 - 重写
Object
类的clone()
方法,并修改访问权限为public
。
class Address {String city;public Address(String city) { this.city = city; }
}class Person implements Cloneable {String name; // 基本类型(包装类)Address address; // 引用类型public Person(String name, Address address) {this.name = name;this.address = address;}// 重写clone()实现浅拷贝@Overridepublic Person clone() throws CloneNotSupportedException {return (Person) super.clone();}
}public class Main {public static void main(String[] args) throws CloneNotSupportedException {Address addr = new Address("北京");Person p1 = new Person("张三", addr);// 浅拷贝Person p2 = p1.clone();System.out.println(p1.name == p2.name); // true(字符串常量池复用)System.out.println(p1.address == p2.address); // true(引用同一块内存)// 修改p2的引用类型成员,p1也会受影响p2.address.city = "上海";System.out.println(p1.address.city); // 输出"上海"(原对象被修改)}
}
2.深拷贝(Deep Copy)
深拷贝会创建一个新对象,不仅复制基本类型成员,还会对引用类型成员也进行拷贝(即创建新的引用类型对象),最终原对象和副本的所有成员都相互独立,互不影响。
实现方式:
- 方式 1:让所有引用类型成员也实现
Cloneable
接口,在clone()
方法中递归拷贝。 - 方式 2:通过序列化(Serialization)实现(将对象写入流,再从流中读取,本质是全新对象)。
// 示例代码(递归克隆实现深拷贝)
// 最底层的引用类型
class City implements Cloneable {String cityName;String zipCode;public City(String cityName, String zipCode) {this.cityName = cityName;this.zipCode = zipCode;}// 重写clone(),拷贝City自身@Overridepublic City clone() throws CloneNotSupportedException {return (City) super.clone();}
}// 中间层的引用类型(包含City成员)
class Address implements Cloneable {String street;City city; // Address类中新增的引用类型成员public Address(String street, City city) {this.street = street;this.city = city;}// 重写clone(),实现深拷贝:// 1. 拷贝Address自身// 2. 对其引用成员city也进行拷贝@Overridepublic Address clone() throws CloneNotSupportedException {Address addressClone = (Address) super.clone(); // 拷贝Address自身addressClone.city = this.city.clone(); // 递归拷贝City成员return addressClone;}
}// 顶层类(包含Address成员)
class Person implements Cloneable {String name;Address address;public Person(String name, Address address) {this.name = name;this.address = address;}// 重写clone(),实现深拷贝:// 1. 拷贝Person自身// 2. 对其引用成员address也进行拷贝(而address的clone()会继续拷贝City)@Overridepublic Person clone() throws CloneNotSupportedException {Person personClone = (Person) super.clone(); // 拷贝Person自身personClone.address = this.address.clone(); // 递归拷贝Address成员(触发Address的clone())return personClone;}
}
3.引用拷贝(Reference Copy)
在 Java 中,对象变量存储的是引用(内存地址),而非对象本身。引用拷贝就是创建一个新的引用变量,让它指向原对象的内存地址。
简单说:两个引用变量指向同一个对象实体。
4.三者区别
类型 | 操作对象 | 是否创建新对象 | 引用类型成员处理 | 原对象与副本的关系 |
---|---|---|---|---|
引用拷贝 | 引用变量 | 否 | 不处理(仅复制引用地址) | 同一对象(完全共享) |
浅拷贝 | 对象本身 | 是 | 仅复制引用地址(共享引用对象) | 基本类型独立,引用类型共享 |
深拷贝 | 对象及所有成员 | 是 | 递归复制所有引用类型成员 | 完全独立(无共享部分) |