java中的继承与多态


继承

java中实现继承需要用到extends关键字,在java中,一个子类只能继承自一个父类,即java不支持多继承,但支持继承的多代传递。子类默认继承父类的无参构造方法,如果父类没有则报错,如果父类的构造方法是带参的,则必须在子类中显式调用super()方法以调用父类的构造方法。在子类中,利用super关键字可调用父类成员。

简单区分super与this:

  • super :指向当前对象的父类的引用;
  • this :指向自己的引用。

为了让后续的讲解更方便些,这里举一个例子,如下定义一个父类Animal,子类Dog。

public class Main {
	public static void main(String[] args){
		Dog dg = new Dog("tom",5);
		dg.run();
		dg.info();
	}
}

class Animal {
	protected String name;
	protected int age;
	protected Animal(String name,int age) {
		this.name = name;
		this.age = age;
	}
	protected void info() {
		String mes;
		mes = name.substring(0,1).toUpperCase() + name.substring(1) + " is " + age + ".";
		System.out.println(mes);
	}
}

class Dog extends Animal {
	protected Dog(String name,int age) {
		super(name,age);
	}
	protected void run() {
		String mes;
		mes = name.substring(0,1).toUpperCase() + name.substring(1) + " is running.";
		System.out.println(mes);
	}
}

注:java里面没有让字符串直接变为首字符大写的标题形式的函数,在Python里可以用title()直接转换,但java里就比较麻烦,除了上面那一种用substring()函数结合toUppercase()函数的方法之外,还用一种方法提供参考:

String str;
char[] cr = str.toCharArray();
cr[0] -= 32;
str = str.valueOf(cr);

向上转型

观察这样一条语句:

Animal am = new Dog("tom",3);

父类型的变量指向了一个子类型的实例,即将子类型变为更加抽象的父类型,这种指向是允许的,它被称为向上转型

向上转型之后,该变量便只能调用父类型的方法和成员,但不要认为变量am就此变为Animal类型了,也不要单纯的认为它还是Dog类型,事实上,变量am既是Animal类型,也是Dog类型,因为Dog本就是Animal的子类。即子类型变量可以看成父类型变量,但父类型变量不可以看成子类型变量

在上例的继承树 :Dog > Animal > Object 中,只要是从左向右的转型,均为向上转型,均是被允许的。


向下转型

与向上转型相反,你觉得下面这条语句编译会通过么?

Dog dg = new Animal("tom",3);

肯定是不会通过的,把父类型转换成更加具体化的子类,子类的额外功能父类是无法凭空变出来的,所以这种向下转型就会失败。

但是如果这样的话就会被允许:

Animal am = new Dog("tom",3);
Dog dg = (Dog)am;

也就是说,那些先经历过向上转型后的引用变量,才有可能向下转型成功,否则这种向下转型就很有可能是不被允许的。

为了避免向下转型出错,也可以使用java提供的instanceof()操作符判断一个引用变量指向的实例是否是某种类型。
比方上面的语句:

Animal am = new Dog("tom",3);
System.out.println(am instanceof Dog); //true

注意instanceof是java中的关键字,不是函数,在用法上不要出错。

final关键字

之前提到数值型变量被final关键字修饰后便不能再修改,那只是final关键字的很小的用法,final关键字还有如下用法:

#1

final修饰的类无法被其它类继承。如果该final类中有方法,那么这些方法都会被隐式的指定为final方法,final类中成员变量可以根据实际需要决定是否要使用final。final方法无法被重写,final变量无法重新修改值。

#2

一个类中的private方法会被隐式的指定为final方法。private方法只能在该类中被调用,无法在该类之外被调用,当然也无法被继承。如果父类中有final修饰的方法,那么子类无法去重写该方法,但可以被继承,只有private方法才无法被继承,这里不要把final与private混为一谈。

#3

final修饰的成员变量必须要赋初始值,但如果该成员变量被用于类的构造方法的参数中,则可以在生成类的实例时赋值。即final修饰的变量的值只有在两种情况下才会被确定:一种是在定义后就初始化,之后无法再被修改;一种是定义之后在类的构造方法中被初始化,之后无法被修改。两种情况不能在同一个变量中同时出现。另外,final不能用来修饰类的构造方法。

#4

final修饰值类型变量与引用类型变量的区别:final修饰的如果时值类型变量,则该变量的值无法被改变;如果修饰的是引用类型,则该引用指向的堆中的地址的值无法被修改,但该地址里面存的内容还是可以改变的。


implements关键字

尽管java仅支持单继承,但一个类可以通过implements关键字继承多个接口,基本语法如下:

class A extends B implements C,D,E {
    
}

这里仅作了解,有关接口将在下一篇介绍。


多态

多态使用的前提是继承与方法重写,接下来先介绍java中的方法重写。

方法重写

方法重写,也叫做方法覆写或者覆盖,即Override,方法重载是Overload。

方法重载要求方法参数可以不同,但方法名必须相同,返回类型通常相同,方法重载使功能相同的方法使用同一个名字,更容易被记住,调用起来也更简单。方法重写则要求方法参数与返回类型必须与被重写方法相同。

简而言之,Overload方法是一个新方法,而Override方法则是重新写入的原有方法。

举个例子:

class Animal{
    public void run(){
        System.out.println("The animal is running.");
    }
}

class Dog extends Animal{
    @Override
    public void run(){
        System.out.println("The dog is running.");
    }
}

@Override作为装饰器可以让编译器检查是否进行了正确的方法重写,如果没有进行正确的Override就会报错。

方法的重写规则补充:

  • 访问权限不能比被重写方法的权限低。比如父类方法被声明为public,则重写该方法时就不能声明为比public权限低的权限修饰符,比如protected。
  • final修饰的方法无法被重写。
  • static修饰的方法无法被重写,但可以再次声明。
  • 构造方法不能被重写。
  • 方法重写建立在继承的基础上,没有继承该方法则不能重写该方法。

多态

我就不捡抽象的语句来说明了,java中的多态必须在继承的基础上来实现,它指的是同一个方法在处理问题时具有不同表现形式的能力。

举个例子:

class Animal{
    protected void info(){
        System.out.println("I am a animal.");
    }
}

class Dog extends Animal{
    protected void info(){
        System.out.println("I am a dog.");
    }
}

class Cat extends Animal{
    protected void info(){
        System.out.println("I am a cat.");
    }
}

class Bird extends Animal{
    protected void info(){
        System.out.println("I am a bird.");
    }
}

public class Main {
    public static void main(String[] args){
        Animal[] animals = new Animal[]{
            new Dog(),
            new Cat(),
            new Bird()
        };
        infos(animals);
    }
    public static void infos(Animal ... animals){
        for(Animal animal:animals){
            animal.info();
        }
    }
}

运行结果:

I am a dog.
I am a cat.
I am a bird.

在上例中,利用多态,主函数中的infos方法根本不必去知道Animal的子类,就能够得到正确的结果,这就是处理一个问题的不同表现形式。同时,多态允许添加更多类型的子类实现功能上的拓展,却不需要修改基于父类的代码。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值