java学习基础课之面向对象继承(渡一教育)(五)

类和类之间的关系

  • A is-a B:泛化(继承 实现)
  • A has-a B: 包含(组合 聚合 关联)
  • A use-a B: 依赖(依赖) (need-a)

一、继承 is-a

1.子类继承父类,通过一个关键字 extends
2.子类的对象可以调用父类中的(public protected)属性和方法 当做自己的来使用
3.子类可以添加自己独有的属性和方法的
4.子类从父类中继承过来的方法不能满足子类需要,可以在子类中重写(覆盖)父类的方法 更多指的是内容
5.每一个类都有继承类,如果不写extends关键字,默认继承Object,如果写了extends则继承后面那个父类
可以理解为Object类非常重要 是任何一个引用类型的父类(直接或间接的继承Object) Object类没有父类
6.Java中继承是单个存在的(单继承) 每一个类只能有一个继承类 (在extends关键字后面只能写一个类)
可以通过传递的方式实现多继承的效果 后续还会有多实现
7.继承在内存中的存储形式
8.关于this和super的使用

  1. 子类继承父类,通过一个关键字extends
//父类
public class Animal {
}

//子类,继承了父类Animal
public class Person extends Animal{
}
  1. 子类的对象可以调用父类中的(public protected)属性和方法 ,当做自己的来使用。
//基类:Animal  有两个方法一个属性
public class Animal {
	public String name;
    public void eat(){
        System.out.println("动物的吃饭方法");
    }
    public void sleep(){
        System.out.println("动物的睡觉方法");
    }
}

基类:Animal 有两个方法一个属性

//子类:Person中什么方法也没有定义,但是继承了父类Animal,所以可以使用父类Animal中的方法。
public class Person extends Animal {
}

子类:Person中什么方法也没有定义,但是继承了父类Animal,所以可以使用父类Animal中的方法。

//主方法,中创建Person类的对象,发现Person类中虽然没有方法和属性,但是Person类的对象可以使用Animal类中函数,因为Person继承了Animal,所以可以使用Animal中的方法和属性。
public class Test {
    public static void main(String[] args){
        Person p = new Person();
        p.name="郑中拓";
        p.eat();
        p.sleep();
    }
}

主方法,中创建Person类的对象,发现Person类中虽然没有方法和属性,但是Person类的对象可以使用Animal类中函数,因为Person继承了Animal,所以可以使用Animal中的方法和属性。

  1. 子类可以添加自己独有的属性和方法的。

父类Animal中不变

//子类
public class Person extends Animal {
    //添加一些独有的属性和方法
    public void study(){
        System.out.println("good good study,day day up");
    }
}

子类Person:添加一些独有的属性和方法

//主方法
public class Test {
    public static void main(String[] args){
        Person p = new Person();
        p.name="郑中拓";
        p.eat();
        p.sleep();
        p.study();//子类Person 独有的方法
    }
}

子类Person的对象可以使用,独有的方法

  1. 子类从父类中继承过来的方法不能满足子类需要,可以在子类中重写(覆盖)父类的方法 ,更多指的是内容
    方法的重写:override ,注意区分方法的重载。
//Person类中重写方法,原代码中加上下面这个新的重写方法
 //方法重写
    public void eat(){
        System.out.println("人类的吃饭方法 讲究色香味俱全");
    }

结果在主方法中调用p.eat()的时候,发现重写成功。输出:人类的吃饭方法 讲究色香味俱全。

方法重载和方法重写:

