校园信息管理 Day04(终)通过Java特性完成代码的优化

阅读 88

2022-03-30

校园信息管理 Day04(终)通过Java特性完成代码的优化

1案例驱动

1.1学习方式

  • 根据项目来驱动学习,找出项目存在的问题,通过新的知识点进行解决问题
  • 代码配合文字说明,一步一步慢慢走
  • 开发原则;对扩展内容开放,对修改内容关闭

1.2项目名称

校园信息管理系统

1.3思想介绍

分类思想

1.3.1概述

  • 分工协作,专人做专事

1.3.2目的

  • 解决重复代码过多的冗余,提高代码的复用性
  • 解决业务逻辑聚集紧密导致的可读性差,提高代码的可读性
  • 解决代码可维护性差,提高代码的维护性

1.3.3方法

  • 创建多个类来存储不同的对象,来模拟现实的系统运作
  • Student类 标准学生类,封装键盘录入的学生信息(id , name , age , birthday)
  • StudentDao类 Dao : (Data Access Object 缩写) 用于访问存储数据的数组或集合
  • StudentService类 用来进行业务逻辑的处理(例如: 判断录入的id是否存在)
  • StudentController类 和用户打交道(接收用户需求,采集用户信息,打印数据到控制台)

2系统介绍

2.1需求介绍

  • 添加学生: 键盘录入学生信息(id,name,age,birthday)
    使用数组存储学生信息,要求学生的id不能重复
  • 删除学生: 键盘录入要删除学生的id值,将该学生从数组中移除,如果录入的id在数组中不存在,需要重新录入
  • 修改学生: 键盘录入要修改学生的id值和修改后的学生信息将数组中该学生的信息修改,如果录入的id在数组中不存在,需要重新录入
  • 查询学生: 将数组中存储的所有学生的信息输出到控制台

2.2项目实现过程

  • 各个类的现实映射

    • StudentController:客服接待,用于与用户的直接接触,接受各种数据,反馈接受结果

    • StudentService:业务员,与数据库的桥梁,进行业务逻辑的判断,

    • StudnetDeo:库管,进行数据的管理

    • Student:学生类

  • 数据库的技术选型:数组—复习数组的知识点,需要自己写方法

  • 注意书写格式:包:小驼峰;类:大驼峰

  • entry为程序主入口,创建完的包和类如下

2.2.1 菜单的搭建

  • 主界面的菜单
  • 学生界面的菜单
  • 老师界面的菜(先空着)
    • 菜单实现步骤:
      • 在主输出语句完成主界面的编写,
      • 获取用户的选择Scanner的键盘如入
      • 根据用户的选择进行对应的操作——switch语句
  • 代码实现
    • 学生类:
package domain;

public class Student {
    // 学号,姓名,年龄,生日  四个属性
    private String sid;
    private String name;
    private String age;
    private String birthday;

    //使用快捷键 alt+ins 进行空参,有参,get,set方法的构造
    public Student(String sid, String name, String age, String birthday) {
        this.sid = sid;
        this.name = name;
        this.age = age;
        this.birthday = birthday;
    }

    public Student() {
    }

    public String getSid() {
        return sid;
    }

    public void setSid(String sid) {
        this.sid = sid;
    }

    public String getName() {
        return name;
    }

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

    public String getAge() {
        return age;
    }

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

    public String getBirthday() {
        return birthday;
    }

    public void setBirthday(String birthday) {
        this.birthday = birthday;
    }
}
  • 主方法的实现
package entry;

import controller.StudentController;

import java.util.Scanner;

public class Entry {
    //创造主方法
    public static void main(String[] args) {

        //键盘录入创建对象
        Scanner sc = new Scanner(System.in);

        //对其进行   while (true) 死循环,只有输入3 退出系统
        while (true) {
            System.out.println("--------欢迎来到校园信息管理系统--------");
            System.out.println("请输入您的选择: 1.学生管理  2.老师管理  3.退出");
            //键盘录入
            String choice = sc.next();
            // 对choic 进行swith判断
            switch (choice) {
                case "1":
                    //模拟学生管理功能
                    System.out.println("1.学生管理 ");   
                    break;
                case "2":
                    //模拟老师管理功能
                    System.out.println("2.老师管理 ");
                    break;
                case "3":
                    //直接退出jvm虚拟机
                    System.exit(0);
                default:
                    System.out.println("输入错误");
                    break;
            }
        }

    }
}
  • 随后我们去添加学生界面的设计,我们则需要在主方法的case1 后面进行操作
  • 思想 ;公司做大做强了。我要招聘一个前台客服来进行与用户的直接交流,所以创造一个StundetController对象来处理
   					//创造一个业务员
StudentController studentController=new StudentController();
                    //业务员中的start方法,进入学生管理的主界面
                    //因为是不存在的方法,用alt+entry进行快速建造
                    studentController.start();
                    
  • 代码与主界面差不多 ,只是跳出循环的时候不能直接跳出虚拟机,需要命名循环跳出循环,此处是studentLoop
package controller;

import java.util.Scanner;

public class StudentController {

    public void start() {
        //键盘录入创建对象
        Scanner sc = new Scanner(System.in);
        studentLoop:
        while (true) {
            System.out.println("--------欢迎来到 <学生> 管理系统--------");
            System.out.println("请输入您的选择: 1.添加学生  2.删除学生  3.修改学生  4.查看学生  5.退出");
            String choic = sc.next();
            switch (choic) {
                case "1":
                    System.out.println("添加");
                    break;
                case "2":
                    System.out.println("删除");

                    break;
                case "3":
                    System.out.println("修改");

                    break;
                case "4":
                    System.out.println("查询");
                    break;
                case "5":
                    System.out.println("感谢使用,再见");
                    break studentLoop;
                default:
                    System.out.println("您的输入有误, 请重新输入");
                    break;
            }
        }
    }
}
  • 页面已经设计完成

2.2.2学生各个功能实现步骤

