9 面向对象之继承

继承思想

  1. 描述事物(类)的时候,如果有几个重复的属性或方法,我们就可以采用继承的方式来设计

    在这里插入图片描述

    未使用继承的写法

    class Rabbit{
    	double weight;
    	int age;
    	
    	//奔跑
    	public void run() {
    		System.out.println("兔子在奔跑");
    	}
    	//吃蔬菜
    	public void eatVegetables() {
    		System.out.println("吃蔬菜");
    	}
    }
    
    class Tiger{
    	double weight;
    	int age;
    	
    	//奔跑
    	public void run() {
    		System.out.println("老虎在奔跑");
    	}
    	//吃肉
    	public void eatMeat() {
    		System.out.println("吃肉");
    	}
    }
    
  2. 继承语法,使用extends关键字来继承

    class 子类 extends 父类{
      子类属性...
      子类方法...
    }
    
  3. 继承的特点:

    • 子类会把父类所有的能够继承的属性和方法继承下来

      private int type;//private修饰的属性和行为不能被继承
      
    • 子类除了能够继承父类的属性和行为外,自己也能够扩展自己的属性和行为

    • 父类更通用,子类更具体

    • 子类只能获得父类中的非private属性,如果想要使用就必须提供public的get和set方法,私有方法无法继承

    • java中Class类只能做单继承,class上面只能出现一个extends关键字

    • java支持多级继承,即美洲虎继承老虎,老虎继承动物

    • Java中所有的类,都是继承自Object类,Object类是所有类的父类,这种继承是隐式,JDK已经帮我们完成了这步继承操作

super关键字

