Java基础|继承

面向对象的三大特征:封装性、继承性、多态性。
继承是多态的前提,如果没有继承,就没有多态。

继承是什么

  • 这里说一个师傅和徒弟的例子。师傅把自己所有会的技艺都教给了徒弟,那从徒弟的角度来看,我们就可以说徒弟继承了师傅的技艺。在面向对象中,师傅叫做父类基类超类,徒弟就叫做子类派生类。这里父类和子类,并不能对应于现实生活中的儿子继承家产的例子。儿子继承家产,儿子拿了钱,父亲就没钱了。在面向对象中,继承强调的是一种行为和属性的复制。如果硬要举父子的例子,你可以说父亲不秃顶,儿子也不秃顶,继承了他优秀的基因。父亲能运动吃饭,儿子也能运动吃饭,这个例子就比较合适。

继承解决了什么问题

  • 还是说上面那个师傅和徒弟的例子。一方面,师傅一个人学习了技艺,他就可以把这种能力传授给很多徒弟。表现在面向对象中,就是父类完成了共性抽取,子类通过继承父类就可以获得这种共性。另一方面,我们知道徒弟不仅仅获得了师傅的技艺,他还有可能拥有自己独特的技艺。注意,这里独特的技艺并不是直接通过其它师傅得到的,java 中类与类之间只允许单继承,不允许有多个直接父类,也就是说,徒弟不能直接拜多个师傅。但是可以间接有多个师傅,比如徒弟->师傅b->师傅a,徒弟继承师傅a,师傅a继承师傅b。再回到徒弟独特的技艺上说,子类通过继承父类,不仅仅拥有了父类的成员变量和成员方法,还可以拥有自己独特的成员变量和方法,这样就完成了对父类的扩展。