2.2.2.1添加功能
  • 步骤

    • 1.用户输入信息
    • 2.StudentController客服接受数据,并进行封装,之后信息传递给StudentService业务员,并进行业务逻辑判断(id是否存在,此处先不进行),若可以添加,则返回添加成功,若不能添加,则返回添加失败
    • 3.StudentService业务员收到数据但无权对数据进行直接的添加,需要传给StudentDao尽心后续操作
    • 4.StudentDao需要先创建数组,此处长度为5,随后将把从StudentDao中接收到的数据进行添加,是否添加成功返回Boolean给StudentService
    • 5.StudentService接受到的boolean返回给StudentController,StudentController根据返回的boolean返回结果
  • 代码实现:

  • 1.在case1之后创建添加学生的方法:addStudent();

  • 2.进行键盘录入,信息的提示输入,数据的封装

    private void addStudent() {
        //键盘录入创建对象
        Scanner sc = new Scanner(System.in);
        //提示输入信息
        System.out.println("请输入姓名学号");
        String sid = sc.next();
        System.out.println("请输入姓名");
        String name = sc.next();
        System.out.println("请输入年龄");
        String age = sc.next();
        System.out.println("请输入生日");
        String birthday = sc.next();
        //封装数据
        //创造一个学生
        Student student=new Student();
        student.setSid(sid);
        student.setName(name);
        student.setAge(age);
        student.setBirthday(birthday);
    }
  • 3.把数据给业务员,要知晓能否储存,(雇佣一个业务员)返回类型为boolean,如果返回为true则可以储存,返回为false,则储存失败
    //创建一个业务员
        StudentService studentService = new StudentService();
        //把数据给业务员,我只要结果
        Boolean result = studentService.addstudent(student);
        //根据结果result的值进行判断
        if (result) {
            System.out.println("输入成功");
        }else {
            System.out.println("输入失败");
        }
  • 4.补充业务员addstudent方法(alt+entry),因暂时没有逻辑判断,只起到一个桥梁的作用
    public Boolean addstudent(Student student) {
        //创建一个库管对象
        StudentDao studentDao = new StudentDao();
        //调用库管的添加方法,并接受
        boolean result = studentDao.addstudent(student);

        //把接收到的值返回给业务员
        return result;
    }
  • 5.在添加之前,我们应该先创建存储对象——数组,因为数组存放的都是学生 ,所以数组类型为学生,数组进来了我们如何判断能不能存储呢?
    • 现在我们要实现两个功能1:判断是否能存储2:如果能存储还要把数据存进去
    • 思路:只要数组里还有空数据,则能存,所以需要遍历数组
    • 要存数据,则需要知道哪一个位置是空的,所以需要一个索引值
    • 还要返回是否存进去了,所以我们要根据这个索引值,来返回Boolean值
    • 所以我们定义一个index为-1,假设都满了存不了
  • 以下是代码实现
public class StudentDao {
     Student[] stus = new Student[5];
    public boolean addstudent(Student student) {
        //定义索引值为-1,假设不存在
        int index = -1;
        //遍历数组
        for (int i = 0; i < stus.length; i++) {
            //如果找到了一个空位
            if (stus[i] == null) {
                //把i赋值给index;确定索引位
                index = i;
                //一找到就要跳出循环
                break;
            }
        }
        //不用写else 没有就 index的值就是-1
        if ( index==-1) {
            return false;
        } else {
            //进行存储
            stus[index]=student;
            return true;
        }
    }
}
2.2.2.2 添加功能的优化
  • 优化目的,判断添加的学号是否已经存在于组中
  • 操作步骤
    • 客服接收到学生学号信息,交给业务员进行判断
    • 业务员则需要库管提供的数据进行判断
    • 判断的结果返回给客服,客服则根据这个结果反馈id的正确性
  • 代码实现
  • 1.客服接收到学生学号信息
    在客服接收学号的时候创建一个死循环
        while (true) {
            System.out.println("请输入姓名学号");
            String sid = sc.next();
            //把学号传给业务员,调用业务员的方法
           boolean flag = studentService.isExist(sid);
            //根据业务员返回的值来进行判断
            if (flag) {//存在
                System.out.println("请重新输入");
            } else {//不存在
                System.out.println("输入成功");
                break;
            }
        } 

需要注意
1:由于studentService(业务员)的创建对象在下面,要上提此代码
· StudentService studentService = new StudentService();
2:由于添加了死循环,限制了sid学号的范围,下面封装学生数据的sid报错
student.setSid(sid);
解决方案:提高sid的级别,先定义,在赋值

		String sid ;
        while (true) {
            System.out.println("请输入姓名学号");
             sid = sc.next();
            //把学号传给业务员,调用业务员的方法
            boolean flag = studentService.isExist(sid);
            //根据业务员返回的值来进行判断
            if (flag) {//存在
                System.out.println("请重新输入");
            } else {//不存在
                System.out.println("输入成功");
                break;
            }
        }
  • 2.编写业务员的业务逻辑
    • 1接收库管返回的数组,同理也要用到库管,所以要提升库管等级
    • 2根据数组判断sid是否存在,需要遍历数组,因为不需要赋值,所以无需像day01那样定义index
    • 3.返回结果.
        String sid;
        while (true) {
            System.out.println("请输入姓名学号");
            sid = sc.next();
            //把学号传给业务员,调用业务员的方法
            boolean flag = studentService.isExist(sid);
            //根据业务员返回的值来进行判断
            if (flag) {//存在
                System.out.println("请重新输入");
            } else {//不存在
                break;
            }
        }
  • 3补充库管的返回代码
   public Student[] retunAllStundet() {
        //返回即可
       return stus;
    }
  • 4测试发现无作业----寻找原因
  • 我们每次调用StudentDao这个类时都会新建一个数组,都会新在内存中开辟一块新的空间
  • 而我们则需要把数据都存储在一个数组中
    • 解决方法:使用static关键字
    • 把数组变为静态类
    • 特点
      • 随着类的加载而加载
      • 被类的所有对象共享
      • 可以通过类名调用,也可以通过对象名调用
        public static Student[] stus = new Student[5];//数组的创建更换为这个就行
