继承
继承的概念:泛指把前人的作风、文化、知识、财产等接受过来
Java中的继承
让类与类之间的产生父子关系
被继承的类叫做父类(基类,超类)
继承的类叫做子类(派生类)
格式(extends)
class 父类 {
//……
}
class 子类 extends 父类 {
//……
}
子类继承父类:子类拥有了父类的非私有成员(成员变量、成员方法)
package study2.demo;
public class Parent {
private String name;
private int age;
// 快捷键:alt+insert,快速成成构造方法(Constructor)和setXxx()和getXxx()方法(Getter and setter)
// 构造方法
public Parent() {
}
public Parent(String name, int age) {
this.name = name;
this.age = age;
}
//setXxx()和getXxx()方法
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;
}
}
package study2.demo;
/*
Child:子类,派生类
Parent:父类,基类,超类
*/
public class Child extends Parent {
}
package study2.demo;
//测试类
public class Test {
// main函数是程序的主入口,所有的代码都是从这里开始执行的
public static void main(String[] args) {
// 创建Child类的对象
Child c = new Child();
// 给对象的姓名设置为张三
c.setName("张三");
c.setAge(30);
// c.name="张三"; 这个报错的原因是这是父类的私有成员(private修饰)
System.out.println("他的名字是:"+c.getName());
System.out.println("他的年龄是:"+c.getAge());
/*
java中,子类只能继承父类的非私有成员(成员变量、成员方法,由public修饰的)
*/
}
}
继承的使用场景
多个类中存在相同的属性和行为时,可以将这些内容提取出来放到一个新类中,让这些类和新类产生父子关系,实现代码复用
案例:定义继承关系的动物类并使用
需求:分别定义Dog类、Pig类,他们共有的属性有:name,age,sex,共有的行为有:eat()。两者特有的行为分别是:watch(),snore()
不用继承
package study2.demo;
//定义一个dog类
public class Dog {
private String name;
private int age;
private String sex;
public Dog() {
}
public Dog(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
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 String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
// 共有的行为
public void eat() {
System.out.println("我们都会吃饭");
}
// Dog类特有的行为
public void watch() {
System.out.println("Dog类特有的行为");
}
}
package study2.demo;
//定义一个pig类
public class Pig {
private String name;
private int age;
private String sex;
public Pig() {
}
public Pig(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
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 String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
// 共有的行为
public void eat() {
System.out.println("我们都会吃饭");
}
public void snore() {
System.out.println("Pig类特有的行为");
}
}
package study2.demo;
/*
需求:
分别定义Dog类、Pig类
它们共有的属性有name,age,sex
两者特有的行为分别是:watch(),snore()
解决方案
方案一:传统代码(不是有继承)
方案二:使用继承后的代码
*/
public class TestAnimal {
public static void main(String[] args) {
// 测试狗类
Dog d = new Dog();
d.eat();
d.watch();
System.out.println("————————————");
// 测试猪类
Pig p = new Pig();
p.eat();
p.snore();
}
}
使用继承
package study2.demo;
//定义一个dog类
public class Dog extends Animal {
// Dog类特有的行为
public void watch() {
System.out.println("Dog类特有的行为");
}
}
package study2.demo;
//定义一个pig类
public class Pig extends Animal {
public void snore() {
System.out.println("Pig类特有的行为");
}
}
package study2.demo;
//定义一个父类
public class Animal {
private String name;
private int age;
private String sex;
//无参
public Animal() {
}
//全参
public Animal(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
// setXxx(),getXxx()
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 String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public void eat() {
System.out.println("我们都会吃饭");
}
}
package study2.demo;
/*
需求:
分别定义Dog类、Pig类
它们共有的属性有name,age,sex
两者特有的行为分别是:watch(),snore()
解决方案
方案一:传统代码(不是有继承)
方案二:使用继承后的代码
*/
public class TestAnimal {
public static void main(String[] args) {
Dog d = new Dog();
d.eat();
d.watch();
System.out.println("——————————");
Pig p = new Pig();
p.snore();
p.eat();
}
}
继承的优点:
1.功能复用:直接将已有的属性和行为继承过来,实现了功能的复用,节省了大量的工作
2.便于扩展新功能:在已有功能的基础上,更容易建立,扩充新功能
3.结构清晰,简化认识:同属于一个继承体系的相关类,他们之间结构层次清晰,简化了人们对代码结构的认识
4.易维护性:不同类之间的继承关系,让这些事物之间保持一定程度的一致性,大大降低了维护成本
继承的缺点:
1.打破了封装性:父类向子类暴露了实现细节,打破了父类对象的封装性
2.高耦合性:类与类之间紧密的结合在一起,相互依赖性高
程序设计的追求
低耦合,高内聚
耦合:两个(或更多)模块相互依赖对方
内聚:模块内部结构紧密,独立性强
继承关系中类成员的使用
继承关系中子父类成员变量的使用
需求:子父类中定义了同名的成员变量,如何使用
package study2.demo;
//父类,苹果类
public class Fruit {
// 成员变量
int price = 20;
}
package study2.demo;
//子类,苹果类
public class Apple extends Fruit {
// 成员变量
int price = 10;
public void showPrice() {
// 局部变量
int price = 5;
System.out.println(price); //5
System.out.println(this.price); //10 this在本类找
System.out.println(super.price); //15 super在父类找
}
}
package study2.demo;
public class Test2 {
public static void main(String[] args) {
// 需求:测试继承关系中,子父类间成员变量的使用
Apple a = new Apple();
a.showPrice();
/*
Java中使用变量的规则
遵循“就近原则”,局部位置有就使用,
没有就去本类的成员位置找,有就使用
没有就去父类的成员位置找,有就使用,没有就报错
*/
}
}
Java中使用变量的规则:
遵循“就近原则”,局部位置有就使用,
没有就去本类的成员位置找,有就使用
没有就去父类的成员位置找,有就使用,没有就报错
继承关系中成员变量使用结论
查找变量的原则:就近原则
查找变量的顺序:局部变量—>成员变量—>父类—>更高的父类……Object
访问父类变量的方式:super.父类变量名;
super:当前对象父类的引用(父类内存空间的标识)
对象初始化顺序:先初始化父类内容,再初始化子类内容
this和super的区别
this
本质:对象;
用法:从本类开始找
super
本质:父类内存空间的标识
用法:从父类开始找
区别:
直接写变量名: 局部变量
this.成员变量名: 本类的成员变量
super.成员变量名: 父类的成员变量名
package study2.demo;
//父类
public class Fu {
int num = 30;
}
package study2.demo;
//子类
public class Zi extends Fu {
int num = 20;
public void show() {
int num = 10;
System.out.println(num); //10
System.out.println(this.num); //20
System.out.println(super.num); //30
}
}
package study2.demo;
public class Test3 {
public static void main(String[] args) {
/*
遵循“就近原则”
先在局部位置找,有就使用
没有就去本类的成员位置找,有就使用
没有就去父类的成员位置找,有就使用,没有就报错
如果局部变量,本类的成员变量,父类的成员变量重名了,如何解决?
直接写变量名: 局部变量
this.成员变量名: 本类的成员变量
super.成员变量名: 父类的成员变量名
*/
Zi z = new Zi();
z.show();
}
}
继承关系中子父类成员方法的使用
需求:子父类中定义了同名的成员方法,如何使用
package study3.demo;
//父类,武功类
public class Martial {
// 练习内功
public void internalStrength() {
System.out.println("练习内功");
}
// 练习招式
public void stroke() {
System.out.println("练习招式");
}
}
package study3.demo;
public class NineYin extends Martial {
// 练习内功
public void internalStrength() {
// 这里是在调用父类的成员方法
super.internalStrength();
System.out.println("以柔克刚");
}
// 练习招式
public void stroke() {
System.out.println("九阴白骨爪");
}
}
package study3.demo;
/*
需求:
定义武功类Martial:
练习内功:internalStrength()
练习招式:stroke()
定义九阴真经类NineYin,继承Martial类
练习内功:internalStrength()
练习招式:stroke()
九阴真经的修炼,不仅要练习基本内功,还要能够以柔克刚,需要扩展父类方法
简单的招式已经不足为用,必须有九阴白骨爪这样的大招才能制胜,需要重新实现父类方法
*/
public class Test {
public static void main(String[] args) {
// 需求:调用NineYin类中的功能
NineYin ny = new NineYin();
ny.internalStrength();
ny.stroke();
}
}
继承关系中成员方法使用结论
查找方法的原则:就近原则
查找方法的顺序:本类—>父类—>更高的父类……Object
访问父类方法的方式:super.父类方法名();
定义重名方法的前提:父类功能不能完全满足现实需求,扩展父类功能;父类功能已过时,重新实现父类功能
继承关系中子父类构造方法的使用
需求:创建对象时,构造方法是如何被调用的?
需求:父类不存在默认无参构造方法怎么办(须手动调用父类其他构造)
package study3.demo;
//父类
public class Person {
public Person(String name) {
// System.out.println("Person的空参构造");
System.out.println("Person类的带参构造"+name);
}
}
package study3.demo;
//子类
public class Worker extends Person {
public Worker() {
super("A"); //用于初始化父类成员的,必须写在第一行
System.out.println("Worker的空参构造");
}
}
package study3.demo;
/*
测试类:用来演示构造方法的调用的
结论:子类所有构造方法的第一行都有一个默认的super()用来访问父类的无参构造方法
如果父类没有无参构造,可以通过super(参数)的形式访问父类的带参构造,
*/
public class Test2 {
public static void main(String[] args) {
Worker w = new Worker();
}
}
结论:创建子类对象时,优先调用父类构造方法;子类构造方法的第一行,隐含语句super(),用于调用父类的默认无参构造
结论:子类所有构造方法的第一行都有一个默认的**super()用来访问父类的无参构造方法;如果父类没有无参构造,可以通过super(参数)**的形式访问父类的带参构造,
方法重写(Override)
定义:子类中出现和父类方法定义相同的方法的现象
解释:方法重写也叫方法的复写、覆盖方法名、参数列表、返回值类型都相同
注意事项:
父类私有方法无法重写;
子类方法访问权限不能小于父类方法;
子类不能比父类方法抛出更大的异常(了解);
四大权限修饰的修饰范围从小到大分别是:
private>默认(什么都不写就是默认)>protected>public
使用场景:
扩展父类功能;父类功能过时,重新实现父类功能
package study3.demo;
/*
四大权限访问修饰符:
本类 同包下的类 不同包下的子类 不同包下的无关类 private: Y N N N
默认: Y Y N N
protected: Y Y Y N
public: Y Y Y Y
结论:
private:强调的是给自己来使用
默认:强调的是给同包下的类来使用
protected:强调的是给子类使用
public:强调的是给大家使用
*/
public class Father {
private void show1() {
System.out.println("show private");
}
void show2() {
System.out.println("show 默认");
}
protected void show3() {
System.out.println("show protected");
}
public void show4() {
System.out.println("show public");
}
public static void main(String[] args) {
Father f = new Father();
f.show1();
f.show2();
f.show3();
f.show4();
}
}
package study2.demo;
import study3.demo.Father;
public class Son extends Father {
public static void main(String[] args) {
Father f = new Father();
// f.show1(); 报错
// f.show2(); 报错
// f.show3(); 报错
f.show4();
System.out.println("————————");
Son s = new Son();
// f.show1(); 报错
// f.show2(); 报错
s.show3();
s.show4();
}
}
package study3.demo;
public class Test3 {
public static void main(String[] args) {
Father f = new Father();
// f.show1(); //报错
f.show2();
f.show3();
f.show4();
}
}
package study3.demo;
public class Son extends Father {
public static void main(String[] args) {
Father f = new Father();
// f.show1(); 报错
f.show2();
f.show3();
f.show4();
System.out.println("——————————");
Son s = new Son();
s.show2();
s.show3();
s.show4();
}
}
结论:
private:强调的是给自己来使用
默认:强调的是给同包下的类来使用
protected:强调的是给子类使用
public:强调的是给大家使用
方法重写和方法重载的区别:
重载(Overload):
方法名:相同
参数列表:不同(个数或对应的位置类型)
返回值类型:无关
修饰符:无关
定义位置:同一个类
重写(Override):
方法名:相同
参数列表:相同
返回值类型:相同
修饰符:访问权限不小于被重写方法
定义位置:子父类中
Java中继承的特点
1.单继承:Java只支持类的单继承(只能继承一个类),但是支持多层(重)继承(爷爷—>爸爸—>儿子,1对n);Java支持接口的多继承,语法为:接口A extends 接口B,接口C,接口D……
2.私有成员不能继承:只能继承父类的非私有成员(成员变量,成员方法)
3.构造方法不能继承:构造方法用于初始化本类对象;创建子类对象时,需要调用父类构造初始化该对象的父类内容,若父类构造可以被继承,该操作会造成调用的混乱。
4.继承体现了“is a”的关系:子类符合“is a(是一个,比如:红富士苹果(子类)是一个苹果(父类))”父类的情况下,才使用继承,其他情况不建议使用