文章目录
Java学习之继承与多态
1.继承
1.1 为什么要有继承
我们通过下面的代码进行解释:
class Dog{
String name;
int age;
public void eat(){
System.out.println(name+"正在吃饭");
}
public void bark(){
System.out.println(name+"汪汪叫");
}
}
class Cat{
string name;
int age;
public void eat(){
System.out.println(name+"正在吃饭");
}
public void mew(){
System.out.println(name+"喵喵叫");
}
}
通过观察上面的代码,我们发现猫和狗的类存在大量的重复,那能否将这些共性进行抽取呢?面向对象思想中就提到了继承的概念:对共性的抽取,从而实现对代码的复用。
1.2继承的概念
例如猫和狗都是动物,我们则可以将共性的属性进行抽取,从而实现对代码的复用。
上图中,Dog和Cat类都继承了Animal类,其中Animal类被称为父类、基类或超类,D og和Cat类被称为子类或派生类。继承之后,子类可以复用父类中的成员。
1.3如何实现继承
在Java中要表示继承关系,需要用到extends关键字。
class Animal{
String name;
int age;
public void eat(){
System.out.println(name+"正在吃饭");
}
}
class Dog extends Animal{
public void bark(){
System.out.println(name+"汪汪叫");
}
}
class Cat extends Animal{
public void mew(){
System.out.println(name+"喵喵叫");
}
}
public class Text {
public static void main(String[] args) {
Dog dog=new Dog();
Cat cat=new Cat();
dog.name="七月";
cat.name="遇见";
dog.bark();
cat.mew();
}
}
通过运行可以看出,猫和狗这两个类中没有定义任何成员变量,name和age变量一定是从父类中继承下来的。
1.4 super关键字
由于一些原因,子类和父类中会出现相同名称的成员,如果在子类对象中访问父类同名成员时,该如何操作?直接访问是无法做到的,Java中提供了super关键字,该关键字的主要作用:在子类方法中访问父类成员。
class Father{
int a;
int b;
int c;
public void methodA(){
System.out.println("父类中无参的methodA");
}
public void methodB(){
System.out.println("父类中的methodB");
}
}
class Son extends Father{
int a;
char c;
//与父类中的func构成重载
public void methodA(int a){
System.out.println("子类中带有参数的methodA");
}
//与父类中的func构成重写,重写后序会介绍
public void methodB(){
System.out.println("子类中的methodB");
}
public void methodC(){
a=100;
c='y';
super.a=101;
b=105;
//子类和父类中存在重载的方法,直接可以通过参数列表区分访问父类还是子类的方法
methodA();//没有参数,调用父类中的methodA()
methodA(15);//传递int参数,调用子类中的methodA()
methodB();//直接访问,永远访问的是子类的
super.methodB();//通过super关键字访问父类成员变量
System.out.println(a);
System.out.println(c);
System.out.println(super.a);
System.out.println(b);
}
}
public class Text {
public static void main(String[] args) {
Son son=new Son();
son.methodC();
}
}
通过运行结果可以看出,当子类和父类中存在相同的成员变量时,直接访问时,都是访问子类的,如果想要在子类方法中明确访问父类的方法,则有到super关键字即可。
注:
super关键字只能在非静态方法中使用
在子类方法中,访问父类成员变量和方法
2.1再谈构造方法
在类与对象中我们介绍过构造方法,那么在继承中构造方法又是如何使用的呢?
父子父子,先有父再有子,即当我们调用子类构造方法时,得先调用基类构造方法,再执行子类构造方法。
class Base{
public Base() {
System.out.println("Base");
}
}
class Derived extends Base{
public Derived(){
//super() 子类构造方法中会默认调用父类的无参构造方法,super()
//没写时系统会默认加上,且super()必须是子类构造方法的第一条语句
System.out.println("Derived");
}
}
public class Text {
public static void main(String[] args) {
Derived derived=new Derived();
}
从运行结果我们可以看出,当我们new一个子类的时候,调用子类构造方法前会先帮助父类进行构造,
注:
- 若父类显式定义无参或者默认的构造方法,在子类构造方法第一行默认有隐含的super()调用,即调用基类构
造方法- 如果父类构造方法是带有参数的,此时需要用户为子类显式定义构造方法,并在子类构造方法中选择合适的
父类构造方法调用,否则编译失败。- 在子类构造方法中,super(…)调用父类构造时,必须是子类构造函数中第一条语句。
- super(…)只能在子类构造方法中出现一次,并且不能和this同时出现
2.2再谈初始化
在类与对象中,我们也提到过初始化的问题,那么在继承关系中,又是怎样的呢。
class Animal {
String name;
int age;
static{
System.out.println("Animal中的static{}");
}
{
System.out.println("Animal中的{}");
}
public Animal() {
System.out.println("Animal()");
}
}
class Dog extends Animal {
static{
System.out.println("Dog中的static{}");
}
{
System.out.println("Dog中的{}");
}
public Dog() {
System.out.println("Dog()");
}
}
在类与对象中我们提到过代码块这个概念,通过学习我们可以发现,静态代码块是先与构造代码块且只执行一次,即随类的加载而加载,而构造代码块则是先与构造方法的。那么在发生继承的情况下又是如何的呢,通过运行上述代码我们发现:
静态代码块是最先执行的,然后在执行父类构造代码块和构造方法,最后执行子类的构造代码块和构造方法。
3.多态
多态的概念,通俗来讲,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生不同的形态。
3.1多态的实现
在Java中发生多态必须具备以下条件:
1.向上转型
2.对父类方法进行重写
3.必须在继承体系下
下面,我们通过代码来进行解释:
//多态发生的条件:1.向上转型 2.重写 3.继承
class Animal{
String name;
public void eat(){
System.out.println(name+"正在吃饭");
}
}
class Dog extends Animal{
@Override
public void eat(){ //重写父类方法
System.out.println(name+"正在吃狗粮");
}
}
class Bird extends Animal{
@Override
public void eat(){
System.out.println(name+"正在吃鸟粮");
}
}
public class Text {
public static void main(String[] args) {
Animal animal1=new Dog();// 发生向上转型
//注意,发生向上转型之后,此时通过父类的引用只能访问父类自己的成员,不能访问子类特有的成员
Animal animal2=new Bird();
animal1.name="遇见";
animal2.name="七月";
animal1.eat();
animal2.eat();
}
}
3.2向上转型和向下转型
3.2.1.向上转型
向上转型:实际就是创建一个子类对象,把他当成父类对象来使用。
语法格式: 父类类型 对象名=new 子类类型()
Animal animal=new Cat(“七月”);
向上转型优点:让代码实现更简单灵活
向上转型缺点:无法调用子类特有的方法
3.2.2向下转型
将一个子类对象经过向上转型转化为父类对象后无法调用子类特有的方法,但有时候要调用子类特有的方法,此时,将父类引用还原为子类引用即可,即向下转型。
Animal animal=new Dog();
//向下转型
Dog dog=(Dog)animal;
dog.name="遇见";
dog.wangwang();
当然,向下转型是十分不安全的
Animal animal=new Dog();
//向下转型
/*Dog dog=(Dog)animal;
dog.name="遇见";
dog.wangwang()*/;
Bird bird=(Bird)animal;
bird.fly();
当我们用Bird对象引用Dog对象时,编译没问题,但运行时则会发生错误。
那么如何解决呢?这里我们可以使用instanceof这个关键字。
if(animal instanceof Bird) {
Bird bird = (Bird) animal;
bird.fly();
}
此时,animal如果不是引用了Bird这个对象,则不会进入if语句。
写到这,继承和多态就大致结束了,如有补充,欢迎各位大佬留言。