面向对象进阶部分学习方法:

特点:

逻辑性没有那么强,但是概念会比较多。

记忆部分重要的概念,理解课堂上讲解的需要大家掌握的概念,多多练习代码。

今日内容

  • 复习回顾
  • static关键字
  • 继承

教学目标



第一章 复习回顾

1.1 如何定义类

类的定义格式如下:

修饰符 class 类名 {
    // 1.成员变量(属性)
    // 2.成员方法 (行为) 
    // 3.构造方法 (初始化类的对象数据的)
}

例如:

public class Student {
    // 1.成员变量
    public String name ;
    public char sex ; // '男'  '女'
    public int age;
}

1.2 如何通过类创建对象

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

例如:

Student stu = new Student();

1.3 封装

1.3.1 封装的步骤

1.使用 private 关键字来修饰成员变量。

2.使用public修饰getter和setter方法。

1.3.2 封装的步骤实现
  1. private修饰成员变量
public class Student {
    private String name;
    private int age;
}
  1. public修饰getter和setter方法
public class Student {
    private String name;
    private int age;

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

    public String getName() {
      	return name;
    }

    public void setAge(int a) {
        if (a > 0 && a <200) {
            age = a;
        } else {
            System.out.println("年龄非法!");
        }
    }

    public int getAge() {
      	return age;
    }
}

1.4 构造方法

1.4.1 构造方法的作用

在创建对象的时候,可以给成员变量进行初始化。

初始化即赋值的意思。

1.4.2 构造方法的格式

修饰符 类名(形参列表) {
    // 构造体代码,执行代码
}

1.4.3 构造方法的应用

首先定义一个学生类,代码如下:

public class Student {
    // 1.成员变量
    public String name;
    public int age;

    // 2.构造方法
    public Student() {
		System.out.println("无参数构造方法被调用");
    }
}

接下来通过调用构造方法得到两个学生对象。

public class CreateStu02 {
    public static void main(String[] args) {
        // 创建一个学生对象
        // 类名 变量名称 = new 类名();
        Student s1 = new Student();
        // 使用对象访问成员变量,赋值
        s1.name = "张三";
        s1.age = 20 ;

        // 使用对象访问成员变量 输出值
        System.out.println(s1.name);
        System.out.println(s1.age); 

        Student s2 = new Student();
        // 使用对象访问成员变量 赋值
        s2.name = "李四";
        s2.age = 18 ;
        System.out.println(s2.name);
        System.out.println(s2.age);
    }
}

1.5 this关键字的作用

1.5.1 this关键字的作用

this代表所在类的当前对象的引用(地址值),即代表当前对象。

1.5.2 this关键字的应用

1.5.2.1 用于普通的gettter与setter方法

this出现在实例方法中,谁调用这个方法(哪个对象调用这个方法),this就代表谁(this就代表哪个对象)。

public class Student {
    private String name;
    private int age;

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

    public String getName() {
      	return name;
    }

    public void setAge(int age) {
        if (age > 0 && age < 200) {
            this.age = age;
        } else {
            System.out.println("年龄非法!");
        }
    }

    public int getAge() {
      	return age;
    }
}
1.5.2.2 用于构造方法中

this出现在构造方法中,代表构造方法正在初始化的那个对象。

public class Student {
    private String name;
    private int age;
    
    // 无参数构造方法
    public Student() {} 
    
    // 有参数构造方法
    public Student(String name,int age) {
    	this.name = name;
    	this.age = age; 
    }
}

面向对象—构建一个JavaBean对象

特征 1: 成员变量私有.2:提供get set。3:一般会有 空参 满参。

第二章 static关键字

2.1 概述

以前我们定义过如下类:

public class Student {
    // 成员变量
    public String name;
    public char sex; // '男'  '女'
    public int age;

    // 无参数构造方法
    public Student() {

    }
    
    // 有参数构造方法
    public Student(String  a) {

    }
}

我们已经知道面向对象中,存在类和对象的概念,我们在类中定义了一些成员变量,例如name,age,sex ,结果发现这些成员变量,每个对象都存在(因为每个对象都可以访问)。

而像name ,age , sex确实是每个学生对象都应该有的属性,应该属于每个对象。

