继承与多态
一、概念
- 继承
继承是面向对象编程中的一个基本概念,它允许我们定义一个类(称为子类或派生类)来继承另一个类(称为父类或基类)的属性和方法。通过继承,子类可以复用父类的代码,同时也可以添加自己的特定属性和方法。
在Java中,继承是通过extends关键字来实现的。一个类只能直接继承一个父类(单继承),但可以通过接口(Interfaces)实现多重继承的效果。 - 多态
多态是面向对象编程中的另一个重要概念,它允许我们以统一的接口处理不同类型的对象。换句话说就是,根据接受对象的不同,进行不同的操作。
在Java中,多态主要有两种形式:编译时多态(通过方法重载实现)和运行时多态(通过继承和方法重写实现,也称为动态多态)。
二、代码示例解读
继承
package test1;
class Animal {
public void eat() {
System.out.println("Animal is eating");
}
protected void sleep() {
System.out.println("Animal is sleeping");
}
private void run() {
System.out.println("Animal is running");
}
void play() {
System.out.println("Animal is playing");
}
}
class Dog extends Animal {
public void bark() {
System.out.println("Dog is barking");
}
}
public class demo {
public static void main(String[] args) {
Animal animal = new Animal();
animal.eat(); // 执行结果:Animal is eating
Dog dog = new Dog();
dog.bark(); // 执行结果:Dog is barking
dog.eat(); // 执行结果:Animal is eating
dog.sleep(); // 执行结果:Animal is sleeping
dog.play(); // 执行结果:Animal is playing
// dog.run(); // 编译错误:Animal类中没有run()方法
}
}
在上述代码中,通过extend 关键字Dog继承了Animal 的eat方法。Animal 是Dog的父类,Dog是Animal 的子类。当实例化Dog类后调用eat方法,可以看到执行的就是从父类继承来的方法,sleep、run、paly方法同理。
继承范围:
在Java中,子类可以继承父类的非私有(non-private)成员方法,这包括:
public 方法:公开的方法可以被任何类访问,因此子类可以继承并调用这些方法。
protected 方法:受保护的方法可以被同一个包内的类以及任何子类访问。子类可以继承这些方法,并可以在子类内部或者子类的子类内部调用它们。
默认(包)访问权限的方法:如果没有指定访问修饰符(即没有使用public、protected或private),则该方法具有包访问权限。这意味着这些方法只能在定义它们的同一个包内的类中被访问和继承。
这里要注意:
private 方法:私有方法是不可继承的。它们只能在定义它们的类内部被访问和调用。如果子类试图重写(override)一个父类的私有方法,这实际上是在子类中定义了一个全新的私有方法,它与父类中的那个私有方法没有任何关系。如果运行示例代码可以看到 dog.run();是无法运行的。
重写
在这里将重写和重载的概念对比解释一下。
重写:
重写是发生在子类与父类之间的。当子类中存在一个与父类中具有相同方法签名(方法名和参数列表完全相同)的方法时,子类的方法就重写了父类的方法。
特点:
1、发生在子类与父类之间。
2、 方法名、参数列表必须完全相同。
3、 访问级别不能比被重写的方法更严格(但可以更宽松)。例如,如果父类方法是protected的,那么子类重写的方法不能是private的。
子类方法不能抛出比父类方法更广泛的检查型异常(unchecked exceptions除外)。
4、如果父类方法是static的,那么子类不能重写该方法;如果子类提供了一个与父类静态方法具有相同签名的方法,那么这实际上是隐藏了父类的静态方法,而不是重写。
重载:
它允许在同一个类中定义多个同名的方法,只要这些方法的参数列表不同即可。参数列表的不同可以体现在参数的类型、参数的个数或者参数的顺序上。重载与方法的访问修饰符和返回类型无关。
特点:
1、发生在同一个类中。
2、方法名必须相同。
3、参数列表必须不同(参数类型、个数或顺序至少有一项不同)。
4、与返回类型无关。
5、与访问修饰符无关。
package test1;
class Animal {
public void eat() {
System.out.println("Animal is eating");
}
private void run() {
System.out.println("Animal is running");
}
void play() {
System.out.println("Animal is playing");
}
}
class Dog extends Animal {
public void bark() {
System.out.println("Dog is barking");
}
@Override
public void eat() {
System.out.println("Dog is eating");
}
}
public class demo {
public static void main(String[] args) {
Animal animal = new Animal();
animal.eat();
Dog dog = new Dog();
dog.bark();
dog.eat(); // 执行结果:Dog is eating
}
}
通过上述代码可以看到dog.eat(); 实际上是执行的重写后的方法。
package test1;
class Animal {
int age = 1;
public void eat() {
System.out.println("Animal is eating");
}
private void run() {
System.out.println("Animal is running");
}
void play() {
System.out.println("Animal is playing");
}
}
class Dog extends Animal {
int age = 2;
public void bark() {
System.out.println("Dog is barking");
}
@Override
public void eat() {
System.out.println("Dog is eating");
}
}
public class demo {
public static void main(String[] args) {
Dog dog = new Dog();
System.out.println(dog.age); //输出结果:2
dog.eat(); //输出结果:Dog is eating
System.out.println("*******************");
Animal animal = new Dog();
System.out.println(animal.age); //输出结果:2
animal.eat(); //输出结果:Dog is eating
}
}
执行上述代码,可能会对变量age的打印结果有疑惑。虽然dog和animal都是Dog类的实例化对象,但当你通过 Dog 类型对象访问 age 变量时,你访问的是 Dog 类中定义的 age 变量,因此输出为 2。当你通过 Animal 类型的引用访问 age 变量时,你访问的是 Animal 类中定义的 age 变量,因为引用类型是 Animal,所以输出为 1。
类型转换
package test1;
class Animal {
int age = 1;
public void eat() {
System.out.println("Animal is eating");
}
private void run() {
System.out.println("Animal is running");
}
void play() {
System.out.println("Animal is playing");
}
}
class Dog extends Animal {
int age = 2;
public void bark() {
System.out.println("Dog is barking");
}
@Override
public void eat() {
System.out.println("Dog is eating");
}
public void getAge() {
System.out.println("Dog's age is " + this.age); //输出结果:Dog's age is 2
System.out.println("animal's age is " + super.age); //输出结果:animal's age is 1
super.play(); //输出结果:Animal is playing
}
}
public class demo {
public static void main(String[] args) {
Animal animal = new Dog();
System.out.println(animal.age); //输出结果:1
// animal.bark(); // 无法调用,因为Animal没有bark方法
Dog animal1 = (Dog)animal; //向下转型
System.out.println(animal1.age); //输出结果:2
animal1.bark(); //输出结果:Dog is barking
System.out.println("*********************");
//优化后的方案: 判断当前对象是否是Dog类的对象, 如果是, 再调用bark()方法.
if(animal instanceof Dog) { //判断animal是否是Dog类的对象
//能走到这里, 说明条件满足
Dog animal3 = (Dog)animal;
animal3.bark(); // 输出结果: Dog is barking
}
}
}
向上转型:
向上转型是指将子类的引用转换为父类的引用。由于子类继承自父类,因此子类对象可以视为父类的一个特例,所以这种转换是安全的。向上转型是自动进行的,不需要显式地进行类型转换。子类可以直接调用从父类继承来的方法。
向下转型:
向下转型是指将父类的引用转换为子类的引用。由于父类引用可能指向多种子类对象(如果存在多个子类继承自同一个父类),因此这种转换是不安全的,需要进行显式转换,并且可能会抛出ClassCastException。可以在向下转型前使用instanceof 方法进行判断。
执行上述代码,看星号分割线上方的执行结果。在执行 Dog animal1 = (Dog)animal; 向下转型之前,Animal 类型的Dog实例化对象无法调用Dog类中的独有方法dark方法,当执行向下转型之后,可以调用dark方法,并且调用的变量也发生了变化。
this和super
this用于引用当前对象,可以调用当前对象的成员变量和方法,以及构造器。
super用于引用当前对象的直接父类,可以访问父类的成员变量和方法,以及调用父类的构造器
当使用super调用父类的方法时,可以调用父类中定义的任何非私有(non-private)实例方法(即,除了被声明为private的方法以外的所有实例方法)。这是因为私有方法仅在其定义的类内部可见,无法通过继承访问。当然,父类的私有变量也是不可以的。
package test1;
class Animal {
int age = 1;
public void eat() {
System.out.println("Animal is eating");
}
private void run() {
System.out.println("Animal is running");
}
void play() {
System.out.println("Animal is playing");
}
}
class Dog extends Animal {
int age = 2;
public void bark() {
System.out.println("Dog is barking");
}
@Override
public void eat() {
System.out.println("Dog is eating");
}
public void getAge() {
System.out.println("Dog's age is " + this.age); //输出结果:Dog's age is 2
System.out.println("animal's age is " + super.age); //输出结果:animal's age is 1
super.play(); //输出结果:Animal is playing
}
}
通过上述代码可以知道,通过this.age调用的是当前类中定义的int age = 2;以及方法,super调用的是父类中的方法或变量。
标签:System,java,继承,void,多态,Dog,Animal,println,out From: https://blog.csdn.net/kopokliv/article/details/141144436