Java基础教程-05-面向对象

本文详细介绍了面向对象编程的基础概念,包括类、对象、继承、多态等核心概念。通过实例展示了如何定义类、创建对象、使用构造方法、封装、继承和多态的运用。同时,讲解了抽象类、接口、访问修饰符的作用,并通过猫狗案例展示了类和接口的实战应用。此外,还探讨了包的使用以及权限修饰符的选择策略。
摘要由CSDN通过智能技术生成

Java基础教程-05-面向对象

1. 面向过程和面向对象解释

1.1 面向过程

1.1.1 简述

我们回想一下, 前面我们完成一个需求的步骤是怎样的?

  1. 首先是搞清楚我们要做什么.
  2. 然后在分析怎么做.
  3. 最后我们在通过代码一步一步去实现.

上述的每一个步骤, 我们都是参与者, 并且需要面对具体的每一个步骤和过程, 这就是面向过程最直接的体现.

1.1.2 举例

• 生活中的例子: 洗衣服. 想完成洗衣服这个需求, 我们得完成下述的每一个步骤:

  1. 找个盆.
  2. 接点水.
  3. 到洗衣液.
  4. 把衣服放进去浸泡10分钟.
  5. 把衣服揉搓干净.
  6. 漂洗.
  7. 拧干水分.
  8. 晾晒.

注意:

  1. 上述的这些步骤, 但凡有一个我们不会做, 就没有办法完成洗衣服这个需求.
  2. 即: 每一步都需要我们亲自做, 这就是: 面向过程思想.

• 代码举例: 把数组元素按照[11, 22, 33, 44, 55]的格式进行输出.

  1. 定义方法printArray(), 参数列表为: int[] arr, 返回值的类型为String.
  2. 定义字符串变量s, 用来记录数组元素拼接后的结果.
  3. 字符串变量s的初始化值设置为[.
  4. 对数组对象进行判断, 看其是否是合法对象.
    即: 数组对象不为: null
  5. 通过for循环遍历数组, 获取到每一个元素.
  6. 判断当前获取到的元素是否是最后一个元素, 并和字符串变量s进行拼接.
     • 如果是最后一个元素, 则在后边在拼接上]
     • 如果不是最后一个元素, 则在后边在拼接上,
  7. 当for循环执行结束后, 字符串变量s记录的就是拼接后的结果, 将其直接返回即可.

注意: 上述的每一步代码都需要我们编写, 这样做比较繁琐.

1.1.3 总结

面向过程思想其实就是一种编程思想. 所谓的面向过程开发, 指的就是面向着具体的每一个步骤和过程, 把每一个步骤和过程完成, 然后由这些功能方法相互调用, 完成需求.
记忆: 面向过程的代表语言是: C语言.

1.2 面向对象

1.2.1 简述

当需求单一, 或者简单时, 我们一步一步去操作是没问题的, 并且效率也挺高. 可随着需求的更改, 功能的增多, 发现需要面对每一个步骤就很麻烦了, 于是我们就想着, 能不能把这些步骤和功能在进行封装, 封装时根据不同的功能, 进行不同的封装, 功能类似的用一个类封装在一起, 这样结构就清晰了很多. 用的时候, 找到对应的类就可以了. 这就是面向对象的思想.

1.2.2 举例

• 生活中的例子: 洗衣服.

  1. 想完成洗衣服这个需求, 我们可以通过洗衣机 来实现.
  2. 对于我们来讲, 洗衣机就是我们的对象.
  3. 总结: 万物皆对象.

• 代码举例: 把数组元素按照[11, 22, 33, 44, 55]的格式进行输出.
上述的需求, 我们可以通过Arrays类的toString()方法来实现.

Int[] arr  = {11, 22, 33, 44, 55};
sop(Arrays.toString(arr));   ->[11, 22, 33, 44, 55]
1.2.3 思想特点
  1. 是一种符合我们思考习惯的思想.
  2. 可以将复杂的事情简单化.
  3. 让我们从执行者变成了指挥者.
1.2.4 总结

面向对象思想是一种编程思想, 它是基于面向过程的, 强调的是以对象为基础完成各种操作.
总结来讲, 万物皆对象.

2. 类和对象

2.1 概述

问题一: 你为什么学习编程语言?
我们学习编程语言, 其实就是为了把现实世界的事物通过代码模拟出来, 实现信息化.
例如:
• 超市的计费系统.
• 银行的核心系统.
• 千亿级数据仓库.

分析PB级数据,为企业提供高效、稳健的实时数据洞察。
采用类似阿里巴巴大数据数仓设计的分层架构思想,使用主流的实时仓库技术Flink、Druid、Kafka。

• 企业级360°全方位用户画像.
360°全方位还原用户画像,实现对个体和群体信息的标签化,实现精准推荐和营销.
• 电商推荐系统.
项目利用Neo4j构建用户和商品的关系图示,基于词向量相似度推荐商品、CTR/CVR点击率预估模型、逻辑斯特回归算法进行CTR点击率预估。

问题二: 我们是如何表示现实世界的事物呢?

  1. 属性.
    – 属性指的就是事物的描述信息(名词).
    – 属性在Java中被称之为成员变量.
  2. 行为.
    – 行为指的就是事物能够做什么.
    – 行为在Java中被称之为成员方法.

例如: 学生
• 属性: 姓名, 年龄, 性别…
• 行为: 学习, 吃饭, 睡觉…

问题三: Java语言是如何表示现实世界的事物呢?

  1. 在Java语言中, 是通过类来体现事物的. Java语言最基本的单位是类, 它是一个抽象的概念, 看不见, 摸不着.
  2. 对象: 对象就是该类事物的具体体现, 实现.

举例:
类 学生 大象
对象 张三, 23 北京动物园叫图图的大象

2.2 类的定义格式

2.2.1 简述

定义类其实就是定义类的成员(成员变量和成员方法)
• 成员变量:

  1. 和以前定义变量是一样的, 只不过位置发生了改变, 写到类中, 方法外
  2. 而且成员变量还可以不用赋值, 因为它有默认值.

• 成员方法:

  1. 和以前定义方法是一样的, 只不过把static关键字去掉.
  2. 这点先记忆即可, 后面我们再讲解static关键字的用法.
2.2.2 格式
public class 类名 {
    //成员变量
    
    //成员方法
}
2.2.3 示例

需求
定义一个学生类.
参考代码

public class Student{
    //成员变量, 就是属性.
    String name;	//姓名
    int age;		//年龄
    
    //成员方法, 就是行为.
    //学习的方法
    public void study() {
        System.out.println("键盘敲烂, 月薪过万!...");
    }
    
    //吃饭的方法
    public void eat() {
        System.out.println("学习饿了要吃饭!...");
    }
}

2.3 类的使用

2.3.1 简述

所谓类的使用, 就是使用类中定义的成员(成员变量和成员方法).

2.3.2 格式
  1. 创建该类的对象.

类名 对象名 = new 类名();

  1. 通过对象名.的形式, 调用类中的指定成员即可.

//成员变量
对象名.成员变量

//成员方法
对象名.成员方法(参数列表中各数据类型对应的值…)

2.3.3 示例

需求
使用学生类中的成员.
参考代码

public class StudentTest{
    public static void main(String[] args) {
        //1. 创建学生类的对象.
        Student s = new Student();
        //2. 访问成员变量.
        System.out.println(s.name);
        System.out.println(s.age);
        //3. 给成员变量赋值.
        s.name = "张三";
        s.age = 23;
        //4. 访问成员变量.
        System.out.println(s.name);
        System.out.println(s.age);
        //5. 访问成员方法. 
        s.study();
        s.eat();
    }
}

运行结果为:

null
0
张三
23
好好学习, 天天向上!
为了保持精力, 要按时吃饭.

3. 手机类的定义和使用

3.1 需求

  1. 定义手机类Phone.
    属性: 品牌(brand), 价格(price), 颜色(color)
    行为: 打电话(call), 发短信(sendMessage)
  2. 创建测试类PhoneTest, 在类中定义main方法, 并访问手机类(Phone类)中的成员.

3.2 参考代码

手机类

public class Phone {
    // 属性: 也叫成员变量, 就是用来描述事物的外在特征的(即: 名词)
    String brand;           //品牌
    int price;              //价格
    String color;           //颜色

