继承
在面向对象编程中,继承是一种机制,允许一个类(称为子类或派生类)从另一个类(称为父类或基类)获取属性和方法。当子类继承了父类的成员时,它可以选择重新定义或覆盖父类的方法。这就是继承中的覆盖(Override)。
覆盖是指子类重新实现了父类中已经存在的方法。子类可以根据自己的需要,重新定义同名的方法,并提供不同的实现。当通过子类对象调用该方法时,程序会执行子类中的方法而不是父类中的方法。
覆盖有以下几个特点:
(1)方法签名必须与父类中被覆盖的方法相同,包括方法名、返回类型和参数列表。这是为了确保覆盖的方法能够正确地重写父类的方法。
(2)覆盖只能发生在子类中对父类方法的重写,而不能在父类中对自己的方法进行覆盖。
(3)子类覆盖父类方法时,可以选择是否调用父类的方法。通过使用关键字 super,可以在子类中调用父类的方法,从而在子类方法中扩展父类方法的功能。
覆盖的目的通常是为了实现子类的特定行为,或者改变父类方法的实现方式以适应子类的需求。通过覆盖,可以在继承关系中实现多态性和灵活性
实现代码如下:
package day01;
class person{
String name;
int age;
public person(String name, int age) {
this.name = name;
this.age = age;
System.out.println("我是一个人");
}
void story(){
System.out.println("我是"+name);
}
}
class Student extends person {
String sex;
public Student(String name, int age, String sex) {
super(name, age);
this.sex = sex;
}
void story(){
super.story();
System.out.println("我是"+name+"性别"+sex);
}
}
public class Test{
public static void main(String[] args) {
Student p=new Student("大明",19,"男");
p.story();
}}
Student子类中的story方法覆盖了父类中的方法,但是在子类的方法中可以选择调用父类的被覆盖的方法,如上代码就调用了父类的方法。
抽象
抽象类和抽象方法是面向对象编程中常用的概念,它们提供了一种让子类实现特定行为的方式。
抽象类(Abstract Class)是不能被实例化的类,它只能被用作其他类的父类。抽象类可以包含抽象方法和具体实现的方法。抽象方法(Abstract Method)是没有实现的方法,其关键字一般为 abstract,它只有声明部分而没有方法体,需要在子类中进行实现。
抽象类通常具有以下特点:
(1)抽象类不能被实例化,只能被用作其他类的父类。
(2)抽象类可以包含抽象方法和具体实现的方法。
(3)子类必须实现所有父类中的抽象方法,否则子类也必须定义为抽象类。
抽象类通常用于定义一些通用的行为和属性,但是不需要提供具体的实现,而是通过子类来实现具体的行为和功能。抽象类可以提供一些默认的实现方法,以便子类在不需要重新实现全部方法的情况下继承和使用这些方法。
抽象方法通常用于定义一些必须由子类实现的行为。抽象方法只有声明部分而没有方法体,因此需要在子类中进行具体的实现。抽象方法通常用于定义一些接口或协议,以便子类实现特定的功能。在Java中,抽象方法的关键字为 abstract
抽象方法是在抽象类中声明但没有具体实现的方法。它只包含方法的签名(方法名、参数列表和返回类型),而没有方法体。抽象方法用于定义一种规范或约束,要求派生类必须实现这些抽象方法才能被实例化。
抽象方法的主要特点如下:
(1)无法在抽象类中定义具体的方法体:抽象方法没有具体的实现代码,因此在抽象类中无法为抽象方法提供具体的方法体。只有非抽象方法可以在抽象类中提供具体的实现。
(2)必须在派生类中实现:如果一个类继承了抽象类,并且这个抽象类有抽象方法,那么派生类必须实现这些抽象方法,否则派生类也必须被声明为抽象类。
(3)实现方式与普通方法相同:派生类实现抽象方法的方式与实现普通方法相同,需要提供具体的方法体。派生类可以根据自己的需求来实现抽象方法,完成具体的功能。
(4)可以通过多态性调用:抽象方法可以通过多态性的特性来调用。当抽象类的引用指向派生类的对象时,可以通过抽象类的引用来调用派生类中实现的抽象方法,这样可以根据实际对象的类型来调用相应的方法。
抽象方法的存在使得抽象类具有了一定的规范和约束性。通过在抽象类中声明抽象方法,可以要求派生类必须实现这些方法,从而确保派生类具备特定的行为和功能。抽象方法在面向对象编程中常用于实现多态性、定义接口规范和实现代码复用等方面。
package day01;
abstract class Animal{
String style;
int age;
public Animal(String style, int age) {
this.style = style;
this.age = age;
}
abstract void Action();
}
class Dog extends Animal{
public Dog(String style, int age) {
super(style, age);
}
@Override
void Action() {
System.out.println(style+"会汪汪叫");
}
}
class Cat extends Animal{
public Cat(String style, int age) {
super(style, age);
}
@Override
void Action() {
System.out.println(style+"会喵喵叫");
}
}
public class Test1 {
public static void main(String[] args) {
Dog d=new Dog("小狗",1);
Cat c=new Cat("小猫",3);
d.Action();
c.Action();
}
}
接口
接口是一种在面向对象编程中用于定义规范的结构。它可以看作是一组方法的集合,其中每个方法都是抽象的(没有具体实现)。通过接口,可以定义类应该具有哪些行为和功能,从而实现代码的模块化和灵活性。
接口的主要特点如下:
(1)定义规范:接口定义了一组方法的签名,即方法名、参数列表和返回类型。这些方法表示了一个类应该具备的行为和功能,相当于一份使用手册或契约,提供了对类的使用者的指导。
(2)约束实现类:实现类必须遵循接口中定义的方法签名,即实现接口中声明的所有方法。这样可以确保实现类具有接口所规定的行为和功能,并且可以使不同的实现类之间可以互相替换使用。
(3)多继承的实现:一个类可以实现多个接口,从而实现多继承的效果。通过实现不同的接口,一个类可以具备不同接口所定义的行为和功能,实现更加灵活的代码组织和复用。
(4)接口与抽象类的区别:接口只能包含抽象方法和常量,而不能包含普通方法和属性。此外,一个类可以实现多个接口,但只能继承一个抽象类。
(5)接口的实现方式:实现接口的类需要使用implements关键字,然后实现接口中定义的所有方法。接口中的方法必须在实现类中被具体实现,否则该实现类也必须声明为抽象类。
package day01;
abstract class Animal{
String style;
int age;
public Animal(String style, int age) {
this.style = style;
this.age = age;
}
abstract void ID();
}
interface Action{
abstract void eat();
}
interface Sound{
abstract void Speak();
}
class Cat extends Animal implements Action,Sound{
public Cat(String style, int age) {
super(style, age);
}
@Override
public void ID() {
System.out.println(style+"今年"+age+"岁");
}
public void eat(){
System.out.println(style+"吃鱼");
}
@Override
public void Speak() {
System.out.println(style+"喵喵叫");
}
}
public class Test2 {
public static void main(String[] args) {
Cat c=new Cat("小猫",2);
c.ID();
c.eat();
c.Speak();
}
}
接口中的方法都是抽象方法。在接口中定义的方法没有具体的实现代码,只有方法的声明,没有方法体。因此,接口中的方法被默认为抽象方法。抽象方法只提供了方法的签名,但没有具体的实现逻辑
在面向对象编程中,接口之间也可以进行继承,这种继承被称为接口的继承
接口的继承允许一个接口继承另一个或多个接口的方法签名,从而可以将多个相关的接口组合成一个更大的接口。通过继承,子接口可以继承父接口的方法签名,并可以在子接口中添加新的方法签名或重写父接口的方法签名。
接口的继承使用关键字extends来表示,其语法如下:
interface 接口名 extends 父接口1, 父接口2, ... {
// 子接口的方法和常量定义
}
子接口可以继承一个或多个父接口,多个父接口间使用逗号分隔。子接口会继承父接口中的所有方法签名,并且可以在子接口中增加新的抽象方法或常量。
在Java 8中引入了接口中的静态方法和默认方法,这两种方法为接口带来了更强大的灵活性和功能性。
1.静态方法:接口中的静态方法可以直接通过接口名调用,无需依赖实现类的对象。静态方法通常用于提供一些与接口相关的工具方法或辅助方法。例如:
public interface MyInterface {
static void staticMethod() {
System.out.println("This is a static method in MyInterface");
}
}
在调用静态方法时,可以直接通过接口名调用:
MyInterface.staticMethod();
2.默认方法:接口中的默认方法是指在接口中可以提供方法的默认实现,实现类可以选择性地覆写默认方法,也可以直接继承使用。默认方法的引入使得接口可以向后兼容地添加新的方法,而不会影响已有的实现类。例如:
public interface MyInterface {
default void defaultMethod() {
System.out.println("This is a default method in MyInterface");
}
}
实现类可以选择性地覆写默认方法:
public class MyClass implements MyInterface {
@Override
public void defaultMethod() {
System.out.println("Overridden default method in MyClass");
}
}
在调用默认方法时,如果实现类没有覆写该方法,则会使用接口中提供的默认实现:
MyClass myClass = new MyClass();
myClass.defaultMethod(); // 输出为:Overridden default method in MyClass
接口中的方法通常是没有方法体的。在Java 8之前,接口中只能声明方法,而实现类需要提供具体的方法实现。但是,在Java 8中引入了默认方法(Default Method)的概念。
默认方法是指在接口中可以提供方法的默认实现,这样实现类可以选择性地覆写默认方法,也可以直接继承使用。为了支持默认方法,Java 8在接口中引入了关键字default。
默认方法的引入主要是为了向后兼容地添加新的方法,而不会影响已有的实现类。当我们在接口中定义一个默认方法时,这个方法就有了一个默认的方法体,实现类可以直接继承该方法体。
例如,下面是一个接口中定义了一个默认方法的示例:
public interface MyInterface {
void normalMethod(); // 普通方法声明
default void defaultMethod() {
System.out.println("This is a default method in MyInterface");
}
}
在上述示例中,defaultMethod()是一个默认方法,它有一个默认的方法体。实现类可以选择性地覆写该方法,或者直接继承使用。
在接口多重继承中,如果两个或多个接口定义了同名的方法,实现类就会面临名字冲突的问题。为了解决这个问题,可以采取以下两种方式:
1.显式指定方法实现:当实现类需要处理两个接口中具有相同方法名的情况时,可以在实现类中显式地指定要调用的接口方法。例如:
interface A {
void method();
}
interface B {
void method();
}
class MyClass implements A, B {
public void method() {
// 指定调用A接口的method方法
A.super.method();
// 或者指定调用B接口的method方法
B.super.method();
}
}
在上述示例中,MyClass实现了接口A和B,并且这两个接口都定义了名为method的方法。在MyClass中,我们可以使用A.super.method()和B.super.method()来分别指定调用接口A和B的方法。
2.方法重写:另一种解决名字冲突问题的方式是在实现类中进行方法重写,重新定义一个新的方法。例如:
interface A {
void method();
}
interface B {
void method();
}
class MyClass implements A, B {
public void method() {
// 实现自己的逻辑
}
}
在上述示例中,MyClass实现了接口A和B,并且这两个接口都定义了名为method的方法。在MyClass中,我们可以重新定义一个新的method方法,实现自己的逻辑。
需要注意的是,在使用方法重写时,只能重写一次方法,不能分别为不同的接口重写方法。因此,这种方式适用于当两个接口中的同名方法的逻辑相同或者可以通过调用其他方法来公用逻辑的情况。
通过显式指定方法实现或方法重写,可以解决接口多重继承中的名字冲突问题,并根据具体需求选择适合的方式。