2.2.2.3查看学生
  • 需求:客服接收到指令,返回学生信息,若没有信息,则返回无成员
    • 思路:客服接收,业余员调用库管给的数据,返回给客服
  • 代码实现:
    • 1客服接收
      添加方法:findAllStudent();
      创造方法
    • 任务–需要向业余员要去返回数组:空数组null 或者真实数组
    • 任务二-根据业余员的返回值,来进行人机交流
    public void findAllStudent() {
        //返回数组-----
        //数组有两种情况  1 有数据正常返回 2 没有数据 返回值为null
        Student[] stus = studentService.findAllStundent();
        if (stus == null) {//判断数组里有没有数据
            System.out.println("无信息,请先添加");
            return;//结束方法
        }
        System.out.println("学号\t姓名\t年龄\t生日");
        for (int i = 0; i < stus.length; i++) {
            Student stu = stus[i];
            if (stu != null) {
                stu = stus[i];
                System.out.println(stu.getSid() + "\t" + stu.getName() + "\t" + stu.getAge() + "\t" + stu.getBirthday());
            }
        }
    }
  • 2业务员逻辑判断
    • 任务:1调取数据库的数组,2判断数组是不是空数组,并且返回给业务员
    //查询学生
    public Student[] findAllStundent() {
        //向库管要数据
        Student[] stu = studentDao.retunAllStundet();
        //假设没有数据
        boolean flag = false;
        //遍历stu
        for (int i = 0; i < stu.length; i++) {
            //接收学生对象
            Student student = stu[i];
            if (student != null) {//找到了一个不为零的值,说明数组里面有数据源
                flag = true;
                break;//一个足以,跳出循环
            }
        }
        //根据flag的值来进行返回数组的确定
        if (flag) {//存在
            return stu;//返回数组
        } else {
            return null;//没有 ,返回null
        }
    }
  • 3库管的方法上文已经写过
//返回数组
    public Student[] retunAllStundet() {
        //返回即可
        return stus;
    }
}
2.2.2.4删除代码的实现
  • 需求:用户输入要删除的id,进行删除,若没有要删除的则提示没有要删除的id,并重新输入,若数组没有数据,则提示先添加数据
  • 思路
    • 客服先要知晓数组里面有没有数据,但无需自己去做,让业务员返回给我
    • 把要删除的id给业余员,业务员把此id给库管,由库管来删除(此处需要定义一个方法,早出输入id所在的索引,随后服织物null,则可以进行删除)
    • 删除成功与否有业务员返回给用户
  • 代码实现
    • 1客服删除方法的添加
      delayStundet();
    • 2.删除方法的实现
    // 删除学生
    public void delayStundet() {
        //返回数组-----
        //数组有两种情况  1 有数据进行删除 2 没有数据 返回值为null
        Student[] stus = studentService.findAllStundent();
        if (stus == null) {//判断数组里有没有数据
            System.out.println("无信息,请先添加");
            return;//结束方法
        }
        //数组不是null,进行删除操作
        String sid;
        while (true) {
            System.out.println("请输入要删除的id");
             sid = sc.next();
            //把id给业务员,但业务员也无权修改数据库的信息
            boolean flag = studentService.isExist(sid);
            if (!flag) {//id不存在,则重新输入
                System.out.println("id不存在,请重新输入");
            } else {
                break;
            }
        }
        studentService.delayStundet(sid);
        System.out.println("删除成功");
    }
  • 3业务员进行传递
    public void delayStundet(String sid) {
        studentDao.delayStundent(sid);
    }
  • 4,查询索引的方法,在库管中哦
    //由于我们要查找sid所在的索引,先定义一个方法
    public int findIndex(String sid) {
        //假设不存在
        int index = -1;
        //遍历数组
        for (int i = 0; i < stus.length; i++) {
            //接收学生,但可能是null,需要进行判断
            Student stu = stus[i];
            if (stu != null && stu.getSid().equals(sid)) {//如果相同,则表示存在
                index = i;
                break;//跳出循环
            }
        }
        return index;
    }
  • 5库管进行删除操作,
   public void delayStundent(String sid) {
        //调用方法,查找索引
        int index = findIndex(sid);
            stus[index] = null;

    }
2.2.2.5修改功能的实现
  • 需求 :根据学生id,来修改其他三个属性,没有id发出提示,数组没数据也发出对应提示
  • 思路:与删除基本类似,直接代码实现
  • 代码实现
    • 1客服修改方法的添加
      changeStudent();
    • 2客服修改方法的添加
      • 先进行数组是否为空的判断
      • 循环要修改的id,
      • 录入新的学生对象
      • 把新的学生对象和学号传给业务员
      • 业务员再给数组
      • 由数组进行修改
    //返回数组-----
        //数组有两种情况  1 有数据进行修改 2 没有数据 返回值为null
        Student[] stus = studentService.findAllStundent();
        if (stus == null) {//判断数组里有没有数据
            System.out.println("无信息,请先添加");
            return;//结束方法
        }
        String sid;
        while (true) {
            System.out.println("请输入要修改的id");
            sid = sc.next();
            //把id给业务员,但业务员也无权修改数据库的信息
            boolean flag = studentService.isExist(sid);
            if (!flag) {//id不存在,则重新输入
                System.out.println("id不存在,请重新输入");
            } else {
                break;
            }
        }
        //录入
        System.out.println("请输入姓名");
        String name = sc.next();
        System.out.println("请输入年龄");
        String age = sc.next();
        System.out.println("请输入生日");
        String birthday = sc.next();
        //封装数据
        //创造一个学生
        Student student = new Student();
        student.setSid(sid);
        student.setName(name);
        student.setAge(age);
        student.setBirthday(birthday);