    // 行为: 打电话(call), 发短信(sendMessage)
    //打电话
    public void call(String name) {   //夯哥
        System.out.println("给" + name + "打电话!...");
    }

    //发短信
    public void sendMessage(String name) {
        System.out.println("给" + name + "发短信!...");
    }
}

手机类的测试

public class PhoneTest {
    //main方法是程序的主入口, 所有的代码都是从这里开始执行的.
    public static void main(String[] args) {
        //1. 创建手机类的对象.
        Phone p = new Phone();
        //2. 设置成员变量值.
        p.brand = "华为";
        p.price = 5999;
        p.color = "黑色";

        //3. 打印成员变量值.
        System.out.println(p.brand);
        System.out.println(p.price);
        System.out.println(p.color);

        //4. 调用成员方法
        p.call("夯哥");
        p.sendMessage("夯哥");
    }
}

运行结果为:

华为
5999
黑色
给夯哥打电话!...
给夯哥发短信!...

4. 对象的内存图

4.1 一个对象的内存图

代码

//手机类
public class Phone {
    //属性, 成员变量
    String brand;	//品牌
    int price;	    //价格
    String color;	//颜色

    //行为, 成员方法
    //打电话
    public void call(String name) {
        System.out.println("给" + name + "打电话!...");
    }

    //发短信
    public void sendMessage(String name) {
        System.out.println("给" + name + "发短信!...");
    }
}

//手机类的测试类
public class PhoneTest {
    public static void main(String[] args) {
        //1. 创建手机类的对象.
        Phone p = new Phone();
        //2. 设置成员变量值.
        p.brand = "华为";
        p.price = 6666;
        p.color = "黑色";
        //3. 打印成员变量.
        System.out.println(p.brand + "--" + p.price + "--" + p.color);
        //4. 调用成员方法.
        p.call("夯哥");
        p.sendMessage("夯哥");
    }
}

内存图解
在这里插入图片描述

4.2 两个对象的内存图

代码

//手机类
public class Phone {
    //属性, 成员变量
    String brand;	//品牌
    int price;	    //价格
     String color;	//颜色

    //行为, 成员方法
    //打电话
    public void call(String name) {
        System.out.println("给" + name + "打电话!...");
    }

    //发短信
    public void sendMessage(String name) {
        System.out.println("给" + name + "发短信!...");
    }
}

//手机类的测试类
public class PhoneTest {
    public static void main(String[] args) {
        //1. 创建手机类的对象.
        Phone p = new Phone();
        //2. 设置成员变量值.
        p.brand = "华为";
        p.price = 6666;
        p.color = "黑色";
        //3. 打印成员变量.
        System.out.println(p.brand + "--" + p.price + "--" + p.color);
        //4. 调用成员方法.
        p.call("传智播客");
        p.sendMessage("传智播客");

        Phone p2 = new Phone();
        p2.brand = "小米";
        p2.price = 3333;
        p2.color = "白色";
        System.out.println(p2.brand + "--" + p2.price + "--" + p2.color);
        p2.call("黑马程序员");
        p2.sendMessage("黑马程序员");
    }
}

内存图解
在这里插入图片描述

5. 成员变量和局部变量的区别

5.1 简述

• 成员变量: 指的是定义在类中, 方法外的变量.
• 局部变量: 指的是定义在方法中, 或者方法声明上的变量.

它们的区别如下:

  1. 定义位置不同.
      – 成员变量: 定义在类中, 方法外.
      – 局部变量: 定义在方法中, 或者方法声明上.
  2. 在内存中的存储位置不同.
      – 成员变量: 存储在堆内存.
      – 局部变量: 存储在栈内存.
  3. 生命周期不同.
      – 成员变量: 随着对象的创建而存在, 随着对象的消失而消失.
      – 局部变量: 随着方法的调用而存在, 随着方法的调用完毕而消失.
  4. 初始化值不同.
      – 成员变量: 有默认值.
      – 局部变量: 没有默认值, 必须先定义, 再赋值, 然后才能使用.

5.2 代码演示

public class VariableDemo{
    int x;
    
    public void show() {
        int y = 10;
        System.out.println(x);
        System.out.println(y);
    }
}

6. 封装

上述的代码中, 我们可以任意的设置属性的值, 包括我们可以设置一些非法值, 例如: 把年龄设置成负数, 这样做程序就容易出问题, 针对于这种情况, 我们可以通过private关键字来优化它.

6.1 private关键字

6.1.1 简述

private是一个关键字, 也是访问权限修饰符的一种, 它可以用来修饰类的成员(成员变量和成员方法).

6.1.2 特点

被private修饰的内容只能在本类中直接使用.

6.1.3 应用场景
  1. 在实际开发中, 成员变量基本上都是用private关键字来修饰的.
  2. 如果明确知道类中的某些内容不想被外界直接访问, 都可以通过private来修饰.
6.1.4 示例

需求

  1. 定义学生类Student, 包含姓名, 年龄属性.
  2. 在StudentTest测试类中, 创建Student类的对象, 并调用Student类中的成员.
  3. 对年龄或者姓名属性加private修饰, 然后观察结果.

参考代码

//学生类
public class Student{
    //属性
    String name;		//姓名
    private int age;	//年龄
    
    //getXxx()和setXxx()方法
    public void setAge(int a) {
        if(a >= 0 && age <= 150) {
            //年龄合法, 就赋值.
            age = a;
        }
    }
    
    public int getAge() {
        return age;
    }
    
    //行为
    public void show() {
        //被private修饰的内容, 可以在本类中直接访问.
        System.out.println(name + "..." + age);
    }
}

//学生类的测试类
public class StudentTest{
    public static void main(String[] args) {
        //1. 创建学生类的对象
        Student s = new Student();
        //2. 给成员变量赋值.
        s.name = "张三";
        //s.age = -23;		//被private修饰的内容, 外界无法直接访问.
         s.setAge(-23);
        //3. 调用成员方法.
        s.show();
    }
}

6.2 标准代码

刚才的代码, 为了讲解private关键字, 只给年龄属性加了private, 而在实际开发中, 除非必要, 否则成员变量都要用private来修饰, 然后提供对应的getXxx()和setXxx()方法, 方便用户访问对应的成员变量, 接下来, 我们来写一个实际开发中的标准代码.

需求

  1. 定义一个标准的学生类Student, 属性: 姓名和年龄, 行为: 学习, 吃饭.
  2. 在测试类中创建学生类的对象, 然后访问类中的成员.

参考代码

public class Student {
    //属性, 全部用private修饰.
    //姓名
    private String name;
    //年龄
    private int age;

    public String getName() {
        return name;
    }
    public void setName(String n) {
        name = n;
    }

    public int getAge() {
        return age;
    }
    public void setAge(int a) {
        age = a;
    }

    //行为, 也就是成员方法.
    public void study() {
        System.out.println("键盘敲烂, 月薪过万!");
    }
    public void eat() {
        System.out.println("学习饿了就要吃饭!.");
    }
}

//测试类
public class StudentTest {
    public static void main(String[] args) {
        //1. 创建学生对象.
        Student s = new Student();
        //2. 设置成员变量值.
        s.setName("张三");
        s.setAge(23);
        //3. 打印成员变量值.
        System.out.println(s.getName() + "..." + s.getAge());
        //4. 调用成员方法.
        s.study();
        s.eat();
    }
}

6.3 封装的概述和好处

6.3.1 概述

封装是面向对象编程思想的三大特征之一, 所谓的封装指的就是隐藏对象的属性和实现细节, 仅对外提供一个公共的访问方式.

记忆:
== 面向对象的三大特征: 封装, 继承, 多态==.

问题一: 怎么隐藏?
通过private关键字实现.
问题二: 公共的访问方式是什么?
getXxx() 和 setXxx()方法.

6.3.2 原则
  1. 把不需要对外提供的内容都隐藏起来.
  2. 把属性隐藏, 并提供公共方法对其访问.

解释: 就是成员变量都用private修饰, 并提供对应的getXxx()和setXxx()方法, 除此以外, 都用public修饰.

