多态
基本信息:
应用场景:
可以把子类对象赋值给父类对象,实现多态从而使用同一种方法;
多态中调用成员的特点
1.调用成员变量都看左边
调用成员变量:编译看左边,运行也看左边
编译看左边: javac编译代码的时候,会看左边的父类中有没有这个变量,如果有,编译成功,如果没有编译失败报错。
运行也看左边: java运 行代码的时候,实际获取的就是左边父类中成员变量的值
2.调用成员方法一左一右
调用成员方法:编译看左边,运行看右边
编译看左边: javac编译代码的时候,会看左边的父类中有没有这个方法,如果有,编译成功,如果没有报错。
运行看右边: java运 行代码的时候,实际上运行的是子类中的方法。
3.成员变量和成员方法
成员变量:在子类的对象中,会把父类的成员变量也继承下的,所以使用的是弗雷德变量。
成员方法:如果子类对方法进行了重写,那么在虚方法表中是会把父类的方法进行覆盖的,所以调用成员方法输出就是成员重写的方法。
多态的优势
多态的弊端
不能调用子类的特有共能;
解决方案:强转回子类即可;
判断+转换+调用
小总结:
继承多态小案例:
package 多态的实例;
public abstract class Animal {
private String colour;
private int age;
public Animal() {
}
public Animal(String colour, int age) {
this.colour = colour;
this.age = age;
}
public void eat(String something){
System.out.println("吃"+something);
}
/**
* 获取
* @return name
*/
public String getColour() {
return colour;
}
/**
* 设置
*/
public void setColour(String colour) {
this.colour = colour;
}
/**
* 获取
* @return age
*/
public int getAge() {
return age;
}
/**
* 设置
* @param age
*/
public void setAge(int age) {
this.age = age;
}
public String toString() {
return "Animal{colour = " + colour+ ", age = " + age + "}";
}
public abstract void eat();
}
package 多态的实例;
public class Dog extends Animal{
@Override
public void eat(){
System.out.println(getAge()+"岁"+getColour()+"颜色"+"的狗两只前腿死死的抱住骨头猛吃");
}
public void lookHome(){
System.out.println("lookHome");
}
}
package 多态的实例;
public class Cat extends Animal{
@Override
public void eat() {
}
public void catchMouse(){
System.out.println("catchMouse");
}
public Cat() {
}
public Cat(String colour, int age) {
super(colour, age);
}
}
package 多态的实例;
public class Polymorphic_demo {
public static void main(String[] args) {
Person p =new Person();
p.setName("老王");
p.setAgeMan(33);
Dog d =new Dog();
d.setAge(3);
d.setColour("黑色");
Cat c =new Cat();
c.setAge(2);
c.setColour("黑白");
p.grrenxinxi(d,"骨头");
p.grrenxinxi(c,"粑粑");
}
}
抽象类
抽象类(Abstract Class)是一种在编程语言中用于表示不能被实例化的类的概念。它的主要目的是提供一个或多个抽象方法(即没有实现体的方法,只有方法声明),这些方法必须由继承自抽象类的子类来实现。抽象类还可以包含已实现的方法(即具有方法体的方法)以及字段、属性、构造函数等。
抽象类的主要特点包括:
-
不能被实例化:抽象类不能直接创建对象,即不能使用
new
关键字来实例化抽象类。它的主要目的是被其他类继承,并由这些子类提供具体的实现。 -
包含抽象方法:抽象类可以包含一个或多个抽象方法。抽象方法是没有方法体的方法,只有方法的声明(即方法签名),并且必须以
abstract
关键字进行修饰。 -
允许包含非抽象方法和字段:除了抽象方法外,抽象类还可以包含已经实现的方法(即非抽象方法)以及字段、属性等。
-
子类必须实现父类的所有抽象方法:如果子类继承了一个抽象类,并且这个子类不是抽象类(即这个子类需要被实例化),那么子类必须实现父类中所有的抽象方法。否则,子类也必须被声明为抽象类。
-
作为类型引用:虽然不能直接实例化抽象类,但可以使用抽象类作为类型引用,指向其子类的实例。
抽象类在软件设计中非常有用,尤其是在实现多态和框架设计时。通过使用抽象类,可以定义一套接口规范,让不同的子类按照这套规范来实现具体功能,同时保证了代码的灵活性和可扩展性。
示例(以Java为例):
abstract class Animal { | |
// 抽象方法 | |
abstract void eat(); | |
// 已实现的方法 | |
void sleep() { | |
System.out.println("This animal sleeps."); | |
} | |
} | |
class Dog extends Animal { | |
// 必须实现父类的抽象方法 | |
@Override | |
void eat() { | |
System.out.println("Dog eats meat."); | |
} | |
} | |
public class Test { | |
public static void main(String[] args) { | |
Animal myDog = new Dog(); // 使用抽象类作为类型引用 | |
myDog.eat(); // 调用Dog类实现的eat方法 | |
myDog.sleep(); // 调用Animal类中已实现的sleep方法 | |
} | |
} |
抽象类的构造方法、成员方法和成员变量的几问
在Java中,抽象类(Abstract Class)与它的构造方法(Constructor)、成员变量(Member Variables)、以及成员方法(Member Methods,包括抽象方法和非抽象方法)之间有着特定的关系。下面分别解释这些关系:
构造方法(Constructor)
- 构造方法的存在:抽象类可以有构造方法。这些构造方法主要用于在创建子类对象时初始化父类(即抽象类)的成员变量。
- 构造方法的调用:当子类被实例化时,会首先调用父类(如果父类是抽象类)的构造方法(除非子类也是抽象类且仅被用作其他类的基类)。这是Java中对象初始化的一个基本步骤,确保父类被正确初始化。
- 限制:构造方法本身不能被声明为抽象(
abstract
)。
成员变量(Member Variables)
- 定义:抽象类可以定义成员变量,这些成员变量可以是任何类型(基本数据类型、对象引用等)。
- 访问:成员变量可以在抽象类的构造方法、成员方法(包括抽象方法和非抽象方法)中被访问和修改。同时,它们也可以被子类继承,并在子类中通过继承的
super
关键字或直接访问(如果它们不是私有的)来访问和修改。
成员方法(Member Methods)
- 抽象方法:抽象类中可以定义抽象方法,这些方法只有声明没有实现体(即方法体为空,并且使用
abstract
关键字修饰)。子类必须实现这些抽象方法,除非子类也是抽象类。 - 非抽象方法:抽象类中也可以定义非抽象方法,这些方法有具体的实现体,可以直接被调用。这些非抽象方法可以被继承到子类,并在子类中被直接使用或重写。
- 访问:成员方法(无论是抽象的还是非抽象的)都可以在抽象类的构造方法、其他成员方法中被调用(抽象方法除外,因为抽象方法没有实现体)。同时,这些方法也可以被子类继承,并在子类中通过继承或重写来使用。
小总结
- 抽象类可以有构造方法,但构造方法本身不能是抽象的。
- 抽象类可以定义成员变量,这些变量可以在构造方法、成员方法中被访问和修改。
- 抽象类可以定义抽象方法和非抽象方法。抽象方法必须由子类实现(除非子类也是抽象类),而非抽象方法可以直接在子类中使用或重写。
- 成员变量和成员方法(非抽象的)都可以被子类继承,并根据需要在子类中进行访问、修改或重写。
接口
在Java中,接口(Interface)是一种引用类型,是一种抽象的类型,用于指定一组方法规范,但不提供这些方法的具体实现。接口是一种形式上的契约,它要求实现了该接口的类(称为接口的实现类或实现接口的类)必须遵循接口中定义的规范。
接口的主要特点:
-
抽象性:接口中的所有方法默认都是抽象的(在Java 8之前),即它们只有声明没有实现体。从Java 8开始,接口中可以包含带有实现体的默认方法和静态方法。
-
继承性:接口可以继承另一个或多个接口,使用
extends
关键字。接口之间的继承是多继承的,即一个接口可以继承多个其他接口。 -
多态性:接口是支持多态性的重要手段。通过接口,我们可以指向实现了该接口的任何对象的引用。
-
实现接口的类必须实现接口中的所有方法(在Java 8之前),除非该类是抽象类。从Java 8开始,如果一个类实现了接口,但它不想实现接口中的某个默认方法,它可以选择不覆盖该方法,而是直接使用接口中提供的默认实现。
-
接口中不能包含实例变量,但可以包含常量(即使用
public static final
修饰的变量,但通常省略这些修饰符)。 -
接口中的方法默认是
public
的,且不允许使用其他访问修饰符(如private
、protected
或包级私有)。 -
接口可以包含嵌套接口、枚举和静态类(在Java 9之前,接口中只能包含静态内部类;从Java 9开始,接口中可以包含非静态内部类,但内部类不能是抽象的)。
接口的使用场景包括定义一组方法的规范,使不相关的类可以实现这个接口,从而实现多态性。例如,List
、Set
和Map
等Java集合框架中的接口定义了集合操作的标准方法,不同的类如ArrayList
、LinkedList
和HashMap
等实现了这些接口,提供了不同的实现方式。
接口在Java编程中非常重要,它促进了代码的模块化、可重用性和可扩展性。通过定义接口,我们可以让不同的类按照相同的约定进行工作,而不需要知道这些类的具体实现细节。
Java如何表示接口:
public interface Animal {
// 这是一个抽象方法,默认是public abstract的
void eat();
// 接口中的常量,默认是public static final的
int AGE = 10;
// Java 8 引入的默认方法
default void sleep() {
System.out.println("This animal sleeps.");
}
// Java 8 引入的静态方法
static void info() {
System.out.println("This is an interface for animals.");
}
}
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 in a doghouse.");
}
// 静态方法和常量可以直接通过接口名访问,无需实现
public static void main(String[] args) {
Animal.info(); // 直接访问接口的静态方法
System.out.println(Animal.AGE); // 直接访问接口的常量
Dog dog = new Dog();
dog.eat();
dog.sleep(); // 调用的是Dog类中覆盖的sleep方法
}
}
接口实例化(虽然不能直接实例化抽象类,但可以使用抽象类作为类型引用,指向其子类的实例。)
// 定义接口
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.");
}
}
// 在另一个类中实例化Dog类
public class Main {
public static void main(String[] args) {
// 实例化Dog类,但可以将实例引用视为Animal类型
Animal myDog = new Dog();
// 调用接口中定义的方法,但实际上是调用Dog类中的实现
myDog.eat();
myDog.sleep();
// 注意:你不能直接实例化接口,下面的代码会编译错误
// Animal anotherAnimal = new Animal(); // 错误:接口不能被实例化
}
}
在这个例子中,Dog类实现了Animal接口,所以我们可以通过new Dog()来创建一个Dog对象。但是,由于Dog实现了Animal接口,我们可以将这个Dog对象的引用视为Animal类型(即Animal myDog = new Dog();)。这允许我们在不直接依赖于具体类(Dog)的情况下编写更灵活和可重用的代码。我们可以将myDog引用传递给期望Animal类型参数的任何方法或函数,只要这些方法或函数不依赖于Animal接口中未由Dog类实现的任何额外方法。
接口的成员方法和成员变量
成员方法
接口中的成员方法默认是public abstract
的,这意味着它们必须被实现接口的类所实现(除非该类也是抽象的)。然而,从Java 8开始,接口还可以包含默认方法(使用default
关键字)和静态方法(使用static
关键字)。
- 抽象方法:没有方法体的方法,必须由实现接口的类提供具体实现。
- 默认方法:提供了一种方式,允许接口在不破坏向后兼容性的情况下添加新的方法实现。
- 静态方法:可以直接通过接口名来调用,不需要实现接口的类实例。
成员变量
接口中的成员变量实际上是常量,因为它们在接口中默认是public static final
的。这意味着你可以直接通过接口名来访问这些常量,而不需要创建接口的实例。
使用示例
// 定义一个接口 | |
public interface Animal { | |
// 成员变量(实际上是常量) | |
int MAX_AGE = 100; | |
// 抽象方法 | |
void eat(); | |
// 默认方法 | |
default void sleep() { | |
System.out.println("This animal sleeps."); | |
} | |
// 静态方法 | |
static void info() { | |
System.out.println("This is an interface for animals."); | |
} | |
} | |
// 实现接口的类 | |
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 in a doghouse."); | |
} | |
// 使用接口中的常量 | |
public void printMaxAge() { | |
System.out.println("The maximum age of an animal is " + Animal.MAX_AGE); | |
} | |
} | |
// 在另一个类中测试 | |
public class Main { | |
public static void main(String[] args) { | |
// 访问静态方法 | |
Animal.info(); | |
// 实例化Dog类 | |
Dog myDog = new Dog(); | |
// 调用抽象方法的实现 | |
myDog.eat(); | |
// 调用默认方法(可能被覆盖) | |
myDog.sleep(); | |
// 调用打印常量的方法 | |
myDog.printMaxAge(); | |
} | |
} |
在这个例子中,Animal
接口定义了一个常量MAX_AGE
、一个抽象方法eat()
、一个默认方法sleep()
和一个静态方法info()
。Dog
类实现了Animal
接口,并提供了eat()
方法的具体实现,同时覆盖了sleep()
默认方法。在Main
类中,我们展示了如何通过接口名直接访问静态方法和常量,以及如何通过实现接口的类实例来调用抽象方法和默认方法。