方法重写override 方法重载overload
产生两个继承关系的类,子类重写父类的方法 一个类中的一组方法
权限 子类可以大于等于父类 没有要求
特征final 父类方法是final 子类不能重写 没有要求
特征static 父类方法是static 子类不存在 没有要求
特征abstract 父类方法是abstract 子类必须重写 没有要求
返回值 子类可以小于等于父类 没有要求
名字 子类与父类一致 一个类中的好多方法名必须一致
参数 子类与父类一致 每一个方法的参数必须不一致(个数 类型 顺序)
方法体 子类的方法内容与父类不一致 每一个重载的方法 执行过程不一致
异常 如果父类方法抛出运行时异常 ,子类可以不予理会。如果父类方法抛出编译时异常,子类抛出异常的个数少于等于父类,子类抛出异常的类型小于等于父类 没有要求
方法体 子类的方法内容与父类不一致 执行过程不一致
  1. 每一个类都有继承类,如果不写extends关键字,默认继承Object,如果写了extends则继承后面那个父类。
    可以理解为Object类非常重要,是任何一个引用类型的父类(直接或间接的继承Object) ,Object类没有父类。
    Object类中的方法:
    hashCode()将对象在内存中的地址经过计算得到一个int整数
    equals() 用来比较两个对象的内容
    toString() 打印输出时将对象变成String字符串
    getClass() 获取对象对应类的类映射(反射)
    wait() 线程进入挂起等待状态 存在方法重载
    notify() 线程唤醒
    notifyAll() 唤醒所有
    finalize() 权限修饰符是protected 在对象被GC回收的时候 默认调用执行的方法
    clone() 权限修饰符是protected 为了克隆对象

  2. Java中继承是单个存在的(单继承) 每一个类只能有一个继承类 (在extends关键字后面只能写一个类)。
    可以通过传递的方式实现多继承的效果,后续还会有多实现。

  3. 继承在内存中的存储形式。剥洋葱。

//基类
public class Animal {
    public Animal(){
        System.out.println("animal中无参数的构造方法");
    }
    public void eat(){
        System.out.println("动物的吃饭方法");
    }
}
//子类:Person
public class Person extends Animal {
    public Person(){
     //隐藏一行代码 super();调用了父类的构造方法,所以执行了animal中无参数的构造方法
        System.out.println("person中的无参数构造方法");
    }
     //方法重写
    public void eat(){
        System.out.println("人类的吃饭方法 讲究色香味俱全");
    }
}
//主方法
public class Test {
    public static void main(String[] args){
        Person p = new Person();
    }
    p.hashcode();
    p.eat();
}

发现输出结果是:
animal中无参数的构造方法
person中的无参数构造方法
为什么会有:animal这个的构造函数呢???因为Person的构造函数,隐藏了一行:super();调用了父类的构造方法,所以执行了animal中无参数的构造方法。

java学习基础课之面向对象继承(渡一教育)(五)_第1张图片

主方法中执行过程:
加载类模板的过程,
Person类 Animal类 Object类,
Person p = new Person();创建一个Person类型的对象,
通过new, 堆内存中申请空间,然后创建了一个变量p,将地址传给了p,p指向了堆内存的那个空间。
假设继续执行p.hashcode()方法,如何执行的呢?首先通过p找到了堆内存,然后一层一层找,发现第一层没有hashcode()方法,在进入第二层,父类Animal那一层,发现也没有hashcode()方法,那就继续下一层Object类中找hashcode()方法,发现有,那么可以调用。
p.eat()执行,以后一个输出为:人类的吃饭方法 讲究色香味俱全。因为同样类似上面那种查找方式,发现第一层就找到了eat()方法,所以直接调用方法,不用再找第二层第三层了。

  1. 关于this和super的使用
public class Animal {

    public Animal(){
        System.out.println("animal中无参数的构造方法");
    }
    public void eat(){
        System.out.println("动物的吃饭方法");
    }
    public void sleep(){
	    this.eat();
        //?动物吃饭   人类重写的吃饭
        //代替是当前调用方法时的那个对象,不一定是当前类
        System.out.println("动物的睡觉方法");
    }
}
public class Person extends Animal{

    public Person(){
        //隐藏一行代码 super();调用了父类的构造方法,所以执行了animal中无参数的构造方法
        System.out.println("person中的无参数构造方法");
    }
    //方法重写
    public void eat(){
        System.out.println("人类的吃饭方法 讲究色香味俱全");
    }

    //添加一些独有的属性 或 方法
    public void study(){
        System.out.println("good good study,day day up");
    }
}
//主方法
public class Test {
    public static void main(String[] args){
        //加载类模板的过程
        //Person类  Animal类  Object类
        //创建一个Person类型的对象
        Person p = new Person();
        p.sleep();//从Animal类中继承过来的方法,问题在于Animal中this.eat();到底是调用动物的吃饭方法,还是人类重写的吃饭方法?this指的是当前的对象,当前的对象是人类的对象,所以this指的是人类的吃饭方法。
    }
}
//所以结果为:人类的吃饭方法 讲究色香味俱全