6.3.3 好处
  1. 提高代码的安全性.
    这点是由private关键字来保证的.
  2. 提高代码的复用性.
    这点是由方法来保证的, 方法也是封装的一种体现形式.

6.4 this关键字

6.4.1 概述

this代表本类当前对象的引用, 大白话翻译: 谁调用, this就代表谁.

6.4.2 作用

用来解决局部变量和成员变量重名问题的.

6.4.3 示例

需求

  1. 定义一个标准的学生类Student, 属性: 姓名和年龄, 行为: 学习, 吃饭.
  2. 在测试类中创建学生类的对象, 然后访问类中的成员.

参考代码

public class Student {
    //属性, 全部用private修饰.
    //姓名
    private String name;
    //年龄
    private int age;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
        //s.age = 23;
    }

    //行为, 也就是成员方法.
    public void study() {
        System.out.println("键盘敲烂, 月薪过万!");
    }
    public void eat() {
        System.out.println("学习饿了就要吃饭!.");
    }
}

//测试类
public class StudentTest {
    public static void main(String[] args) {
        //1. 创建学生对象.
        Student s = new Student();
        //2. 设置成员变量值.
        s.setName("张三");
        s.setAge(23);
        //3. 打印成员变量值.
        System.out.println(s.getName() + "..." + s.getAge());
        //4. 调用成员方法.
        s.study();
        s.eat();
    }
}
6.4.4 总结

Java中, 使用变量遵循就近原则, 局部位置有就使用, 没有就去本类的成员位置找, 有就使用, 没有就报错.
解释: 先这么记忆, 不严谨, 因为本类没有, 还会去父类中查找, 这点在继承部分再解释.

6.5 构造方法

6.5.1 概述

构造方法是用来创建对象的, 捎带着可以给对象的各个成员变量赋值.
大白话:
构造方法就是用来快速对对象的各个属性赋值的.

6.5.2 格式
  1. 构造方法名必须和类名完全一致(包括大小写).
  2. 构造方法没有返回值类型, 连void都不能写.
  3. 构造方法没有具体的返回值, 但是可以写return(实际开发, 一般不写).
public 类名(参数类型 参数名1, 参数类型 参数名2) {  //这里可以写多个参数.
    //给对象的各个属性赋值即可.
}
6.5.3 示例: 构造方法入门

需求

  1. 定义学生类Student, 在类的空参构造中打印一句话"这是构造方法".
  2. 在StudentTest测试类中, 创建学生类的对象, 并观察程序的运行结果.
    小技巧: 创建对象的格式如下:
    类名 对象名 = new 构造方法(参数列表);

参考代码

//学生类.
public class Student{
    //类的空参构造.
    public Student() {
        System.out.println("这是构造方法");
    }
}

//学生类的测试类
public class StudentTest{
    public static void main(String[] args) {
        Student s = new Student();
    }
}
6.5.4 构造方法的注意事项
  1. 如果我们没有给出构造方法, 系统将给出一个默认的无参构造供我们使用.
  2. 如果我们给出了构造方法, 系统将不再提供默认的构造方法给我们使用.
  3. 这个时候, 如果我们还想使用无参构造, 就必须自己提供.
  4. 建议定义类时, 我们给出无参构造, 方便用户调用(实际开发都这么做的).

思考题
问: 给成员变量赋值有几种方式?
答: 1. 通过setXxx()方法实现(该方式不会创建新对象).
2. 通过构造方法实现(该方式会创建新对象).

6.6 标准的类的定义和使用

6.6.1 格式

以后在实际开发中, 也都是这样写的, 即: 标准的类的定义格式如下:

public class 类名{
    //属性(成员变量), 全部用private修饰.
    
    //构造方法, 一般提供两个(无参, 全参)
    
    //getXxx()和setXxx()方法
    
    //行为(成员方法), 根据需求来定义.
}
6.6.2 示例

需求

  1. 定义一个标准的学生类Student, 属性: 姓名和年龄, 行为: 学习, 吃饭.
  2. 在测试类中创建学生类的对象, 然后访问类中的成员.

参考代码

//学生类
public class Student {
    //属性(成员变量), 全部用private修饰.
    private String name;    //姓名
    private int age;        //年龄
    
    //构造方法, 一般提供两个(无参, 全参)
    public Student() {
    }
    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }
    //getXxx()和setXxx()方法
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }

    //行为(成员方法), 根据需求来定义.
    public void study() {
        System.out.println("键盘敲烂, 月薪过万!");
    }

    public void eat() {
        System.out.println("学习饿了要吃饭");
    }
}

//学生类的测试类
public class StudentTest {
    public static void main(String[] args) {
        //1. 创建学生对象.
        Student s = new Student();
        //2. 设置成员变量值.
        s.setName("张三");
        s.setAge(23);
        //3. 打印成员变量值.
        System.out.println(s.getName() + "..." + s.getAge());
        //4. 调用成员方法.
        s.study();
        s.eat();
    }
}

7. 继承

7.1 概述

多个类中存在相同属性和行为时, 将这些内容抽取到单独的一个类中, 那么这多个类就无需再定义这些属性和行为了, 只要继承那个类即可. 这个关系, 就叫继承.
注意:
有了继承以后, 我们在定义一个类的时候, 可以在一个已经存在的类的基础上, 还可以定义自己的新成员.

7.2 格式

在Java中, 可以通过extends关键字来实现类与类的继承, 具体格式如下:

public classA extendsB {	//子承父业
    
}

解释:
• 类A: 叫子类, 或者派生类.
• 类B: 叫父类, 基类, 或者超类.
我们一般会念做: 子类和父类.

7.3 继承的好处和弊端

7.3.1 示例

需求

  1. 按照标准格式定义一个人类(Person类), 属性为姓名和年龄.
  2. 定义老师类(Teacher), 继承自人类, 并在老师类中定义teach()方法.
  3. 定义学生类(Student), 继承自人类, 并在学生类中定义study()方法.
  4. 在PersonTest测试类的main方法中, 分别创建老师类和学生类的对象, 并调用各自类中的成员.

参考代码

人类代码:

public class Person {
    //属性
    private String name;
    private int age;

    //构造方法, 如果我们不写, 系统会默认加一个: 空参构造.


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    //行为
    public void eat() {
        System.out.println("人要吃饭");
    }

    public void sleep() {
        System.out.println("人要睡觉");
    }

}

老师类代码:

public class Teacher extends Person {

    //老师类独有的功能: 讲课
    public void teach() {
        System.out.println("老师要讲课!");
    }
}

学生类代码:

public class Student extends Person {

    //学生类独有的功能: 学习
    public void study() {
        System.out.println("好好学习, 天天向上!");
    }
}

测试类代码

public class PersonTest {
    public static void main(String[] args) {
        //1. 测试老师类
        Teacher t = new Teacher();
        //属性
        //从Person类继承.
        t.setName("刘亦菲");
        t.setAge(33);
        System.out.println(t.getName() + "..." + t.getAge());
        //方法
        t.eat();        //从Person类继承.
        t.sleep();      //从Person类继承.
        t.teach();      //本类的独有功能.
        //2. 测试学生类, 留给你自己写.
    }
}

运行结果为:

刘亦菲...33
人要吃饭
人要睡觉
老师要讲课!
7.3.2 好处
  1. 提高了代码的复用性.
  2. 提高了代码的可维护性.
  3. 让类与类之间产生关系, 是多态的前提.
7.3.3 弊端

让类与类之间产生了关系, 也就让类的耦合性增强了.
解释:
开发原则: 高内聚, 低耦合.
• 内聚: 指的是类自己独立完成某些事情的能力.
• 耦合: 指的是类与类之间的关系.

7.4 Java中继承的特点

7.4.1 示例
  1. 定义GrandFather类, 该类有一个grandFatherSay()方法, 该方法打印一句话爷爷都是从孙子过来的.
  2. 定义Father类, 该类有一个fatherSay()方法, 该方法打印一句话爸爸都是从儿子过来的.
  3. 定义Son类, 分别先继承自GrandFather类和Father类, 此时发现, 上述的两个方法只能同时调用一个.
  4. 如果想让Son类的对象, 同时能调用上述的两个方法, 则可以这样做:
    Son类继承自Father类, Father类继承自GrandFather类.
