Java-面向对象-继承
概念
继承是面向对象最显著的一个特征。继承是从已有的类中派生出新的类,新的类能吸收已有类的数据属性和行为,并能扩展新的能力。
Java继承是使用已存在的类的定义作为基础建立新的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。
这种技术使得复用以前地代码非常容易,能够大大缩短开发周期,降低开发费用。(此段来自百度百科)
详细解释
-
继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
-
类和类之间的继承关系可以用UML符号表示,其中父类又叫超类或基类,子类又叫派生类。父类是子类的一般化,子类是父类的特化(具体化)。
-
JAVA不支持多继承,单继承使JAVA的继承关系很简单,一个类只能有一个父类,易于管理程序,同时一个类可以实现多个接口,从而克服单继承的缺点。
-
继承避免了对一般类和特殊类之间共同特征进行的重复描述。同时,通过继承可以清晰地表达每一项共同特征所适应的概念范围——在一般类中定义的属性和操作适应于这个类本身以及它以下的每一层特殊类的全部对象。运用继承原则使得系统模型比较简练也比较清晰。
-
继承所描述的是“is-a”的关系,如果有两个对象A和B,若可以描述为“A是B”,则可以表示A继承B,其中B是被继承者称之为父类或者超类,A是继承者称之为子类或者派生类。
-
继承者是被继承者的特殊化,它除了拥有被继承者的特性外,还拥有自己独有得特性。
-
继承定义了类如何相互关联,共享特性。对于若干个相同或者相识的类,我们可以抽象出他们共有的行为或者属性,并将其定义成一个父类或者超类,然后用这些类继承该父类,他们不仅可以拥有父类的属性、方法,还可以定义自己独特的属性或者方法。
总结
- 子类拥有父类非private的属性和方法。
- 子类可以拥有自己属性和方法,即子类可以对父类进行扩展。
- 子类可以用自己的方式实现父类的方法。
代码演示
当没有继承
class Animal{
private String kind;
private String sex;
public void setKind(String kind)
{
this.kind = kind;
}
public String getKind()
{
return kind;
}
public void setSex(String sex)
{
this.sex = sex;
}
public String getSex()
{
return sex;
}
}
class Dog{
private String kind;
private String sex;
public void setKind(String kind)
{
this.kind = kind;
}
public String getKind()
{
return kind;
}
public void setSex(String sex)
{
this.sex = sex;
}
public String getSex()
{
return sex;
}
}
由以上代码可以看出,当不使用继承时,则会有大量的重复代码 java语言一般不允许大量重复的代码出现,这段代码不仅从代码上重复,而且从概念上讲Dog一定是动物,只是Gog类描述的范围小,具备更多的属性和方法,这个时候想要消除结构定义上的重复,就要用到继承。
下面使用extends关键字来实现:
class Animal{
private String kind;
private String sex;
public void setKind(String kind)
{
this.kind = kind;
}
public String getKind()
{
return kind;
}
public void setSex(String sex)
{
this.sex = sex;
}
public String getSex()
{
return sex;
}
}
public Dog extends Animal{
}
public class Test{
public static void main(String[] args)
{
Dog dog = new Dog();
dog.setKind("金毛");
dog.setSex("公");
System.out.println("种类:"+dog.getKind()+"性别:"+dog.getSex());
}
}
通过以上代码可以发现,当发生了类继承关系之后,子类可以直接继承父类的操作,可以实现代码的重用,子类最低也维持和父类相同的功能。 当然子类也可以进行额外的扩充:
public Dog extends Animal{
private int age;
public void setAge(int age)
{
this.age = age;
}
public int getAge()
{
return age;
}
}
//接着可在Test类中调用
子类对象在进行实例化前首先调用父类构造方法,再调用子类构造方法实例化子类对象。
通过前面我们知道子类可以继承父类的属性和方法,除了那些private的外还有一样是子类继承不了的—构造器。对于构造器而言,它只能够被调用,而不能被继承。 调用父类的构造方法我们使用super()即可。
对于子类而已,其构造器的正确初始化是非常重要的,而且当且仅当只有一个方法可以保证这点:在构造器中调用父类构造器来完成初始化,而父类构造器具有执行父类初始化所需要的所有知识和能力。
public class Animal {
private String kind;
private String sex;
public void setKind(String kind)
{
this.kind = kind;
}
public String getKind()
{
return kind;
}
public void setSex(String sex)
{
this.sex = sex;
}
public String getSex()
{
return sex;
}
public Animal(){
System.out.println("父类中的构造方法!");
}
}
public class Dog extends Animal{
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Dog(){
System.out.println("子类中的构造方法!");
}
}
public class TestDog {
public static void main(String[] args) {
Dog dog = new Dog();
dog.setKind("金毛");
dog.setSex("公");
dog.setAge(4);
System.out.println("种类:"+dog.getKind()+" 性别:"+dog.getSex()+" 年龄:"+dog.getAge());
}
}
执行结果:
通过这个示例可以看出,构建过程是从父类“向外”扩散的,也就是从父类开始向子类一级一级地完成构建。而且我们并没有显示的引用父类的构造器,这就是java的聪明之处:编译器会默认给子类调用父类的构造器。
但是,这个默认调用父类的构造器是有前提的:父类有默认构造器。如果父类没有默认构造器,我们就要必须显示的使用super()来调用父类构造器,否则编译器会报错:无法找到符合父类形式的构造器。
总结
对于继承而言,子类会默认调用父类的构造器,但是如果没有默认的父类构造器,子类必须要显示的指定父类的构造器,而且必须是在子类构造器中做的第一件事(第一行代码)。
实际在子类构造方法中,相当于隐含了一个语句super(),调用父类的无参构造。同时如果父类里没有提供无参构造,那么这个时候就必须使用super(参数)明确指明要调用的父类构造方法。