所以Java中成员(变量和方法)等是存在所属性的,Java是通过static关键字来区分的。static关键字在Java开发非常的重要,对于理解面向对象非常关键。

关于 static 关键字的使用,它可以用来修饰的成员变量和成员方法,被static修饰的成员是属于类的是放在静态区中,没有static修饰的成员变量和方法则是属于对象的。我们上面案例中的成员变量都是没有static修饰的,所以属于每个对象。

2.2 定义格式和使用

static是静态的意思。 static可以修饰成员变量或者修饰方法。

2.2.1 静态变量的引入

有static修饰成员变量,说明这个成员变量是属于类的,这个成员变量称为类变量或者静态成员变量。 直接用 类名访问即可。因为类只有一个,所以静态成员变量在内存区域中也只存在一份。所有的对象都可以共享这个变量。

如何使用呢

例如现在我们需要定义传智全部的学生类,那么这些学生类的对象的老师属性应该都是“石破天”,这个时候我们可以把这个属性定义成static修饰的静态成员变量。

定义格式

修饰符 static 数据类型 变量名 = 初始值;

举例

public class Student {
    String name;//成员变量--实例变量
    int age ;//成员变量--实例变量
    static String teacherName;//静态变量 (共享变量)


    public void study(){
        System.out.println(name+"正在"+teacherName+
                "指导下学习...");
    }
}

静态成员变量的访问:

格式:类名.静态变量

/*
   如果想定义 被多个对象 共享的成员 可以在 这个成员的前面加上
     一个关键字 static
       对于 静态成员访问  建议直接类名访问
         因为 加了静态之后属于类 不属于某个对象
         优先于对象出现的。
 */
public class Test {

    public static void main(String[] args) {

        Student.teacherName = "石破天老师";//访问静态变量

        //创建学生对象
        Student s1 = new Student();
        s1.name="努力的迪迪";//不加static的变量属于对象
        s1.age = 23;
        s1.study();

        Student s2 = new Student();
        s2.study();

    }
}

2.2.2 静态使用语法

总结
static关键字是什么?
  static表示静态,是Java中的一个修饰符,可以修饰成员方法,成员变量。
什么是静态变量,静态方法?
   被static修饰的成员变量,叫静态变量。
   被static修饰的成员方法,叫静态方法。
有什么特点?
    静态成员不属于对象,但是可以被所有对象共享。
    静态成员属于类,也叫类成员,随着类的加载而加载,优先于对象存在。
    一般通过类名调用,也可以通过对象名调用,但是不推荐。

2.2.3 静态变量的应用案例

有static修饰成员变量,说明这个成员变量是属于类的,这个成员变量称为类变量或者静态成员变量。 直接用 类名访问即可。因为类只有一个,所以静态成员变量在内存区域中也只存在一份。所有的对象都可以共享这个变量。

学习完static修饰成员变量的基本使用之后,接下来我们学习一下static修饰成员变量在实际工作中的应用。

在实际开发中,如果某个数据只需要一份,且希望能够被共享(访问、修改),则该数据可以定义成类变量来记住。

我们看一个案例

需求:系统启动后,要求用于类可以记住自己创建了多少个用户对象。**

  • 第一步:先定义一个User类,在用户类中定义一个static修饰的变量,用来表示在线人数;
public class User{
    public static int number;
    //每次创建对象时,number自增一下
    public User(){
        User.number++;
    }
}
  • 第二步:再写一个测试类,再测试类中创建4个User对象,再打印number的值,观察number的值是否再自增。
public class Test{
    public static void main(String[] args){
        //创建4个对象
        new User();
        new User();
        new User();
        new User(); 
        
        //查看系统创建了多少个User对象
        System.out.println("系统创建的User对象个数:"+User.number);
    }
}

运行上面的代码,查看执行结果是:系统创建的User对象个数:4

2.2.4 静态方法应用案例

学习完static修饰方法之后,我们讲一个有关类方法的应用知识,叫做工具类。

如果一个类中的方法全都是静态的,那么这个类中的方法就全都可以被类名直接调用,由于调用起来非常方便,就像一个工具一下,所以把这样的类就叫做工具类。

