首页 > 编程语言 >Java 继承机制的笔记 1

Java 继承机制的笔记 1

时间:2024-11-02 18:15:25浏览次数:6  
标签:Java 继承 void Dog 笔记 Animal 类型 new public

Java 继承机制的笔记_1


笔记的来源:CS 61B-2024 春季的课程
课程主要内容:数据结构与算法分析
课程运用语言:Java

这个课有6 个 Homework,10 个 Lab,9 个 Project。其中第一个 project 是一个完整的 2024 游戏的实现,很有意思。此文章对应的是课程 8-9 节的内容。 由于内容较多,还有 10-11 节的内容写在楼下一篇文章中。

此笔记对应资源:CS 61B 课本资源

上位词,下位词

在语言学中,上下位词的概念用于形容词的关系。比如红色的上位词可以是颜色。在 java 中,这种关系被用于形容类之间的继承关系。

比如说我定义了一个类Animal,它有一些共同的属性和方法,比如说叫做“吃”,“睡觉”,“跑”。然后我定义了一个类Dog,它继承了Animal的属性和方法,并添加了一些狗独有的属性和方法,比如说“抱”,“拉”,“摇”。Dog类可以认为是Animal类的子类。

这里称Dog类是Animal类的子类 subclassAnimal类是Dog类的超类 superclass

在这里我们先介绍接口继承的概念。

接口继承

public interface Animal {
    public void eat();
    public void sleep();
    public void run();
}

在上面的例子当中,可以称Animal类为接口,它本质上是一个契约,指定动物类能有什么行为。接下来我们用定义关系的关键词implements来建立一个Dog类,继承Animal接口。

public class Dog implements Animal {
    @Override
    public void eat() {
        System.out.println("Dog is eating");
    }
    ...
}

默认方法 Default Method

如果在接口中定义了一个方法,如:

public interface Animal {
    public void eat(){
        System.out.println("Animal is eating");
    };
}

那么系统会报错Interface methods cannot have body,因为接口方法不能有方法体。

为了解决这个问题,Java 8 引入了默认方法的概念。默认方法可以有方法体,可以被子类继承,也可以被实现类实现。需要加上default关键字。

public interface Animal {
    public default void eat() {
        System.out.println("Animal is eating");
    }
}

覆盖

在子类中实现所需函数时,@Override在方法签名的前面,用来表示覆盖父类中的默认方法。

其实不加这个标签,依然可以实现对父类方法的覆盖。这个标签的一大作用,便是对拼写错误的检查,如果你添加了@Override,但是对应的方法名称在父类的接口中不存在,编译器会报错。

静态类型以及动态类型

在 Java 中,每一个变量都有一个静态类型和一个动态类型。静态类型是在编译时确定的,而动态类型是在运行时确定的。

比如说:

Animal animal ;

在上面的代码中,animal的静态类型是Animal,而它的动态类型是null,因为还没有给它赋值。

Animal animal = new Dog();
animal = new Cat();

在上面的代码中,animal的静态类型是Animal,而它的动态类型是Dog。而且我们可以改变animal的动态类型,但是不能改变它的静态类型。

Extends关键词

当我们继承一个接口的时候我们使用的关键词是implements,但是当我们继承一个类而不是继承接口的时候我们使用的关键词是extends–扩展。

public class Puppy extends Dog{
    public void play(){
        System.out.println("Puppy is playing");
    }
}

扩展可以使的子类继承父类的所有成员,包括:

  1. 所有实例和静态变量
  2. 所有方法
  3. 所有嵌套类

构造函数不能被继承!

构造函数

构造函数不可继承。但是,Java 规则规定,所有构造函数都必须从调用超类的构造函数之一开始。可以使用关键字super明确调用构造函数。如果您没有明确调用构造函数,Java 将自动为您执行该操作。

下面的代码等价:

public class Puppy extends Dog{
    public Puppy(){
        super();
        puppy_a = new Dog();
    }
}
public class Puppy extends Dog{
    public Puppy(){
        puppy_a = new Dog();
    }
}

但是,如果父类构造函数有参数,则子类构造函数必须调用父类构造函数,并传入相应的参数。

下面两段代码就不一样了:

public class Puppy extends Dog{
    public Puppy(String name){
        super(name);
        puppy_a = new Dog();
    }
}
public class Puppy extends Dog{
    public Puppy(){
        super();
        puppy_a = new Dog();
    }
}

Object

所有类的祖先都是Object类,它是所有类的父类。Object类中定义了一些方法,如:equals()hashCode()toString()等。具体文档查看Object 类Object类声明了这些方法:

String toString()//返回对象的字符串表示
boolean equals(Object obj)//判断两个对象是否相等
int hashCode()//返回对象的哈希码
Class<?> getClass()//返回对象的类
protected Object clone()//创建并返回对象的浅拷贝
protected void finalize()///在垃圾回收器将对象从内存中清除之前调用
void notify()//唤醒一个正在等待对象的线程
void notifyAll()//唤醒所有正在等待对象的线程
void wait()//等待对象的通知
void wait(long timeout)//等待对象的通知,最长时间为timeout毫秒
void wait(long timeout, int nanos)//等待对象的通知,最长时间为timeout毫秒和nanos纳秒

IS-A 关系和 HAS-A 关系

这两种关系用来描述的是类和对象之间彼此的两种基本关系。