继承的格式

  • public class 子类名 extends 父类名称 {//类内容}
public class Fu {
    int numFu;//父类成员变量
    public void methodFu() {
        System.out.println("这里是父类成员方法");
    }
}

public class Zi extends Fu {
}

public class Demo {
        public static void main(String[] args) {
            Zi zi = new Zi();
            zi.numFu = 1;
            zi.methodFu();
        }
}
  • 在上面的 demo 中我们可以看到,子类继承父类后就可以访问 父类的成员变量和成员方法。

继承中成员方法的访问特点

public class Fu {
    public void method() {//父子都有
        System.out.println("这里是父类method方法");
    }
    public void methodFu() {//父类独有,被继承后子类也会拥有
    	System.out.println("这里是父类methodFu方法");
    }
}

public class Zi extends Fu {
    public void method() {//父子都有
        System.out.println("这里是子类method方法");
    }
     public void methodZi() {//子类独有
     System.out.println("这里是子类methodZi方法");
    }
}
  • 上面的代码展示了成员方法存在的三种情况。
    访问规则: 创建的对象是谁,就优先用谁中的方法。没有则向上找。
  1. 父类和子类有重名的方法。(这里涉及到一个重写(Override)的概念,下面会讲)
		Fu fu = new Fu();
		fu.method();//输出:"这里是父类method方法"
		
		Zi zi = new Zi();
		zi.method();//输出:"这里是子类method方法"。优先用Zi类中的method方法
  1. 父类拥有独有的方法,此时子类继承后也会拥有。
		Fu fu = new Fu();
		fu.methodFu();//输出:"这里是父类methodFu方法"。父亲调用自己的成员方法,当然可以。
		
		Zi zi = new Zi();
		zi.methodFu();//输出:"这里是父类methodFu方法" 。Zi类中没有methodFu方法,则向上找。
  1. 子类拥有独有的方法。
		Fu fu = new Fu();
		fu.methodZi();//错误,父类不会向下找子类中的方法
		
		Zi zi = new Zi();
		zi.methodZi();//输出:"这里是子类methodZi方法"

继承中成员变量的访问特点

  1. 直接通过 对象名.成员变量名 访问。
    规则: new的时候等号左边是谁,就优先去谁中找相应的成员变量。没有则向父类找。
public class Fu {
    int num;
    int numFu;
}

public class Zi extends Fu {
    int num;
}

public class Demo {
        public static void main(String[] args) {
            Fu fu = new Fu();
			fu.num;//访问父类的num
			
			Zi zi = new Zi();
			zi.num;//访问子类的num
			zi.numFu;//先去Zi类中找numFu,没有则向上找父类中的numFu。
        }
}		

 2. 通过成员方法访问成员变量
  规则: 方法属于谁,就优先去谁中找相应的成员变量。没有则向父类找。

public class Fu {
    int num = 10;
    public int methodFu(){
    	return num;
    }
}

public class Zi extends Fu {
    int num = 20;
    public int methodZi(){
    	return num;
    } 
}

public class Demo {
    public static void main(String[] args) {
        Zi fu = new Zi();
        zi.methodZi();//返回20,methodZi属于Zi类,所以会输出Zi中的num
        zi.methodFu();//返回10,methodFu属于Fu类,所以会输出Fu中的num
    }
}

方法重写

  • 如果子类对父类继承过来的方法不满意,想要增加逻辑或者自己编写逻辑,就需要重写。
public class Fu {
    public void method() {
        System.out.println("这是父类method方法");
    }
}

public class Zi extends Fu {
    @Override//这个注解可以帮助我们检查父类是否有这个方法,同时表明这是一个覆写的方法。
    public void method() {
        System.out.println("这是子类method方法");
    }
}

public class Demo {
    public static void main(String[] args) {
        Zi zi = new Zi();
		zi.method();//输出:"这是子类method方法"
    }
}
  • 重写和重载的区别:
    • 重写(Override):方法的名称一样,参数列表也一样。
    • 重载(Overload):方法的名称一样,参数列表不一样。
  • 方法重写的注意点:
  1. 子类必须保证重写的方法的名称和参数列表与父类的相同。
  2. 子类重写的方法的返回值必须小于等于父类方法的返回值范围。
public class Fu {
    public Object method() {
        return null;
    }
}

public class Zi extends Fu {
    @Override
    public String method() {//这样写是可以的,如果父类是返回String,这里返回Object就不行了。
        return null;
    }   
}
  1. 子类重写的方法的权限必须大于等于父类方法的权限修饰符。不过当父类方法的权限修饰符为 private 时,无法被重写。
    权限修饰符 :public > protected > default(代表什么都不写) > private
public class Fu {
    String method() {
        return null;
    }
}

public class Zi extends Fu {
    @Override
    public String method() {//这里权限修饰符不写,写protected,public都行
        return null;
    }
}

this 和 super 关键字

  • this 代表本类对象,super 代表父类对象。
  1. 从成员变量的使用上来说:
public class Fu {
    int num;
}

public class Zi extends Fu {
    int num;
    private void method(int num) {
        System.out.println(num);//输出method方法上的num参数
        System.out.println(this.num);//输出Zi类的成员变量num
        System.out.println(super.num);//输出Fu类的成员变量num
    }
}
  1. 从成员方法的使用来说:
public class Fu {
    public void method() {}
}

public class Zi extends Fu {
    public void method() {
        super.method();//调用父类的method方法
        this.methodZi();//调用本类的methodZi方法
    }
    public void methodZi() {}
}
  1. 从构造方法上来说:
public class Fu {
	//父类无参构造
    public Fu() { }
}

public class Zi extends Fu {
    public Zi() {
        super();//调用父类的无参构造
    }

    public Zi(int num) {
        this(num,null);//调用下面的构造方法
    }

    public Zi(int num,Object obj) {
	}
}
  • this 和 super 关键字内存图解
    在这里插入图片描述
    讲到super关键字,在这就可以补充一个小知识点:为什么 java 类与类只允许单继承?假设一个类继承了两个父类,父类a有一个成员变量obj,父类b也有一个成员变量obj。那么在子类中调用super.obj的时候就会出问题,我调用的到底是父类a的obj还是父类b的obj那?很明显,我们不清楚。所以java不允许多继承。

继承中构造函数的访问特点

明确一点:new 一个子类对象,会先去调用父类的构造方法,再调用子类的构造方法。没有子类,哪来父类。所以必须先有父类。所以在子类的构造方法中,一定要调用父类构造方法。

  1. 情形一:父类没写构造函数(没写会默认赠送一个无参构造)或者只写了一个无参构造。那么在子类的构造方法必须调用父类的无参构造。如果子类也没写构造函数:没写会默认赠送一个无参构造,并且在其中调用父类的无参构造。如果子类显式写了无参构造,那就必须调用父类的无参构造。如果子类写了有参构造,那就必须调用父类的无参构造。
public class Fu {
    public Fu() {}//不写会赠送一个无参构造,如果写了有参构造就不会赠送了
}

public class Zi extends Fu {
    public Zi() {//不写会赠送一个无参构造,如果写了有参构造就不会赠送了
        super();//这行代码不写也行,会默认赠送一个
    }
	//这两个构造函数都行,明确我们的目的:构造出一个父类对象
    public Zi(Object obj) {
        super();//这行代码不写也行,会默认赠送一个
    }
}

  1. 情形二:父类只有有参构造。此时不会给父类赠送一个无参构造了。所以在我们的子类构造函数中,必须调用父类的有参构造。
public class Fu {
    public Fu(Object obj) {}
}

public class Zi extends Fu {
    public Zi() {
        super(null);
    }
	//这两种构造函数都行
    public Zi(Object obj) {
        super(obj);
    }
}
  1. 情形三:父类显式写出无参构造,同时还有有参构造。子类此时没写构造函数,没写会默认赠送一个无参构造,并且在其中调用父类的无参构造。子类此时写了构造函数,可以在构造函数中调用父类的无参构造,也可以调用父类的有参构造。
public class Fu {
    public Fu() {}
    public Fu(Object obj) {}
}

public class Zi extends Fu {}//不会报错

public class Zi extends Fu {
    public Zi() {}//会赠送一个super()
}

public class Zi extends Fu {
    public Zi() {
        super(null);
    }
	//这两个构造函数都行
    public Zi(Object obj) {
        super(obj);
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章