//        //创建一个业务员
//        StudentService studentService = new StudentService();
        //把数据给业务员,进行数据的修改
        studentService.changeStudent(sid,student);
        System.out.println("修改成功");
    }
  • 3.业务员的传递
   public void changeStudent(String sid, Student student) {
        studentDao.changeStundetn(sid, student);
    }
  • 4.库管修改学生
    public void changeStundetn(String sid, Student student) {
        int index = findIndex(sid);
        stus[index] = student;
    }

到此学生系统的建设已经完毕
接下来要针对代码的重复等部分进行修改

2.2.2.6学生功能的优化——抽取成方法
  • studentController中都需要进行输入学号的操作,对其进行方法的抽取,并微改输入语句,快捷键:ctrl+alt+m
  • 这里要注意 添加方法中的判断语句是
   if (flag) {//存在
                System.out.println("请重新输入");
            } else {//不存在
                break;
            }

与其相反,不能抽取为一个方法

    //输入id
    private String getString() {
        String sid;
        while (true) {
            System.out.println("请输入id");
            sid = sc.next();
            //把id给业务员,但业务员也无权修改数据库的信息
            boolean flag = studentService.isExist(sid);
            if (!flag) {//id不存在,则重新输入
                System.out.println("id不存在,请重新输入");
            } else {
                break;
            }
        }
        return sid;
    }
  • studentController中添加学生和修改都需要进行学生封装的操作,对其进行方法的抽取
//封装学生
    public Student getStudent(String sid) {
        System.out.println("请输入姓名");
        String name = sc.next();
        System.out.println("请输入年龄");
        String age = sc.next();
        System.out.println("请输入生日");
        String birthday = sc.next();
        //封装数据
        //创造一个学生
        Student student = new Student();
        student.setSid(sid);
        student.setName(name);
        student.setAge(age);
        student.setBirthday(birthday);
        return student;
    }

2.2.3老师环境的搭建

  • domain中的学生类直接复制粘贴到domain,随后改名
  • 新建类。完成框架的建设

2.2.4系统的优化

2.2.4.1集成改进:优化原理:继承
  • 把老师类和学生类向上提取成一个Person类,同时删除老师类和学生类中所有代码,然后idea自动生成构造方法
package domain;

public class Teacher extends Person {
    public Teacher(String sid, String name, String age, String birthday) {
        super(sid, name, age, birthday);
    }

    public Teacher() {
    }
}
package domain;

public class Student extends Person{
    public Student(String sid, String name, String age, String birthday) {
        super(sid, name, age, birthday);
    }

    public Student() {
    }
}
  • 在封装学生的时候,我们是调用无参构造的方法,我们也可以调用有参构造的方法,由于我们的开发原则:对扩展内容开放,对修改内容关闭。我们新建一个OtherStudentController,并与StudentController一同继承于BaseStudentController,并且把entry中的StudentController换成OtherStudentController
  • 删除子类与父类相同的代码逻辑,保留子类独有的代码逻辑
    +父类更改要重写的方法,然后return
//  父类封装学生
    public Student getStudent(String sid) {
        return null;
    }

OtherStudentController的封装学生方法

    private Scanner sc = new Scanner(System.in);
    //封装学生
    public Student getStudent(String sid) {
        System.out.println("请输入姓名");
        String name = sc.next();
        System.out.println("请输入年龄");
        String age = sc.next();
        System.out.println("请输入生日");
        String birthday = sc.next();
        //封装数据
        //创造一个学生
        Student student = new Student(sid,name,age,birthday);
        return student;
    }

StudentController的封装学生方法

public class StudentController extends BaseStudentController{
    private Scanner sc = new Scanner(System.in);
    //封装学生
    public Student getStudent(String sid) {
        System.out.println("请输入姓名");
        String name = sc.next();
        System.out.println("请输入年龄");
        String age = sc.next();
        System.out.println("请输入生日");
        String birthday = sc.next();
        //封装数据
        //创造一个学生
        Student student = new Student();
        student.setSid(sid);
        student.setName(name);
        student.setAge(age);
        student.setBirthday(birthday);
        return student;
    }
}
2.2.4.2抽象类改进:优化原理:抽象,finall
  • 将BaseStudnet中封装学生的方法变成一个抽象方法,不改变的加上finall关键字,整个类变成抽象类
package controller;

import domain.Student;
import service.StudentService;

import java.util.Scanner;

public  abstract class BaseStudentController {
    //  创建一个业务员
    private StudentService studentService = new StudentService();
    //键盘录入创建对象
    private Scanner sc = new Scanner(System.in);

    public final void start() {
        lo:
        while (true) {
            System.out.println("--------欢迎来到 <学生> 管理系统--------");
            System.out.println("请输入您的选择: 1.添加学生  2.删除学生  3.修改学生  4.查看学生  5.退出");
            String choic = sc.next();
            switch (choic) {
                case "1":
//                    System.out.println("添加");
                    //添加学生的方法
                    addStudent();
                    break;
                case "2":
//                    System.out.println("删除");
                    delayStundet();
                    break;
                case "3":
//                    System.out.println("修改");
                    changeStudent();
                    break;
                case "4":
//                    System.out.println("查询");
                    //查询
                    findAllStudent();
                    break;
                case "5":
                    System.out.println("感谢使用,再见");
                    break lo;
                default:
                    System.out.println("您的输入有误, 请重新输入");
                    break;
            }
        }
    }

    //修改学生
    private final void changeStudent() {
        //返回数组-----
        //数组有两种情况  1 有数据进行修改 2 没有数据 返回值为null
        Student[] stus = studentService.findAllStundent();
        if (stus == null) {//判断数组里有没有数据
            System.out.println("无信息,请先添加");
            return;//结束方法
        }

        //输入要修改的id
        String sid = getString();

        //封装学生
        Student student = getStudent(sid);
//        //创建一个业务员
//        StudentService studentService = new StudentService();
        //把数据给业务员,进行数据的修改
        studentService.changeStudent(sid, student);
        System.out.println("修改成功");
    }