p.sleep();从Animal类中继承过来的方法,问题在于Animal中this.eat();到底是调用动物的吃饭方法,还是人类重写的吃饭方法?this指的是当前的对象,当前的对象是人类的对象,所以this指的是人类的吃饭方法。

那么有一个问题?我就想调用父类的eat的方法呢?这时就用到了super。
this和super都是指代词,代替的是对象。

  • this:代替的是当前执行方法时的那个对象 。不一定是当前类的
  • super:代替的是当前执行方法时的对象的父类对象,空间内部的那个
  • 都能调用一般属性 和 一般方法
    (1)可以放置在类成员的任意位置(属性 方法 构造 块)
    (2)注意调用一般方法的时候可以来回互相调用(写法 编译好用) 执行可能产生问题(StackOverflowError)
    (3)可以调用构造方法(放在构造方法的第一行)
    (4)this和super在构造方法中调用另一个类的构造方法不能同时出现在第一行。

类的个数变多啦—>需要管理类—>包package(可以理解为是一个文件夹)
在我们类的第一行会出现package关键字
如果package和import同时出现
先写package后写import

包含 has-a

包含关系(组合 聚合 关联)
从亲密程度来讲不太一样

  • 组合–>人和大脑 人和心脏的关系
    整体和部分的关系,不可分割,要出现都出现 要消亡都消亡
  • 聚合–>汽车和车轮子 电脑和主板
    整体和部分的关系,创建时有可能是分开的 ,没有组合那么紧密
  • 关联–>人有汽车 人有电脑
    整体和部分的关系,可以分割,后来形成在一起

从Java程序来描述这样的关系 : 通过一个类的对象当做另一个类的属性来存储
例子:车有轮子
建立Wheel.java

package contains;

public class Wheel {
    //属性
    public String brand;//品牌
    public int size;//尺寸
    public String color;//颜色

    //构造方法
    public Wheel(){}
    public Wheel(String brand,int size,String color){
        this.brand = brand;
        this.size = size;
        this.color = color;
    }

    //方法
    public void turn(){
        System.out.println("车轮子可以旋转");
    }
}

建立Car.java,使用了包含关系:public Wheel wheel;//车里面有一个轮子--->包含关系
想要使用Wheel的属性:wheel.brand
想要使用Wheel的方法:wheel.turn()

package contains;

public class Car {

    //属性
    public String brand;//汽车品牌
    public String type;//型号
    public String color;//颜色
    public Wheel wheel;//车里面有一个轮子--->包含关系

    //构造方法
    public Car(){}
    public Car(String brand,String type,String color,Wheel wheel){
        this.brand=brand;
        this.type=type;
        this.color=color;
        this.wheel=wheel;
    }
    //方法
    public void showCar(){
        System.out.println("这是一辆"+brand+"牌"+type+"型号"+color+"的小汽车");
        System.out.println("车上搭载着"+wheel.brand+"牌的"+wheel.size+"尺寸"+wheel.color+"颜色的车轮子");
        wheel.turn();//方法一定对象调用的 
    // 车轮子的方法肯定是车轮子对象调用   可以放置在任何地方
    }
}

设置一个主函数试验一下:

package contains;

public class Test {

    public static void main(String[] args){
        Car car = new Car;
        car.showCar();//展示汽车
    }
}

正常理解,是不应该有错的,但是运行时发现错误。错误指向:System.out.println("车上搭载着"+wheel.brand+"牌的"+wheel.size+"尺寸"+wheel.color+"颜色的车轮子"); 原因在于Wheel是一个空对象,空对象属性是空也不要紧,但是wheel.brand就不行,空对象加点就不可以。所以应当按照下面的样子赋值。

package contains;

public class Test {
    public static void main(String[] args){
        Car car=new Car();
        car.brand="宝马";
        car.color="宝石蓝";
        car.type="Z4";
        car.wheel=new Wheel();
        car.wheel.brand="米其林";
        car.wheel.color="酷黑";
        car.wheel.size=400;
//      Car car = new Car("宝马","Z4","宝石蓝色",new Wheel("米其林",400,"酷黑"));
//和上面那一段等价,
//可以用这一句也是因为我们自己定义了构造方法。
        car.showCar();//展示汽车
    }
}

