继 承的本质:复用已经存在的方法和域
一.概念:
继承是java面向对象编程的基石,继承就是子类继承父类的特征和行为,是的子类对象(实例)具有父类的实例和方法,或子类从父类继承方法,使得子类具有父类相同的行为。
类似于咱们现实生活中的这种情况:父亲家里有坐姿板凳等物品,儿子可以继承父亲的东西,所以儿子也可以使用自己父亲的东西。
public class Person {
public void run() {
System.out.println("任何人都可以跑。。。");
}
}
//学生继承人,所以学生可以调用人里面的方法
class Student extends Person{
private void eat() {
System.out.println("学生正在吃。。。。");
}
public static void main(String[] args) {
Student student = new Student();
student.run();//使用人里面的方法
student.eat();
}
}
由以上代码我们可以看出:关键字extends表明正在构造的新类Student派生于一个已经存在的类Person,已存在的类称为超类或者父类;子类称为子类。我们还可以看出子类可以调用父类的方法,而且子类可以拥有自己的方法,功能比超类更丰富
(子类可以调用父类的方法,但是父类不能调用子类的方法)(父亲会把自己的东西都给予孩子,而孩子不会把自己东西全部给予父亲)
二、明确方法重载和方法重写
1.方法重载(有着相同的名字,不同的参数)
2.方法重写---覆盖(儿子也有自己的凳子,所以不用父亲的,用在家的)
方法的覆盖 ,子类和父类当中都有相同的方法,子类通过重写父类方法实现方法的覆盖
在有些时候,子类当中需要调用父类当中的方法,但是父类当中的方法对子类来说并不一定适用。这个时候我们就需要使用到方法的覆盖。例如
public class Person {
//父亲的run方法
public void run() {
System.out.println("任何人都可以跑。。。");
}
}
class Student extends Person{
//儿子的run方法
public void run() {
System.out.println("这个学生在跑。。。");
}
private void eat() {
System.out.println("学生正在吃。。。。");
}
public static void main(String[] args) {
Student student = new Student();
student.run();//这里的方法用的是孩子的,被重写了
student.eat();
}
}
//我们可以看到子类和父类当中都有run方法,
//而我们实际调用的却是子类自己的run方法,这就是方法的覆盖,
//子类通过重写和父类一样的方法实现方法的覆盖。
//方法的覆盖
//子类和父类当中都有相同的方法,
//子类通过重写父类方法实现方法的覆盖
public void getRun() {
System.out.println("所有小猫都能跑。。。。");
}
//一个方法的签名是由方法名称以及参数列表所组成
//方法的重载是指在一个类当中有相同的方法名不同的参数列表
public void getRun(int s) {
System.out.println("所有小猫都能跑。。。。"+s);
}
public static void main(String[] args) {
Cat cat = new Cat();
cat.getRun();
}
重载和重写(覆盖)的区别
- 重载: 发生在同一个类中,方法名必须相同,参数类型不同、个数不同、顺序不同,方法返回值和访问修饰符可以不同,发生在编译时。
- 重写: 发生在父子类中,方法名、参数列表必须相同,返回值范围小于等于父类,抛出的异常范围小于等于父类,访问修饰符范围大于等于父类;如果父类方法访问修饰符为 private 则子类就不能重写该方法。
三、super关键字
super 关键字有两个用途:一是调用超类的方法,二是调用超类的构造器。调用构造器语句的时候只能作为另一个构造器的第一条语句出现。
构造器必须与类同名,参数不一样
public class People {
//通过new People()调用
public People(){
}
//通过new People("字符串") 调用
public People(String str){
}
}
四、继承层次
java只支持单继承
继承并不限于一个层次。
public class Person {
public void run() {
System.out.println("任何人都可以跑。。。");
}
}
class Student extends Person{
public void run() {
super.run();
System.out.println("这个学生在跑。。。");
}
private void eat() {
System.out.println("学生正在吃。。。。");
}
public static void main(String[] args) {
Student student = new Student();
student.run();
student.eat();
}
}
class XiaoMing extends Student{
}
class XiaoHong extends Student{
}
由一个公共超类派生出来的所有类的集合被称为继承层次,在继承层次当中,从某个特定的类到其祖先的路径被称为继承链,但是一定要注意java是单继承的。
五、类的加载顺序(先加载父类子类先调父类,有static先执行)
以下代码的输出结果是什么?
package com.qcby;
public class Test extends Base{
static{
System.out.println("test static");
}
public Test(){
System.out.println("test constructor");
}
public static void main(String[] args) {
new Test();
}
}
package com.qcby;
public class Base{
static{
System.out.println("base static");
}
public Base(){
System.out.println("base constructor");
}
}
先来想一下这段代码具体的执行过程,在执行开始,先要寻找到main方法,因为main方法是程序的入口,但是在执行main方法之前,必须先加载Test类,而在加载Test类的时候发现Test类继承自Base类,因此会转去先加载Base类,在加载Base类的时候,发现有static块,便执行了static块。在Base类加载完成之后,便继续加载Test类,然后发现Test类中也有static块,便执行static块。在加载完所需的类之后,便开始执行main方法。在main方法中执行new Test()的时候会先调用父类的构造器,然后再调用自身的构造器。因此,便出现了上面的输出结果。
public class Demo {
public Demo(String aa){
System.out.println("===="+aa);
}
static {
System.out.println("11");
}
public static Demo demo = new Demo("+++");
static {
System.out.println("22");
}
}
class Test{
public static void main(String[] args) {
Demo demo = new Demo("----");
}
}
执行结果
- 11
- ====+++
- 22
- ====---
所有的静态的类的初始化都交给类的第一个对象去执行