    // 删除学生
    public final void delayStundet() {
        //返回数组-----
        //数组有两种情况  1 有数据进行删除 2 没有数据 返回值为null
        Student[] stus = studentService.findAllStundent();
        if (stus == null) {//判断数组里有没有数据
            System.out.println("无信息,请先添加");
            return;//结束方法
        }
        //数组不是null,进行删除操作

        //输入要删除的id
        String sid = getString();
        studentService.delayStundet(sid);
        System.out.println("删除成功");
    }


    //查询学生
    public final void findAllStudent() {
        //返回数组-----
        //数组有两种情况  1 有数据正常返回 2 没有数据 返回值为null
        Student[] stus = studentService.findAllStundent();
        if (stus == null) {//判断数组里有没有数据
            System.out.println("无信息,请先添加");
            return;//结束方法
        }

        System.out.println("学号\t姓名\t年龄\t生日");
        for (int i = 0; i < stus.length; i++) {
            Student stu = stus[i];
            if (stu != null) {
                stu = stus[i];
                System.out.println(stu.getSid() + "\t" + stu.getName() + "\t" + stu.getAge() + "\t" + stu.getBirthday());
            }
        }
    }

    //添加学生
    public final void addStudent() {
        //提示输入信息
        String sid;
        while (true) {
            System.out.println("请输入姓名学号");
            sid = sc.next();
            //把学号传给业务员,调用业务员的方法
            boolean flag = studentService.isExist(sid);
            //根据业务员返回的值来进行判断
            if (flag) {//存在
                System.out.println("请重新输入");
            } else {//不存在
                break;
            }
        }

        //封装学生
        Student student = getStudent(sid);

//        //创建一个业务员
//        StudentService studentService = new StudentService();
        //把数据给业务员,我只要结果
        boolean result = studentService.addstudent(student);
        //根据结果result的值进行判断
        if (result) {
            System.out.println("输入成功");
        } else {
            System.out.println("输入失败");
        }


    }

    //输入id
    public String getString() {
        String sid;
        while (true) {
            System.out.println("请输入id");
            sid = sc.next();
            //把id给业务员,但业务员也无权修改数据库的信息
            boolean flag = studentService.isExist(sid);
            if (!flag) {//id不存在,则重新输入
                System.out.println("id不存在,请重新输入");
            } else {
                break;
            }
        }
        return sid;
    }
    //封装学生
    public abstract Student getStudent(String sid);
}
2.2.4.3代码块改进:优化原理:静态代码块
  • 需求:初始化学生数据
  • 步骤:在StudentDao中创建静态代码块,初始化学生的数据
  • 代码如下
 static {
        //创建两个学生
        Student student = new Student("1", "1", "1", "1");
        Student student2 = new Student("2", "2", "2", "2");
        //进行存储
        stus[0]=student;
        stus[1]=student2;
    }
2.2.4.4集合改进:优化原理:集合
  • 数组存储不方便,使用集合,但根据开发原则,不直接进行修改,创建一个新的类来实现
  • 使用数组容器的弊端
    1. 容器长度是固定的,不能根据添加功能自动增长
    2. 没有提供用于赠删改查的方法
  • 优化步骤
    1. 创建新的StudentDao类,OtherStudentDao
    2. 创建ArrayList集合容器对象
    3. OtherStudentDao中的方法声明,需要跟StudentDao保持一致
      注意:如果不一致,StudentService中的代码就需要进行修改
    4. 完善方法(添加、删除、修改、查看)
    5. 替换StudentService中的Dao对象
    6. 代码实现
package dao;

import domain.Student;

import java.util.ArrayList;

public class OtherStudentDao {
    static {
        //创建两个学生

        Student student = new Student("1", "1", "1", "1");
        Student student2 = new Student("2", "2", "2", "2");

        //进行存储
        stus.add(student);
        stus.add(student2);

    }

    public boolean addstudent(Student student) {
        //一定有的位置,直接添加
        stus.add(student);
        return true;
    }


    //返回数组-----由于之后全是数组操作,所以要返回数组
    public Student[] retunAllStundet() {
        //返回数组
        Student[] retunStus=new Student[stus.size()];
        for (int i = 0; i < stus.size(); i++) {
            retunStus[i]=stus.get(i);
        }
        return retunStus;
    }


    // 删除学号
    public void delayStundent(String sid) {

        //调用方法,查找索引
        int index = findIndex(sid);
        stus.remove(index);

    }

    //由于我们要查找sid所在的索引,先定义一个方法
    public int findIndex(String sid) {
        //假设不存在
        int index = -1;
        //遍历数组
        for (int i = 0; i < stus.size(); i++) {
            //接收学生,但可能是null,需要进行判断
            Student stu = stus.get(i);
            if (stu != null && stu.getSid().equals(sid)) {//如果相同,则表示存在
                index = i;
                break;//跳出循环
            }
        }
        return index;
    }
    public void changeStundetn(String sid, Student student) {
        int index = findIndex(sid);
       stus.set(index,student);
    }
}
2.2.4.5集合进行公共类的抽取:优化原理:继承,抽象
  1. 将方法向上抽取,抽取出一个父类 ( BaseStudentDao )
  2. 方法的功能实现在父类中无法给出具体明确,定义为抽象方法
  3. 让两个类分别继承 BaseStudentDao ,重写内部抽象方法
  4. 代码实现
package dao;

import domain.Student;

import java.util.ArrayList;

public abstract class BaseStudentDao {

    public abstract boolean addstudent(Student student) ;


    //返回数组-----由于之后全是数组操作,所以要返回数组
    public abstract Student[] retunAllStundet();

    // 删除学号
    public abstract void delayStundent(String sid) ;

    //由于我们要查找sid所在的索引,先定义一个方法
    public abstract int findIndex(String sid);