package com.itheima.static03;
/*
  ArrayUtil 数组工具类
    里面提供了 操作数组的方法
    为什么叫工具类呢?
      区别于javabean类的,不表示任何事物。
      里面只是定义 操作数组的方法!!
      因为主要是来调用方法,所以对象不重要,所以这里方法都是静态的,构造私有的。
  工具类特点
    1: 构造私有。
    2: 方法静态。
    3: 见名知意。
 */
public class ArrayUtil {
    // 构造私有的目的 不叫别人创建对象
    private ArrayUtil(){}

    /*
    设计方法
      提供一个 打印方法 printArr 用于返回整数数组的内容。
      一个模板 两个明确
         明确参数列表  :
                 整数数组  int[]
         明确返回值类型
                  String
     实现方法:
         int[] arr = {1,2,3,4};
         通过方法
          产出一个字符串 [1,2,3,4]
         拼接字符串
     */
    public static String printArr(int[] arr){
        // 定义一个 初始字符串
        String str = "[";
        // 循环遍历 进行字符串拼接
        for (int i = 0; i < arr.length; i++) {
            // arr[i]每个元素
            if(i== arr.length-1){//是最后一个元素
                str += arr[i];//不要逗号
            }else {//不是最后一个元素
                str += arr[i]+",";//要逗号
            }
        }
        // 拼接末尾
       str += "]";
        // 拼接完成再返回
        return str;
    }

    /*
     提供一个getAverage 用于返回平均分
        求 浮点型数组  的 平均分
        明确 返回值  平均分     double
        明确 参数    浮点型数组 double[]
     */
    public static double getAverage(double[] arr){
        // 求和 套路
        // 定义求和变量
        double sum = 0;
        // 遍历 进行累加
        for (int i = 0; i < arr.length; i++) {
            sum += arr[i];
        }
        // 处理 求和变量
        double avg = sum/arr.length;

        return avg;
    }
}
package com.itheima.static03;

import java.util.Arrays;

public class TestDemo {

    public static void main(String[] args) {
        //测试 printArr方法 将一个整数数组 转换成 字符串
        // 定义 整数数组
        int[] arr = {9,5,2,7};
        //调用 的 工具方法 完成转换
        String s = ArrayUtil.printArr(arr);
        System.out.println(s);

        //测试getAverage 获取平均分方法
        double[] arr2 = {98.5,97.5,99.5,0};
        double average = ArrayUtil.getAverage(arr2);
        System.out.println(average);

        //刚刚自己写的工具类---玩一次就行
        String s1 = Arrays.toString(arr);
        System.out.println(s1);
    }
}
总结
类方法有啥应用场景?
    可以用来设计工具类。
工具类是什么,有什么好处?
      工具类中的方法都是类方法,每个类方法都是用来完成一个功能的。
       提高了代码的复用性;调用方便,提高了开发效率。
为什么工具类要用类方法,而不是用实例(普通成员)方法?
       实例方法需要创建对象来调用,会浪费内存。
工具类定义时有什么要求?
       工具类不需要创建对象, 建议将工具类的构造器私有化

2.2.4 类成员 实例成员辨析

Java中的成员按照有无static修饰分成两种:类成员,实例成员。

有static修饰的,被称为类成员,属于类:比如

  • 类变量:也叫静态变量,有static修饰,属于类,计算机里只有一份,会被类的全部对象共享。
  • 类方法:也叫静态方法,有static修饰的方法,属于类,可以通过类名直接调用。

无static修饰的,被称为实例成员,属于对象:比如

  • 实例变量:属于对象,每个对象中都有一份,只能用对象访问。
  • 实例方法:无static修饰的成员方法,属于对象,只能对象访问。
public class People {
    //  静态变量
    static String name;

    // 实例变量
    int age ;


    public static void show1(){
        System.out.println("这是一个静态方法");
    }

    public void show2(){
        System.out.println("这是一个实例方法");
    }
}

访问:

package com.itheima.static02;
/*
  加入了静态之后
     方法和变量访问  可以直接通过类
     不用创建对象情况下 完成方法和变量调用。
 */
public class Test {
    public static void main(String[] args) {
        //静态变量 直接类名调用
        People.name = "jackMa";
        //静态方法 类名直接调用
        People.show1();
        // 静态内容 被对象 共享
//        People.show2(); 类无法调用实例内容
    }
}