7.4.2 参考代码
//爷爷类,
//一个类如果没有显式的写父类, 默认就继承自Object类.
//Object类, 是所有类的父类.
public class GrandFather {
    public void grandFather() {
        System.out.println("grandFather....");
    }
}
//父亲类
public class Father extends GrandFather{
    public void father() {
        System.out.println("father....");
    }
}
//儿子类
public class Son extends Father {
}
//测试类
public class SonTest {
    public static void main(String[] args) {
        //创建子类对象.
        Son s = new Son();
        //问: Son的对象, 可以调用哪些方法?
        s.father();         //从Father类继承过来的.
        s.grandFather();    //从Father类继承过来的, 而Father类又从GrandFather类继承过来了.
    }
}
7.4.3 总结
  1. Java中类与类之间只能单继承, 不能多继承.
public classA extendsB,C {	//这种写法会报错. 
}
  1. Java中类与类之间, 可以多层继承.
public class A {	}

public class B extends A{	}

public class C extends B{	}		//这种写法可以.

7.5 继承中成员变量的特点

7.5.1 示例
  1. 定义Father类, 在该类的成员位置定义变量: int age = 30;
  2. 定义Son类, 让它继承Father类, 并在该类的成员位置定义变量: int age = 20;
  3. 在测试类FatherTest的main方法中, 定义变量: int age = 10;
  4. 通过输出语句, 直接打印age变量的值, 并查看程序的运行结果.
7.5.2 参考代码
public class Father {
    int age = 30;
}

public class Son extends Father {
    int age = 20;
}

public class FatherTest {
    public static void main(String[] args) {
        int age = 10;
        System.out.println(age);	 //这里打印结果会是多少呢? 
    }
}
7.5.3 总结

Java中使用变量遵循就近原则, 局部位置有就使用, 没有就去本类的成员位置找. 有就使用, 没有就去父类的成员位置找, 有就使用, 没有就报错.
注意: 不考虑父类的父类这种情况, 因为会一直往上找, 直到把所有的父类都找完, 还找不到, 就报错了.

7.6 super关键字

7.6.1 概述

super的用法和this很像:
• this: 代表本类对象的引用.
• super: 代表当前对象的父类的内存空间标识.

解释: 可以理解为父类对象引用.
7.6.2 用法
功能本类父类
访问成员变量this.成员变量名super.成员变量名
访问构造方法this(…)super(…)
访问成员方法this.成员方法名(参数值…)super.成员方法名(参数值…)

7.7 方法重写

7.7.1 概述

子类中出现和父类一模一样的方法时, 称为方法重写. 方法重写要求返回值的数据类型也必须一样.

7.7.2 应用场景

当子类需要使用父类的功能, 而功能主体又有自己独有需求的时候, 就可以考虑重写父类中的方法了, 这样, 即沿袭了父类的功能, 又定义了子类特有的内容.

7.7.3 示例

需求

  1. 定义Phone类, 并在类中定义call(String name)方法.
  2. 定义NewPhone类, 继承Phone类, 然后重写call(String name)方法.
  3. 在PhoneTest测试类中, 分别创建两个类的对象, 然后调用call()方法, 观察程序执行结果.

参考代码

手机类

public class Phone {
    //打电话的功能.
    public void call(String name) {
        System.out.println("给" + name + "打电话!...");
    }
}

新手机类

//定义一个手机类, 表示: 新式手机.
public class NewPhone extends Phone{
    //重写父类Phone#call()方法, 在打电话的时候, 播放彩铃.
    @Override
    public void call(String name) {
        //因为基本的打电话的功能, 父类已经实现了, 我们可以直接拿来用.
        //System.out.println("给" + name + "打电话!...");

        //调用父类的call()方法, 实现打电话功能.
        super.call(name);
        //在父类call()方法的功能主体上, 加入自己独有的需求: 播放彩铃
        System.out.println("播放彩铃...");
    }
}

测试类

public class PhoneTest {
    public static void main(String[] args) {
        //1. 测试 老式手机.
        Phone p = new Phone();
        p.call("刘亦菲");
        System.out.println("----------------");

        //2. 测试 新式手机
        NewPhone np = new NewPhone();
        np.call("赵丽颖");
    }
}

运行结果为:

给刘亦菲打电话!...
----------------
给赵丽颖打电话!...
播放彩铃...
7.7.4 注意事项
  1. 子类重写父类方法时, 方法声明上要用@Override注解来修饰.
  2. 父类中私有的方法不能被重写.
  3. 子类重写父类方法时, 访问权限不能更低.

8. 多态

8.1 概述

多态指的是同一个事物(或者对象)在不同时刻表现出来的不同状态.
例如: 一杯水.
• 常温下是液体.
• 高温下是气体.
• 低温下是固体.
但是水还是那杯水, 只不过在不同的环境下, 表现出来的状态不同.

8.2 前提条件

  1. 要有继承关系.
  2. 要有方法重写.
  3. 要有父类引用指向子类对象.

8.3 示例: 多态入门

需求

  1. 定义动物类Animal, 并在类中定义一个成员方法: eat()
  2. 定义猫类Cat, 继承Animal类, 并重写eat()方法.
  3. 在AnimalTest测试类的main方法中, 通过多态的方式创建猫类对象.
  4. 通过猫类对象, 调用eat()方法.

参考代码

动物类:

//父类, 动物类
public class Animal {
    public void eat() {
        System.out.println("动物要吃");
    }
}

猫咪类:

//子类, 猫类
public class Cat extends Animal {
    @Override
    public void eat() {
        System.out.println("猫吃鱼!");
    }

    public void catchMouse() {
        System.out.println("猫会抓老鼠!....");
    }
}

测试类:

public class AnimalTest {
    public static void main(String[] args) {
        //1 多态形式的创建猫类对象
        Animal an = new Cat();
        //2. 演示多态的弊端.
        an.eat();               //编译看左 运行看右.

        //3. 升级功能: 尝试通过多态版的 猫类对象, 调用猫类中独有的 catchMouse()功能.
        //an.catchMouse();      //这样做不行, 因为an是Animal动物类的引用, 我们不能说  动物会抓老鼠

        //4. 通过向下转型, 把 多态版的 猫类对象 ->  Cat类型的猫类对象.
        Cat c = (Cat)an;          //引用类型的: 向下转型,     即: 大 -> 小.
        c.catchMouse();
        //int a = (int)10.3;        基本类型的: 强制类型转换, 即: 大 -> 小.
    }
}

运行结果为:

猫吃鱼!
猫会抓老鼠!....

8.4 多态中的成员访问特点

• 成员变量: 编译看左边, 运行看左边.
• 成员方法: 编译看左边, 运行看右边.

需求

  1. 定义一个人类Person. 属性为姓名和年龄, 行为是: eat()方法.
  2. 定义Student类, 继承自Person类, 定义age属性及重写eat()方法.
  3. 在PersonTest测试类的main方法中, 创建Student类的对象, 并打印其成员.

参考代码
人类:

public class Person {
    //属性
    String name = "刘亦菲";
    int age = 50;

    //行为
    public void eat() {
        System.out.println("人要吃饭!");
    }
}

学生类:

//子类, 学生类
public class Student extends Person{
    int age = 30;

    @Override
    public void eat() {
        System.out.println("学生吃牛肉!");
    }
}

测试类:

public class PersonTest {
    public static void main(String[] args) {
        //1. 通过多态, 创建学生类对象.
        Person p = new Student();

        //2. 调用成员变量.
        System.out.println(p.age);      //50

        //3. 调用成员方法.
        p.eat();                        //1学生吃牛肉
    }
}

运行结果为:

50
学生吃牛肉!

8.5 好处和弊端.

8.5.1 好处

提高了程序的扩展性.

8.5.2 弊端

父类引用不能访问子类的特有功能.
问: 那如何解决这个问题呢?
答: 通过向下转型来解决这个问题.

8.5.3 示例

需求

  1. 定义动物类Animal, 该类有一个eat()方法.
  2. 定义猫类Cat, 继承自Animal类, 该类有一个自己独有的方法: catchMouse().
  3. 在AnimalTest测试类中, 通过多态的方式创建Cat类的对象, 并尝试调用catchMouse()方法.

参考代码

