继承(下)

继承中构造方法的访问特点

子类在初始化之前必须先初始化父类。即在调用子类的构造方法之前必须调用父类的构造方法。因为子类里面的有部分属性和方法使用的父类的,必须将父类的方法和属性初始化,子类才可以拿来使用(有父才有子)

我们的父类中会有JVM隐藏添加的空参构造方法

class Person{
   //隐藏构造方法 public Person(){}
}

子类中隐藏构造方法

class Student extends Person{
  //  public Student(){隐藏构造方法
       super();
    } 

当我们没有写空参构造方法时,JVM会默认添加上述空参构造。如果自己写了空参构造JVM则取消默认添加。每一个构造方法中默认隐藏代码super()
注意:当类创建了构造方法,JVM将默认不提供默认空参构造方法
**子类的所以构造方法都是默认调用空参构造super()

  • 总结


关于this()必须放在构造方法第一行原因的理解:

  • 在Java中,this() 是一个特殊的构造方法调用语句,用于在当前类的构造方法中调用其他重载的构造方法。而且,this() 必须作为构造方法内的第一条语- 句,这是因为以下两个原因:

  • 初始化顺序:在构造方法中,对象的成员变量需要进行初始化。如果某个构造方法中使用了 this() 来调用其他构造方法,那么被调用的构造方法会先执行,进行相应的成员变量赋值操作。因此,this() 语句必须放在构造方法的第一行,确保在当前构造方法之前没有执行其他的成员变量初始化代码。

内存图解

  • 总结

信息管理系统--集成改进

开发原则:开闭原则:对拓展内容开放,对修改内容关闭
对于想拓展一些新的内容开放,但是不能修改代码的原有内容

举例:我们发现StudentControlled类中关于对学生对象的封装,有更好的方式(原来使用空参构造,然后set方法赋值),现在有了新 的方式

我们可以复制一份StduentControllr文件,在这个文件的基础上进行修改,而不修改原有的文件

  • 想要
  • 我们复制了原来的StudentController类为OtherStudentController类,并修改OtherStuentController类中的代码()
  • 修改一下入口类里面的类就可以使用新的类了,而原有的类可以不必修改或者删除了

但是我们可以看到我们的StudentControler类和OtherStudentController类有大量相同的代码,只有键盘录入学生对象有差异,这时我们就可以将StudentController类和OtherStudentController类向上抽取成一个BeseStudentControllerl父类,在子类中重写键盘录入录入学生对象的方法就可以了

  • 信息管理系统改进详细步骤
  • 子类StudentController
package com.itheima.edu.info.manager.controller;

import com.itheima.edu.info.manager.domain.Student;
import com.itheima.edu.info.manager.service.StudentService;

import java.util.Scanner;

//客服接待,和用户打交道
public class StudentController extends BaseStudentController {

private Scanner sc = new Scanner(System.in);
    //键盘录入id

    //键盘录入学生信息
    @Override
    public Student inputStudentInfo(String id){

        System.out.println("请输入学生姓名:");
        final String name = sc.next();
        System.out.println("请输入学生年龄:");
        final int age = sc.nextInt();
        System.out.println("请输入学生生日:");
        final String birthday = sc.next();
        //2.将学生信息封装成对象
        Student student = new Student();
        student.setId(id);
        student.setName(name);
        student.setAge(age);
        student.setBirthday(birthday);
        return student;
    }
}

  • 子类OtherStudentController
package com.itheima.edu.info.manager.controller;

import com.itheima.edu.info.manager.domain.Student;
import com.itheima.edu.info.manager.service.StudentService;

import java.util.Scanner;

//客服接待,和用户打交道
public class OtherStudentController extends BaseStudentController {
    private Scanner sc = new Scanner(System.in);
    //键盘录入学生信息
    @Override
    public Student inputStudentInfo(String id){

        System.out.println("请输入学生姓名:");
        final String name = sc.next();
        System.out.println("请输入学生年龄:");
        final int age = sc.nextInt();
        System.out.println("请输入学生生日:");
        final String birthday = sc.next();
        //2.将学生信息封装成对象
        Student student = new Student(id, name, age, birthday);//对象封装对象进行修改

        return student;
    }
}

  • 父类BaseStudentController
package com.itheima.edu.info.manager.controller;

import com.itheima.edu.info.manager.domain.Student;
import com.itheima.edu.info.manager.service.StudentService;