2.2.5 static使用注意事项

  • 静态方法只能访问静态变量和静态方法
  • 非静态方法可以访问静态变量或者静态方法,也可以访问非静态的成员变量和非静态的成员方法
  • 静态方法中是没有this关键字

演示–了解目的

package com.itheima.static04notice;

public class People {

    static String name;// 静态变量--类变量

    int age;//成员变量--实例变量

    //静态方法--类方法
    public static void show1(){
        System.out.println("这是一个静态方法");
        System.out.println(name);//静态方法中 访问静态变量
        method1();//静态方法中 可以调用静态方法

//        System.out.println(age);无法访问非静态
//        show2();无法访问非静态
    }

    //非静态--普通方法--实例方法
    public void show2(){
        System.out.println("这是一个实例方法");
        System.out.println(name);//可以访问静态
        method1();
    }

    public static void method1(){}
}

2.2.6 单例模式

各位同学,关于static的应用我们再补充一个使用的技巧,叫做单例设计模式。

所谓设计模式指的是,一类问题可能会有多种解决方案,而设计模式是在编程实践中,多种方案中的一种最优方案。

示例

package com.itheima.static05;
/*
   保证 程序中 有且只有 一个app对象
   合理隐藏
    1:私有构造(不让外界new)
   合理暴露
    2:new 出一个对象 在当前类中
      定义为私有的静态变量  因为类加载一次 静态变量初始化一次。
    3:如何暴露出来
        提供get方法---也得是静态

        饿汉式
            立即加载,当类产生的时候,对象就产生了,不管你用不用对象。
            大学食堂
            (用于--访问量大的对象。)

        懒汉式  --多线程补充
           延迟加载,当类产生的时候,不创建对象。需要再创建。
           小饭馆
           (用于--个性的 访问量小的)

 */
public class App {

    private static App app = new App();//实例变量?  静态变量?
    // 保证new的事情 做一次  静态变量 随着类的加载而加载 一次

    private App(){}

    public static App getApp(){
        return app;
    }
}
package com.itheima.static05;

public class Phone {

    public static void main(String[] args) {
        //需求 打开 app的时候 只允许在内存中运行一个对象
        App a1 = App.getApp();
        App a2 = App.getApp();
        App a3 = App.getApp();

        System.out.println(a1);
        System.out.println(a2);
        System.out.println(a3);
    }
}

2.3 小结

1.当 static 修饰成员变量或者成员方法时,该变量称为静态变量,该方法称为静态方法。该类的每个对象都共享同一个类的静态变量和静态方法。任何对象都可以更改该静态变量的值或者访问静态方法。但是不推荐这种方式去访问。因为静态变量或者静态方法直接通过类名访问即可,完全没有必要用对象去访问。

2.无static修饰的成员变量或者成员方法,称为实例变量,实例方法,实例变量和实例方法必须创建类的对象,然后通过对象来访问。

3.static修饰的成员属于类,会存储在静态区,是随着类的加载而加载的,且只加载一次,所以只有一份,节省内存。存储于一块固定的内存区域(静态区),所以,可以直接被类名调用。它优先于对象存在,所以,可以被所有对象共享。

4.无static修饰的成员,是属于对象,对象有多少个,他们就会出现多少份。所以必须由对象调用。

第三章 继承

3.1 概述

3.1.1 引入

假如我们要定义如下类:
学生类,老师类和工人类,分析如下。

  1. 学生类
    属性:姓名,年龄
    行为:吃饭,睡觉
  2. 老师类
    属性:姓名,年龄,薪水
    行为:吃饭,睡觉,教书
  3. 班主任
    属性:姓名,年龄,薪水
    行为:吃饭,睡觉,管理

如果我们定义了这三个类去开发一个系统,那么这三个类中就存在大量重复的信息(属性:姓名,年龄。行为:吃饭,睡觉)。这样就导致了相同代码大量重复,代码显得很臃肿和冗余,那么如何解决呢?

假如多个类中存在相同属性和行为时,我们可以将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那一个类即可。如图所示:

其中,多个类可以称为子类,单独被继承的那一个类称为父类超类(superclass)或者基类

3.1.2 继承的含义