IS-A 关系

  • 一个类是另一个类的子类,或者说,它是另一个类的一种。
  • 例如,Dog类是Animal类的子类。

HAS-A 关系

  • 一个类包含另一个类的实例变量,或者说,它是一个类的组成部分。
  • 例如,Dog类包含一个name变量,表示狗的名字。

在这里extends方法只运用于 IS-A 关系。

类型检查和类型转换

Animal animal = new Dog();

在上面的代码中,animal的静态类型是Animal,而它的动态类型是Dog。我们称含有 new 的类型声明为运行时类型(动态类型),而不含有 new 的类型声明为编译时类型(静态类型)

一个重要的性质就是,animal可以使用Dog类的任何方法,因为Dog类是Animal类的子类。但是如果使用Dog类中新添加而不是Animal类中定义的方法,则会出现编译错误

如果将上面等号两边反过来,则会发生类型检查错误:

Dog dog = new Animal();

在上面的代码中,dog的静态类型是Dog,而它的动态类型是Animal。这时编译器会报错,因为Animal不是Dog的子类。


假如我们有一个方法 oldestAnimal(),用来比较两个动物的年龄,他的类型是Animal

public Animal oldestAnimal(Animal a1, Animal a2){...}

那么我们就不能这么写:

Dog dog1 = new Dog();
Dog dog2 = new Dog();
Dog oldestDog = oldestAnimal(dog1, dog2);

因为oldestAnimal()方法传出的参数类型是Animal,而 oldestDog 的静态类型是Dog,所以编译器会报错。这个时候我们可以利用类型转换

Dog oldestDog = (Dog) oldestAnimal(dog1, dog2);

高阶函数

下面展示如何用复杂的 java 实现此简洁的 python 代码 : )

def tenX(x):
   return 10*x

def do_twice(f, x):
   return f(f(x))

print(do_twice(tenX, 2))

java 实现:

public interface IntUnaryFunction {
	int apply(int x);
}

public class TenX implements IntUnaryFunction {
	public int apply(int x) {
   		return 10 * x;
	}
}

public class HoFDemo {
	public static int do_twice(IntUnaryFunction f, int x) {
   		return f.apply(f.apply(x));
	}

	public static void main(String[] args) {
   		System.out.println(do_twice(new TenX(), 2));
	}
}

可以看出来,这里运用了一个apply方法作为中间过渡,看起来是把函数当成变量,实则是改变了apply方法的内容。

标签:Java,继承,void,Dog,笔记,Animal,类型,new,public
From: https://blog.csdn.net/qq_55297504/article/details/143454388

相关文章

  • 统计学习方法笔记
    统计学习方法1.3统计学习方法的三要素1.3.1模型好,为什么要从1.3开始呢,因为看前面的课,我还没有用到这个软件。方法=模型+策略+算法模型有好多个,试试策略:按照什么样的准则去选取模型比如说看预测值和真实值有多大,或者损失函数最小等算法即怎样去实现去寻找这个模型决策......
  • 《程序员的修炼之道——从小工到专家》阅读笔记1
    这里面针对程序员,反复提到一个形容词,就是“注重实效”。根据书中所讲,结合我的理解,我认为注重时效这个词主要体现在责任上,对自己负责,对自己的代码负责,对自己的代码中的错误负责。“最大的弱点就是害怕暴露弱点”,我非常认同这句话,要坦然面对自己的错误其实并不是一件容易的事情,不仅......
  • 机器学习入门基础----白板推导笔记输出
    为了能够建立知识学习后输出体系,开设这个系列,旨在通过记录博客输出学习到的机器学习内容,笔者所学为B站upshuhuai008白板推导系列,记录可能比不上原创,也可能有没理解不严谨的地方,请善意指正。感兴趣的可以去看UP白板-------------------------------------------------------------......
  • java.数据流.study
         (紧接输出流代码,需要与输入流代码中类型对应)  对象字节输入流与对象字节输出流:     可以使用transient可以实现变量不会序列化:  ......
  • java.IO打印流.study
            ......
  • java.IO转换流.study
            ......
  • Java.IO流.study
       IO流的体系结构: FileInputStream(文件字节输入流): 方法:   文件字节输入流(一次读取多个数据):   注意事项:  使用文件字节输入流一次读取完文件的全部字节。    文件字节输出流:     ......
  • java.file文件与IO流.study
     但断电后后数据消失。 而IO流就是对数据进行读写File创建对象: 绝对路径与相对路径:  File提供的判断文件类型,获取文件信息功能:   File创建和删除文件相关方法:   File遍历文件夹的方法:  代码实现: 文件搜索,实现遍历文件夹下的多级目录:......
  • Java中“=”克隆理解
    在Java中,对于基本类型可以使用“="来进行克隆,此时两个变量除了相等是没有任何关系的。而对于引用类型却不能简单地使用”=“进行克隆,这与java的内存空间使用有关。Java将内存空间分成两块,即栈和堆。在栈中保存基本类型和引用变量;在堆中保存对象。对于引用变量而言,使用”=“......
  • Java面向对象简述
    Java是一门面向对象的编程语言,那么我们要先了解什么是面向对象。编程语言分为:面向机器语言(例如汇编语言),面向过程语言(例如c语言),以及面向对象语言。而面向对象编程主要体现在:封装性,继承性和多态性。封装性:将数据和对数据的操作封装在一起,通过抽象,形成一般概念(类)。举例而言,公......