动物类:

//父类, 动物类
public class Animal {
    public void eat() {
        System.out.println("动物要吃");
    }
}

猫咪类:

//子类, 猫类
public class Cat extends Animal {
    @Override
    public void eat() {
        System.out.println("猫吃鱼!");
    }

    public void catchMouse() {
        System.out.println("猫会抓老鼠!....");
    }
}

测试类:

public class AnimalTest {
    public static void main(String[] args) {
        //1 多态形式的创建猫类对象
        Animal an = new Cat();
        //2. 演示多态的弊端.
        an.eat();               //编译看左 运行看右.

        //3. 升级功能: 尝试通过多态版的 猫类对象, 调用猫类中独有的 catchMouse()功能.
        //an.catchMouse();      //这样做不行, 因为an是Animal动物类的引用, 我们不能说  动物会抓老鼠

        //4. 通过向下转型, 把 多态版的 猫类对象 ->  Cat类型的猫类对象.
        Cat c = (Cat)an;          //引用类型的: 向下转型,     即: 大 -> 小.
        c.catchMouse();
        //int a = (int)10.3;        基本类型的: 强制类型转换, 即: 大 -> 小.
    }
}
8.5.4 向上转型和向下转型

向上转型

//格式
父类型 对象名 = new 子类型();

//例如
Animal an = new Cat();

向下转型

//格式
子类型 对象名 = (子类型)父类引用;

//例如
Cat c = (Cat)an;

小Bug

//下述代码会报: ClassCastException(类型转换异常)
Animal an = new Cat();
Cat c = (Cat)an;		//这样写不报错.
Dog d = (Dog)an;		//这样写会报错. 

8.6 示例: 猫狗案例

需求

  1. 已知猫狗都有姓名和年龄, 都要吃饭.
  2. 猫类独有自己的catchMouse()方法, 狗类独有自己的lookHome()方法.
  3. 在AnimalTest测试类的main方法中, 通过多态分别创建猫类, 狗类的对象.
  4. 分别通过猫类对象和狗类对象, 访问对象的成员.

参考代码
动物类:

//父类, 动物类.
public class Animal {
    private  String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

猫咪类:

//子类, 猫.
public class Cat extends Animal {
}

狗狗类:

//子类, 狗.
public class Dog extends Animal {
}

测试类:

public class AnimalTest {
    public static void main(String[] args) {
        //1. 多态的形式创建, 猫类对象.
        Animal an = new Cat();          //向上转型.
        //Animal an2 = new Dog();      //向上转型.

        //2. 向下转型.
        Cat c = (Cat)an;      //这样写不报错.

        //细节: 编译期间只检查语法问题, 所以下边这样代码在编译期间不会报错.
//        Dog d = (Dog) an;       //这样写报错, ClassCastException, 类型转换异常.

    }
}

9. 两个关键字

9.1 final关键字

9.1.1 概述

final是一个关键字, 表示最终的意思, 可以修饰类, 成员变量, 成员方法.
• 修饰的类: 不能被继承, 但是可以继承其他的类.
• 修饰的变量: 是一个常量, 只能被赋值一次.
• 修饰的方法: 不能被子类重写.

9.1.2 示例

需求

  1. 定义Father类, 并定义它的子类Son.
  2. 先用final修饰Father类, 看Son类是否还能继承Father类.
  3. 在Son类中定义成员变量age, 并用final修饰, 然后尝试给其重新赋值, 并观察结果.
  4. 在Father类中定义show()方法, 然后用final修饰, 看Son类是否能重写该方法.

参考代码

父亲类:

public class Father {
    //int age = 20;    //变量
    final int AGE = 20;    //自定义常量

    String name;        //变量

    public void show() {
        System.out.println("这个是绝密文件, 不能动!");
    }
}

儿子类:

//子类
public class Son extends Father {
    @Override
    public void show() {
        System.out.println("这个是垃圾文件, 赶紧删除!");
    }
}

测试类:

public class FatherTest {
    public static void main(String[] args) {
        Father f = new Father();
        //f.age = 25;   常量只能赋值一次, 不能重复赋值.
        System.out.println(f.AGE);
        System.out.println("------------------");

        Son s = new Son();
        s.show();
    }
}

运行结果为:

20
------------------
这个是垃圾文件, 赶紧删除!

9.2 static关键字

9.2.1 概述

static是一个关键字, 表示静态的意思, 可以修饰成员变量, 成员方法.

9.2.2 特点
  1. 随着类的加载而加载.
  2. 优先于对象存在.
  3. 被static修饰的内容, 能被该类下所有的对象共享.
    解释: 这也是我们判断是否使用静态关键字的条件.
  4. 可以通过类名.的形式调用.
9.2.3 示例

需求

  1. 定义学生类, 属性为姓名, 年龄, 毕业院校(graduateFrom).
  2. 在学生类中定义show()方法, 用来打印上述的各个属性信息.
  3. 在测试类的main方法中, 创建学生对象, 并调用学生类的各个成员.

参考代码

//学生类
public class Student {
    //属性
    String name;
    int age;
    static String graduateFrom;

    //行为
    public void show() {
        System.out.println(name + ".." + age + ".." + graduateFrom);
    }
}

//测试类
public class StudentTest {
    public static void main(String[] args) {
        //1. 设置学生的毕业院校.
        Student.graduateFrom = "传智学院";
        //2. 创建学生对象s1.
        Student s1 = new Student();
        s1.name = "刘亦菲";
        s1.age = 33;

        //3. 创建学生对象s2.
        Student s2 = new Student();
        s2.name = "赵丽颖";
        s2.age = 31;

        //4. 打印属性值.
        s1.show();
        s2.show();
    }
}
9.2.4 静态方法的访问特点及注意事项

• 访问特点
静态方法只能访问静态的成员变量和静态的成员方法.
简单记忆: 静态只能访问静态.
• 注意事项

  1. 在静态方法中, 是没有this, super关键字的.
  2. 因为静态的内容是随着类的加载而加载, 而this和super是随着对象的创建而存在.
    即: 先进内存的, 不能访问后进内存的.

需求

  1. 定义学生类, 属性为姓名和年龄(静态修饰), 非静态方法show1(),show2(), 静态方法show3(), show4().
  2. 尝试在show1()方法中, 调用: 姓名, 年龄, show2(), show4().
    结论: 非静态方法可以访问所有成员(非静态变量和方法, 静态变量和方法)
  3. 尝试在show3()方法中, 调用: 姓名, 年龄, show2(), show4().
    结论: 静态方法只能访问静态成员.

参考代码

public class Student {
    String name = "刘亦菲";        //非静态成员变量
    static int age = 33;           //静态的成员变量

    public void show1() {
        System.out.println(name);
        System.out.println(age);
        //this.show2();         标准写法
        show2();

        //Student.show3();      标准写法
        show3();                //因为是访问本类的静态成员方法, 所以可以省略类名不写.
        show4();
        System.out.println("show1方法  非静态方法");
    }

    public void show2() {
        System.out.println("show2方法  非静态方法");
    }

    public static void show3() {
        //System.out.println(name);       //非静态成员变量
        System.out.println(age);         //静态成员变量

        //this.show2();                   //非静态方法
        Student.show4();                //静态方法

        System.out.println("show3方法  静态方法");
    }

    public static void show4() {
        System.out.println("show4方法  静态方法");
    }
}

测试类:

public class StudentTest {
    public static void main(String[] args) {
        Student s = new Student();
        //s.show1();  //结论: 非静态方法可以访问所有成员(非静态变量和方法, 静态变量和方法)

        //s.show3();    这样写不报错, 只是不推荐.
        Student.show3();    //静态方法只能访问静态成员
    }
}

运行结果为:

33
show4方法  静态方法
show3方法  静态方法

10. 抽象类

10.1 概述

回想前面我们的猫狗案例, 提取出了一个动物类, 这个时候我们可以通过Animal an = new Animal();来创建动物对象, 其实这是不对的, 因为, 我说动物, 你知道我说的是什么动物吗? 只有看到了具体的动物, 你才知道, 这是什么动物. 所以说, 动物本身并不是一个具体的事物, 而是一个抽象的事物. 只有真正的猫, 狗才是具体的动物. 同理, 我们也可以推想, 不同的动物吃的东西应该是不一样的, 所以, 我们不应该在动物类中给出具体的体现, 而是应该给出一个声明即可. 在Java中, 一个没有方法体的方法应该定义为抽象方法, 而类中如果有抽象方法, 该类必须定义为抽象类

10.2 入门案例

需求

