接口和抽象类共同点和区别
接口和抽象类共同点
- 不能直接实例化:
- 接口和抽象类都不能直接创建对象。它们必须通过子类(实现接口的类或继承抽象类的类)来实例化。
- 定义行为:
- 接口和抽象类都可以定义类应该实现的方法或属性。
- 提高代码复用性:
- 通过定义接口和抽象类,可以将公共行为或属性提取出来,从而减少代码重复,提高代码复用性。
- 支持多态性:
- 接口和抽象类都支持多态性,即允许使用父类(接口或抽象类)的引用来指向子类对象。
区别
- 定义方法的方式:
- 接口:接口只能包含抽象方法(在Java 8之前)和默认方法(在Java 8及之后),以及静态方法和常量(在Java 9及之后)。抽象方法没有方法体,只有声明。
- 抽象类:抽象类可以包含抽象方法(没有方法体的方法),也可以包含具体方法(有方法体的方法)。
- 多继承:
- 接口:一个类可以实现多个接口,从而继承多个接口中的方法。这是实现多重继承的一种方式。
- 抽象类:一个类只能继承一个抽象类(单继承)。
- 字段:
- 接口:接口中的字段默认是
public static final
的,即它们是全局常量。 - 抽象类:抽象类中的字段可以是任意的访问修饰符(如
private
、protected
、public
),并且可以是静态的或非静态的。
- 接口:接口中的字段默认是
- 构造函数:
- 接口:接口不能有构造函数,因为接口不能被实例化。
- 抽象类:抽象类可以有构造函数,这些构造函数主要用于初始化抽象类中的字段。
- 目的和设计意图:
- 接口:接口通常用于定义对象的行为,而不关注对象的具体实现。接口主要关注“是什么”,即对象应该具备哪些能力。
- 抽象类:抽象类则更多地用于定义对象的共享属性或行为,并可以包含部分实现。抽象类主要关注“是什么以及部分怎么做”,即对象应该具备哪些能力,以及部分能力的具体实现。
接口的示例
public interface Animal {
void eat();
void sleep();
}
public class Dog implements Animal {
@Override
public void eat() {
System.out.println("Dog is eating");
}
@Override
public void sleep() {
System.out.println("Dog is sleeping");
}
}
抽象类的示例
public abstract class Shape {
protected String color;
public Shape(String color) {
this.color = color;
}
public abstract void draw();
public void setColor(String color) {
this.color = color;
}
public String getColor() {
return color;
}
}
public class Circle extends Shape {
public Circle(String color) {
super(color);
}
@Override
public void draw() {
System.out.println("Drawing a circle with color " + color);
}
}
浅拷贝和深拷贝
-
浅拷贝:
浅拷贝是对象的逐位复制,即创建一个新对象,该对象具有原始对象中值的精确副本。但如果对象的任何字段是对其他对象的引用,则只复制引用地址,即复制内存地址。这意味着,浅拷贝后的两个对象共享对同一个内存地址的引用。因此,修改其中一个对象的引用类型属性,另一个对象的相应属性也会发生变化。 -
深拷贝:
深拷贝是指在复制对象时,不仅复制其基本类型值,还递归地复制其所有引用类型的属性所指向的对象,直到所有的可到达对象都是独立的副本。这样,修改新副本中的任何内容都不会影响原对象及其任何其他副本。深拷贝确保了新对象和原对象在内存中是完全独立的。
区别
-
独立性:
- 浅拷贝:两个对象不是完全独立的,因为它们可能共享对同一个内存地址的引用。
- 深拷贝:两个对象是完全独立的,它们在内存中有不同的地址,且各自的属性也是独立的副本。
-
内存使用:
- 浅拷贝:由于只复制了引用地址,所以内存使用相对较少。
- 深拷贝:由于需要递归地复制所有对象及其属性,所以内存使用相对较多。
-
修改影响:
- 浅拷贝:修改一个对象的引用类型属性会影响另一个对象。
- 深拷贝:修改一个对象的属性不会影响另一个对象。
应用场景
-
浅拷贝:
适用于不需要完全独立副本的场景,例如临时存储或传递对象时,如果不需要修改对象的内容,可以使用浅拷贝来减少内存使用。 -
深拷贝:
适用于需要完全独立副本的场景,例如数据备份、多线程编程、对象复制和克隆等。在这些场景中,确保对象的独立性是至关重要的,以避免意外的数据修改或竞态条件。
实现方法
-
浅拷贝:
在Java中,可以使用Object.assign()
方法、slice()
方法(针对数组)或concat()
方法(针对数组)来实现浅拷贝。此外,使用拷贝构造函数或实现Cloneable
接口也可以实现浅拷贝(但需要注意,Cloneable
接口本身并不直接提供深拷贝或浅拷贝的功能,而是由程序员在clone()
方法中实现具体的拷贝逻辑)。 -
深拷贝:
在Java中,实现深拷贝的常用方法包括使用递归算法手动实现深拷贝、使用序列化/反序列化机制(将对象转换为字节流,然后再从字节流中恢复出对象)、使用第三方库(如Lodash的_.cloneDeep()
方法)等。需要注意的是,使用序列化/反序列化机制时,对象必须实现Serializable
接口。