文章目录
看着他们的表情,那满足的模样
看着他们快乐,我也跟着饱了
一、继承
书写代码时,经常发现两个或多个类,在成员变量和成员方法上高度的一致,仅仅有一小部分不同。此时,为了实现两个类,再花费大量时间用来书写重复代码,就很浪费时间。
针对这种情况,Java 给出了一种规则,来规避重复 —— 继承
多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中
那么多个类无需再定义这些属性和行为,只要继承那个类即可
1、如何实现及特点
代码格式
//创建一个 Pokemon 类
class Pokemon{
private String name;
private int level;
private final int skillsQuantity = 4;
}
//创建一个 Pokemon 的派生类 Gardevoir类
class Gardevoir extends Pokemon{
}
继承的特点
1、Java 只允许单继承,不允许多继承(一个派生类拥有多个父类)
2、Java 允许多级继承(B继承A,C继承B)
继承的优势
1、提高代码的复用性
2、提高了代码的维护性
3、让类与类之间产生了关系,是多态的前提,为后面讲解多态做准备
继承的劣势
继承让类与类产生了关系,提高的代码的耦合性
耦合:类与类之间的关系
内聚:就是类自己完成某件事的能力
开发的原则:
低耦合,高内聚
2、注意事项
1、派生类只能继承父类内的非私有成员(变量和方法)
2、构造方法不能被继承,但是可以利用关键字 super 来调用父类的构建方法
3、尽量不要为了小部分功能而使用继承
3、继承与成员变量的关系
当父类和派生类拥有同样变量名的变量时,在调用时依然遵循就近原则
派生类中访问变量的顺序:
1、首先在派生类的局部变量中找,有就返回
2、接着在派生类的成员变量中找,有就返回
3、最后在父类的成员变量中找,有就返回
4、若以上区域内都没有,则会因访问不到变量而报错
(不会找到父类的局部变量区,因为没有调用方法,所以此时不存在这些变量)
4、继承与成员方法的关系
派生类中调用方法的顺序:
1、首先在派生类中查找,有就调用
2、接着在父类中查找,有就调用
3、若以上区域都没有,则会因为调用不到方法而报错
二、super 关键字
派生类只能访问自己重新定义的,同父类同名的变量,那该如何访问父类中原本的同名变量呢。Java 提供了一个关键字来处理这种情况 —— this
1、基本定义
作用:代替父类对象,访问父类数据
使用方法
super . 父类成员
2、和 this 区别
this 代表的是当前对象的引用
super 代表是父类存储空间的引用
3、使用方法
3.1 调用成员
this . 成员变量/方法
调用的是本类中的成员变量/方法
super . 成员变量/方法
调用的是父类的成员变量/方法
3.2 访问构造方法
this(…)
调用本类中构造方法
super(…)
调用父类中构造方法
三、继承中的构造方法
在继承中,派生类并不会继承父类的构造方法
(个人认为是因为派生类和父类是不同的两个类,不需要额外继承构造方法)
但是在创建子类对象之前,会先访问一遍父类的构造方法,用来完成初始化
class Pokemon{
Pokemon(){
System.out.println("Pokemon无参构造方法");
}
}
class PokemonPearl extends Pokemon{
PokemonPearl(){
System.out.println("PokemonPearl无参构造方法");
}
}
public class StractureTest {
public static void main(String[] args) {
PokemonPearl gardevoir = new PokemonPearl();
}
}
1、父类未提供无参构造方法
上面说派生类在创建对象时会先访问父类的无参构造方法,那么若父类没有定义无参构造方法,就会造成无法使用派生类的结果。那么该如何解决
1、在父类中加一个无参构造
2、使用super关键字去调用父类中带参数的构造方法 super("…")
3、子类通过调用本类其他的构造方法
使用this关键字间接的调用super去访问父类的构造方法
要想初始化子类,就必须先初始化父类(也就是调用父类的构造方法)
//方法二
class Pokemon{
String name;
Pokemon(String name){
System.out.println("Pokemon无参构造方法");
}
}
class PokemonPearl extends Pokemon{
PokemonPearl(){
super("不填或随意填"); //调用父类含参构造方法
System.out.println("PokemonPearl无参构造方法");
}
}
//方法三
class Pokemon{
String name;
Pokemon(String name){
System.out.println("Pokemon无参构造方法");
}
}
class PokemonPearl extends Pokemon{
PokemonPearl(){
this("不填或随意填"); //调用自己的含参构造方法
System.out.println("PokemonPearl无参构造方法");
}
PokemonPearl(String name){
super("不填或随意填") //再调用父类含参构造方法
System.out.println("PokemonPearl含参构造方法");
}
}
方法三的输出结果
2、注意事项
super(…)和this(…)必须出现在构造方法的第一条语句上
在Java中不允许对一个进行多次初始化,每个类只能初始化一次
四、重写
方法的重写:
子类中出现了和父类中一模一样的方法声明,叫做重写,也叫覆盖、复写
方法的重载:
本类中出现了方法名一样,参数列表不一样的方法,与返回值无关的方法,叫重载
注意事项
1、方法的重写,出现在继承中或者后面的实现
2、父类的方法被private修饰不能被重写,甚至算不上重写
(子类重写父类方法时,访问权限要么和父类方法的访问权限一样,要么比它大)
3、父类若是静态方法,子类也必须通过静态方法进行重写
(本质不是重写,但是操作类似)
五、练习
//看程序写结果
class Fu{
public int num = 10;
public Fu(){
System.out.print("fu");
}
}
class Zi extends Fu{
public int num = 20;
public Zi(){
System.out.print("zi");
}
public void show(){
int num = 30;
System.out.print(num);
System.out.print(this.num);
System.out.print(super.num);
}
}
class ExtendsTest {
public static void main(String[] args) {
Zi z = new Zi();
z.show();
}
}
fu zi 30 20 10
//看程序写结果
class Fu1 {
static {
System.out.println("静态代码块Fu"); // 1
}
{
System.out.println("构造代码块Fu");// 2
}
public Fu1() {
System.out.println("构造方法Fu");// 3
}
}
class Zi1 extends Fu1 {
static {
System.out.println("静态代码块Zi");// 4
}
{
System.out.println("构造代码块Zi");// 5
}
public Zi1() {
System.out.println("构造方法Zi");// 6
}
}
public class ExtendsTest2 {
public static void main(String[] args) {
Zi1 z = new Zi1();
}
}
1 4 2 3 5 6
1、静态的内容是随着类的加载而加载的,静态代码块里面的内容会优先执行
2、静态代码块 > 构造代码块 > 构造方法
3、子类初始化之前会先对父类进行初始化
//看程序写结果
class X {
Y b = new Y();
X() {
System.out.print("X");
}
}
class Y {
Y() {
System.out.print("Y");
}
}
public class Z extends X {
Y y = new Y();
Z() {
// super();
System.out.print("Z");
}
public static void main(String[] args) {
new Z();
}
}
Y X Y Z
每次初始化父类时,只需要初始化一次
总结
派生类只能继承父类内的非私有成员(变量和方法)
构造方法不能被继承,但是可以利用关键字 super 来调用父类的构建方法
this 代表的是当前对象的引用
super 代表是父类存储空间的引用
重写(Override) 派生类中出现和父类同方法名,参数列表的方法
重载(Overload) 同一类中,出现同方法名,不同参数列表的方法
父类可以被强制转换成子类,但是不能使用子类特有的方法和属性