  1. 创建抽象类Animal.
  2. 在该类中定义抽象方法eat()

参考代码

//抽象的动物类.
public abstract class Animal {
    //抽象方法, 吃.
    public abstract void eat();
}

10.3 抽象类的特点

  1. 抽象类和抽象方法必须用abstract关键字修饰.
//抽象类
public abstract class 类名{ }

//抽象方法
public abstract void eat();
  1. 抽象类中不一定有抽象方法, 有抽象方法的类一定是抽象类.
  2. 抽象类不能实例化.
    – 那抽象类如何实例化呢?
    – 可以通过多态的方式, 创建其子类对象, 来完成抽象类的实例化. 这也叫: 抽象类多态.
  3. 抽象类的子类:
  • 如果是普通类, 则必须重写父抽象类中所有的抽象方法.
  • 如果是抽象类, 则可以不用重写父抽象类中的抽象方法.

需求

  1. 定义抽象类Animal , 类中有一个抽象方法eat(), 还有一个非抽象方法sleep().
  2. 尝试在测试类中, 创建Animal类的对象, 并观察结果.
  3. 创建普通类Cat, 继承Animal类, 观察是否需要重写Animal#eat()方法.

参考代码
动物类:

//父类, 动物类
public abstract class Animal {
    //1.类中有一个抽象方法eat(), 还有一个非抽象方法sleep().
    public abstract void eat();

    public void sleep() {
        System.out.println("动物要睡!");
    }
}

猫咪类:

public class Cat extends Animal{
    @Override
    public void eat() {
        System.out.println("猫吃鱼!");
    }
}

测试类:

public class AnimalTest {
    public static void main(String[] args) {
        //1. 演示: 抽象类不能实例化.
        //Animal an = new Animal();   这样写会报错.


        //2. 抽象类可以通过创建其子类对象的形式, 来完成抽象类的初始化.
        //抽象类多态
        Animal an = new Cat();
    }
}

10.4 抽象类的成员特点

抽象类中可以有变量, 常量, 构造方法, 抽象方法和非抽象方法.
思考: 既然抽象类不能实例化, 那要构造方法有什么用?
答: 用于子类对象访问父类数据前, 对父类数据进行初始化.

需求

  1. 定义抽象类Person, 在类中定义变量age, 常量country, 空参, 全参构造.
  2. 在Person类中定义非抽象方法show(), 抽象方法eat().
  3. 在测试类的main方法中, 创建Person类的对象, 并调用类中的成员.

参考代码

人类:

public abstract class Person {
    //变量
    int age;                            //年龄
    //常量
    static final String COUNTRY = "中国";     //国籍

    //构造方法
    public Person() {
    }
    public Person(int age) {
        this.age = age;
    }

    //getXxx(), setXxx()

    //成员方法,
    public void show() {
        System.out.println("show方法  非抽象方法");
    }

    //抽象方法, 强制要求子类必须完成某些事情
    public abstract void eat();
}

学生类:

//学生类
public class Student extends Person {

    public Student() {
        super();
    }

    public Student(int age) {
        super(age);
    }

    @Override
    public void eat() {
        System.out.println("学生吃牛肉!...");
    }
}

测试类:

public class PersonTest {
    public static void main(String[] args) {
        //1. 创建Person类型的对象.
        //只能通过 抽象类多态的形式 实现.
        Person p = new Student();

        //2. 调用其成员.
        System.out.println(p.age);
        System.out.println(p.COUNTRY);
        System.out.println(Person.COUNTRY);

        p.show();
        p.eat();
    }
}

运行结果为:

0
中国
中国
show方法  非抽象方法
学生吃牛肉!...

10.5 案例: 老师类

10.5.1 需求
  1. 传智播客公司有基础班老师(BasicTeacher)和就业班老师(WorkTeacher), 他们都有姓名和年龄, 都要讲课.
  2. 不同的是, 基础班老师讲JavaSE, 就业班老师讲解JavaEE.
  3. 请用所学, 模拟该知识点.
10.5.2 分析
  1. 定义父类Teacher, 属性: 姓名和年龄, 行为: 讲课(因为不同老师讲课内容不同, 所以该方法是抽象的).
  2. 定义BasicTeacher(基础班老师), 继承Teacher, 重写所有的抽象方法.
  3. 定义WorkTeacher(就业班老师), 继承Teacher, 重写所有的抽象方法.
  4. 定义TeacherTest测试类, 分别测试基础班老师和就业班老师的成员.
10.5.3 参考代码

老师类:

//父类, 表示老师类.
public abstract class Teacher {
    //属性.
    private String name;
    private int age;

    //构造方法
    public Teacher() {
    }

    public Teacher(String name, int age) {
        this.name = name;
        this.age = age;
    }

    //getXxx(), setXxx()
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    //行为
    //抽象方法, 讲课.
    public abstract void teach();
}

基础班类:

//子类, 基础班老师
public class BasicTeacher extends Teacher{
    //构造方法
    public BasicTeacher() {
    }

    public BasicTeacher(String name, int age) {
        super(name, age);
    }

    //重写Teacher#teach()
    @Override
    public void teach() {
        System.out.println("基础班老师讲: JavaSE");
    }
}

就业班类:

//子类, 就业班老师
public class WorkTeacher extends Teacher{
    //构造方法
    public WorkTeacher() {
    }

    public WorkTeacher(String name, int age) {
        super(name, age);
    }

    //重写Teacher#teach()
    @Override
    public void teach() {
        System.out.println("就业班老师讲: JavaEE, Linux, Hadoop...");
    }
}

测试类:

//案例: 演示抽象类版的 老师类案例.
public class TeacherTest {
    public static void main(String[] args) {
        //1. 测试基础班老师, 空参.
        //抽象类多态版
        Teacher t1 = new BasicTeacher();
        //BasicTeacher t1 = new BasicTeacher();
        t1.setName("刘亦菲");
        t1.setAge(33);
        System.out.println(t1.getName() + "..." + t1.getAge());
        t1.teach();
        System.out.println("---------------------");

        //2. 测试基础班老师, 全参.
        Teacher t2 = new BasicTeacher();
        t2.setName("赵丽颖");
        t2.setAge(31);
        System.out.println(t2.getName() + "..." + t2.getAge());
        t2.teach();



        //3. 测试就业班老师, 空参, 自己做.
        //4. 测试就业班老师, 全参.
    }
}

运行结果为:

刘亦菲...33
基础班老师讲: JavaSE
---------------------
赵丽颖...31
基础班老师讲: JavaSE

11. 接口

11.1 概述

继续回到我们的猫狗案例,我们想想狗一般就是看门,猫一般就是作为宠物了。但是,现在有很多的驯养员或者是驯兽师,可以训练出:猫钻火圈,狗跳高,狗做计算等。而这些额外的动作,并不是所有猫或者狗一开始就具备的,这应该属于经过特殊的培训训练出来的。所以,这些额外的动作定义到动物类中就不合适,也不适合直接定义到猫或者狗中,因为只有部分猫狗具备这些功能。
所以,为了体现事物功能的扩展性,Java中就提供了接口来定义这些额外功能,并不给出具体实现,将来哪些猫狗需要被训练,只需要这部分猫狗把这些额外功能实现即可。

11.2 特点

  1. 接口用interface关键字修饰.
  2. 类和接口之间是实现关系, 用implements关键字表示.
  3. 接口不能实例化.
    – 那接口如何实例化呢?
    – 可以通过多态的方式, 创建其子类对象, 来完成接口的实例化. 这也叫: 接口多态.
  4. 接口的子类:
  • 如果是普通类, 则必须重写父接口中所有的抽象方法.
  • 如果是抽象类, 则可以不用重写父接口中的抽象方法.

需求