依赖关系 use-a(need-a)

屠夫 杀 猪 ; 农夫 养 猪
一个类屠夫
可以做一件事情 ,杀猪
需要一头猪
不是整体和部分的关系,某一件事情产生了关系
临时组合在一起,这件事情一旦做完关系即解散
Java程序体现的形式为:
一个类的方法中使用到了另一个类的对象。

  • 第一个可以在方法中传递参数;
  • 第二个可以在方法中自己创建。

设计类的关系遵循的原则:高内聚低耦合
耦合度: 紧密 继承(实现) > 包含 > 依赖

例子:模拟刚刚我们讲过的农民养猪,屠夫杀猪
先写猪的java文件:

package rely;

public class Pig {//描述猪

    //属性
    private String name;//名字
    private int weight = 20;//体重

    //构造方法
    public Pig(){}
    public Pig(String name){
        this.name=name;
    }

    //方法
    //描述一个方法  表示小猪被杀啦
    public void beKilled(){
        System.out.println(this.name+"被杀啦,好惨呀");
    }

    //描述一个方法  让猪长肉
    //    每一个月涨到前一个月的两倍
    public void growUp(int month){
        for(int i=1;i<=month;i++){
            this.weight*=2;
        }
    }

    //描述一个方法  猪告知他的体重
    public int getWeight(){
        return this.weight;
    }
    public String getName(){
        return this.name;
    }
}

屠夫

package rely;

public class Butcher {//描述屠夫

    //属性  名字 有刀

    //方法
    //描述一个屠夫杀猪的方法   需要提供条件 一头猪
    public void killPig(Pig pig){
        Pig pig=new Pig("小花");
        System.out.println("屠夫执行了杀猪方法");
        String pigName = pig.getName();
        int pigWeight = pig.getWeight();
        System.out.println(pigName+"的体重为:"+pigWeight);
        pig.beKilled();
    }
}

主方法:

package rely;

public class Test {
    public static void main(String[] args){
        Butcher butcher = new Butcher();
        //屠夫做事--->杀猪
        butcher.killPig(pig);
    }
}

屠夫只负责杀猪,农夫才养猪,所以加一个农夫。
农夫:

package rely;

public class Farmer {//农夫

    //农夫养猪--->
    //    参数--->几个月    返回值-->是一头猪
    public Pig feedPig(int month){
        Pig pig = new Pig("小花");//依赖--->在屠夫的方法中使用到了猪的对象
        pig.growUp(month);//20 --> 640
        return pig;
    }
}

所以屠夫变为了:

package rely;

public class Butcher {//描述屠夫

    //属性  名字 有刀

    //方法
    //描述一个屠夫杀猪的方法   需要提供条件 一头猪
    public void killPig(Pig pig){
        System.out.println("屠夫执行了杀猪方法");
        String pigName = pig.getName();
        int pigWeight = pig.getWeight();
        System.out.println(pigName+"的体重为:"+pigWeight);
        pig.beKilled();
    }
}

主方法:

package rely;

public class Test {
    public static void main(String[] args){
        //创建农夫对象
        Farmer farmer = new Farmer();
        //农夫做一件事情--->养猪
        Pig pig = farmer.feedPig(5);
        //创建屠夫对象
        Butcher butcher = new Butcher();
        //屠夫做事--->杀猪
        butcher.killPig(pig);
    }
}

总结:

类和类的关系

  • is-a 继承 实现
    继承通过extends关键字 (单继承)
    实现通过implements关键字 (多实现 接口)
    注意方法重写 方法重载区别 注意Object类及方法 注意内存结构

  • has-a 组合 聚合 关联
    一个类的对象放置在另一个类中作为属性

  • use-a(need-a) 依赖
    一个类的方法中使用到了另外一个类的对象
    方法内部new 方法传递参数

    类关系的设计: 高内聚 低耦合 继承(实现) > 组合 > 聚合 > 关联 > 依赖

你可能感兴趣的:(java学习)