继承,super,重写,多态,抽象,接口
继承,super,重写,多态,抽象,接口
继承
extends
用于表示两个类之间的继承关系,继承是OOP的四大特性之一,他允许一个类(称之为子类或派送类) 继承另一个类(称之为父类或基类)的变量和方法,子类可以复用父类的方法和变量,也可以添加和覆盖父类的方法和变量
extends的基本语法
- 使用
extends
关键字来声明子类继承父类
class ParentClass()
{
//父类属性和方法
}
class ChildrenClass extends ParentClass()
{
//子类的属性与方法
}
- 上述ChildrenClass类继承了ParentClass类,子类可以复用ParentsClass类的属性与方法
继承的特性
- 代码复用:子类可以之间使用父类的方法和属性,无需重新编写
- 扩展性:子类可以添加新的属性和方法,以满足实际开发需求
- 多态性:通过继承子类可以覆盖父类的方法,实现多态性
继承的限制
- java中不支持多继承,即一个类只能有一个父类.但后续学的接口是可以多继承的.
- 再java中所有类都直接或间接的继承Object类
继承的层级结构
- 由于继承的限制性,即一个子类只能继承一个父类,但一个父类可以被多个子类继承,子类也可以作为其他子类的父类.再java中就形成了一个树状层级结构,
继承下构造器的调用
- 在创建子类时,java会优先调用父类的构造器(如果没有显式调用,则会调用无参构造器),然后再调用子类的构造器,.如果需要显式调用,则需用
super
关键字来实现
class ParentClass
{
public ParentClass()
{
System.out.println("这是父类的无参构造方法");
}
public ParentClass(String str)
{
System.out.println("这是父类的一个带参构造器");
}
}
class ChildrenClass extends ParentClass{
public ChildrenClass()
{
System.out.println("这是子类的无参构造方法");
}
public ChildrenClass(String str){
System.out.println("这是一个子类的带参构造器");
}
public static void main(String[] args) {
ChildrenClass childrendclass = new ChildrenClass();
ChildrenClass childrenclass new ChildrenClass("11");
}
}
- 分析:
ChildrenClass
继承了ParentClass
接着再main函数创建ChildrenClass
的无参实例对象时,会先调用父类的无参构造器打印这是父类的无参构造方法
,接着再调用子类的无参构造器这是子类的无参构造方法
当实例化子类带参构造器时,也会先调用父类的带参构造器这是父类的无参构造方法
,接着调用子类的带参构造器这是一个子类的带参构造器
- 当子类构造器调用了父类的有参方法,则父类的无参构造器不会被调用,只会调用有参构造器
调用了无参构造器构造器,则父类的有参构造器不会被调用, 即每次只会调用一种构造器
super关键字
super
关键字是java中重要的一个引用变量,他主要用于引用当前对象的父类对象,super()
实际是调用父类的构造器
调用父类构造函数
- 在子类的构造器中,你可以使用
super()
调用父类的构造函数,
访问父类中的成员变量
- 若子类想要调用父类的成员变量时可以用
super
关键字进行调用
class Parent {
int value = 100;
}
class Child extends Parent {
int value = 200;
void display() {
System.out.println("Parent value: " + super.value); //调用父类的value
System.out.println("Child value: " + this.value);
}
}
public class Main {
public static void main(String[] args) {
Child child = new Child();
child.display();
}
}
访问父类的方法
在子类中可以使用super关键字子访问父类的方法
class Parent {
void display() {
System.out.println("Parent display method");
}
}
class Child extends Parent {
@Override
void display() {
super.display(); // 调用父类的display方法
System.out.println("Child display method");
}
}
public class Main {
public static void main(String[] args) {
Child child = new Child();
child.display();
}
}
重写(Override
override:也称覆盖,重写是子类对父类非静态,非private,非final方法的实现过程进行重新编写, 返回值(JDK7之后,被重写的方法返回值,类型可以不同,但必须有父子关系)和形参都不能改变.即外壳不能改变→重写的核心
方法名与参数列表必须相同
子类中的重写方法必须与父类中的被重写方法具有相同的方法名和参数列表,相同的参数列表即意味着,参数类型,数量,顺序都必须相同
返回类型
- 如果父类被重写的方法放回的是基本数据类型或
void
,则子类重写的方法的返回值必须与父类方法一直 - 如果父类被重写方法放回的是引用数据类型,则子类重写的方法返回值的类型可以与父类相同或者是其子类型
public class Animal {
// ... 其他方法 ...
public Animal getAnimal() {
return new Animal(); // 返回一个Animal对象
}
}
public class Dog extends Animal {
// ... 其他方法 ...
// 重写getAnimal方法,但返回更具体的类型Dog
@Override
public Dog getAnimal() {
return new Dog(); // 返回一个Dog对象,它是Animal的子类 也是合法的
}
}
访问权限(封装
- 子类重写方法的权限(
public,protected,默认,private
)都不能低于父类方法的访问权限,例如父类中方法是public
,则子类中必须是public
异常处理
- 子类重写方法抛出的异常类型,应该是父类被重写方法抛出的异常的子集或者相同,即子类不能抛出比父类多的异常
静态方法不能被重写
- 静态方法只与类相关,并非与实例相关,.静态方法不属于继承的层次结构.
final方法不能被重写
被final
修饰的方法表示该方法不能被覆盖或继承,因此子类不能重写父类中的final
方法
多态
严格的文字定义:它允许我们使用父类类型变量来引用子类对象这意味着,当我们对父类类型的变量进行操作时,具体的行为取决于该变量所引用的实际对象的类型
简单的理解:同一个行为具有不同的表现形式,即执行一段代码java在运行中能根据对象产生不同的结果
多态的前提条件
- 子类继承同一父类
- 子类覆盖(重写)父类方法
- 父类引用指向子类对象
- 注意点:多态下,引用调用的方法是子类重写方法,调用的属性是父类的属性
抽象类 (abstract
抽象类也是类,只是具备了一些特殊的性质;抽象类的主要作用是提供了一个模板或是框架,这个模板中定义了一些属性与方法,但是并不需要完全实现这些方法.→相当于你告诉其他类:你们可以按照我的这个模板来创建自己的类,但是你需要实现我未完成的部分;
模板作用
- 想象你有一个抽象类叫做
Animal
,它有一个方法eat()
。但不同的动物吃东西的方式可能不同,所以eat()
方法没有具体的实现。其他类,比如Dog
和Cat
,可以继承Animal
类,然后各自实现自己的eat()
方法.
强制子类实现某个方法
- 如果你有一个抽象方法(即在抽象类中声明但没有实现的方法),那么任何继承这个抽象类的子类都必须实现这个方法。这确保了子类具有某种特定的功能或行为
代码复用
- 抽象类可以包含一些通用的、非抽象的方法和属性,这些方法和属性可以被所有子类共享,避免了在每个子类中重复编写相同的代码
定义了接口
- 虽然接口(Interface)也用于定义类的行为,但抽象类可以包含方法的实现,而接口只能定义方法签名。因此,在某些情况下,抽象类可以提供更灵活的接口定义。
注意点
- 抽象类不能被实例化
- 这是
abstract
类最基本的特点。由于抽象类可能包含没有具体实现的抽象方法,因此不能创建抽象类的实例。只能创建抽象类的非抽象子类的实例
- 抽象类中可以包含抽象方法和非抽象方法
- 有抽象方法的类一定是抽象类,但抽象类不一定有抽象方法。即使抽象类中可以没有抽象方法,普通类中不能有抽象方法
- 子类必须实现父类中的所有抽象方法
- 如果一个类继承了抽象类,并且该抽象类中有抽象方法,那么子类必须提供这些方法的具体实现,除非子类也是抽象类
- 抽象方法不能有方法体
- 被
abstract
修饰的方法不能有具体的方法体,即没有大括号{}
和具体的实现代码
- 不能与
private
、static
、final
等关键字共用
private
修饰的方法表示私有,不允许子类访问,而abstract
修饰的方法必须被子类覆盖重写才能使用,所以二者矛盾。static
关键字修饰的是静态方法,静态方法属于类而不属于实例→属于类,而抽象方法没有具体的实现,需要子类来提供,所以抽象方法不能是静态的。final
关键字表示最终的,不可改变的,而抽象方法需要子类来实现,所以不能是final
的。
- 抽象类必须有构造方法
- 虽然抽象类不能被实例化,但它的构造方法可以用于被子类调用。子类在创建对象时,会先调用父类的构造方法
// 抽象类 Animal
abstract class Animal {
// 抽象方法
abstract void makeSound();
// 非抽象方法
void eat() {
System.out.println("The animal eats.");
}
// 构造方法
Animal() {
System.out.println("Animal is being constructed.");
}
}
// Dog 类继承自 Animal
class Dog extends Animal {
// 实现从 Animal 继承的抽象方法
@Override
void makeSound() {
System.out.println("The dog barks.");
}
// Dog 类的构造方法
Dog() {
super(); // 调用父类的构造方法
System.out.println("Dog is being constructed.");
}
}
// 主类
public class Main {
public static void main(String[] args) {
// 抽象类不能被实例化
// Animal animal = new Animal(); // 这是错误的
// 创建 Dog 和 Cat 的对象
Dog dog = new Dog();
Cat cat = new Cat();
// 调用方法
dog.eat();
dog.makeSound();
}
}
- 结果如下
Animal is being constructed. //父类无参构造器先被调用
Dog is being constructed.
The animal eats.
The dog barks.
接口
接口声明了一组方法(规范),但不关心这些方法(规范)是怎么实现的,任何实现这些接口的类都必须遵循这些方法(规范),但对于具体方法的细节完全由类自身决定.
接口可以看作是一种特殊的类,但接口中所有的方法都是抽象方法(即没有方法体),通常不会写属性;所有的属性都是常量(即使用
public static final
修饰
接口的特性
1.抽象性
- 接口中的所有方法都是抽象的,或者说都被隐型定义成abstract;的因此它们没有方法体。实现接口的类必须为接口中的所有方法提供具体的实现
2.接口不能被实例化
- 接口本身不能被实例化,即不能使用
new
关键字来创建接口的实例。但是,可以声明接口的引用变量,该变量引用实现该接口的类的实例。
3.接口可以多实现
- 一个类可以实现多个接口,通过逗号分隔接口名。这允许类继承多个接口中的方法规范
public class Dog implements Animal, Runnable { //多实现接口
@Override
public void eat() {
System.out.println("Dog is eating");
}
@Override
public void sleep() {
System.out.println("Dog is sleeping");
}
@Override
public void run() {
System.out.println("Dog is running");
}
}
4.多继承的替代
- 在Java中,类只能继承自一个父类,但可以实现多个接口。这提供了一种替代多重继承的方式,允许类继承多个行为。
5.接口的继承
- 接口可以继承自其他接口,通过
extends
关键字实现。这允许接口之间共享方法的规范
// 定义一个父接口
interface ParentInterface {
void method1();
void method2();
int CONSTANT = 10; // 可以在接口中定义常量
}
// 定义一个子接口,继承自ParentInterface
interface ChildInterface extends ParentInterface {
void method3();
}
- 在这个例子中,
ChildInterface
继承自ParentInterface
,因此它自动包含了ParentInterface
中定义的所有方法(method1()
和method2()
)和常量(CONSTANT
)。同时,ChildInterface
还可以定义自己特有的方法(如method3()
)。
接口的作用
- 多继承
- 不同子类实现相同的行为
JDK1.8之后(包括1.8)接口的变化
- 可以在接口写非抽象方法
// 使用接口名.方法名调用
static void 方法名称() {
}
// 可以被子类继承,实例化子类之后调用
default void 方法名称() {
}
接口与抽象类的区别
实现与继承
- 接口:一个类可以实现多个接口,通过
implements
关键字实现,接口中所有的方法都是抽象的,即没有方法体 - 抽象类: 一个类只能继承一个抽象类,沟通
extends
关键字实现,抽象类中可以包含抽象方法和非抽象方法
使用场景
- 接口: 通常用于定义一种规范或契约,不关心对象的具体实现.如定义API,插件架构等
- 抽象类: 常用于提供一个公共父类,包含一些公共的方法和属性,有子类继承并实现具体的方法.
设计原则
- 接口:强调“契约”和“规范”,关注对象的行为。一个类可以实现多个接口,从而具备多种能力
- 抽象类:强调模板与复用,通过继承实现代码的复用,子类继承抽象类后,可以直接使用父类中的非抽象方法和属性。
实例化
- 接口:接口不能被实例化,只能被实现
- 抽象类:可以被实例化,但只能通过子类被实例化,如果抽象方法中包括非抽象方法则子类可以直接调用.