  1. 定义Jumpping接口, 接口中有一个抽象方法jump().
  2. 定义Cat类, 实现Jumpping接口, 重写jump()方法.
  3. 在测试类的main方法中, 创建Jumpping接口对象, 并调用其jump()方法

接口:

//定义Jumpping接口, 表示: 跳高的功能
public interface Jumpping {
    //定义jump方法, 表示跳高.
    public abstract void jump();
}

猫咪类:

public class Cat implements Jumpping {
    public void catchMouse() {
        System.out.println("猫会抓老鼠!");
    }

    @Override
    public void jump() {
        System.out.println("猫会跳高");
    }
}

测试类:

public class CatTest {
    public static void main(String[] args) {
        //1. 创建猫类对象.
        Cat c = new Cat();
        //2. 调用猫类中的功能.
        c.catchMouse();
        c.jump();       //这个方法, 是Cat从Jumpping接口中继承过来的.
        System.out.println("------------------");

        //3. 测试接口多态
        Jumpping jp = new Cat();
        jp.jump();

        //向下转型
        Cat c2 = (Cat)jp;
        c2.catchMouse();
    }
}

运行结果为:

猫会抓老鼠!
猫会跳高
------------------
猫会跳高
猫会抓老鼠!

11.3 成员特点

接口中有且只能有常量或者抽象方法, 原因是因为:
• 成员变量有默认修饰符: public static final
• 成员方法有默认修饰符: public abstract
注意: 因为接口主要是扩展功能的, 而没有具体存在, 所有接口中是没有构造方法的.
记忆: JDK1.8的时候, 接口中加入了两个新的成员: 静态方法, 默认方法(必须用default修饰).

需求
定义接口Inter, 测试接口中的成员特点.
参考代码

接口类:

public interface Inter {
    //变量的默认修饰符: public static final
    public static final  int age = 20;


    //方法的默认修饰符: public abstract
    public abstract void show();

    //尝试编写接口中的构造方法(这样写会报错, 因为接口中没有构造方法)
    //public Inter(){}

    //以下是JDK1.8的新特性: 接口中加入了静态方法和默认方法.
    public static void method() {
        System.out.println("我是JDK1.8的新特性: 接口中可以写静态方法");
    }


    public default void method2() {
        System.out.println("我是JDK1.8的新特性: 接口中可以写默认方法");
    }
}

测试类:

public class InterTest {
    public static void main(String[] args) {
        //1. 测试接口中的成员变量 ->  其实是一个常量
        //Inter.age = 50;
        System.out.println(Inter.age);
    }
}

运行结果为:

20

11.4 类与接口之间的关系

• 类与类之间: 继承关系, 只能单继承, 不能多继承, 但是可以多层继承.
• 类与接口之间: 实现关系, 可以单实现, 也可以多实现. 还可以在继承一个类的同时实现多个接口.
• 接口与接口之间: 继承关系, 可以单继承, 也可以多继承.

12. 运动员和教练案例

12.1 需求

  1. 已知有乒乓球运动员(PingPangPlayer)和篮球运动员(BasketballPlayer), 乒乓球教练(PingPangCoach)和篮球教练(BasketballCoach).
  2. 他们都有姓名和年龄, 都要吃饭, 但是吃的东西不同.
  3. 乒乓球教练教如何发球, 篮球教练教如何运球和投篮.
  4. 乒乓球运动员学习如何发球, 篮球运动员学习如何运球和投篮.
  5. 为了出国交流, 跟乒乓球相关的人员都需要学习英语.
  6. 请用所学, 模拟该知识.

12.2 代码:

人类:

//父类, 人类
public abstract class Person {
    //属性, 姓名, 年龄.
    private String name;
    private int age;

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
    //行为: 抽象方法eat()
    public abstract void eat();
}

教练类:

//Person类的子类, 表示教练类
public abstract class Coach extends Person{
    //构造方法
    public Coach() {
    }

    public Coach(String name, int age) {
        super(name, age);
    }

    //自身的方法: teach()
    public abstract void teach();
}

球员类:

//Person类的子类, 表示运动员类
public abstract class Player extends Person{
    //构造方法
    public Player() {
    }

    public Player(String name, int age) {
        super(name, age);
    }

    //自身的方法: study()
    public abstract void study();
}

说英语接口:

//定义接口, 表示: 说英语的功能
public interface SpeakEnglish {
    void speak();
}

篮球教练类:

//定义类, 表示篮球教练
public class BasketballCoach extends Coach{
    //构造方法
    public BasketballCoach() {
    }

    public BasketballCoach(String name, int age) {
        super(name, age);
    }

    //重写父类的抽象方法.
    @Override
    public void teach() {
        System.out.println("篮球教练教如何运球和投篮");
    }

    @Override
    public void eat() {
        System.out.println("篮球教练吃牛肉, 喝牛奶");
    }
}

篮球球员类:

//篮球运动员
public class BasketballPlayer extends Player{
    //构造方法
    public BasketballPlayer() {
    }

    public BasketballPlayer(String name, int age) {
        super(name, age);
    }

    //重写父类的抽象方法
    @Override
    public void study() {
        System.out.println("篮球运动员学习如何运球和投篮");
    }

    @Override
    public void eat() {
        System.out.println("篮球运动员吃羊肉, 喝羊奶");
    }
}

乒乓球教练类:

//定义乒乓球教练类
public class PingPangCoach extends Coach implements SpeakEnglish {
    //构造方法
    public PingPangCoach() {
    }

    public PingPangCoach(String name, int age) {
        super(name, age);
    }

    //重写父类(接口)的所有抽象方法
    @Override
    public void teach() {
        System.out.println("乒乓球教练教如何发球");
    }

    @Override
    public void eat() {
        System.out.println("乒乓球教练吃大白菜, 喝小米粥");
    }

    @Override
    public void speak() {
        System.out.println("乒乓球教练需要学习英语");
    }
}

乒乓球球员类:

//定义类, 表示乒乓球运动员
public class PingPangPlayer extends Player implements SpeakEnglish {
    //构造方法
    public PingPangPlayer() {
    }

    public PingPangPlayer(String name, int age) {
        super(name, age);
    }

    //重写父类(接口)的所有的抽象方法
    @Override
    public void study() {
        System.out.println("乒乓球运动员学习如何发球");
    }

    @Override
    public void eat() {
        System.out.println("乒乓球运动员吃小白菜, 喝大米粥");
    }

    @Override
    public void speak() {
        System.out.println("乒乓球运动员需要学习英语!");
    }
}

测试类:

//案例: 运动员和教练类案例,   该类是一个测试类
public class PersonTest {
    public static void main(String[] args) {
        //1. 测试乒乓球运动员.
        //接口多态
        //SpeakEnglish se = new PingPangPlayer("马龙", 31);

        //抽象类多态
        //Person p = new PingPangPlayer("马龙", 31);
        //Player p2 = new PingPangPlayer("马龙", 31);

        //标准写法
        PingPangPlayer p1 = new PingPangPlayer("马龙", 31);
        //打印属性值.
        System.out.println(p1.getName() + "..." + p1.getAge());
        //调用方法
        p1.eat();
        p1.study();
        p1.speak();
        System.out.println("----------------------------");

        //2. 测试乒乓球教练.
        PingPangCoach p2 = new PingPangCoach("刘国梁", 45);
        //打印属性值.
        System.out.println(p2.getName() + "..." + p2.getAge());
        //调用方法
        p2.eat();
        p2.teach();
        p2.speak();

        //3. 测试篮球运动员.

        //4. 测试篮球教练.
    }
}

运行结果为:

马龙...31
乒乓球运动员吃小白菜, 喝大米粥
乒乓球运动员学习如何发球
乒乓球运动员需要学习英语!
----------------------------
刘国梁...45
乒乓球教练吃大白菜, 喝小米粥
乒乓球教练教如何发球
乒乓球教练需要学习英语

13. 包

13.1 简述层

包(package)就是文件夹, 用来对类进行分类管理的. 例如:
• 学生的增加, 删除, 修改, 查询.
• 老师的增加, 删除, 修改, 查询.
• 其他类的增删改查…
• 基本的划分: 按照模块和功能划分.
• 高级的划分: 就业班做项目的时候你就能看到了.

13.2 格式

package 包名1.包名2.包名3;	//多级包之间用.隔开

注意:

