什么是继承?看一下百度百科的解释:
继承是面向对象最显著的一个特性。继承是从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为,并能扩展新的能力。
Java继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。
嗯哼?是不是有些飘飘然,似懂非懂的感觉?这就对了,咱们不看他的解释。
首先说一下继承出现的目的:让类可以复用和扩展
复用和扩展:简单来说就是把你写的优秀的代码,不仅仅用于单个地方,让它能在多个地方使用,在必要的时候,可以对类的功能进行扩充。
在讲继承之前,我们有必要先了解一下组合,组合和继承的语法和行为大多是相似的。由于他们是利用现有类型生成新的类型,所有这么做是非常有意义的。
其实在之前写代码的时候,我们已经用到了组合。
什么时候用到了呢?
当然是在我们在一个类中去 new另外一个类对象的时候啊!上面已经说了,组合和继承的语法和行为大多是相似的,所以组合也是一种代码复用的体现。
好了,组合了解过了,我们来扯一下继承吧
- 继承
继承呢,是所有的OOP(面向对象)语言不可缺少的一部分,在创建一个类时,即使你不写extends关键字,你也在使用着继承,这是因为Java提供了一个标准的根类(Object类),如果你不显示的使用继承,那么默认就是继承的Object类。
继承的语法规则:
一个类不能同时继承多个父类,即不能同时出现两个extends关键字,但是它可以传递继承(B继承A,C继承B,C同样可以拥有A的方法和属性,可以理解为爸爸的爸爸依然是你的家人),最后我们将会看到传递继承的代码;
代码演示:
public class Student {
}
public class Student extends People {
}
这表明Student类继承了People类(可以理解为爹和儿子),当你这么写时,Student类会自动得到基类(父类)中的所有域和方法(这里的所有包括private修饰的属性和方法,但是由于private属性只允许本类访问,所以在Student类中无法直接访问,但是本质上还是继承了)
下面改动一下Student 和 People类,演示一下Student继承People中的属性和方法
public class People {
public String name;
public Integer age;
private double money;
public void m3() {
System.out.println("People类中的m3()方法,public....");
}
private void m4() {
System.out.println("private....")
}
public void m1() {
System.out.println("People类中的m1()方法,public....");
}
}
我们把年龄和名字设置成公开的,money设置成私有的(毕竟money不外漏嘛,还是设置成私有的比较安全,不然让别人看见咱那6位密码保护着2位数的money,怪不好意思的),设置三个方法 m1(public),m3(public),m4(private),不要问m2跑哪了,他可能走丢了吧。。。。
再写个子类,为了方便,我们直接在子类中写一个主函数,方便测试!
public class Student extends People {
public void m5() {
System.out.println("m4()....");
}
public void m2() {
System.out.println("Student类中的方法....");
m1();
m5();
}
public static void main(String[] args) {
m5();
Student student = new Student();
student.m3();
student.m2();
student.name = "fmh";
student.age = 18;
}
}
由于我们继承了People类,所以就会获得该类中的所有域和方法(不要较真私有属性和方法,它也被继承了,或者你直接认为它不可继承),看看m2(),偷偷跑着来了,在m2()中可以直接调用m1(),这说明什么?不清楚?不是吧,那你看m5(),直接在m2()调用了,这不就说明,继承了来自People类中的方法了吗!
- 初始化基类(父类)
由于现在涉及了两个类,所以要他们是怎么初始化的呢?
继承并不是简单地把基类的属性和方法复制了一份给子类,而是在创建子类对象的时候,该对象包含了一个基类的子对象。这个包含的子对象和你直接创建基类的对象是一样的(这也是为什么上边代码利用子类对象,可以调用父类方法的原因)。直接创建和包含于子对象的区别就在于,直接创建来自于外部,而基类的子对象被包装在了子类对象的内部(类似于你家附近的小水沟中有鱼,鱼可被认为是包含在水中,你把水抽干,自然也能得到鱼了,这个比喻可能不太恰当,但是由于本人语文太差,实在想不到好的比喻了)。
修改一下代码,看看继承关系,初始化是怎么执行的
public class People {
public People() {
System.out.println("People()....");
}
}
public class Teacher extends People{
public Teacher() {
System.out.println("Teacher()....");
}
}
public class Student extends Teacher {
public Student() {
System.out.println("Student()....");
}
public static void main(String[] args) {
Student student = new Student();
}
}
看下运行结果
为什么会这样呢?明明就创建了一个Student对象,怎么初始化了三个构造方法呢?
这就说明:构建过程,是从基类向外扩散开的,即在你创建子类对象的时候,基类的的构造方法先执行。
这里的原因呢,就是在你调用构造方法的时候,编译器会给你自动加一个super();让你在初始化子类的构造方法之前,先初始化父类的无参构造器,如果你的基类自己写了有参构造,却忘了写无参构造,那么这个super();就需要你显示的写出来了,并传递相应的参数,否则编译器可能会骂你!
这个super()我就不再演示了,自己动手写一下代码,印象更深刻!