继承描述的是事物之间的所属关系,这种关系是:is-a 的关系。例如,兔子属于食草动物,食草动物属于动物。可见,父类更通用,子类更具体。我们通过继承,可以使多种事物之间形成一种关系体系。

继承:就是子类继承父类的属性行为,使得子类对象可以直接具有与父类相同的属性、相同的行为。子类可以直接访问父类中的非私有的属性和行为。

3.1.3 继承的好处

  1. 提高代码的复用性(减少代码冗余,相同代码重复利用)。
  2. 使类与类之间产生了关系。

3.1.4 继承的格式

通过 extends 关键字,可以声明一个子类继承另外一个父类,定义格式如下:

class 父类 {
	...
}

class 子类 extends 父类 {
	...
}

需要注意:Java是单继承的,一个类只能继承一个直接父类,跟现实世界很像,但是Java中的子类是更加强大的。

3.2 继承案例

各位同学,学习完继承的快速入门之后,接下来我们学习一下继承的好处,以及它的应用场景。

我们通过一个案例来学习

观察代码发现,我们会发现Teacher类中和Consultant类中有相同的代码;其实像这种两个类中有相同代码时,没必要重复写。

我们可以把重复的代码提取出来,作为父类,然后让其他类继承父类就可以了,这样可以提高代码的复用性。改造后的代码如下:

接下来使用继承来完成上面的案例,这里只演示People类和Teacher类,然后你尝试自己完成Consultant类。

  • 先写一个父类 People,用来设计Teacher和Consultant公有的成员。
public class People{
    private String name;
    
    public String getName(){
        return name;
    }
    public void setName(String name){
        this.name=name;
    }
}
  • 再写两个子类Teacher继承People类,同时在子类中加上自己特有的成员。
public class Teacher extends People{
    private String skill; //技能
    
    public String getSkill(){
        return skill;
    }
    
    public void setSkill(String skill){
        this.skill=skill;
    }
    
    public void printInfo(){
        System.out.println(getName()+"具备的技能:"+skill);
    }
}
  • 最后再写一个测试类,再测试类中创建Teacher、Consultant对象,并调用方法。
public class Test {
    public static void main(String[] args) {
        // 目标:搞清楚继承的好处。
        Teacher t = new Teacher();
        t.setName("播仔");
        t.setSkill("Java、Spring");
        System.out.println(t.getName());
        System.out.println(t.getSkill());
        t.printInfo();
    }
}

执行代码,打印结果如下:

关于继承的好处我们只需要记住:继承可以提高代码的复用性

3.3 权限修饰符

在Java中提供了四种访问权限,使用不同的访问权限修饰符修饰时,被修饰的内容会有不同的访问权限,我们之前已经学习过了public 和 private,接下来我们研究一下protected和默认修饰符的作用。

  • public:公共的,所有地方都可以访问。
  • protected:本类 ,本包,其他包中的子类都可以访问。
  • 默认(没有修饰符):本类 ,本包可以访问。
    注意:默认是空着不写,不是default
  • private:私有的,当前类可以访问。
    public > protected > 默认 > private

public

protected

默认

private

同一类中





同一包中的类




不同包的子类



不同包中的无关类


可见,public具有最大权限。private则是最小权限。

编写代码时,如果没有特殊的考虑,建议这样使用权限:

  • 成员变量使用private ,隐藏细节。
  • 构造方法使用 public ,方便创建对象。
  • 成员方法使用public ,方便调用方法。

小贴士:不加权限修饰符,就是默认权限

3.4 单继承 Object

刚才我们写的代码中,都是一个子类继承一个父类,那么有同学问到,一个子类可以继承多个父类吗?

Java语言只支持单继承,不支持多继承,但是可以多层继承。就像家族里儿子、爸爸和爷爷的关系一样:一个儿子只能有一个爸爸,不能有多个爸爸,但是爸爸也是有爸爸的。

public class Test {
    public static void main(String[] args) {
        // 目标:掌握继承的两个注意事项事项。
        // 1、Java是单继承的:一个类只能继承一个直接父类;
        // 2、Object类是Java中所有类的祖宗。
        A a = new A();
        B b = new B();

        ArrayList list = new ArrayList();
        list.add("java");
        System.out.println(list.toString());
    }
}

