继承
继承是面向对象中一个非常独特的特性,避免了代码的重复提高了代码简洁性和灵活性。比如我们要写bird和wolf对象,不管是bird还是wolf都会吃喝拉撒,这些是他们的共同点,那么我们可以将这些共同点提取出来写成一个名为Animal的父类。然后让bird和wolf 继承(extends)自Animal。
public class Animal{
private String name;
private int age;
public Animal(String name,int age){
this.name=name;
this.age=age;
}
public void eat(){
System.out.println("I want to eat!");
}
public void sleep(){
System.out.println("I want to sleep!");
}
public int getAge(){
return this.age;
}
}
class bird extends Animal{
public bird(String name,int age){
super(name,age);
}
}
class wolf extends Animal{
public wolf(String name,int age){
super(name,age);
}
}
但从这里来看好像并没有什么不同,真正的继承从现在开始。Animal被称为父类,bird和wolf则被称为子类。
super关键字
在bird和wolf的构造器里出现了super关键字。可是Animal却没有。其实Animal也有super关键字的,因为所有的类其实都继承自一个名为Object的类,虽然Animal没有写extends,java会默认的。super有超级的意思,而父类其实也有另外一种翻译叫超类super calss。那么super有什么用呢?super类似于一个传送门会把括号里的东西传送回父类,那么括号里的是啥呢?括号里就是对应的构造器的参数。比如在bird和wolf里
super(name,age)
会把name和age传送给Animal,然后在Animal里找对应的构造器。
可是上面才说了所有的对象其实都继承自Object,那Animal的super去哪了呢?如果子类构造器函数中没有指定执行父类中哪个构造函数,默认会调用父类中无参数构造函数。所有实际上Animal的构造器是这样的
public Animal(String name.int age){
super();//super()必须放在构造器的第一行
this.name=name;
this.age=age;
}
而super()必须放在第一行也暗示着构造器的调用顺序,是层层往上回溯的。我们继续用一个例子说明
calss Some{
Some(){
System.out.println("调用Some");
}
}
class Other extends Some{
Other(){
System.out.println("调用Other");
}
public static void main(String[] args){
Other a=new Other();
}
}
输出
调用Some
调用Other
我们在new一个Other的时候把Some的构造器也调用了一次。
重新定义
子类会得到父类的所有东西,private成员也会被继承只是无法直接使用,除非父类提供了访问方法。子类可以把继承得来的方法进行重写(override),比如bird从Animal继承的eat方法,我们可以进行override。其他部分需要保持一致,需要改变的只是方法体。
public void eat(){
System.out.println("I want to eat fruits");
}
2020.3.20更正
private成员也会被继承只是无法直接使用,准确来说是无法继承private字段的,那么为什么又可以使用呢?(当然这个前提是你提供了getter/setter方法)因为JVM在内存中创建一个子类的时候会建立一个叫super区父类的东西会被放进去(并不是创建一个父类对象哦,只是有一块区域)。
拿一个被重写的方法举例
@Override
public String getName() {
// TODO Auto-generated method stub
System.out.println("这里是Son,来自getName");
return super.getName();
}
//来自父类的getName
public String getName() {
return name;
}
return的写法就非常清晰的说明了问题,调用super区(即父类)的getName方法。证明了super区的存在
新写方法
很显然,bird会飞,但是Animal里并没有fly方法。而且wolf也并不会飞。所以我们不能把fly方法写进Animal里如果写在Animal里的话,wolf也会继承fly方法的。而是把fly方法先进bird里,这样的事情是允许的,这也是继承灵活性的体现。
class bird extends Animal{
public bird(String name,int age){
super(name,age);
}
public void fly(){
System.out.println("Blue sky!!!");
}
}
final关键字
被final关键字修饰的东西会被打上最后一次定义的标签,标明这个类,成员,方法都不可被继承。因为ta已经是最后一次定义了。
多态
什么叫多态?以抽象讲法解释,就是使用单一接口操作多种类型的对象。
//可以通过编译
bird a=new bird();
wolf b=new wolf();
Animal c=new bird();
Animal d=new wolf();
//不可以通过编译
bird e=new Animal();
wolf f=new Animal();
判断是否能通过编译,我们需要知道一种is-a的关系
很显然这是is a的关系,同样的我们用is-a来看看编译不通过代码是什么关系。
可以看到是is not a的关系,所以编译不通过。
cast扮演(造型)
通过上面的is-a我们刚刚踏入多态的门栏,接下来我们看看多态的基础
Animal a=new bird();
bird b=(bird)a;
因为我们在实例a的时候a是一个Animal,可是上面的图也说明Animal is not a bird所以我们用cast让a来扮演bird实际上a就是一个bird,所以编译通过。
wolf c=(bird)a;
这样的cast扮演则会出现错误,因为a实际上是个bird,只是在实例化时被打上了Animal的标签。
然鹅这并不是终极的单一接口操作多种类型的对象
单一接口操作多种类型的对象
在不知道多态之前,我们如果想知道bird和wolf的age就得分别给bird和wolf写一个getAge方法了
pubilc int getAge(){
return this.age;
}
可是现在我们是学习了多态的java学习者了,那么该怎么使用多态呢?
我们可以直接在animalWord里写一个getAge方法
class animalWord{
public static void getAge(Animal animal){
System.out.println(animal.getAge());
}
public static void main(String[] args){
bird a=new bird(10);
wolf b=new wolf(20);
getAge(a);
getAge(b);
}
}
尽管a是bird,b是wolf。但是都属于Animal,所以这是完全ok的。这个例子应该很好的诠释了单一接口操作多种类型的对象。我们用一个参数是一个animal类型的getAge方法。以后不管新增的fish,sheep统统使用extends继承Animal就能用这个getAge方法。