继承
继承的概念
多个列中存在相同的属性和行为时,将这些内容凑渠道单独一个类中,那么多个类无需再定义这些属性和行为,只要继承了那个类即可。
通过extends关键字可以实现类与类的继承
class 子类名 extends 父类名{ }
单独的这个类称为父类,基类或者超类;这多个类可以称为子类或者派生类。
有了继承以后,我们定义一个类的时候,可以在一个已经存在的类的基础上,还可以定义自己的新成员。
继承中类之间体现的是:"is a"的关系
继承的语法
class Fu{
//相同的属性和行为(公共财产)
}
class Zi extends Fu{
//自己的新的行为和属性
}
继承案例
class Person{ //人类
String name;
int age;
public void eat(){
System.out.println("吃饭");
}
}
//学生类
class Student2 extends Person{
}
//老师
class Teacher2 extends Person{
}
public class Demo2 {
public static void main(String[] args) {
Student stu = new Student();
System.out.println(stu.name);
System.out.println(stu.age);
stu.eat();
Teacher teacher = new Teacher();
teacher.name ="doubleyong";
teacher.age = 18;
teacher.eat();
System.out.println(teacher.name);
System.out.println(teacher.age);
}
}
继承的优点和缺点
继承的优点:
- 提高了代码的复用性
- 提高了代码的维护性
- 让类与类产生了关系,是多态的前提
继承的缺点(弊端):
让类与类产生了关系 (让耦合增强了)
原则 : 高内聚,低耦合
继承的特点
java中只能单继承,不能多继承。
java中继承支持多层继承。
class Father{
}
class Son extends Father{
}
class Grandson extends Son{
}
继承的注意事项
A. 子类只能继承父类所有的非私有的成员(成员属性和成员方法)
B. 子类不能继承父类的构造方法, 但是可以通过 super关键字去访问父类的构造方法
C:不要为了部分功能而继承
继承中成员变量的关系
我们应该考虑一下,继承后类的各部分之间的关系
继承中成员变量的关系
A:子类成员变量与父类成员变量名称不一样 很简单,直接使用名称调用
B:子类成员变量与父类成员变量名称一样
子类使用变量,查找的顺序
1.找子类的局部变量
2.找子类的成员变量
3.找父类的成员变量
4.如果都找不到,就报错
super关键字
**this 与 super 的区别?分别是什么? **
this 代表当前类的对象
super 代表父类的对象(super 代表父类存储空间的标识 ), 可以通过super去操作父类的属性和方法
怎么用?
1. 调用成员
this.成员变量;
super.成员变量; super.成员方法();
-
调用构造函数
this(…) 调用本类的构造方法
super(…) 调用父类名的构造方法
继承中构造方法的关系
-
子类中所有的构造方法默认都会访问父类的无参构造方法
为什么?
因为子类会继承父类中的数据,可以还会使用父类的数据
所以,子类初始化之前,一定要先完成父类的数据初始化
注: 子类每个构造方法的第一句默认都是:super()
子类初始化之前,一定要先完成父类的数据初始化
一个类的初始化:
- 默认值初始化
- 显示初始化
- 构造方法初始
继承中构造方法的注意事项
如果父类中没有无参构造方法,会怎么
(报错)
怎么办?
- 给父类加一个无参构造方法
- 子类通过super(…)去调用有参的父类构造方法
- 子类通过this去调用本类的其他构造方法, 其它的构造方法,必须先访问父类构造方法
注意:
this(…), super(…) 都必须放到第一个句中 。
**super()**在第一行的原因就是: 子类有可能访问了父类对象, 假如在构造函数中使父类对象的成员函数和变量, 在成员初始化使用了父类,在代码块中使用了父类等, 所以为保证在子类可以访问父类对象之前要完成对父类对象的初始化。
**this()**在第一行的原因就是: 为保证父类对象初始化的唯一性. 我们假设这种情况, 类B是类A的子类, 如果this()可以在构造函数的任意行使用, 那么会出现什么情况呢? 首先程序运行到构造函数B()的第一行, 发现没有调用this()和super(), 就自动在第一行补齐了super() , 完成了对父类对象的初始化, 然后返回子类的构造函数继续执行, 当运行到构造函数B()的"this(2) ;"时, 调用B类对象的B(int) 构造函数, 在B(int)中, 还会对父类对象再次初始化! 这就造成了对资源的浪费, 当然也有可能造成某些意想不到的结果。
继承中成员方法的关系
继承中成员方法的关系
-
子类方法和父类方法,声明不一样, 这个很简单
-
子类方法与父类方法,声明一样, 调用哪一个
通过子类调用方法:
a. 先找子类中有没有这个方法,有就使用
b. 再看父类中有没有这个方法,有就使用
c. 如果都没有就报错
class X {
Y b = new Y();
X() {
System.out.print("X");
}
}
class Y {
Y() {
System.out.print ("Y");
}
}
public class Demo9 extends X {
Y y = new Y();
Demo9() {
//super();
System.out.print("Z");
}
public static void main(String[] args) {
new Demo9();
}
}
//输出结果为: Y X Y Z
重写
子类与父类方法相同是,我们应用的是子类,所以可以称 子类 重写 父类的方法, java中这种情况为: 重写
重载( overload ) : 在同一个类中, 方法名相同,参数类型不同,或参数的个数,称为重载 (注: 和返回值没有关系)
重写( override ) : 继承关系中, 子类去重写(复写/覆盖)父类已经有的方法,方法的声明要一致(包括方法名,返回值,参数都要一致)
重写的注意事项
-
父类的私有方法不能重写,因为父类的私有方法根本没有办法进行继承
-
子类在重写父类时 权限不能更低 (最好保持一致)
-
父类静态方法,子类必须是静态方法,通过静态方法重写(实际上静态方法不算是重写)
权限修饰符:
public
private
default
protected
@Override // 通过它检查是否去重写了父类的方法,可以写也可以不写,如果写了,必须是重写的方法,也就是说它的方法名必须和父类中可以重写的方法名中有一个相同
public void call(){
System.out.println("给张三打电话");
}
final 关键字
final:最终的意思。常见的是它可以修饰类,方法,变量
final修饰的类: 此类就不能被继承了
final修饰的方法: 方法不能被重写(覆盖/复写)
final修饰的变量: 此就是不能被重新赋值,因为被它修饰变量其实就是常量
常量:
-
字面量常量
“hello”,6,true
-
自定义常量(符号常量)
final int 常量名= 值; //常量 ,全大写
final修饰局部变量
局部变量:
基本类型:值不能变
引用类型: 地址值不能变,但地址值指向的堆中存储的数据值可以改变。
final只初始化1次;声明和赋值可以分开写,但是只能有一次赋值。
final int a;
a = 10;
a = 0; //会报错