目录
一、继承
1.1. 继承的目的
1.2. 继承的概念
1.3. 继承的语法
1.4. 父类的访问
1.5. 继承中的重载与重写
1.6. 子类的构造方法
1.7. 再谈初始化
一、继承
1.1. 继承的目的
我们来定义一个Dog和Cat的类:
public class Dog { public int age; public String breed; public String name; public void eat(){ System.out.println("在吃饭"); }}public class Cat { public int age; public String breed; public String name; public void eat(){ System.out.println("在吃饭"); }}
我们可以看到这两个类当中的成员变量是相同的,那么我们能不能把相同的成员变量抽取出来,放到同一个类中呢?面向对象思想中提出了继承的概念,专门用来进行共性抽取,实现代码复用。
public class Animal { public int age; public String breed; public String name; public void eat(){ System.out.println("在吃饭"); }}
1.2. 继承的概念
继承机制:允许程序员在保持原有类特 性 的基础上进行扩展,增加新功能,这样产生新的类,称派生类。通俗点说,可以看成是…… is …… a 的关系。
继承呈现了面向对象程序设计的层次结构, 体现了 由简单到复杂的认知过程。继承主要解决的问题是:共性的抽取,实现代码复用。
1.3. 继承的语法
语法规则:
修饰符 static 子类(派生类) extends 父类(基类、超类){public static Cat extends Animal{public static Dog extends Animal{
1.4. 父类的访问
(1)父类与子类的成员变量不同名时
public class Base { public int a = 1; public int b = 2;}public class Derived extends Base{ public int c = 3; public void test(){ System.out.println(a); System.out.println(b); System.out.println(c); } public static void main(String[] args){ Derived num = new Derived(); num.test(); }}
(2)父类与子类的成员变量同名时
public class Base { public int a = 1; public int b = 2;}public class Derived extends Base{ public int a = 10; public int c = 3; public void test(){ System.out.println(a); System.out.println(b); System.out.println(c); } public static void main(String[] args){ Derived num = new Derived(); num.test(); }}
此时就会优先访问子类成员变量。那如果我就想访问父类的成员变量呢?我们就可以引入一个super关键字。
public class Base { public int a = 1; public int b = 2;}public class Derived extends Base{ public int a = 10; public int c = 3; public void test(){ System.out.println(super.a); System.out.println(b); System.out.println(c); } public static void main(String[] args){ Derived num = new Derived(); num.test(); }}
下面博主将会画图来给大家讲解一下原理:
父类与子类的成员变量都可以通过this来进行访问,而父类中的成员变量也可以通过super来访问。所以说,我们在访问成员变量b时,也可以在前面加上super关键字,这样就能提高代码的可读性,以至于不会搞混成员变量到底是父类的还是子类的。
我们可以总结下来就是:1、当访问不同的成员时,优先看父类有没有;2、父类有访问父类的,父类没有,看子类有没有;3、子类有,访问子类的,如果没有,就会报错。
1.5. 继承中的重载与重写
public class Base { public int a = 1; public int b = 2; public void Basefunc(){ System.out.println("base::func"); }}public class Derived extends Base{ public void Derivedfunc(){ System.out.println("derived::func"); } public void Basefunc(){ System.out.println("base::func"); } public void Basefunc(int a){ System.out.println(a); } public void Method(){ Derivedfunc(); Basefunc(); Basefunc(20); } public static void main(String[] args) { Derived num = new Derived(); num.Method(); }}
在上面的代码中我们可以看到父类与子类中,存在名称相同的方法。父类中的Basefunc方法与子类中的Derivedfunc方法构成了方法重写;而与子类中的Derivedfunc(a)构成了方法重载。
1.6. 子类的构造方法
父子父子,先有父再有子,即:子类对象构造时,需要先调用父类构造方法,然后执行子类的构造方法。 在构造子类对象时候 ,先要调用父类的构造方法,将从父类继承下来的成员构造完整 ,然后再调用子类自己的构造方法,将子类自己新增加的成员初始化完整 。
public class Animal { public int age; public String name; public Animal(int age, String name) { this.age = age; this.name = name; } public void eat(){ System.out.println("在吃饭"); }}
public class Cat extends Animal{ public void sleep(){ System.out.println("睡觉"); } public Cat(String name,int age){ super(age, name); }}public class Dog extends Animal{ public void bark(){ System.out.println("汪汪叫"); } public void wag(){ System.out.println("摇尾巴"); } public Dog(String name,int age){ super(age, name); }}
public class TestDemo { public static void main(String[] args) { Cat cat = new Cat("小黑",5); cat.name = "大黄"; cat.eat(); System.out.println("======"); Dog dog = new Dog("大黄",10); dog.name = "小黑"; dog.bark(); }}
如果我们对TestDemo里的类进行调试,可以看到在父类里面进行传参和赋值。
注意:(1)若父类显式定义无参或者默认的构造方法,在子类构造方法第一行默认有隐含的super()调用,即调用父类构造方法。 如果我们在父类里面再写一个构造方法,则子类里面就会报错。
//父类里面默认的一个构造方法public Animal(int age, String name) { this.age = age; this.name = name;}
(2)如果父类构造方法是带有参数的,此时需要用户为子类显式定义构造方法,并在子类构造方法中选择合适的 父类构造方法调用,否则编译失败。
(3) 在子类构造方法中,super(...)调用父类构造时,必须是子类构造函数中第一条语句。
1.7. 再谈初始化
继承关系上的执行顺序
public class Person { public String name; public int age; public Person(String name, int age) { this.name = name; this.age = age; } { System.out.println("Person:实例代码块执行"); } static { System.out.println("Person:静态代码块执行"); }}public class Student extends Person{ public Student(String name,int age) { super(name,age); System.out.println("Student:构造方法执行"); } { System.out.println("Student:实例代码块执行"); } static { System.out.println("Student:静态代码块执行"); }}
所以,继承的顺序为:1、父类静态代码块优先于子类静态代码块执行,且是最早执行;2、父类实例代码块和父类构造方法紧接着执行;3、子类的实例代码块和子类构造方法紧接着再执行;4、第二次实例化子类对象时,父类和子类的静态代码块都将不会再执行。