  1. package语句必须是程序的第一条可执行的代码.
  2. package语句在一个.java文件中只能有一个.

13.3 常见分类

• 按照功能分
 – com.itheima.add
  • AddStudent.java
  • AddTeacher.java
 – com.itheima.delete
  • DeleteStudent.java
  • DeleteTeacher.java
• 按照模块分
 – com.itheima.student
  • AddStudent
  • DeleteStudent
 – com.itheima.teacher
  • AddTeacher
  • DeleteTeacher

13.4 导包

13.4.1 概述

不同包下的类之间的访问,我们发现,每次使用不同包下的类的时候,都需要加包的全路径。比较麻烦。这个时候,java就提供了导包的功能。

13.4.2 格式
import 包名;
  1. import java.util.*; 意思是导入java.util包下所有类, 这样做效率较低, 不推荐使用.
  2. import java.util.Scanner; 意思是导入java.util.Scanner类, 推荐使用. 用谁就导入谁.

14. 权限修饰符

14.1 概述

权限修饰符是用来修饰类, 成员变量, 构造方法, 成员方法的, 不同的权限修饰符对应的功能不同. 具体如下:

publicprotected默认private
同一个类中
同一个包中的子类或者其他类
不同包中的子类
不同包中的其他类(无关类)

14.2 示例

需求

  1. 在com.itheima包下定义Father类, 该类中有四个方法, 分别是show1(), show2(), show3(), show4().
  2. 上述的四个方法分别用权限修饰符private, 默认, protected, public修饰.
  3. 在Father类中, 创建main方法, 然后创建Father类的对象, 并调用上述的4个方法, 并观察结果.
  4. 在com.itheima包下创建Test类及main方法, 然后创建Father类的对象, 并调用上述的4个方法, 并观察结果.
  5. 在com.itheima包下创建Father类的子类Son, 添加main方法.
  6. 在main方法中, 创建Son类的对象, 并调用上述的4个方法, 并观察结果.
  7. 在cn.itcast包下创建Father类的子类Son, 然后在该类中添加main方法.
  8. 在main方法中, 创建Son类的对象, 并调用上述的4个方法, 并观察结果.
  9. 在cn.itcast包下创建Test类及main方法, 然后创建Father类的对象, 并调用上述的4个方法, 并观察结果.

参考代码

father类:

public class Father {
    private void show1() {
        System.out.println("Father show1方法, private 修饰");
    }

    void show2() {
        System.out.println("Father show2方法, 默认权限修饰符 修饰");
    }

    protected void show3() {
        System.out.println("Father show3方法, protected 修饰");
    }

    public void show4() {
        System.out.println("Father show4方法, public 修饰");
    }

    public static void main(String[] args) {
        //1. 创建Father对象.
        Father f = new Father();
        //2. 尝试调用Father类中的方法.
        f.show1();
        f.show2();
        f.show3();
        f.show4();
    }
}

son类:

//和Father的关系: 同包下的子类
public class Son extends Father {
    public static void main(String[] args) {
        //1. 创建Father对象.
        Father f = new Father();
        //2. 尝试调用Father类中的方法.
        //f.show1();
        f.show2();
        f.show3();
        f.show4();
        System.out.println("------------------------------");

        Son s = new Son();
        //s.show1();
        s.show2();
        s.show3();
        s.show4();
    }
}

测试类:

public class Test {
    public static void main(String[] args) {
        //1. 创建Father对象.
        Father f = new Father();
        //2. 尝试调用Father类中的方法.
        //f.show1();
        f.show2();
        f.show3();
        f.show4();
    }
}

运行结果为:

Father show2方法, 默认权限修饰符 修饰
Father show3方法, protected 修饰
Father show4方法, public 修饰

14.3 总结

  1. 访问权限修饰符的权限从大到小分别是: public > protected > 默认 > private
  2. 在实际开发中, 如果没有特殊需求, 则成员变量都用private修饰, 其它都用public修饰.
  3. 大白话总结四个访问权限修饰符的作用:
      1. private: 强调的是给自己使用.
      2. 默认: 强调的是给同包下的类使用.
      3. protected: 强调的是给子类使用.
      4. public: 强调的是给大家使用.

15. 作业

  1. 定义手机类Phone, 属性和行为如下, 并在测试类PhoneTest中, 创建手机类的对象, 然后访问类中的成员.
    属性:
    品牌: brand
    价格: price
    颜色: color
    行为:
    打电话: call(String name) //该方法接收一个"姓名"参数, 输出格式为: 给张三打电话…
    发短信: sendMessage(String name) //该方法接收一个"姓名"参数, 输出格式为: 给张三发短信…

代码:
手机类:

public class Phone {
    // 属性.成员变量
    // 手机品牌
    String brand;
    // 价格
    int price;
    // 颜色
    String color;

    public Phone() {
    }

    public Phone(String brand, int price, String color) {
        this.brand = brand;
        this.price = price;
        this.color = color;
    }

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    //行为,成员方法
    //打电话
    public void call(String name) {
        System.out.println("给" + name + "打电话!");
    }

    // 发短信
    public void sendMessage(String name) {
        System.out.println("给" + name + "发短信!!");
    }
}

测试类:

public class PhoneTest {
    public static void main(String[] args) {
        // 创建手机类的对象
        Phone p = new Phone();

        // 设置变量值
        p.brand = "华为";
        p.price = 5999;
        p.color = "曜石黑";

        // 打印成员变量
        System.out.println(p.brand);
        System.out.println(p.price);
        System.out.println(p.color);

        // 调用方法
        p.call("落空空");
        p.sendMessage("落空空");
    }
}

运行结果为:

华为
5999
曜石黑
给落空空打电话!
给落空空发短信!!
  1. 定义老师类Teacher, 属性和行为如下, 并在测试类TeacherTest中, 创建老师类的对象, 然后访问类中的成员.
    属性:
    姓名: name
    年龄: age
    讲课内容: content
    行为:
    吃饭: eat()
    讲课: teach()

老师类:

public class Teacher {
    private String name;
    private int age;
    private String content;

    public Teacher() {
    }

    public Teacher(String name, int age, String content) {
        this.name = name;
        this.age = age;
        this.content = content;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    // 行为方法
    // 吃饭
    public void eat(){
        System.out.println("年龄为" + age + "的" + name + "正在吃饭");
    }
    // 讲课
    public void teach(){
        System.out.println("年龄为" + age + "的" + name + "正在讲" + content);
    }


}

测试类:

public class TeacherTest {
    public static void main(String[] args) {
        Teacher teacher = new Teacher("刘老师",32,"java");

        // 打印行为
        teacher.eat();
        teacher.teach();
    }
}

运行结果为:

年龄为32的刘老师正在吃饭
年龄为32的刘老师正在讲java
  1. 定义项目经理类Manager, 属性和行为如下, 并在测试类ManagerTest中, 创建项目经理类的对象, 然后访问类中的成员.
    属性:
    姓名: name
    工号: id
    工资: salary
    奖金: bonus
    行为:
    工作: work()

经理类:

public class Manager {
    private String name;
    private  int id;
    private  int salary;
    private  int bonus;

    public Manager() {
    }

    public Manager(String name, int id, int salary, int bonus) {
        this.name = name;
        this.id = id;
        this.salary = salary;
        this.bonus = bonus;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public int getSalary() {
        return salary;
    }

    public void setSalary(int salary) {
        this.salary = salary;
    }

    public int getBonus() {
        return bonus;
    }

    public void setBonus(int bonus) {
        this.bonus = bonus;
    }

    // 行为
    public void work(){
        System.out.println("工号为" + id + ",基本工资为" + salary + ",奖金为" + bonus + "的项目经理" + name + "正在努力工作!!!");
    }
}

测试类:

public class ManagerTest {
    public static void main(String[] args) {
        Manager m1 = new Manager();
        m1.setId(123);
        m1.setSalary(16666);
        m1.setBonus(6666);
        m1.setName("张三");
        m1.work();

        Manager m2 = new Manager("李四",124,12345,1234);
        m2.work();
    }
}

运行结果为:

工号为123,基本工资为16666,奖金为6666的项目经理张三正在努力工作!!!
工号为124,基本工资为12345,奖金为1234的项目经理李四正在努力工作!!!
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值