    public abstract void changeStundetn(String sid, Student student) ;
}

public class OtherStudentDao extends BaseStudentDao
public class StudentDao extends BaseStudentDao

2.2.4.6接口改进:优化原理:接口
  • 实现步骤
    1. 将 BaseStudentDao 改进为一个接口
    2. 让 StudentDao 和 OtherStudentDao 去实现这个接口
  • 代码实现
    public abstract interface BaseStudentDao
    public class OtherStudentDao implements BaseStudentDao
    public class StudentDao implements BaseStudentDao
2.2.4.7 多态改进:优化原理:多态
  • 引入,如今我们可以使用集合,也可以使用数组,那我们业务员StudntService中
    调用库管也有两个方法
    private StudentDao studentDao = new StudentDao();
    private OtherStudentDao studentDao = new OtherStudentDao();
    因为studentDao 和StudntService之间的耦合性太强,要解开耦合性,我们需要引入第三方变量
  • 实现步骤
  1. 创建factory包,创建 StudentDaoFactory(工厂类)
  2. 提供 static 修改的 getStudentDao 方法,该方法用于创建StudentDao对象并返回

OtherStudentDao:通过静态访问的方式,固定右半语句

package factory;

import dao.OtherStudentDao;
import dao.StudentDao;

public class StudentDaoFactory {
    public static OtherStudentDao getStudentDao() {
        return new OtherStudentDao();

    }
}

private OtherStudentDao studentDao = StudentDaoFactory.getStudentDao();

StudentDao:

package factory;

import dao.OtherStudentDao;
import dao.StudentDao;

public class StudentDaoFactory {
    public static StudentDao getStudentDao() {
        return new StudentDao();

    }
}

private StudentDao studentDao = StudentDaoFactory.getStudentDao();

此时发现还是要改变StudntService的语句

  • 此时引入多态
    将工厂类中返回值类型改为父类的BaseStudentDao,这样既可以返回OtherStudentDao,也可以返回StudentDao,而无论返回什么,只要我用父类BaseStudentDao去接收,都能接受
public class StudentDaoFactory {
    
    public static BaseStudentDao getStudentDao() {
        return new OtherStudentDao();
       // return new StudentDao();
    }
}

BaseStudentDao类型 的接收
private BaseStudentDao studentDao = StudentDaoFactory.getStudentDao();
这应该是最后一个优化了

3知识点的引入

3.1继承

3.1.1引入

  • 根据上面的案例,老师类和学生类有相同的属性,相同的方法放,具有共性,向上抽取,关键字extend,特点 :子类可以直接使用父类中非私有的成员

3.1.2优点

  • 提高代码的复用性,维护性,并且让类与类产生了联系,是多态的前提

3.1.3缺点

  • 继承是侵入性的,降低代码的灵活性:子类必须拥有父类非私有的属性和方法
    增强了代码的耦合性

3.1.4使用场景

  • 1 有共性 2:产生了is a 的关系 :例子 我是帅哥 ,你也是帅哥 ,提取帅哥为父类,

3.1.5继承特点

  • 只支持单继承和多层继承(爷祖)

3.1.6继承的成员变量访问特点

  • 就近原则
public class one {
    public static void main(String[] args) {
        zi zi = new zi();
        zi.mothed1();
        zi.mothed2();
        zi.mothed3();
    }
}
//fu类
class fu {
    int a = 10;
}
//子类
class zi extends fu {
    int a = 20;
    public void mothed1() {
        System.out.println(a+"就近原则");
    }
    public void mothed2() {
        int a =30;
        System.out.println(this.a+"this关键字返回本类成员变量");
        System.out.println(a+"局部变量");
    }
    public void mothed3() {
        System.out.println(super.a+"super返回父类");
    }
}
3.1.6.1 this and super
  • this&super关键字:
    • this:代表本类对象的引用
    • super:代表父类存储空间的标识(可以理解为父类对象引用)
  • this和super的使用分别
    • 成员变量:
      • this.成员变量 - 访问本类成员变量
      • super.成员变量 - 访问父类成员变量
    • 成员方法:
      • this.成员方法 - 访问本类成员方法
      • super.成员方法 - 访问父类成员方法
  • 构造方法:
    • this(…) - 访问本类构造方法
    • super(…) - 访问父类构造方法

3.1.7继承的成员方法访问特点

  • 先去子类找 ,没有再去父类找
public class Two {
    public static void main(String[] args) {
        zi2 zi = new zi2();
        zi.mothed2();
        zi.mothed3();

    }
}

//fu类
class fu2 {
    //fu类方法
    public void mothed() {
        System.out.println("fu2类方法");
    }
}

//子类
class zi2 extends fu2 {

    //子类与父类同名方法
    public void mothed() {
        System.out.println("zi2类方法");
    }

//子类中的方法通过super调用父类同名方法
    public void mothed2() {
        super.mothed();
    }
// 默认有个this.  -- 调用子类方法
    public void mothed3() {
        mothed();
    }
}

3.1.8方法的重写

  • 1、方法重写概念
    • 子类出现了和父类中一模一样的方法声明(方法名一样,参数列表也必须一样)
  • 2、方法重写的应用场景
    • 当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容
//需求 升级手机功能
public class Three {
    public static void main(String[] args) {
        ipear2  i =new ipear2();
        i.smallBlack();

    }
}

//ipear1类-----一代
class ipear1 {
    //打电话
    public void call(String name) {
        System.out.println("给" + name + "打电话");
    }
//只会说英文
    public void smallBlack() {
        System.out.println("speak English");
    }
}

//二代
class ipear2 extends ipear1 {
    //进行重写
    public void smallBlack() {
        //调用父类的方法
        super.smallBlack();
        //添加自己的方法
        System.out.println("speak Chinese");
    }

}
3.1.8.1方法重写的注意事项
  1. 私有方法不能被重写(父类私有成员子类是不能继承的)
  2. 子类方法访问权限不能低于父类的访问权限
  3. 静态方法不能被重写,如果子类也有相同的方法,并不是重写的父类的方法,子类只是将父类的方法隐藏起来了

