继承的由来
我们用代码来引出继承的由来。
定义一个猫(Cat)类,有name和color两个属性,有eat一个行为;
class Cat {
String name;//名字
String color;//颜色
public void eat(){
System.out.println("吃东西");
}
}
然后定义一个狗(Dog)类,也有name和color两个属性,有eat一个行为;
class Dog {
String name;//名字
String color;//颜色
public void eat(){
System.out.println("吃东西");
}
}
如上代码,我们很容易看出这么一个问题,cat和dog都有相同的name,color属性,也同样具有eat行为。同样的代码我们却要写两次,那么有没有一个方法,只声明一次name,color属性,和eat行为呢。首先,猫和狗都属于动物,都有name和color属性,那么我们是否可以创建一个Animal类,让它包含猫和狗吗。继承
的出现,为我们解决了这个问题。
class Demo_Extends {
public static void main(String[] args) {
Cat c = new Cat();
c.color = "橘色";
c.name = "二喜";
c.eat();
System.out.println(c.name + "..." + c.color);
//运行结果为
//吃东西
//二喜...橘色
}
}
//定义一个Animal类
class Animal {
String color; //动物的颜色
String name; //动物的名字
public void eat() { //吃东西的功能
System.out.println("吃东西");
}
}
class Cat extends Animal {
}
class Dog extends Animal {
}
/*
extends是继承的意思
Animal是父类
Cat和Dog都是子类
*/
综上代码我们发现,我们定义了一个Animal类,含有color和name两个属性,一个eat行为。然后分别定义一个Cat和Dog类,并且集成自Animal类。我们在构造函数中直接new一个Cat对象,然后直接给它的name和color属性赋值,并调用eat行为,发现时可行的。这样,我们就不用分别再为Cat和Dog声明属性和定义行为了。
继承的好处和弊端
- A:继承的好处
- a:提高了代码的复用性(从上述例子可以看出)
- b:提高了代码的维护性(比如为Cat和Dog添加一个leg属性,只需要在Animal中添加即可)
- c:让类与类之间产生了关系,是多态的前提
- B:继承的弊端
类的耦合性增强了。(类与类之间的关联太紧密,父类改变,子类也会相应的做出改变)
开发的原则:高内聚,低耦合。
耦合:类与类的关系
内聚:就是自己完成某件事情的能力
Java中类的继承特点
- Java只支持单继承,不支持多继承。(一个儿子只能有一个爹)
//一个类只能有一个父类,不可以有多个父类。
class SubDemo extends Demo{} //ok
class SubDemo extends Demo1,Demo2...//error
- 有些语言是支持多继承,格式:extends 类1,类2,...
- Java支持多层继承(继承体系)
class A{}
class B extends A{}
class C extends B{}
- 如果想用这个体系的所有功能用最底层的类创建对象
看下面代码示例
- 如果想看这个体系的共性功能,看最顶层的类
看下面代码示例
class Demo_Extends {
public static void main(String[] args) {
//如果想用这个体系的所有功能用最底层的类创建对象
C c = new C();
c.age=11;
c.name="二喜";
c.leg=4;
c.color="橘色";
c.eat();
c.sleep();
c.run();
/*
c对象就包含了age,name,leg,color属性,以及eat,sleep,run的行为
*/
//如果想看这个体系的共性功能,看最顶层的类
A a =new A();
a.age=2;
a.name="三毛";
a.eat();
/*
此时的a对象只包含age和name两个属性,以及eat一个行为。
*/
}
}
class A{
int age;
String name;
public void eat(){
System.out.printlb("吃东西");
}
}
class B extends A{
int leg;
public void sleep(){
System.out.printlb("睡觉");
}
}
class C extends B{
String color;
public void run(){
System.out.printlb("快跑");
}
}
继承的注意事项
- a:子类只能继承父类所有非私有的成员(成员方法和成员变量)
class Demo_Extends {
public static void main(String[] args) {
Son son =new Son();//声明一个son对象
son.age=11;//这样用是正确的
son.wife="王婆";//这样用是错误的,因为wife是Father的私有变量
son.eat();//son具有eat的行为
son.sleep();//son不能访问父类Father的私有方法sleep;这样用是错误的
}
}
class Father {
int age;
private wife;
public void eat(){
System.out.println("我要吃饭");
}
private void sleep(){
System.out.print("我要去睡觉");
}
}
class Son entends Father {
}
- b:子类不能继承父类的构造方法,但是可以通过super关键字去访问父类构造方法。
查看下方this和super关键字的使用
- c:不要为了部分功能而去继承
项目经理含有 姓名 工号 工资 奖金属性;程序员 含有姓名 工号 工资,不要用为了相同的属性去让程序员集成项目经理,或者项目经理集成程序员。
什么时候使用继承
- 继承其实体现的是一种关系:"is a"。
(包含关系)
Person
Student
Teacher
水果
苹果
香蕉
橘子
- 采用假设法。
如果有两个类A,B。只有他们符合A是B的一种,或者B是A的一种,就可以考虑使用继承。
继承中成员变量的关系
- 子类中的成员变量和父类中的成员变量名称不一样,这个太简单
- 子类中的成员变量和父类中的成员变量名称一样,这个怎么玩呢?
在子类方法中访问一个变量的查找顺序
a:在子类方法的局部范围找,有就使用
b:在子类的成员范围找,有就使用
c:在父类的成员范围找,有就使用
d:如果还没找到,就报错
class Demo4_Extends {
public static void main(String[] args) {
Son s = new Son();
s.print();
}
}
/*
* A:案例演示
* a:不同名的变量
* b:同名的变量
子父类出现同名的变量只是在讲课中举例子有,在开发中是不会出现这种情况的
子类继承父类就是为了使用父类的成员,那么如果定义了同名的成员变量没有意义了
*/
class Father {
int num1 = 10;
int num2 = 30;
}
class Son extends Father {
int num2 = 20;
public void print() {
System.out.println(this.num1); //this既可以调用本类的,也可以调用父类的(本类没有的情况下)
System.out.println(this.num2); //就近原则,子类有就不用父类的了
System.out.println(super.num2);
}
}
[注意]实际开发中不会遇到同名变量,自己在今后的开发过程中也不要给对象声明相同名称的变量
继承中成员方法的关系
- 子类和父类的方法名不同,这个就无所谓了
- 子类中的方法和父类方法名不同
通过子类调用方法
1.先找子类中,看有没有这个方法,有就使用
2.再看父类中有没有这个方法,有就使用
3.如果都没有,俺么就报错
this和super的区别和应用
- A:this和super都代表什么
- this:代表当前对象的引用,谁来调用我,我就代表谁
- super:代表当前对象父类的引用
- B:this和super的使用区别
- a:调用成员变量
- this.成员变量 调用本类的成员变量,也可以调用父类的成员变量
- super.成员变量 调用父类的成员变量
- b:调用构造方法
- this(...) 调用本类的构造方法
- super(...) 调用父类的构造方法
- c:调用成员方法
- this.成员方法 调用本类的成员方法,也可以调用父类的方法
- super.成员方法 调用父类的成员方法
- a:调用成员变量
继承中构造方法的关系
子类中所有的构造方法默认都会访问父类中空参数的构造方法
因为子类会继承父类中的数据,可能还会使用父类的数据。
,子类初始化之前,一定要先完成父类数据的初始化。
每一个构造方法的第一条语句默认都是:super() Object类最顶层的父类。
class Demo5_Extends {
public static void main(String[] args) {
Son s = new Son();
}
}
/*
*
* 子类中所有的构造方法默认都会访问父类中空参数的构造方法
* 因为子类会继承父类中的数据,可能还会使用父类的数据。
* 所以,子类初始化之前,一定要先完成父类数据的初始化。
* 每一个构造方法的第一条语句默认都是:super() Object类最顶层的父类。
*/
class Father extends Object {
public Father() {
super();
System.out.println("Father 的构造方法");
}
}
class Son extends Father {
public Son() {
super(); //这是一条语句,如果不写,系统会默认加上,用来访问父类中的空参构造
System.out.println("Son 的构造方法");
}
}
继承中构造方法的注意事项
-
父类没有无参构造方法,子类怎么办?
- super解决
- this解决
super(…)或者this(….)必须出现在构造方法的第一条语句上
class Demo6_Extends {
public static void main(String[] args) {
Son s1 = new Son();
System.out.println(s1.getName() + "..." + s1.getAge());
System.out.println("--------------------");
Son s2 = new Son("张三",23);
System.out.println(s2.getName() + "..." + s2.getAge());
}
}
class Father {
private String name; //姓名
private int age; //年龄
public Father() { //空参构造
System.out.println("Father 空参构造");
}
public Father(String name,int age) { //有参构造
this.name = name;
this.age = age;
System.out.println("Father 有参构造");
}
public void setName(String name) { //设置姓名
this.name = name;
}
public String getName() { //获取姓名
return name;
}
public void setAge(int age) { //设置年龄
this.age = age;
}
public int getAge() { //获取年龄
return age;
}
}
class Son extends Father {
public Son() { //空参构造
this("王五",25); //本类中的构造方法
//super("李四",24); //调用父类中的构造方法
System.out.println("Son 空参构造");
}
public Son(String name,int age) { //有参构造
super(name,age);
System.out.println("Son 有参构造");
}
}