继承是面向对象的三大特征之一,可以使得子类具有父类的属性和方法,还可以在子类中重新定义,追加属性和方法
父类,也被称为基类、超类。子类,也被称为派生类
继承中子类的特点 1、子类可以有父类的内容 2、子类还可以有自己特有的内容
继承的格式:
public class 子类名 extends 父类名{}
继承的练习
package ch6;
public class a_1_1Fu {
public void show(){
System.out.println("show方法被调用");
}
}
package ch6;
//在原来的类后面加一个extends,后面再跟上父类。就表示子类继承了父类
public class a_1_2Zi extends a_1_1Fu {
public void method(){
System.out.println("method方法被调用");
}
}
xxxxxxxxxx
package ch6;
public class a_1_3测试 {
public static void main(String[] args) {
//创建对象,调用方法
a_1_1Fu f = new a_1_1Fu();//无参构造方法
f.show();
a_1_2Zi z = new a_1_2Zi();
z.method();
//在子类的文件里面,在类的位置写一个extends就可以继承了,这里就可以继承父类的show方法
z.show();
}
}
继承的好处和弊端
继承好处 1、提高了代码的复用性,多个类相同的成员可以放到同一个类中 2、提高了代码的维护性,如果方法的代码需要修改,修改一处即可
继承弊端 1、继承让类与类之间产生了关系,类的耦合性增强了,当父类发生变化时,子类实现也不得不跟着变化,削弱了子类的独立性
什么时候使用继承 1、继承体现的关系:is a 即什么是什么的一种 2、假设法:我有两个类A和B,如果他们满足A是B的一种,或者B是A的一种,就说明他们存在继承关系,这个时候就可以考虑使用继承来体现,否则就不能滥用继承 3、举例:苹果和水果(可以用继承),猫和动物(可以用继承),猫和狗(不能用继承)
继承中变量的访问特点
在子类方法中访问一个变量 1、子类局部范围找 2、如果子类局部范围没有找到,就去子类成员范围找 3、如果子类成员范围也找不到,就会去父类(亲父亲,不是祖辈父亲)成员范围找 4、都没有找到就会报错
访问特点的练习
package ch6;
public class a_3_1Fu {
//年龄
public int age = 40;//年龄用public修饰,为了访问方便
}
xxxxxxxxxx
package ch6;
public class a_3_2Zi extends a_3_1Fu{ //这个子类继承另一个父类
//身高
public int height = 175;
//年龄
//public int age = 100;//当父类和子类都有一个age,会优先输出子类的age
public void show(){
System.out.println(age);//age是父类里的,只有继承了父类才可以访问到父类里面的
System.out.println(height);
}
}
package ch6;
public class a_3_3测试 {
public static void main(String[] args) {
//创建对象,调用方法
a_3_2Zi z = new a_3_2Zi();
z.show();//输出子类里的show方法,输出40,175
}
}
super关键字
super关键字的用法和this关键字的用法相似 1、this:代表本类对象的引用 2、super:代表父类存储空间的标识(可以理解为父类对象引用)
关键字 | 访问成员变量本类 | 访问构造方法 | 访问成员方法 |
---|---|---|---|
this | this.成员变量【本类】 | this(…)【本类】 | this.成员方法(…)【本类】 |
super | super.成员变量【父类】 | super(…)【父类】 | super.成员方法(…)【父类】 |
super关键字的练习
xxxxxxxxxx
package ch6;
public class a_4_1Fu {
public int age = 40;
}
xxxxxxxxxx
package ch6;
//注意这里是子类,但是继承了父类
public class a_4_2Zi extends a_4_1Fu {//继承父类
public int age = 100;
public void show(){
int age = 99;//一般来说输出子类的show方法就会输出99
System.out.println(age);
//思考:如果我要访问本类的成员变量age,怎么办。即我想要输出100而不是99
//解决:利用this指向
System.out.println(this.age);//表示访问的是本类的成员变量age,即100
//思考:如果我要访问父类的成员变量age,怎么办。即我想要输出40,而不是100或99
//解决:利用super关键字
System.out.println(super.age);//表示访问的是父类的成员变量age,即40
}
}
package ch6;
public class a_4_3测试 {
public static void main(String[] args) {
//创建对象,调用子类的方法
a_4_2Zi z = new a_4_2Zi();
z.show();//原本是输出99,用了this之后输出100,用了super之后输出40
}
}
继承中构造方法的访问特点
子类中所有的构造方法默认都会访问父类中无参的构造方法
理由: 1、因为子类会继承父类中的数据,可能还会使用父类的数据。所以,子类初始化之前,一定要先完成父类数据的初始化 2、每一个子类构造方法的第一条语句默认都是:super(),而super()又是无参构造方法,所以一定会调用(访问)父类的无参构造方法
每一个子类构造方法的第一条语句默认都是:super()。这个super()调用方法在代码里是不显示的,系统自己额外执行的
如果父类中没有无参构造方法,只有带参构造方法,该怎么办,提供两种解决办法 1、通过使用super关键字做到'显示的调用'父类的带参构造方法 2、在父类中自己提供一个无参构造方法 --> 更推荐这种做法
访问特点的练习
package ch6;
public class a_5_1Fu {
public a_5_1Fu(){
System.out.println("Fu无参构造¬SubmitBuild方法被调用");
}
public a_5_1Fu(int age){
System.out.println("Fu带参构造&haveSubmitBuild方法被调用");
}
}
x
package ch6;
public class a_5_2Zi extends a_5_1Fu{//extends关键字,作用是继承父类
public a_5_2Zi(){
//super(); //默认的super()在这个位置,不写的话,系统会默认自己执行这个super无参方法
//super(20); //也可以指定父类的带参构造方法,即只调用父类的带参构造方法
System.out.println("Zi无参构造¬SubmitBuild方法被调用");
}
public a_5_2Zi(int age){
//super(); //默认的super()在这个位置,不写的话,系统会默认自己执行这个super无参方法
System.out.println("Zi带参构造&haveSubmitBuild方法被调用");
}
}
package ch6;
public class a_5_3测试 {
public static void main(String[] args) {
//创建对象
a_5_2Zi z = new a_5_2Zi();//通过无参构造方法创建对象
//先输出父类的无参语句,再输出子类的无参语句
//即先输出'Fu无参构造¬SubmitBuild方法被调用'再输出'Zi无参构造¬SubmitBuild方法被调用'
//思考:为什么会先去访问父类的构造方法
//创建对象
a_5_2Zi z2 = new a_5_2Zi(20);//通过带参构造方法创建对象
//先输出父类的无参语句,再输出子类的带参语句
//思考:为什么调用子类的带参构造方法时,会访问父类的无参构造方法
}
}
继承中成员方法的访问特点
通过子类对象访问一个方法 1、子类成员范围找 2、父类成员范围找 3、如果都没有找到就报错(只在子类和子类的亲父类找)
访问特点的练习
xxxxxxxxxx
package ch6;
public class a_6_1Fu {
//再把下面的代码在子类里复制一份
public void show(){
System.out.println("Fu中show()方法被调用");
}
}
package ch6;
public class a_6_2Zi extends a_6_1Fu {
public void method(){
System.out.println("Zi中method()方法被调用");
}
//父类里也有show()方法,这个方法的println输出子类中的show()方法被调用,以便于观察
public void show(){
//当加一个super.show()时,则指定在父类里找show()方法
super.show();
System.out.println("Zi中show()方法被调用");
}
}
xxxxxxxxxx
package ch6;
public class a_6_3测试 {
public static void main(String[] args) {
//创建对象,调用方法
a_6_2Zi z = new a_6_2Zi();
z.method();//输出Zi中method()方法被调用
//z.show();//输出Fu中show()方法被调用
//当子类里也有一个show()方法时,调用的是'Zi中的show()方法被调用'
//z.show();//输出Zi中的show()方法被调用
//说明子类对象调用方法时,首先会在子中找对应的方法,如果子中没有找到,就会去父中找
//当子类里写了super.show()时,则指定在父类里找show()方法
z.show();//先输出父类的show()方法被调用,再输出子类的show()方法被调用
}
}
方法重写
方法重写概述 1、子类中出现了和父类中一样的方法声明的现象
方法重写应用 1、当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容
@Override 1、是一个注解 2、可以帮助我们检查重写方法的方法声明是否正确
方法重写的练习
xxxxxxxxxx
package ch6;
//手机类
public class a_7_1_1Phone {
public void call(String name){
System.out.println("给" + name + "打电话");
}
}
xxxxxxxxxx
package ch6;
//新手机类,让这个类继承a_7_1_1Phone类
public class a_7_1_2NewPhone extends a_7_1_1Phone{
//注解。写重写方法时,建议自己在方法前面加这一行,如果这一行没报错,那下面的重写方法就正确
public void call(String name){
System.out.println("开启视频功能,start video");
//System.out.println("给" + name + "打电话");
//因为父类有上面那行的输出语句,如何不在子类即本文件里不多余书写上面那句话呢
//需要使用到super方法,如下,先把System.out.println("给" + name + "打电话");注释
super.call(name);//直接调用,把参数传进去,就可以达到跟注释了的那行的同样效果
}
}
xxxxxxxxxx
package ch6;
public class a_7_2测试 {
public static void main(String[] args) {
//创建对象,调用方法
a_7_1_1Phone p = new a_7_1_1Phone();
p.call("lina");//输出'给lina打电话'
System.out.println("-----------");
//让a_7_1_2NewPhone继承a_7_1_1Phone之后,创建对象,调用方法
a_7_1_2NewPhone np = new a_7_1_2NewPhone();
np.call("lina");//自己没有call方法,但是继承的对象里面有
//子类:a_7_1_2NewPhone
//父类:a_7_1_1Phone
//方法重写:子类里出现跟父类一样的方法,比如这里示例的call方法
//子类重写了父类的方法后,就可以加入自己特有的内容,并且能够使用父类的功能
//在子类重写方法时,建议直接把父类的那部分方法复制过来,因为自己写会不小心报错
//还可以在这个重写方法的上面一行添加@Override,如果这个@Override报错就说明自己写的重写方法出错了
}
}
方法重写的注意事项
方法重写注意事项 1、父类的私有方法不能被子类重写 2、子类方法访问权限(访问权限修饰符)不能比父类低,常见的访问修饰符的访问权限大小比较(public>默认>private)
方法重写注意事项的练习
package ch6;
public class a_8_1Fu {
private void show(){
System.out.println("Fu中show()方法被调用");
}
//public修饰符的访问权限比private高
/*public void method(){
System.out.println("Fu中method()方法被调用");
}*/
void method(){//删掉这里父类的访问修饰符
System.out.println("Fu中method()方法被调用");
}
}
package ch6;
public class a_8_2Zi extends a_8_1Fu {
//下面的重写方法会报错,因为子类不可以重写父类中被private修饰的方法,那是父类的私有内容,子类无法继承
/*private void show(){//重写父类的show方法
System.out.println("Zi中show()方法被调用");
}*/
//下面的重写方法,@Override没有报错,所以是重写正确的
/*public void method(){
System.out.println("Zi中method()方法被调用");
}*/
//如果不写访问修饰符public或private
//系统会给一个默认访问修饰符,但是这个默认访问修饰符的访问权限没有public大
void method(){
System.out.println("Fu中method()方法被调用");
}
//上面会报错,因为父类的method方法被public修饰了,这里的系统默认修饰符访问权限低,就无法访问父类的method方法
//如果想不报错的话,就把父类的访问修饰符public删掉
}
继承的注意事项
1、java中类只支持单继承,不支持多继承 2、java中类支持多层继承(间接实现多继承)
继承注意事项的练习
xxxxxxxxxx
package ch6;
//让这个Father类继承Grandfather类
public class a_9_1Father extends a_9_2Grandfather{
public void smoke(){
System.out.println("father爱抽烟");
}
}
xxxxxxxxxx
package ch6;
public class a_9_2Grandfather {
public void drink(){
System.out.println("grandfather爱喝酒");
}
}
xxxxxxxxxx
package ch6;
public class a_9_3Monther {
public void dance(){
System.out.println("mon爱跳舞");
}
}
xxxxxxxxxx
package ch6;
//让这个Son类继承Father类。注意,在java中类不能同时继承多个类,只能继承一个类
//如果想要这个Son继承多个类,可以通类传递的方式,
//即这里的Son继承Father,在Father类里面继承Grandfather,就间接达到了Son同时继承Father和Grandfather的效果
//上面的类传递方式就是java中的多层继承
public class a_9_4Son extends a_9_1Father{
}
【以下是两个综合练习案例】
老师和学生案例
需求:定义老师类和学生类,然后写代码测试,最后找到老师类和学生类当中的共性内容,抽取出一个父类,用继承的方式改写代码,并进行测试
思路: 1、定义老师类,包括姓名,年龄,教书() 2、定义学生类,包括姓名,年龄,学习() 3、定义测试类,写代码测试
实现如下
xxxxxxxxxx
package ch6;
public class a_10_1Student {
//成员变量
private String name;
private int age;
//无参构造方法
public a_10_1Student() {
}
//带参构造方法
public a_10_1Student(String name,int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void study() {
System.out.println("我是study方法");
}
}
xxxxxxxxxx
package ch6;
public class a_10_2Teacher {
private String name;
private int age;
public a_10_2Teacher() {
}
public a_10_2Teacher(String name,int age){
this.name = name;
this.age = age;
}
public String getName(){
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void teach() {
System.out.println("我是ch6包里面的teach方法");
}
}
xxxxxxxxxx
package ch6;
public class a_10_3测试 {
public static void main(String[] args) {
//创建老师类对象进行测试
//通过无参构造方法创建对象
a_10_2Teacher t1 = new a_10_2Teacher();
t1.setName("张三-无参");
t1.setAge(21);
System.out.println(t1.getName() + "," + t1.getAge());
t1.teach();
//通过带参构造方法创建对象
a_10_2Teacher t2 = new a_10_2Teacher("李四-带参", 22);
System.out.println(t2.getName() + "," + t2.getAge());
t2.teach();
}
}
猫和狗案例
需求: 采用继承的思想实现猫和狗的案例,并测试
xxxxxxxxxx
猫:
成员变量: 姓名,年龄
构造方法: 无参,带参
成员方法: get/set方法,抓老鼠()
狗:
成员变量: 姓名,年龄
构造方法: 无参,带参
成员方法: get/set方法,看门()
共性:
成员变量: 姓名,年龄
构造方法: 无参,带参
成员方法: get/set方法*/
x1、定义动物类
成员变量: 姓名,年龄
构造方法: 无参,带参
成员方法: get/set方法
2、定义猫类
构造方法: 无参,带参
成员方法: 抓老鼠()
3、定义狗类
构造方法: 无参,带参
成员方法: 看门()
4、定义测试类,并测试
实现如下
xxxxxxxxxx
package ch6;
public class a_11_1动物类 {
private String name;
private int age;
//无参构造方法
public a_11_1动物类() {
}
//带参构造方法
public a_11_1动物类(String name, int age) {
this.name = name;
this.age = age;
}
//get和set方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
xxxxxxxxxx
package ch6;
//继承动物类
public class a_11_2Cat extends a_11_1动物类 {
//无参构造方法
public a_11_2Cat() {
}
//带参构造方法
public a_11_2Cat(String name, int age) {
super(name, age);
}
//特有方法
public void catchMouse() {
System.out.println("抓老鼠");
}
}
xxxxxxxxxx
package ch6;
//继承动物类
public class a_11_3Dog extends a_11_1动物类 {
//无参构造方法
public a_11_3Dog() {
}
//带参构造方法
public a_11_3Dog(String name, int age) {
super(name, age);
}
//特有方法
public void lookDoor() {
System.out.println("看门");
}
}
xxxxxxxxxx
package ch6;
public class a_11_4测试 {
public static void main(String[] args) {
//创建猫类对象并测试
//无参
a_11_2Cat c1 = new a_11_2Cat();
c1.setName("加菲猫");
c1.setAge(2);
System.out.println(c1.getName() + "," + c1.getAge());
c1.catchMouse();
//创建猫类对象并测试
//带参
a_11_2Cat c2 = new a_11_2Cat("加菲猫2", 3);
System.out.println(c2.getName() + "," + c2.getAge());
c2.catchMouse();
System.out.println("-------------");
//创建狗类对象并测试
//无参
a_11_3Dog c3 = new a_11_3Dog();
c3.setName("田园犬");
c3.setAge(1);
System.out.println(c3.getName() + "," + c3.getAge());
c3.lookDoor();
//创建猫类对象并测试
//带参
a_11_3Dog c4 = new a_11_3Dog("田园犬", 2);
System.out.println(c4.getName() + "," + c4.getAge());
c4.lookDoor();
}
}