3.1.9权限修饰符

在这里插入图片描述

3.1.10继承中构造方法的访问特点(idea自动生成)

在这里插入图片描述
注意:子类中所有的构造方法默认都会访问父类中无参的构造方法

​ 子类会继承父类中的数据,可能还会使用父类的数据。所以,子类初始化之前,一定要先完成父类数据的初始化,原因在于,每一个子类构造方法的第一条语句默认都是:super()

问题:如果父类中没有无参构造方法,只有带参构造方法,该怎么办呢?

1. 通过使用super关键字去显示的调用父类的带参构造方法
2. 子类通过this去调用本类的其他构造方法,本类其他构造方法再通过super去手动调用父类的带参的构造方法

注意: this(…)super(…) 必须放在构造方法的第一行有效语句,并且二者不能共存

注意:super 会在堆内存开辟自己的存储空间来存放父类成员

3.2抽象类

3.2.1概述

  • 抽象方法:父类中的某些方法不能明确具体实现方式,就需要定义成抽象方法,子类要重写父类中的抽象方法(完成方法体内容的编写)
  • 抽象类:一个类中存在抽象方法,则该类必须声明为抽象类
  • 如何定义抽象类和抽象方法
    abstract关键字
//抽象类的定义
public abstract class 类名 {}
//抽象方法的定义
public abstract void eat();

3.2.2抽象类的注意事项

  • 抽象类中可以没有抽象方法,有抽象方法的类一定是抽象类

  • 抽象类不能实例化(实例化就是创建对象)

  • 抽象类可以有构造方法

    • 但是无法创建对象
    • 构造方法主要用于初始化自己成员变量,以供子类使用
  • 抽象类的子类

    • 要么重写父类所有的抽象方法(推荐)
    • 要么自己也是一个抽象类(工作中可能会用到)

3.2.3模板设计模式

  • 把抽象类整体就可以看做成一个模板,模板中不能决定的东西定义成抽象方法

让使用模板的类(继承模板抽象类的子类)去重写抽象方法实现需求,下面是一个写作文的模板

package Day02;

public abstract class Composition {
//模板
    public void write() {
        System.out.println("《我的dady》");
        body();
        System.out.println("哈哈哈");
    }
//抽象主题方法
    public abstract void body();

}


//创建子类重写抽象方法
class Tom extends Composition {

    @Override
    public void body() {
        System.out.println("俺不会写作文");
    }

}
//创建子类对象,调用父类方法
class test {
    public static void main(String[] args) {

        Tom tom=new Tom();
        tom.write();

    }
}

3.2.4 finall关键字

  • 根据模板设计思想,模板的骨架最好不被重写,
  • finall-最终
  • 被其修饰的方法无法被子类重写
  • 被其修饰的类不能拥有子类
  • 被其修饰的变量不能再被赋值:基本数据类型–值 ;引用数据类型:地址值
  • 如果要修饰成员变量,要注意初始化时机:
    1.在定义final修饰的变量(常量)的时候, 直接赋值(推荐)2. 在构造方法结束之前(白话:就是在构造方法中), 完成赋值
  package com.itheima.mfinal;
  
  public class TestFinal {
      /*
          final修饰变量:
  
              基本数据类型变量: 其值不能被更改
  
              引用数据类型变量: 地址值不能被更改, 但是可以修改对象的属性值
       */
      public static void main(String[] args) {
          // 常量的命名规范: 如果是一个单词, 所有字母大写, 如果是多个单词, 所有字母大写, 但是中间需要使用_分隔
          final int A = 10;//A变量的值是不能被修改
          // a = 10;
          final int MAX = 10;
          final int MAX_VALUE = 20;
  
          final Student stu = new Student();
          stu.setName("张三");
          stu.setName("李四");
  
          // stu = new Student();
      }
  }
  
  class Student {
      // final修饰成员变量 初始化时机
      // 1. 在创建的时候, 直接给值
      // 2. 在构造方法结束之前, 完成赋值
      final int a = 10;
  
      private String name;
  
      public String getName() {
          return name;
      }
  
      public void setName(String name) {
          this.name = name;
      }
  }

3.3代码块

  • 引出:系统调试不方便
  • 目的:每当程序启动完毕以后,系统就初始化一部分学生数据
  • 分类:局部代码块,构造代码块,静态代码块

3.3.1 局部代码块

  • 位置:方法中定义
  • 作用:限定变量的生命周期,及早释放,提高内存利用率
  • 示例代码
 public class Test {
      /*
          局部代码块
              位置:方法中定义
              作用:限定变量的生命周期,及早释放,提高内存利用率
       */
      public static void main(String[] args) {
          {
              int a = 10;
              System.out.println(a);
          }

         // System.out.println(a);
      }
  }

3.3.2 构造代码块

  • 位置:类中方法外定义
  • 特点:每次构造方法执行的时,都会执行该代码块中的代码,并且在构造方法执行前执行
  • 作用:将多个构造方法中相同的代码,抽取到构造代码块中,提高代码的复用性
  • 示例代码
  public class Test {
      /*
          构造代码块:
              位置:类中方法外定义
              特点:每次构造方法执行的时,都会执行该代码块中的代码,并且在构造方法执行前执行
              作用:将多个构造方法中相同的代码,抽取到构造代码块中,提高代码的复用性
       */
      public static void main(String[] args) {
          Student stu1 = new Student();
          Student stu2 = new Student(10);
      }
  }
  
  class Student {
  
      {
          System.out.println("好好学习");
      }
  
      public Student(){
          System.out.println("空参数构造方法");
      }
  
      public Student(int a){
          System.out.println("带参数构造方法...........");
      }
  }