class A {} //extends Object{}
class B extends A{}
// class C extends B , A{} // 报错
class D extends B{}

3.5 方法重写

各位同学,学习完继承之后,在继承的基础之上还有一个很重要的现象需要给大家说一下。

叫做方法重写。为了让大家能够掌握方法重写,我们先认识什么是方法重写,再说一下方法的应用场景。

什么是方法重写

当子类觉得父类方法不好用,或者无法满足父类需求时,子类可以重写一个方法名称、参数列表一样的方法,去覆盖父类的这个方法,这就是方法重写。

注意:重写后,方法的访问遵循就近原则。下面我们看一个代码演示

例如:我们定义了一个动物类代码如下:

public class Animal  {
    public void run(){
        System.out.println("动物跑的很快!");
    }
    public void cry(){
        System.out.println("动物都可以叫~~~");
    }
}

然后定义一个猫类,猫可能认为父类cry()方法不能满足自己的需求

代码如下:

public class Cat extends Animal {
    public void cry(){
        System.out.println("我们一起学猫叫,喵喵喵!喵的非常好听!");
    }
}

public class Test {
	public static void main(String[] args) {
      	// 创建子类对象
      	Cat ddm = new Cat();
        // 调用父类继承而来的方法
        ddm.run();
      	// 调用子类重写的方法
      	ddm.cry();
	}
}

执行代码,我们发现真正执行的是Cat类中的cry方法

知道什么是方法重写之后,还有一些注意事项,需要和大家分享一下。

- 1.重写的方法上面,可以加一个注解@Override,用于标注这个方法是复写的父类方法
- 2.子类复写父类方法时,访问权限必须大于或者等于父类方法的权限
	public > protected > 缺省
- 3. 重写的方法返回值类型,必须与被重写的方法返回值类型一样,或者范围更小
- 4. 私有方法、静态方法不能被重写,如果重写会报错。