在JAVA中,super关键字表示对当前类的父类的引用,super关键字有2种用途,一是访问被子类成员隐藏的父类成员,二是用来调用父类的构造方法,总之super是用来访问父类对象的。

  1. 调用父类成员

    如果子类和父类有同名的成员变量或方法,则父类的成员将会被覆盖,此时我们需要在子类中采用如下方式访问父类的成员变量和成员方法:

    super.<成员变量名>  // 如super.age
    super.<成员方法名>  // 如super.run();
    

    注意:在Java语言中,在继承体系中,成员的访问遵循如下规则

    • 在子类中访问成员变量和方法时将优先查找是否在本类中已经定义,如果该成员在本类中存在,则使用本类的,否则,按照继承层次的顺序往父类查找,如果未找到,继续逐层向上到其祖先类查找。
    • super特指访问父类的成员,使用super首先到直接父类查找匹配成员,如果未找到,再逐层向上到祖先类查找。
  2. 调用父类构造方法

    子类可以通过super关键字调用父类中定义的构造方法,格式如下:

    super();//不带参数调用
    super(....);//带多个参数调用
    

    注意:子类所调用的构造方法中的参数列表必须和父类的某个构造方法的参数列表完全匹配,才能实现调用

    package com.hliedu.supers;
    
    public class Demo2 {
    
    	public static void main(String[] args) {
    		Dog dog = new  Dog();
    		dog.introduce();
    	}
    }
    /*
     * - super关键字,用于引用父类对象(属性,方法)
     * - 继承体系中:属性、方法查找规则
     * 		子类在访问某个属性的时候,首先会在本类中查找,如果本类中存在那么就直接使用本类中的属性或方法,否则会去父类中查找
     * 		如果父类中未找到该属性,那么会一级一级去祖先类查找,如果祖先类都没有,那么会直接报错,所有类的根级父类为Object
     * - super调用构造方法
     * 		构造方法:如果没有给当前类声明构造方法,那么系统会默认给一个无参的构造方法,这个是隐式操作,如果自己定义了带参或不带参的构造方法,那么系统将不会自动给出默认的构造方法
     * 	
     */
    class All{
    	public void equals(String s) {
    		
    	}
    }
    
    class Animal extends All{
    	public Animal(int age) {
    		
    	}
    	
    	public Animal() {
    		
    	}
    	
    	int age = 10;
    	
    	public void eat() {
    		System.out.println("吃东西");
    	}
    }
    
    class Dog extends Animal{
    	
    	//子类中若存在与父类相同的属性和方法,那么父类中的这个属性和方法就会被隐藏,这个隐藏的属性或方法就必须采用super关键字访问
    	
    	public void introduce() {
    		
    		System.out.println("大家好,我今年" + age + "岁");
    		super.eat();
    		
    		//一级一级往父类查找,如果都没有,最终会进入到Object类
    		equals("123");
    	}
    	
    	public void eat() {
    		System.out.println("狗吃屎");
    	}
    }
    
    class Tiger extends Animal {
    	
    	int age = 6;
    	
    	public Tiger() {
    		//this(...)可以调用本类构造方法
    		//super(...)可以调用父类构造方法
    		this("" , 1);
    	}
    	
    	public Tiger(String name) {
    		super(1);
    	}
    
    	public Tiger(String name , int age) {
    		
    	}
    }
    
  3. 子类与其直接父类之间的构造方法存在约束关系,有以下几条重要原则(重要

    • 按继承关系,构造方法是从顶向下进行调用的。
    • 如果子类没有构造方法,则它默认调用父类无参的构造方法,如果父类中没有无参数的构造方法,则将产生错误。
    • 如果子类有构造方法,那么创建子类的对象时,先执行父类的构造方法,再执行子类的构造方法。
    • 如果子类有构造方法,但子类的构造方法中没有super关键字,则系统默认执行该构造方法时会产生super()代码,即该构造方法会调用父类无参数的构造方法。
    • 对于父类中包含有参数的构造方法,子类可以通过在自己的构造方法中使用super(…);来调用,而且super(…);必须放在子类构造方法中的第一行。
    • Java语言中规定当一个类中含有一个或多个有参构造方法,系统不提供默认的构造方法(即不含参数的构造方法),所以当父类中定义了多个有参数构造方法时,应考虑写一个无参数的构造方法,以防子类省略super关键字时出现错误。

继承结构下对象实例化顺序

顺序如下:

  • 父类静态块 or 父类静态成员变量
  • 子类静态块 or 子类静态成员变量
  • 父类普通块 or 父类的普通成员字段
  • 父类构造方法
  • 子类普通块 or 子类的普通成员字段
  • 子类构造方法

代码测试:

package com.hliedu.supers;
/**
 * 
 * 继承结构下对象实例化顺序
 * 
 * 带你轻松学Java:恒骊学堂
 * www.hliedu.com
 * QQ:3020685261
 *
 */
public class Demo4 {

	public static void main(String[] args) {
		JavaseBook javaseBook = new JavaseBook();
	}
}

/*
 * 执行顺序
 * - 父类中的静态块 || 父类中的静态属性
 * - 子类中的静态块 || 子类中的静态属性
 * - 父类中的语句块 || 父类中的对象成员属性
 * - 调用父类的构造方法
 * - 子类中的语句块 || 子类中的对象成员属性
 * - 调用子类的构造方法
 */
class Book{

	public static int age = 10;
	
	static {
		System.out.println("Book中静态块" + age);
	}

	public String name = "Book";
	
	public Book(){
		System.out.println("Book中构造方法");
	}
    
	{
		System.out.println("Book中语句块" + name);
	}
	
}

class JavaseBook extends Book{
	public static int age = 10;
	
	static {
		System.out.println("JavaseBook中静态块" + age);
	}

	public String name = "JavaseBook";
    
	{
		System.out.println("JavaseBook中语句块" + name);
	}
	
	JavaseBook(){
		System.out.println("JavaseBook中构造方法");
	}
}


方法重载(overload)

在一个类中存在2个或2个以上的具有相同方法名,但是参数列表不同的方法,称作为方法重载。

重载要求:

  • 方法名相同
  • 形参列表不同(参数个数或类型)
  • 与返回值无关
  • 与访问修饰符无关
  1. 普通方法重载

    package com.hliedu.test;
    
    public class TestOverLoad {
    	//方法1
        void max(int a, int b) {
            System.out.println(a > b ? a : b);
        }
    	//方法2
        void max(float a, float b) {
            System.out.println(a > b ? a : b);
        }
        //方法3
        int max(int a, int b) { 
            return a > b ? a : b; 
        }
    }
    

    上述代码中:方法1与方法2构造重载,方法3不构成重载

  2. 构造方法重载

    与普通方法一样,构造方法也可以重载

    public class Person {
    	public Person() {
    		System.out.println("无参构造方法");
        }
    	public Person(int i) {
    		System.out.println("有参构造方法");
        }    
    }
    
  3. 目的

    提高代码可读性,减少方法词的命名

方法重写(override)

在子类继承父类中,子类的方法和父类的方法相同(访问修饰符,返回值类型,方法名,参数列表),方法体不同,这种子类的方法将父类的方法覆盖叫做重写,重写仅发生在具有继承关系的类中。

目的:父类的功能无法满足子类的需求,子类自定义扩展父类的功能。

方法重写约束规则:

  • 方法重写时,子类方法名与形参列表必须与父类保持一致。
  • 方法重写时,子类的权限修饰符必须要大于或者等于父类的权限修饰符。
  • 方法重写时,子类的返回值类型必须要小于或者 等于父类的返回值类型。
  • 方法重写时, 子类抛出的异常类型要小于或者等于父类抛出的异常类型。
/*
 * 父类代码
 */
public class Parent {
    
    //要被重写的方法
    public void work(){
        //省略代码块
    }
    //要被重写的方法
    public void run(String p){
        //省略代码块
    }
    public void sleep(String p){
        //省略代码块
    }
    //此方法无法被重写,因为在子类中访问不到
    private void eat(){
        //省略代码块
    }
}

/*
 * 子类代码
 */
public class Child extends Parent {
    
    //重写了父类方法
    public void work(){
        //省略代码块
    }
    //重写了父类方法
    public void run(String people){
        //省略代码块
    }
    //此处无法构成方法重写,因为与父类中的sleep方法的参数列表不同
    public void sleep(){
        
    }
    //这里会提示编译错误,因为与父类的方法返回类型不同
    public String sleep(String p){
        return "nihao";
    }
}

重写与重载的区别

  • 重写(override)

    必须是父子类关系,且父子类有相同的方法,唯一不同的就是方法体,一般是父类的该方法满足不了子类的需求所以才发生重写

  • 重载(overload)

    在同一个类中,有着相同的方法名,但是参数类型或参数个数不相同,这两个方法就构成重载,重载的目的,节省类中方法的命名资源,提高代码可读性

final关键字

final是指最终的不可更改的东西,final在Java中的应用有3个

  1. 修饰变量

    被final修饰的变量必须要初始化,初始化赋值后不能再重新赋值,即变量不能发生改变。

    注意:这里的不可改变是指基本类型的值不可改变,引用类型变量的引用不可改变,即不能再指向其他对象。

    基本类型:

    public class Test01{
        final int x1= 10000;
        final int x2;
        final int x3;
        {
           x2 = 20000;
        }
        public Test01(){
            this.x3 = 3000;
        }
    }
    

    引用类型:

    final Operate operate = new Operate() ;// operate有一个普通变量i初始化为10
    operate.i = 11;
    operate.i = 12;
    System.out.println(operate.i); //输出12
    //引用类型包含:类、接口、数组,所以它们的引用都不能被改变,但引用中对应的值可发生变化
    
  2. 修饰方法,表示方法不能被覆写

  3. 修饰类,表示类无法被继承

    final修饰的类,类中的所有成员方法都被隐式地指定为final方法

    final修饰类的示例:Math,String

package与访问权限

package(包)

就是文件夹,主要用于分类管理,可以区分同名不同包的类

在这里插入图片描述

访问修饰符

4种访问修饰符可以修饰任意的:属性,方法。

外部类的修饰权限一般定义为public或default(内部类存在特殊情况),方法大多是public(设计模式,或本类封装方法可能会采用private和protected修饰),属性绝大多数都为private

访问级别访问控制修饰符同类同包子类不同的包
公开public
受保护protected
默认没有访问控制修饰符
私有private

继承内存结构分析

Java不支持多重继承,也就是说子类最多只能继承一个父类

子类继承了其父类中不是私有的成员变量和成员方法,作为自己的成员变量和方法

子类中定义的成员变量和父类中定义的成员变量相同时,则父类中的成员变量不能被继承

子类中定义的成员方法,并且这个成员方法的名字,返回类型,及参数个数和类型与父类的某个成员方法完全相同,则父类的成员方法不能被继承

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

君哥聊编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值