Java-15 继承
一、继承
1.概念:多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可
package com.shujia.day1; /*通过观察我们发现,name和age这个两个成员变量,以及study方法都是相同的 如果我还有其他的类:医生、程序员、警察等等,他们同样都具备了这些成员内容 那么,每次定义这样的一个类,是不是都需要将重复的内容都写一遍*/ class Person{ String name; int age; public void study() { System.out.println("学习"); } } class Stduent2 extends Person{ } class Teacher2 extends Person{ } public class ExtendsDemo { public static void main(String[] args) { } }
2.定义格式:通过extends关键字可以实现类与类的继承
class 子类名 extends 父类名 { }
3.代码解释:
单独的这个类称为父类,基类或者超类;
这多个类可以称为子类或者派生类。
4.作用:有了继承以后,我们定义一个类的时候,可以在一个已经存在的类的基础上,还可以定义自己的新成员。
5.继承的好处:
1.提高了代码的复用性 多个类相同的成员可以放到同一个类中
2.提高了代码的维护性 如果功能的代码需要修改,修改一处即可
3.让类与类之间产生了关系,是多态的前提 其实这也是继承的一个弊端:类的耦合性很强
继承的好处:
1.类的耦合性很强
2.打破了封装性
6.继承特点:
1.Java只支持单继承,不支持多继承。 一个类只能有一个父类,不可以有多个父类
class SubDemo extends Demo{} //ok class SubDemo extends Demo1,Demo2...//error
2.Java支持多层继承(继承体系)
class GrandFather{
public void show(){
System.out.println("这是在爷爷里面的show方法");
}
}
class Father extends GrandFather{
public void show1(){
System.out.println("这是在父亲里面的show1方法");
}
}
class Son extends Father{
public void show2(){
System.out.println("这是在儿子里面的show2方法");
}
}
public class ExtendsDemo3 {
public static void main(String[] args) {
Son s = new Son();
s.show2(); //这是调用自己特有的方法 自己的
s.show1(); //这是调用继承父类中的方法 父亲的
s.show(); //这是调用多层继承中父类的方法 爷爷的
}
}
7.注意事项
1.子类只能继承父类所有非私有的成员(成员方法和成员变量) 其实这也体现了继承的另一个弊端:打破了封装性
2.子类不能继承父类的构造方法,但是可以通过super(后面讲)关键字去访问父类构造方法。
3.不要为了部分功能而去继承 我们到底在什么时候使用继承呢? 继承中类之间体现的是:”is a”的关系。
/* 使用继承的注意事项: 1、子类只能继承父类所有非私有的成员(成员方法和成员变量) 2、子类不能继承父类的构造方法,但是可以通过super(马上讲)关键字去访问父类构造方法。 3、不要为了部分功能而去继承 class A{ public void show1(){} public void show2(){} } class B{ public void show2(){} public void show3(){} public void show4(){} public void show5(){} public void show6(){} } 通过观察发现,出现了一个相同的show2方法,在类A和B中都出现了 class B extends A{ public void show3(){} public void show4(){} public void show5(){} public void show6(){} } 为了重复的show2方法,我们采用了继承,这样虽然可以做,但是这样并不推荐 1、这样继承,我们发现,虽然把show2方法给继承过来了,但是我们也把show1也继承过来, 但是show1是A特有的,这样做显然是不合理的 2、为了一个相同的方法用继承改进,发现并没有简便到哪去,反而出错了会增加工作量 那问题来了? 到底什么时候使用继承呢? 满足英语中一句话“is a” Person: Student Teacher Police 总结:只要满足is a这个语法,就可以使用继承 */ class Father2{ Father2(){ System.out.println("父类的无参构造"); } int num = 20; private int num2 = 30; public void song(){ System.out.println("唱歌"); } private void diving(){ System.out.println("潜水"); } } class Son2 extends Father2{ // Father2(); public void show(){ System.out.println(num);//子类可以访问父类的非私有成员变量 // System.out.println(num2); //子类不能访问父类中的私有成员变量 song(); //子类可以访问父类中的非私有成员方法 // diving(); //子类不能访问父类中私有的成员方法 } } public class ExtendsDemo4 { public static void main(String[] args) { Son2 s = new Son2(); s.show(); } }
8.继承与成员变量的关系:
package com.shujia.day1; /*1、当父类与子类的成员变量不一样的时候,该访问谁就是谁 2、当父类与子类的成员变量一样的时候,这里一样是指变量名一样 子类中查找变量的顺序: 1)先在子类方法局部范围内找,找到就返回 2)子类中成员范围内去找,找到就返回 3)在父类的成员范围内去找,找到就返回 4)如果在父类中都没有找到该变量,报错*/ class Father3{ int num = 10; public void show2(){ int num3 = 50; } } class Son3 extends Father3{ int num2 = 20; int num = 30; public void show(){ int num = 40; System.out.println(num);//40 System.out.println(num2);//20 // System.out.println(num3);报错 } } public class ExtendsDemo { public static void main(String[] args) { Son3 s = new Son3(); s.show(); } }
9.super关键字
1.super的用法和this很像
this代表本类对应的引用。 super代表父类存储空间的标识(可以理解为父类引用)
2.用法(this和super均可如下使用)
访问成员变量 this.成员变量 super.成员变量
访问构造方法(子父类的构造方法问题讲) this(…) super(…)
访问成员方法(子父类的成员方法问题讲) this.成员方法() super.成员方法()
package com.shujia.day1.day2; class Father { int num = 30; public void song() { System.out.println("唱歌"); } } class Son extends Father { int num = 20; public void song() { int num = 10; System.out.println(num); System.out.println(this.num); System.out.println(super.num); super.song(); } public void eat(){ System.out.println("吃饭"); sleep(); } public void sleep(){ System.out.println("睡觉"); } } public class ExtendsDemo2 { public static void main(String[] args) { Son son = new Son(); son.song(); son.eat(); } }
10.继承中构造方法的关系;
1.子类中所有的构造方法默认都会访问父类中空参数的构造方法
2.子类会继承父类中的数据,可能还会使用父类的数据。所以,子类初始化之前,一定要先完成父类数据的初始化。
3.每一个构造方法的第一条语句默认都是:super()
package com.shujia.day1.day2; /*继承中构造方法的关系 1、子类中所有的构造方法默认都会访问父类中无参构造方法 2、为什么呢? 因为子类会继承父类的数据,可能还会使用父类的数据 所以在初始化子类之前,一定会先完成父类的初始化*/ class Fu { Fu() { System.out.println("这是父类无参构造"); } Fu(String name) { System.out.println("这是父类有参构造"); } } class Son1 extends Fu { Son1(){ System.out.println("这是Son1类无参构造"); } Son1(String name){ super(); System.out.println("这是Son1有参构造"); } } public class ExtendsDemo3 { public static void main(String[] args) { Son1 son1 =new Son1();//这是父类无参构造 //这是Son1类无参构造 Son1 son =new Son1("1");//这是父类无参构造 //这是Son1有参构造 } }
4.当父类没有无参构造怎么初始化
/*那么当父类没有提供无参构造方法的时候,怎么办呢?会出现什么情况? 无法创建子类对象 怎么解决? 1、在父类中加一个无参构造 2、使用super关键字去调用父类中带参数的构造方法 super(...) 3、子类通过调用本类其他的构造方法 使用this关键字间接的调用super去访问父类的构造方法 紧遵循一句话:要想初始化子类,就必须先初始化父类(也就是调用父类的构造方法) 注意事项: 1、super(...)和this(...)必须出现在构造方法的第一条语句上 2、在Java中不允许对一个进行多次初始化, 如果调用了多次super,相当于调用多次父类构造方法,就是对父类数据进行多次初始化 每个类只能初始化一次*/ class Fu { // Fu() { // System.out.println("这是父类无参构造"); // } Fu(String name) { System.out.println("这是父类有参构造"); } } class Son1 extends Fu { Son1(){ this("a"); // super(); // super("das"); System.out.println("这是Son1类无参构造"); } Son1(String name){ super("s"); System.out.println("这是Son1有参构造"); } } public class ExtendsDemo3 { public static void main(String[] args) { Son1 son1 =new Son1(); Son1 son =new Son1("1"); } }
11.继承中成员方法的关系
package com.shujia.day1.day2; /*继承与成员方法的关系 1、子类的成员方法与父类的成员方法不一样,该访问谁就访问谁 2、子类的成员方法与父类的成员方法一样,指的是方法名一样 1)先找子类中,看有没有这个方法,如果有,就直接调用 2)如果子类中没有这个方法,去父类找,如果有,就调用 3)如果连父类都没有这个方法,报错*/ class Father7{ public void show(){ System.out.println("父类的show方法"); } public void show1(){ System.out.println("父类的show1方法"); } } class Son7 extends Father7{ // public void show1(){ // System.out.println("子类的show1方法"); // } public void show(){ System.out.println("子类中的show方法"); } } public class ExtendsDemo4 { public static void main(String[] args) { Son7 s = new Son7(); s.show1();//父类的show1方法 // s.show();//子类中的show方法 } }
12.方法重写
package com.shujia.day1.day2; /* 方法的重写: 子类中出现了和父类中一模一样的方法声明,叫做重写,也被叫做方法的覆盖,方法的复写 方法的重载: 本类中出现了方法名一样,参数列表不一样的方法,与返回值无关的方法,叫重载 子类的成员方法与父类的成员方法一样,指的是方法名一样 1)先找子类中,看有没有这个方法,如果有,就直接调用 2)如果子类中没有这个方法,去父类找,如果有,就调用 3)如果连父类都没有这个方法,报错 方法重写的应用:当子类需要父类的功能,而功能主体子类有自己特有内容时, 可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容。 方法的重写注意事项: 1、父类的方法被private修饰不能被重写,甚至算不上重写 2、子类重写父类方法时,访问权限不能更低 子类重写父类方法的时候,访问权限要么和父类方法的访问权限一样,要么比它大 3、父类静态方法,子类也必须通过静态方法进行重写。 (其实这个算不上方法重写,但是现象确实如此, 至于为什么算不上方法重写,多态中会讲解) */ class Father8{ public void show(){ System.out.println("父类的show方法"); } } class Son8 extends Father8{ public void show1(){ System.out.println("子类的show1方法"); } public void show(){ super.show();//使用super访问父类同名方法 System.out.println("子类中的show方法"); } } public class ExtendsDemo { public static void main(String[] args) { Son8 s = new Son8(); s.show();//子类中的show方法 // 父类的show方法 } }