import java.util.Scanner;

//客服接待,和用户打交道
public class BaseStudentController {
private Scanner sc = new Scanner(System.in);
 private StudentService studentService = new StudentService();

    //开启学生管理系统并展示菜单
    public void start() {

        System.out.println("--------------欢迎来到学生管理系统..............................");

        studentLoop:  while (true) {
            System.out.println("请输入你的选择:1.添加学生 2.删除学生 3.修改学生 4.查看学生 5.退出");
            final String choice = sc.next();
            switch (choice) {
                case "1":
                    addStudent();
                    break;
                case "2":
                    // System.out.println("删除学生");
                    deleteStudentById();
                    break;
                case "3":
                    // System.out.println("修改学生");
                    updateStudent();
                    break;
                case "4":
                    //System.out.println("查看学生");
                    findStudent();
                    break;
                case "5":
                    System.out.println("欢迎使用学生管理系统,再见");

                    break studentLoop;//这里需要跳转到总菜单
                default:
                    System.out.println("输入有误");
            }
        }
    }


    //修改学生
    public void updateStudent() {
        final String id = inputStudentId();
        //3.录入新的学生信息,并封装成对象
        Student student = inputStudentInfo(id);
        //4.调用studentService中的updateStudent方法修改学生信息,修改学生并提示修改成功
        studentService.updateStudent(id,student);
        System.out.println("修改成功");
    }

    public void deleteStudentById() {
        final String id = inputStudentId();
        //3.调用业务员中的deleteStudentById方法根据id删除学生
        studentService.deleteStudentById(id);
        //4.提示删除成功
        System.out.println("删除成功");
    }

    //查看学生
    public void findStudent() {
        //1.调用业务员中的获取方法,得到学生的对象数组
        Student[] students = studentService.findStudent();
        //2.判断数组的内存地址,是否为null
        if (students == null) {
            System.out.println("查无信息,请添加后重试");
            return;
        }
        //3.遍历数组,获取学生信息并打印在控制台上
        System.out.println("学号\t\t姓名\t年龄\t生日");
        for (Student student : students) {
            if (student != null) {
                System.out.println(student.getId() + "\t" + student.getName() + "\t\t" + student.getAge() + "\t\t" + student.getBirthday());
            }
        }
    }

    public void addStudent() {
        //1.    键盘接收学生信息
        StudentService studentService = new StudentService();

        String id;
        while (true) {
            System.out.println("请输入学生id:");
            id = sc.next();
            //判断id是否重复

            boolean reason = studentService.isExact(id);
            if (!reason) {//如果id没有重复则跳出循环
                break;
            } else {
                System.out.println("id已存在,请重新输入");
            }
        }

        //键盘录入学生信息
        final Student student = inputStudentInfo(id);
        //3.将学生对象传递给StudentService(业务员)中的addStudent方法(调用StudentService中的addStudent方法来实现功能)

        boolean reason = studentService.addStudent(student);
        //4.根据返回的boolean类型结果,在控制台打印成功/失败
        if (reason) {
            System.out.println("添加成功");
        } else {
            System.out.println("添加失败");
        }

    }
    //键盘录入id
    public String  inputStudentId(){
        String id;
        while (true) {
            System.out.println("请输入学生id:");
            id = sc.next();
            //判断id是否重复

            boolean reason = studentService.isExact(id);
            if (reason) {//如果id重复则跳出循环
                break;
            } else {
                System.out.println("id不存在,请重新输入");
            }
        }
        return id;
    }
    //键盘录入学生信息
    public Student inputStudentInfo(String id){//子类将重写该方法

       return null;
    }
}

抽象类入门

  • 举例理解抽象类和抽象方法

继承将类中相同的内容向上抽取成抽象类,而在子类中留下其特有的内容。对于父类中无法描述其逻辑的方法可以定义成抽象方法,而抽象方法必须存活在抽象类中

  • 抽象方法和抽象类的定义格式

此时信息管理系统分析:
1.Student类和老师类有相同的特征向上抽取为Person类
2.(修改新创建类中inputTeacherInfo方法被修改)根据开闭原则修改StudentController类,新创建一个OtherStudentController类(在OtherStudentController类中修改,StudentController类不变)
3.自然想到将StudentController类和OtherStudentController类抽取成一个父类BaseStudentController
4.发现在子类中都重写了InputStudentInfo方法,父类中的InputStudentInfo无法表示,所以父类中的该方法写成抽象方法,BaseStudentController写成抽象类
可以发现这一步步优化都是合情合理,都是很自然的