3.3.3 静态代码块

  • 位置:类中方法外定义
  • 特点:需要通过static关键字修饰,随着类的加载而加载,并且只执行一次
  • 作用:在类加载的时候做一些数据初始化的操作
  • 示例代码
  public class Test {
      /*
          静态代码块:
              位置:类中方法外定义
              特点:需要通过static关键字修饰,随着类的加载而加载,并且只执行一次
              作用:在类加载的时候做一些数据初始化的操作
       */
      public static void main(String[] args) {
          Person p1 = new Person();
          Person p2 = new Person(10);
      }
  }
  
  class Person {
      static {
          System.out.println("我是静态代码块, 我执行了");
      }
  
      public Person(){
          System.out.println("我是Person类的空参数构造方法");
      }
  
      public Person(int a){
          System.out.println("我是Person类的带...........参数构造方法");
      }
  }

3.4接口

3.4.1接口的引出

  • 根据2.2.4.5的改进,出现了一个全是抽象方法的类,
  • 接口就是一种公共的规范标准,只要符合规范标准,大家都可以通用。

3.4.2接口的意义

  1. 用来定义规范
  2. 用来做功能的拓展

3.4.3接口的特点

  • 接口用关键字interface修饰
		  public interface 接口名 {} 
  • 类实现接口用implements表示
  public class 类名 implements 接口名 {}

接口不能实例化,我们可以创建接口的实现类对象使用

  • 接口的子类
    ​ 要么重写接口中的所有抽象方法
    ​ 要么子类也是抽象类

3.4.4接口中成员特点

  • 成员变量
    ​ 只能是常量
    ​ 默认修饰符:public static final
  • 构造方法
    ​ 没有,因为接口主要是扩展功能的,而没有具体存在
  • 成员方法
    ​ 只能是抽象方法
    ​ 默认修饰符:public abstract
    ​ 关于接口中的方法,JDK8和JDK9中有一些新特性
    • JDK8:用default可以定义非抽象方法,用static定义静态方法,静态方法通过类名 . 的方式调用,注意default和static只能有一个
    • JDK9:允许接口中定义private私有的方法,(private和static会吗,默认带上default,反正就是不用写default)

3.4.4类与接口的关系

  • 类与类的关系
    ​ 继承关系,只能单继承,但是可以多层继承
  • 类与接口的关系
    ​ 实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口(亲爹更亲,类和接口有一样的方法,执行类中的方法)
  • 接口与接口的关系
    ​ 继承关系,可以单继承,也可以多继承(如果继承的接口有重名且方法体不一样的方法,需要重写该方法)

3.5多态

3.5.1多态的概念

​ 同一个对象,在不同时刻表现出来的不同形态

3.5.2多态的前提

  • 要有继承或实现关系
  • 要有方法的重写
  • 要有父类引用指向子类对象

3.5.3多态中成员访问的特点

  • 成员访问特点

    • 成员变量

      ​ 编译看父类,运行看父类

    • 成员方法

      ​ 编译看父类,运行看子类,父类中要有方法,不然会编译失败,运行则运行子类中重写的方法

    • 代码展示

class Fu {
    int num = 10;

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

class Zi extends Fu {
    int num = 20;

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

public class Test2Polymorpic {
    /*
         多态的成员访问特点:

                成员变量: 编译看左边 (父类), 运行看左边 (父类)

                成员方法: 编译看左边 (父类), 运行看右边 (子类)
     */
    public static void main(String[] args) {
        Fu f = new Zi();
        System.out.println(f.num);
        f.method();
    }
}

3.5.4多态的好处与弊端

  • 好处
    ​ 提高程序的扩展性。定义方法时候,使用父类型作为参数,在使用的时候,使用具体的子类型参与操作
  • 弊端
    ​ 不能使用子类的特有成员,因为要先编译,如果父类中没有调用的方法,编译会失败

3.5.5多态的转型及其风险

  • 向上转型

    ​ 父类引用指向子类对象就是向上转型

  • 向下转型

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

3.5.5多态的风险(解决向下转型的风险)

如果被转的引用类型变量,对应的实际类型和目标类型不是同一种类型,那么在转换的时候就会出现ClassCastException

  • 解决方案
    • 关键字
      instanceof
    • 使用格式
      变量名 instanceof 类型
      通俗的理解:判断关键字左边的变量,是否是右边的类型,返回boolean类型结果
//父类 动物
abstract class Animal {
    public abstract void eat();
}
//狗继承了动物类 ,除了吃还能看家
class Dog extends Animal {
    public void eat() {
        System.out.println("狗吃肉");
    }

    public void watchHome(){
        System.out.println("看家");
    }
}
// 猫也继承了动物
class Cat extends Animal {
    public void eat() {
        System.out.println("猫吃鱼");
    }
}
//测试类
public class Test4Polymorpic {
    public static void main(String[] args) {
    //创建了一条狗,调用方法
        useAnimal(new Dog());
    //创建了一只猫,调用方法
        useAnimal(new Cat());
    }

    public static void useAnimal(Animal a){  
    // Animal a = new Dog();
    // Animal a = new Cat();
    
    //这个方法可以调用父类动物所有子类对象
    
        a.eat();
        //a.watchHome();

//        Dog dog = (Dog) a;
//        dog.watchHome();  // ClassCastException  类型转换异常
      
        // 判断a变量记录的类型, 是否是Dog
        if(a instanceof Dog){
            Dog dog = (Dog) a;
            dog.watchHome();
        }
    }
}

总结

  • 差不多就是根据java语言思想做的小项目,模拟现实生活的处理方式,通过继承,抽象,接口,多态等特性来优化,维护代码。
  • 吾初学java,此当成笔记来记录,若有错误望包涵;若有建议望提出;若有小用望点赞哦,码字不易,小金叹气。
  • 最后附上今天看到的一句话,很多失败不是因为能力有限,而是因为努力没有到底
  • 同时也祝看到最后的小伙伴身体健康,万事如意

精彩评论(0)

0 0 举报