关于这些注意事项,同学们其实只需要了解一下就可以了。实际上我们实际写代码时,只要和父类写的一样就可以( 总结起来就8个字:声明不变,重新实现

方法重写的应用场景

学习完方法重写之后,接下来,我们还需要大家掌握方法重写,在实际中的应用场景。方法重写的应用场景之一就是:子类重写Object的toString()方法,以便返回对象的内容。

比如:有一个Student类,这个类会默认继承Object类。

public class Student extends Object{
    private String name;
    private int age;

    public Student() {
    }

    public Student(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;
    }
}

其实Object类中有一个toString()方法,直接通过Student对象调用Object的toString()方法,会得到对象的地址值。

public class Test {
    public static void main(String[] args) {
        Student s = new Student("播妞", 19);
        // System.out.println(s.toString());
        System.out.println(s);
    }
}

但是,此时不想调用父类Object的toString()方法,那就可以在Student类中重新写一个toSting()方法,用于返回对象的属性值。

package com.itheima.extends05;

public class Student {

    private String name;
    private int age;

    public Student() {
    }

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

    // 快捷键 toS 回车


    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

}

重新运行测试类,结果如下

好了,到这里方法什么是方法重写,以及方法重写的应用场景我们就学习完了。

package com.itheima.extends06;

public class Dog {

    public void eat(){
        System.out.println("吃狗粮");
    }

    public void drink(){
        System.out.println("狗舔水..");
    }

    public void lookDoor(){
        System.out.println("狗子看家");
    }


}
package com.itheima.extends06;

public class EnZuo extends Dog{

    //不重写
    //特有功能
    public void chaiJia(){
        System.out.println("二哈拆家");
    }
}
package com.itheima.extends06;

public class PiPi extends Dog{

    @Override
    public void eat() {
        super.eat();//调用父类的 eat方法
        System.out.println("吃骨头");
    }
}
package com.itheima.extends06;

public class BaJie extends Dog{

    @Override
    public void eat() {
        System.out.println("吃剩饭..");
    }
}
package com.itheima.extends06;

public class Demo {

    public static void main(String[] args) {
        PiPi pp = new PiPi();
        pp.eat();
        pp.drink();
        pp.lookDoor();
    }
}

3.6 继承后的特点—子类中访问成员的特点

各位同学,刚才我们已经学习了继承,我们发现继承至少涉及到两个类,而每一个类中都可能有各自的成员(成员变量、成员方法),就有可能出现子类和父类有相同成员的情况,那么在子类中访问其他成员有什么特点呢?

  • 原则:在子类中访问其他成员(成员变量、成员方法),是依据就近原则的

定义一个父类,代码如下

package com.itheima.extends07;

public class Fu {
    int num = 100 ;

    public void method(){
        System.out.println("父method");
    }
}

再定义一个子类,代码如下。有一个同名的num成员变量,有一个同名的method成员方法;

package com.itheima.extends07;

public class Zi extends Fu{
    int num = 200;

    public void show(){
        int num = 300;
        System.out.println(num);//300
        System.out.println(this.num);//200
        System.out.println(super.num);//100
        //访问method
        this.method();
        //父类的method
        super.method();
    }

    @Override
    public void method() {
        System.out.println("子method");
    }
}

接下来写一个测试类,观察运行结果,我们发现都是调用的子类变量、子类方法。

package com.itheima.extends07;
/*
  理解
   1:子类方法中访问成员的时候,遵循就近原则。
      变量--局部位置找--成员位置--父类成员位置
      方法--当前类中找---父类中找
   2:
     this表示当前对象,
       通过它可以访问成员变量 成员方法
     super 父类的标识
       通过它可以访问 父类的成员变量 成员方法
 */
public class Demo {
    public static void main(String[] args) {
        //创建子类对象
        Zi z = new Zi();
        z.show();
    }
}
  • 如果子类和父类出现同名变量或者方法,优先使用子类的;此时如果一定要在子类中使用父类的成员,可以加this或者super进行区分。

3.7 继承后的特点—构造方法

3.7.1 引入

当类之间产生了关系,其中各类中的构造方法,又产生了哪些影响呢?
首先我们要回忆两个事情,构造方法的定义格式和作用。

  1. 构造方法的名字是与类名一致的。所以子类是无法继承父类构造方法的。
  2. 构造方法的作用是初始化对象成员变量数据的。所以子类的初始化过程中,必须先执行父类的初始化动作。子类的构造方法中默认有一个super() ,表示调用父类的构造方法,父类成员变量初始化后,才可以给子类使用。(先有爸爸,才能有儿子

继承后子类构方法器特点:子类所有构造方法的第一行都会默认先调用父类的无参构造方法

3.7.2 案例演示

代码如下:

package com.itheima.extends08;

public class Fu {

    public Fu(){
        System.out.println("fu的无参构造~");
    }

    public Fu(String name){
        System.out.println("父的有参构造");
    }
}
package com.itheima.extends08;

public class Zi extends Fu{

    public Zi(){
        // 先执行了父类的无参构造...
//        super();
        // 调用父类的带参构造
        super("haha");
        System.out.println("子的无参构造");

    }

    public Zi(String name){
        // 先执行了父类的无参构造
        System.out.println("子的带参构造");
    }
}
package com.itheima.extends08;

public class Demo {
    /*
     理解
       1:子类构造执行时,默认会先执行父类的无参构造。
        子类构造的第一行 默认是 super()
                            访问父类的构造
       为什么有这个情况,因为我们想在子类对象构建的时候
                      先初始化父类成员。
                      所以在执行子类构造的时候,
                      调用了父类的无参构造。
       2: 怎么调用父类的有参构造呢?
          super() 无参构造
          super(...)
     */
    public static void main(String[] args) {
        Zi zi1 = new Zi();

        Zi zi2 = new Zi("jack");

    }
}

3.7.3 小结

  • 子类构造方法执行的时候,都会在第一行默认先调用父类无参数构造方法一次。
  • 子类构造方法的第一行都隐含了一个**super()**去调用父类无参数构造方法,**super()**可以省略不写。

3.8 super(…)和this(…)

3.8.1 引入

请看上节中的如下案例:

package com.itheima.thisuper;

public class Fu {

    private String name;



    public Fu(String name){
        this.name = name;
    }
}

package com.itheima.thisuper;

public class Zi extends Fu{

    public Zi(){
      //super()  访问父类有参
//        super("哈哈");
       this("哈哈");
    }

    public Zi(String name){
        super(name);
    }
}

我们发现,通过super(…)可以访问父类带参构造,this(…)可以访问本类的构造.

3.8.2 super和this的用法格式

super和this完整的用法如下,其中this,super访问成员我们已经接触过了。

this.成员变量    	--    本类的
super.成员变量    	--    父类的

this.成员方法名()  	--    本类的    
super.成员方法名()   --    父类的

接下来我们使用调用构造方法格式:

super(...) -- 调用父类的构造方法,根据参数匹配确认
this(...) -- 调用本类的其他构造方法,根据参数匹配确认

3.8.3 super(…)案例图解

父类空间优先于子类对象产生

在每次创建子类对象时,先初始化父类空间,再创建其子类对象本身。目的在于子类对象中包含了其对应的父类空间,便可以包含其父类的成员,如果父类成员非private修饰,则子类可以随意使用父类成员。代码体现在子类的构造七调用时,一定先调用父类的构造方法。理解图解如下:

3.8.4 小结

  • 子类的每个构造方法中均有默认的super(),调用父类的空参构造。手动调用父类构造会覆盖默认的super()。
  • super() 和 this() 都必须是在构造方法的第一行,所以不能同时出现。
  • super(…)和this(…)是根据参数去确定调用父类哪个构造方法的。
  • super(…)可以调用父类构造方法初始化继承自父类的成员变量的数据。
  • this(…)可以调用本类中的其他构造方法。

4. 关于今天知识的小结:

会写一个继承结构下的标准Javabean即可

  1. 经理
    成员变量:工号,姓名,工资,管理奖金
    成员方法:工作(管理其他人),吃饭(吃米饭)
  2. 厨师
    成员变量:工号,姓名,工资
    成员方法:工作(炒菜),吃饭(吃米饭)

分享书写技巧:

1.在大脑中要区分谁是父,谁是子

2.把共性写到父类中,独有的东西写在子类中

3.开始编写标准Javabean(从上往下写)

4.在测试类中,创建对象并赋值调用

代码示例:

员工类

package com.itheima.test;
/*
  员工类 作为父类出现
 */
public class Employee {
    private String id;//工号
    private String name;//姓名
    private double salary;//工资

   //生成满参空参构造
    public Employee() {
    }

    public Employee(String id, String name, double salary) {
        this.id = id;
        this.name = name;
        this.salary = salary;
    }
    //生成set get方法

    public String getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public double getSalary() {
        return salary;
    }

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

    //工作方法  吃饭方法
    public void work(){
        System.out.println("员工都在干活....");
    }

    public void eat(){
        System.out.println("员工都在吃米饭!!!");
    }
}

管理类

package com.itheima.test;
/*
  经理类 继承  员工类
 */
public class Manager extends Employee {
    //特有成员变量
    private double bonus;
    //写构造
    //  子父类--产生了基础 父类还有成员变量
    // 构造  子类无参调用父类无参  子类带参调用父类带参
    //          只有这样才能实现数据的初始化

   //alt+insert 找到构造 选择 空参的 select none
    public Manager() {
    }
    //alt+insert 找到构造 选择带参  后面 ok
    public Manager(String id, String name, double salary, double bonus) {
        super(id, name, salary);//三个给父类
        this.bonus = bonus;//一个给自己
    }
    //set get

    public double getBonus() {
        return bonus;
    }

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

    //重写 work

    @Override
    public void work() {
        System.out.println(getName()+"正在管理其他人..."+"管理奖金:"+getBonus());
    }
}

厨师类

package com.itheima.test;
/*
  作为子类 继承自 父类员工
 */
public class Cooker extends Employee{
   //无参和带参
    public Cooker() {
    }

    public Cooker(String id, String name, double salary) {
        super(id, name, salary);//调用父类带参构造
    }

    @Override
    public void work() {
        System.out.println("工号为"+getId()+"厨师:"+getName()+"正在炒菜...."+"工资是:"+getSalary());
    }
}

测试类

package com.itheima.test;

public class Test {

    public static void main(String[] args) {
        Cooker cooker = new Cooker("9527","华安",1000);
        cooker.work();
        //测试 经理
        Manager m = new Manager("001", "吴宇航", 100000, 1000000);
        m.work();
    }
}