抽象类注意事项

1.抽象了必须存在构造方法

  • 因为子类继承抽象类,子类的构造方法都是隐藏自动调用父类的无参构造super()(没有就矛盾了)
    2.抽象类不能创建对象
  • 抽象类必有抽象方法,抽象方法没有方法体,如果抽象类可以实例化对象,然后调用抽象方法将没有任何意义
    3.抽象类的子类
  • 选择
  • A:必须重写抽象类所以的抽象方法
  • B:子类也为抽象类

4.抽象类中的方法

  • 有抽象方法的类必须是抽象类(抽象类中也可以没有抽象方法)
  • 总结

模板设计模式

设计模式是一个良好的编码风格


关于模板:模板可以认为了有一部分内容是固定不变的,而有一部分内容则是每一个人都不同。如作文中标题和结尾是固定的,而正文部分则是因人而异。我们的抽象类就可以这样来设计模板:将固定的内容设计成一般方法,子类直接继承,而将不同的内容设计成抽象方法,子类进行抽象。此时我们的抽象父类就是一个模板,而我们的子类就是根据模板衍生出来的不同的设计作品

  • 模板类
package com.itheima.test;
//作文的模板类
public abstract class CompositionTemplate {
    public void write(){
        System.out.println("<<我的爸爸>>");//标题(固定)
        body();//正文(由子类决定)
        System.out.println("这就是我的爸爸");//结尾(固定)
    }
    public abstract void body();
}

  • 子类
package com.itheima.test;

//现在Tom要用模板写作文
public class Tom extends CompositionTemplate {
    @Override
    public void body() {
        System.out.println("那是一个秋天,爸爸骑车带我上学,一切都是那样的美好" );
    }
}
  • 测试类
package com.itheima.test;

public class Test {
    public static void main(String[] args) {
        Tom tom = new Tom();
        tom.write();
    }
}

  • 总结

final关键字

  • 存在的问题

  • 解决

  • 此时将会强制无法重写

    建议以后的模板方法都使用final修饰

  • final修饰类的应用场景
    假如你写好了一个类,这个类中所有的方法都是核心方法,不希望子类去重写这个类的方法。解决方法1.在类中的所有的方法上面加上final2.将类定义成final(更简便)

  • final修饰变量(基本变量和引用变量)


信息管理系统-- 抽象类的改进

  • 修改点

    修改点:在此之前我们的BaseStudentController类已经为抽象类, 很显然我们只希望抽象方法被抽象而,其他方法不变。这符合模板设计模式。所有我们需要保证模板方法不被重写,所有我们可以将模板方法定义为final

代码块

  • 信息系统存在的问题(使用后面学的静态代码块实现优化)


  • 构造方法和构造代码块执行顺序

  • 构造代码块的作用

  • 举例(构造代码块的作用): 每次创建一个对象前都要打印一句“好好学习”

  • 没有使用构造代码块的时

package com.itheima.test1;

public class Student  {
   public String name;
   //空参构造
   public Student(){
       System.out.println("好好学习");
    }
    //全参构造
    public Student(String name){
        System.out.println("好好学习");
        this.name=name;
    }
}

  • 使用构造代码块进行优化
package com.itheima.test1;

public class Student  {
   public String name;
   //构造代码块
   {
       System.out.println("好好学习");
   }
   //空参构造
   public Student(){

    }
    //全参构造
    public Student(String name){
        this.name=name;
    }
}

将多个构造方法中相同的代码抽取出来,提高的代码的复用性

  • 静态代码块
  • 代码块总结

信息管理系统---代码块改进

  • 改进点
  • StudentDao改进
  • 1.因为我们是对数据进行操作的,所以一定是在Dao类中进行操作,所以是在StudentDao中创建静态代码块
  • 2.当我们执行程序入口(加载InfoManageEntry类),点击学生管理(加载StudentService),点击查看学生,调用StduentDao中的FindStudent方法,此时加载StdudntDao类,学生数据被初始化**
    可以发现我们的调用是一级一级进行,然后一级一级返回的
posted @ 2023-06-27 11:01  一往而深,  阅读(5)  评论(0编辑  收藏  举报