目录
1. 为什么需要继承
2. 继承的概念
3. 继承的语法
4. 父类成员访问
4.1 子类中访问父类的成员变量
1. 子类和父类不存在同名成员变量
2. 子类和父类成员变量同名
4.2 子类中访问父类的成员方法
1. 成员方法名字不同
2. 成员方法名字相同
5. super关键字
6. 子类构造方法
7. super和this
相同点
不同点
8. 继承关系的执行顺序
9. 继承方式
单继承
多层继承
不同类继承同一个类
10. final关键字
1. 为什么需要继承
Java 中使用类对现实世界中实体来进行描述,类经过实例化之后的产物对象,则可以用来表示现实中的实体,但是现实世界错综复杂,事物之间可能会存在一些关联。因此需要一些程序设计来解决这个问题。 下面通过代码来发现问题//定义一个狗类class Dog{ public String name; public int age; public String color; public void eat() { System.out.println(this.name + "在吃东西"); } public void run() { System.out.println(this.name + "在跑"); }}//定义一个鸟类class Bird{ public String name; public int age; public String color; public void eat() { System.out.println( this.name + "在吃东西"); } public void fly() { System.out.println( this.name + "在飞"); }}public class blog { public static void main(String[] args) { Dog dog = new Dog(); //创建对象 //初始化 dog.name = "小灰"; dog.age = 10; dog.color = "灰色"; //调用方法 dog.eat(); dog.run(); Bird bird = new Bird(); bird.name = "小花"; bird.age = 10; bird.color = "花色"; bird.eat(); bird.fly(); }}
我们可以看到Dog类和Bird类中有以下相同的成员变量和成员方法
public String name; public int age; public String color; public void eat() { System.out.println(this.name + "在吃东西"); }
因此 我们为了节省代码,提高代码的复用性,我们引入了继承这个概念。
2. 继承的概念
继承 (inheritance) 机制 :是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特 性 的基础上进行 扩展,增加新功能 ,这样产生新的类,称 派生类 。继承呈现了面向对象程序设计的层次结构, 体现了 由简单到复杂的认知过程。继承主要解决的问题是: 共性的抽取,实现代码复用 。3. 继承的语法
修饰符 class 子类名 extends class 父类名 { // ... }
关键字是 extends
因为 小狗和小鸟 都是属于动物,他们有自己的年龄、颜色等等属性。也会吃东西等等相同动作,并且作为宠物都会给他们给予名字 。
因此 我们创建一个新的类(Animals) 作为父类,并将 这些相同的属性和动作作为成员变量和成员方法 放入其中。
以下代码为使用继承之后。我们可以明显的看到 代码少了很多。
并且在现实生活中我们只需要关注子类特有的属性和方法即可
class Animals{ public String name; public int age; public String color; public void eat() { System.out.println(this.name + "在吃东西"); }}class Dog extends Animals{ public void run() { System.out.println(this.name + "在跑"); }}class Bird extends Animals{ public void fly() { System.out.println( this.name + "在飞"); }}public class blog { public static void main(String[] args) { Dog dog = new Dog(); dog.name = "小灰"; dog.age = 10; dog.color = "灰色"; dog.eat(); dog.run(); Bird bird = new Bird(); bird.name = "小花"; bird.age = 10; bird.color = "花色"; bird.eat(); bird.fly(); }}
4. 父类成员访问
4.1 子类中访问父类的成员变量
1. 子类和父类不存在同名成员变量
class Father { public int a = 10; public int b = 20;}class Son extends Father { public int c = 30; public void Method(){ System.out.println(a); System.out.println(b); System.out.println(c); }}public class Test { public static void main(String[] args) { Son son = new Son(); son.Method(); }}
程序正常运行,并且子类可以访问父类的成员变量,并打印值。
2. 子类和父类成员变量同名
使用关键字 super
我们先来看 如果 子类和父类成员变量同名 会输出什么?
class Father { public int a = 10; public int b = 20;}class Son extends Father { public int a = 100; // 如果子类有 先访问子类的。 子类没有的 才会去看父类是否有 public int c = 30; public void Method(){ System.out.println(a); System.out.println(b); System.out.println(c); }}public class Test { public static void main(String[] args) { Son son = new Son(); son.Method(); }}
由此我们可知,会输出子类的 同名成员变量。
那么如果我们非要访问 父类变量呢?
就是前面所提的 super
class Father { public int a = 10; public int b = 20;}class Son extends Father { public int a = 100; public int c = 30; public void Method(){ System.out.println(super.a);// 输出父类的 a System.out.println(this.a);// 谁调用 就输出谁的a System.out.println(b); System.out.println(c); }}public class Test { public static void main(String[] args) { Son son = new Son(); son.Method(); }}
在子类方法中 或者 通过子类对象访问成员时 : 1.如果访问的成员变量子类中有,优先访问自己的成员变量。 2.如果访问的成员变量子类中无,则访问父类继承下来的,如果父类也没有定义,则编译报错。 3.如果访问的成员变量与父类中成员变量同名,则优先访问自己的。 成员变量访问遵循就近原则,自己有优先自己的,如果没有则向父类中找 。
4.2 子类中访问父类的成员方法
1. 成员方法名字不同
class Father{ public void Func() { System.out.println("Func"); }}class Son extends Father{ public void Func2() { System.out.println("Func2"); } public void Func3() { Func(); Func2(); }}public class Test { public static void main(String[] args) { Son son = new Son(); son.Func3(); }}
2. 成员方法名字相同
我们在Son类中添加一个 和 Father类一模一样的Func成员方法,看结果如何?
class Father{ public void Func() { System.out.println("Func"); }}class Son extends Father{ public void Func() { System.out.println("Son::Func"); } public void Func2() { System.out.println("Func2"); } public void Func3() { Func(); Func2(); }}public class Test { public static void main(String[] args) { Son son = new Son(); son.Func3(); }}
结果是 子类有同名方法,先调用自己的方法。
如果想要在子类调用 父类中的同名成员方法,还是需要用到关键字super
//在此就不过多 赘写代码了 //基于上面的代码//这里我将最核心的代码修改部分,单提了出来 public void Func3() { this.Func(); super.Func(); this.Func2(); }
5. super关键字
作用:在子类方法中访问父类的成员。
如何使用,前面代码已经展示了其作用。
注意事项: 1. 只能在非静态方法中使用 2. 在子类方法中,访问父类的成员变量和方法。
6. 子类构造方法
父子,即先有父,后有子。所以在我们的代码中,当我们想创建子类对象时,要先调用和实现父类的构造方法,然后再实现子类构造方法
在之前的代码中,虽然父类和子类都没有显式的定义构造方法。但其实 子类中 有隐藏的代码
public class Father { public Father(){ System.out.println("Father()"); } }public class Son extends Father{ public Son(){ // super(); // 注意子类构造方法中默认会调用基类的无参构造方法:super(), // 用户没有写时,编译器会自动添加,而且super()必须是子类构造方法中第一条语句, // 并且只能出现一次 System.out.println("Son()"); }}
下面代码是父类已经有定义好的构造方法
class Animals{ public String name; public int age; public String color; //父类的构造方法 public Animals(String name, int age, String color) { this.name = name; this.age = age; this.color = color; } public void eat() { System.out.println(this.name + "在吃东西"); }}class Dog extends Animals { //子类的构造方法 public Dog() { super("小罗",10,"白色"); } public void run() { System.out.println(this.name + "在跑"); }}class Bird extends Animals { //子类的构造方法 public Bird(String name, int age, String color) { super(name, age, color); } public void fly() { System.out.println( this.name + "在飞"); }}public class Blog { public static void main(String[] args) { Dog dog = new Dog();// 根据构造方法 //第一种初始化方式 dog.name = "小灰"; dog.age = 10; dog.color = "灰色"; dog.eat(); dog.run(); Bird bird = new Bird("小辣椒",10,"红色");// 根据构造方法 //第二种初始化方法 bird.eat(); bird.fly(); }}
注意:
1. 若父类显式定义无参或者默认的构造方法,在子类构造方法第一行默认有隐含的super()调用,即调用基类构造方法 2. 如果父类构造方法是带有参数的,此时需要用户为子类显式定义构造方法,并在子类构造方法中选择合适的父类构造方法调用,否则编译失败。 3. 在子类构造方法中,super(...)调用父类构造时,必须是子类构造函数中第一条语句。 4. super(...)只能在子类构造方法中出现一次,并且不能和this同时出现
7. super和this
super 和 this 都可以在成员方法中用来访问:成员变量和调用其他的成员函数,都可以作为构造方法的第一条语句,那他们之间有什么区别呢?相同点
1. 都是Java中的关键字 2. 只能在类的非静态方法中使用,用来访问非静态成员方法和字段 3. 在构造方法中调用时,必须是构造方法中的第一条语句,并且不能同时存在不同点
1. this是当前对象的引用,当前对象即调用实例方法的对象,super相当于是子类对象中从父类继承下来部分成员的引用 2. 在非静态成员方法中,this用来访问本类的方法和属性,super用来访问父类继承下来的方法和属性 3. 在构造方法中:this(...)用于调用本类构造方法,super(...)用于调用父类构造方法,两种调用不能同时在构造 方法中出现 4. 构造方法中一定会存在super(...)的调用,用户没有写编译器也会增加,但是this(...)用户不写则没有8. 继承关系的执行顺序
在之前的学习中我们知道了
代码块相关的知识点
1.静态代码块先执行,并且只执行一次,在类加载阶段执行
2.当有对象创建时,才会执行实例代码块,实例代码块执行完成后,最后构造方法执行
class Persons{ String name; int age; public Persons(String name, int age) { this.name = name; this.age = age; System.out.println("Person构造方法被调用"); } { System.out.println("Person实例代码块被调用"); } static { System.out.println("Person静态代码块被调用"); }}class Students extends Persons{ public Students(String name, int age) { super(name, age); System.out.println("Students构造方法被调用"); } { System.out.println("Students实例代码块被调用"); } static { System.out.println("Students静态代码块被调用"); }}public class job { public static void main(String[] args) { Students students = new Students("小罗",18); Students students2 = new Students("小米",18); }}
无奖竞猜:大家猜一下输出结果是什么?
通过分析执行结果,得出以下结论: 1、父类静态代码块优先于子类静态代码块执行,且是最早执行 2、父类实例代码块和父类构造方法紧接着执行 3、子类的实例代码块和子类构造方法紧接着再执行 4、第二次实例化子类对象时,父类和子类的静态代码块都将不会再执行9. 继承方式
单继承
多层继承
不同类继承同一个类
最重要的是,不可以同一个类继承不同的类
10. final关键字
final关键可以用来修饰变量、成员方法以及类。
1.修饰变量或字段,表示常量(即不能修改)
2.修饰类:表示此类不能被继承
3.修饰方法:表示该方法不能被重写