java学习笔记-第八章:面向对象编程(中级部分)

第八章:面向对象编程(中级部分)

总体内容

在这里插入图片描述

IDEA

IDEA介绍

在这里插入图片描述

ECLIPSE介绍

在这里插入图片描述

IDEA使用

破解IDEA2020.2.2

我用的是这个破解IDEA2020.2.2
在这里插入图片描述

IDEA工作界面介绍

在这里插入图片描述

IDEA调整代码,导航栏的字体大小,主题颜色,字符编码

File --> Settings

  1. 调正代码字体大小
    在这里插入图片描述
  2. 调正导航栏字体大小和主题颜色
    在这里插入图片描述
  3. 字符编码
    在这里插入图片描述

IDEA使用

  1. src目录是存放代码源文件的
  2. 目录结构:
    在这里插入图片描述
  3. 在file中新建项目,在src下新建类等
  4. 运行Java程序两种方式
    在这里插入图片描述
  5. 双击标签关闭或打开左边的导航
    在这里插入图片描述
  6. 练习中的两个快捷方式(后面的idea模板专门讲这个)
    6.1 快捷1. 输入main回车
    public static void main(String[] args) {}
    6.2 快捷2. 输入sout回车
    System.out.println();

IDEA快捷键

汇总

  1. 删除当前行 ctrl + d
  2. 复制当前行到下一行 ctrl + shift + 向下光标
  3. 补全代码 alt + /
  4. 导入该行需要的类 alt + enter
  5. 格式化代码 ctrl + shift + l
  6. 运行程序 alt + r
  7. 生成构造器等 alt + insert
  8. 查看一个类的层级结构 ctrl + h
  9. 从调用定位到方法所在位置 ctrl +b
  10. 自动分配对象名 等号右边一串后面 + .var

在这里插入图片描述

  1. 修改删除当前行的快捷键为ctrl+d
    这里是引用
  2. 修改向下复制整行
    在这里插入图片描述

在这里插入图片描述

  1. 自动导入包alt+enter
    这里是引用
  2. 代码格式化
    在这里插入图片描述
  3. 运行
    在这里插入图片描述

在这里插入图片描述

自动分配变量名:
在这里插入图片描述

IDEA模板(template)

  1. file --> settings --> editor --> Live templates
    可以查看有哪些模板快捷键/可以自己增加模板
  2. 常用的模板
    2.1 main生成main方法
    2.2 fori 生成for循环
    2.3 sout 生成输出语句
    2.4 还有很多自己在live template里面看
  3. 自增模板的四步
    在这里插入图片描述

包的作用和语法

  1. 包的三大作用
    在这里插入图片描述
  2. 包的基本语法
    在这里插入图片描述

包的原理

在这里插入图片描述

包的快速入门案例(讲解)

在这里插入图片描述

  1. 创建包
    1.1 在src下新建包就:
    1.2 右键src --> new --> package
  2. 输入包的名字
    2.1 com.xiaoming --> com是一级目录,xiaoming是二级目录
  3. 在包下创建DOG类
    3.1 右键包,new–>Java class
  4. 在com包下新建java程序test,在程序中创建Dog类
    4.1 会提示选择哪个包下的dog类
    在这里插入图片描述
    4.2 使用不同包下相同名称的类
    在这里插入图片描述
  5. 目录结构:
    在这里插入图片描述

包命名

命名规则

在这里插入图片描述

命名规范

在这里插入图片描述

常用包

在这里插入图片描述

包的使用细节

  1. 如何导入包
    1.1 导入包的目的是使用该包下面的类
    1.2 两种方式(推荐1)
    在这里插入图片描述
    1.2 案例:使用Arrays类下的sort()方法进行排序
    前面是使用冒泡排序的,这里使用Arrays中sort()自动排序
    在这里插入图片描述
  2. 细节
    在这里插入图片描述

类的完善

在这里插入图片描述

访问修饰符(modifier)

基本使用

在这里插入图片描述
访问范围图(背):
在这里插入图片描述

  1. 本类中:都可以
  2. 同包中:私有的不可以
  3. 子类中:public和protected可以
  4. 不同包:只有public可以

使用细节

在这里插入图片描述

OOP三大特征(重点)

封装(encapsulation)

封装的介绍和好处

  1. 介绍
    在这里插入图片描述
  2. 好处
    在这里插入图片描述

封装实现的步骤

在这里插入图片描述

封装快速入门案例

在这里插入图片描述

  1. 解读为什么需要写set和get方法
    不能直接操作的(需要校验逻辑的)属性,需要使用private修饰符
    在这里插入图片描述
  2. 代码
package com.wpangzi.encapsulation;

public class encapsulation01 {
    public static void main(String[] args) {
        Person person = new Person();
        person.setName("王胖子啦啦啦啦");
        person.setAge(100000);
        person.setSalary(0);
        System.out.println(person.info());
    }
}

/*
    需求:
    1. 不能随便查看人的年龄,工资等隐私
    2. 名字长度在2 - 6字符之间(校验)
    3. 对设置的年龄进行合理的验证,(校验)
       年龄合理就设置,否则给默认年龄
       必须在1 - 120
    4. 如果需要可以在get方法中添加权限验证
 */

class Person {
    public String name;
    private int age;
    private double salary;
    //alt + insert快捷键写get和set方法
    public String getName() {
        return name;
    }

    public void setName(String name) {
        //增加校验逻辑
        if (name.length() >= 2 && name.length() <= 6) {
            this.name = name;
        }else{
            System.out.println("名字需为2-6字符,默认名字:无名人");
            this.name = "无名人";
        }
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        //增加校验逻辑
        if (age >= 1 && age <= 120) {
            this.age = age;
        }else{
            System.out.println("年龄范围(1 - 120),默认年龄:18");
            this.age = 18;
        }
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }
    //输出三个属性
    public String info() {
        return "name = " + this.name + "\t" + "age = " +
                this.age + "\t" + "salary = " + this.salary;
    }
}
  1. 出现一个问题:

如果写一个构造器来初始化属性的话就会绕过我们写的set方法
解决方法:将构造器和set方法结合

Person wpangzi = new Person("wpangzi", 100000, 0);
        System.out.println(wpangzi.info());
public Person(String name, int age, double salary) {
        setName(name);
        setAge(age);
        setSalary(salary);
    }

引申:idea取消参数提示

在这里插入图片描述

继承

继承基本介绍和使用

  1. 引出为什么需要继承
    因为有些属性和方法是相同的,为了减少重复
    在这里插入图片描述
  2. 介绍
    文字介绍
    在这里插入图片描述
    示意图
    注意:可以说D是B的子类,也可以说D是A的子类
    在这里插入图片描述
  3. 基本语法
    在这里插入图片描述
  4. 继承带来的便利
    在这里插入图片描述

※继承本质详解(重要)

  1. 使用的源码
    在这里插入图片描述
  2. 原理内存图

① 加载类信息:先从父类的最顶层开始加载,也就是从object开始加载到son类
② 创建son对象:在堆中开辟一块地址,然后先从父类最顶层开始初始化(grandpa类)一直到son类
③ 把地址返回给对象引用
④ 根据对象调用方法:服从调用规则,先看子类有没有,如果没有就去找父类…如果父类也没有,则报错(子类不能访问私有的属性或方法)

在这里插入图片描述
3. 方法或者属性查找规则
在这里插入图片描述

继承快速入门案例

  1. 写4个类

1.1 一个父类(子类相同的属性和方法)
1.2 两个子类(特有的属性和方法)
1.3 一个测试类(测试类要有主程序main())
2. 子类一定要写extends来继承父类
3. 代码省略

继承使用的细节

一共有10点

  1. 第一点如下
    在这里插入图片描述

① 子类会继承父类所有属性和方法,但不是所有属性和方法都能直接访问
② 非私有属性和方法可以直接访问
③ 私有属性和方法需要父类提供公共方法去访问
- 访问私有属性的公有方法
在这里插入图片描述

 //在子类中...
 System.out.println("访问父类的n4属性"+ getN4());

- 访问私有方法的公有方法
在这里插入图片描述

 //在子类中...
callinfo();
  1. 第2,3点如下
    在这里插入图片描述

① 不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器
②子类会先调用父类的构造器,再调用自己的构造器,因为子类构造器中会默认写一个super()去调用父类构造器
在这里插入图片描述
如果父类没有提供无参构造器,则必须在子类构造器中用super去指定使用父类的哪个构造器
注意1. 如果父类自定义了有参构造器,则默认的无参构造器消失
注意2. 子类中每一个构造器都需写super(参数)
在这里插入图片描述

  1. 第4,5,6点如下
    在这里插入图片描述

① 子类构造器中如果不是父类的无参构造器则需要显示调用
在这里插入图片描述

  1. 第7,8点如下
    在这里插入图片描述

第8点也就是:在调用子类构造器时,会先调用最顶层父类的构造器,再调用下一层父类,再下一层…直到把父类构造器全部调用完毕,再调用子类构造器(从子类向上回溯一直到Object,然后从Object向下调用)

  1. 第9,10点如下
    在这里插入图片描述

继承三个练习

  1. 练习1
    在这里插入图片描述

解读注意:
在子类B中:

  1. 第一个构造器中没有super(),因为this()和super()在一个构造器中不能同时出现
  2. 第二个构造器中会默认调用父类的无参构造器
  3. 子类构造器中一定有一个super(),或许无参(默认的可不写)或许有参
  1. 练习2:有多个父类时调用构造器的情况
    在这里插入图片描述
  2. 练习3
    在这里插入图片描述
    test.java
package com.wpangzi.extends_.excise.Excise03;

public class Test {
    public static void main(String[] args) {
        PC pc = new PC("intel",16,500,"IBM");
        System.out.println(pc.getPCDetails());
    }
}

父类:Computer.java

package com.wpangzi.extends_.excise.Excise03;

public class Computer {
    private String cpu;
    private int memory;
    private int disk;
    //因为是私有的,所以要写get,set方法来访问--封装
    public String getCpu() {
        return cpu;
    }

    public void setCpu(String cpu) {
        this.cpu = cpu;
    }

    public int getMemory() {
        return memory;
    }

    public void setMemory(int memory) {
        this.memory = memory;
    }

    public int getDisk() {
        return disk;
    }

    public void setDisk(int disk) {
        this.disk = disk;
    }
    //写有参构造器来对父类初始化
    public Computer(String cpu, int memory, int disk) {
        this.cpu = cpu;
        this.memory = memory;
        this.disk = disk;
    }
    //写一个public方法来返回computer信息
    public String getDetails(){
        return "cpu = "+cpu+" 内存 = "+memory+" 硬盘 = "+disk;
    }
}
注意:如果父类没有无参构造器时,子类继承会报错,直到子类构造器调用了父类有参构造器时,报错消失

子类:PC.java

package com.wpangzi.extends_.excise.Excise03;

public class PC extends Computer{
    //对属性进行封装
    private String brand;

    public String getBrand() {
        return brand;
    }

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

    //先调用父类构造器对父类属性进行初始化
    //再子类构造器对子类属性初始化
    //IDEA自动把父类属性加上了
    public PC(String cpu, int memory, int disk, String brand) {
        super(cpu, memory, disk);
        this.brand = brand;
    }
    //输出继承的属性信息以及特有的属性信息
    //可以用get方法对computer类的信息进行访问,
    // 也可以用public方法访问(推荐)
    public String getPCDetails(){
    return getDetails() + this.brand;
    }
}

多态(polymorphic)

多态引出与介绍

  1. 引出多态
    想解决的问题
    在这里插入图片描述
    用传统方法解决很繁琐
    在这里插入图片描述
    在这里插入图片描述
  2. 基本介绍
    在这里插入图片描述

多态具体体现

方法的多态
  1. 方法重载体现多态
    在这里插入图片描述
  2. 方法重写体现多态
    在这里插入图片描述
对象的多态(核心)
  1. 重要的几句话
    在这里插入图片描述
  2. 代码演示说明

初步理解:

  1. 编译类型指的是对象引用,运行类型是存储在堆中实质性的对象
  2. 在运行时,jvm会找到运行类型并调用其方法
    2.1 比如:编译类型是Animal,运行类型是Dog–>Animal animal = new Dog();
    2.2 运行animal.cry()时,jvm会在堆中找到animal指向的对象Dog,并调用Dog的cry方法
  3. 编译类型在定义对象时就确定了(在栈中有一个Animal类的对象引用),不能修改,但是运行类型是可以修改的,因为对象引用可以指向堆中不同的对象,animal先指向dog对象,再指向cat对象
  4. Animal是一个引用数据类型,它声明了一个变量animal,再把堆中某个地址(对象)赋给animal(子类对象赋给父类引用
    在这里插入图片描述

多态快速入门

就用引出多态的哪个案例
在这里插入图片描述
在这里插入图片描述

多态注意事项和细节

  1. 前提
  2. 向上转型
  3. 向下转型
  4. 属性不能重写
  5. instanceOf
  1. 前提
    在这里插入图片描述
  2. 向上转型
    在这里插入图片描述
    代码讲解
    在这里插入图片描述
  3. 向下转型
    在这里插入图片描述
    图解
    在这里插入图片描述
    代码讲解
    在这里插入图片描述
  4. 属性重写问题
    代码讲解
    在这里插入图片描述
  5. instanceOf
    在这里插入图片描述
    代码讲解
    在这里插入图片描述

两个练习

  1. 练习一:判对错
    在这里插入图片描述
  2. 练习二:输出
    在这里插入图片描述

java动态绑定机制(非常重要)

在这里插入图片描述

多态数组

数组 + 向上转型 + 向下转型

  1. 概念
    在这里插入图片描述
  2. 应用实例
    在这里插入图片描述
    2.1 注:如果没有说属性的访问修饰符,那默认写成私有属性+构造器+get和set方法+重写父类的say()方法
    2.2 重写父类的say()方法–>先调用了父类的say方法,然后加上自己多出的内容
    在这里插入图片描述
    2.3 多态数组
    用到向上转型
    在这里插入图片描述
  3. 应用升级
    在这里插入图片描述
    3.1 书写子类特有方法
    注:子类中没有name属性,需要使用父类的name属性,但是父类name属性是私有的,所以要用公有方法getName()来访问name
    在这里插入图片描述
    3.2 用到类型判断+向下转型
    在这里插入图片描述

多态参数

  1. 概念
    在这里插入图片描述
  2. 应用实例
    在这里插入图片描述
    2.1 问题2的解决:
    这样就可以使这两个子类都能传入该方法中
    在这里插入图片描述
    2.2 问题3的解决:
    在这里插入图片描述

Object类详解

方法摘要

==运算符

在这里插入图片描述

引申:idea查看jdk源码

  1. 快捷键:ctrl+b
    在这里插入图片描述
  2. = =和equals区别
    2.1 引用类型用 == 判断地址是否相同
    2.2 引用类型(integer和string)用 equlas() 判断值是否相等

    2.3 equals默认是判断地址是否相同,而integer和string重写了该方法,变成了判断值是否相等
  3. integer重写了object的equals方法
    在这里插入图片描述

equals()方法

  1. 概念
  2. 练习一 - 自己重写equals()方法
  3. 练习二 - 判断输出真假
  4. 练习三 - 判断输出真假
  1. 概念
    在这里插入图片描述
  2. 练习一 - 自己重写equals()方法
    在这里插入图片描述
    2.1 因为person类继承了object类,所以equals()默认是判断地址是否相等,所以这里需要在person类中重写equals()方法来满足题意
    2.2 重写equals()方法–>两个判断(地址是否相等,是否为person类)
    在这里插入图片描述
  3. 练习二 - 判断输出真假
    在这里插入图片描述
  4. 练习三 - 判断输出真假
    在这里插入图片描述

hashCode()方法

在这里插入图片描述

toString()方法

  1. Object 的toString()源码
    在这里插入图片描述
  2. 概念
    在这里插入图片描述
  3. 重写toString()方法
    3.1 重写是默认输出类的属性
    3.2 快捷键alt + insert
    3.3 在子类中重写方法后,子类就近原则先调用自己的方法,
    在这里插入图片描述

finalize()方法

  1. 概念
    在这里插入图片描述
    在这里插入图片描述

  2. 解读
    在这里插入图片描述

  3. 子类中重写finalize()方法
    在这里插入图片描述

  4. 告诉系统现在就释放(系统有时间就会释放)
    在这里插入图片描述

断点调试

断点调试介绍

在这里插入图片描述

断点调试快捷键

蓝色高亮行是执行到该行(还没有执行)
fn + f8 逐行执行代码
在这里插入图片描述

案例 - 数组越界异常

在这里插入图片描述

案例 - 追方法的源码

  1. 用解决方法2后按快捷键 fn + f7(跳入该条语句的方法–>追这个方法的源码)
  2. shift + f8(跳出该方法的源码–>逐层跳出)
  3. 如果fn + f7还是进不去,可以alt + shift +f7强制进入
    在这里插入图片描述
  4. 跳入跳出方法源码的图解
    在这里插入图片描述

案例 - 执行下一个断点

fn + f9 就是执行下一个断点
在这里插入图片描述

断点练习 - 看对象创建过程和动态绑定机制如何工作

在这里插入图片描述

  1. 练习一
    强制进入会进到底层(读取类信息),一般进入就是表层的方法(本类构造器)f7进入,shift+f8跳出,f8逐行执行
    进入方法后逐行执行,再想看方法源码就再次进入即可
    在这里插入图片描述
    1.1 加载类信息
    在这里插入图片描述
    1.2 调用本类toString方法
    在这里插入图片描述

super关键字

基本介绍

在这里插入图片描述

基本语法

super访问构造器时只能写在构造器中,且只能时第一句
在这里插入图片描述

super的好处和细节

  1. 好处和细节
    在这里插入图片描述
    在这里插入图片描述
  2. super,this,直接访问的规则
    在这里插入图片描述

super和this的比较

在这里插入图片描述

类的完善

在这里插入图片描述

方法重写/覆盖

基本介绍

在这里插入图片描述
在这里插入图片描述

方法重写细节

public > protected > 默认 > private
在这里插入图片描述

方法重载和重写的区别

在这里插入图片描述

方法重写练习

在这里插入图片描述

  1. 类的属性格式为:修饰符 + 数据类型 + 属性名
  2. 在子类中返回父类和子类信息
  3. 方法重写:
    3.1 子类和父类都写say()方法
    3.2 方法重写时前两个(修饰符和返回类型)有要求 - 要求细节里有,这里不写了
    3.3 后两个(方法名和形参列表)需要完全一致
    3.4 方法体内写重写的内容就好了
    3.5 父类中say方法输出两个属性信息,子类中say方法多输出两个属性信息(使用super调用父类的say方法,进行代码复用
    在这里插入图片描述

项目 :零钱通

零钱通介绍smallChangeSys

  1. 功能介绍
    在这里插入图片描述
  2. 代码实现介绍
    在这里插入图片描述
  3. 代码实现改进
    在实现基础功能的基础上再想着改进–万丈高楼平地起,化繁为简
    在这里插入图片描述

1. 零钱通菜单

菜单功能实现 :
先完成显示菜单,并可以选择菜单,给出对应提示

  • do - while 来重复打印菜单 + 接收输入选择
  • switch - case 判断执行哪个功能
  • 定义一个布尔变量,当选择退出系统时,将它置false,结束打印菜单
package com.wpangzi.smallChange;

import java.util.Scanner;

public class SmallChangeSys {
    //化繁为简
    //1. 先完成显示菜单,并可以选择菜单,给出对应提示
    public static void main(String[] args) {
        //定义变量
        // - 判断是否继续循环
        boolean loop = true;
        // - 接收用户输入
        String key = "";
        Scanner scanner = new Scanner(System.in);
        do{
            System.out.println("========零钱通菜单=========");
            System.out.println("\t\t1 零钱通明细");
            System.out.println("\t\t2 收益  入账");
            System.out.println("\t\t3 消     费");
            System.out.println("\t\t4 退     出");
            System.out.print("请选择(1 - 4):");
            key = scanner.next();
            //使用switch - case判断选择的功能
            switch (key) {
                case "1" :
                    System.out.println("1 零钱通明细");
                    break;
                case "2" :
                    System.out.println("2 收益  入账");
                    break;
                case "3" :
                    System.out.println("3 消     费");
                    break;
                case "4" :
                    System.out.println("4 退     出");
                    loop = false;
                    break;
                default:
                    System.out.println("选择有误,请重新选择");
            }
        }while(loop);
        System.out.println("--------退出了零钱通系统--------");
    }
}

2. 零钱通明细

明细
完成零钱通明细的三个思路(这里使用了第三种)

  1. 把收益和消费放到数组中
  2. 使用对象
  3. 简单的话可以使用字符串拼接
package com.wpangzi.smallChange;

import java.util.Scanner;

public class SmallChangeSys {
    //化繁为简
    //1. 先完成显示菜单,并可以选择菜单,给出对应提示
    //2. 完成零钱通明细
    public static void main(String[] args) {
        //定义变量
        //1. 先完成显示菜单,并可以选择菜单,给出对应提示
        // - 判断是否继续循环
        boolean loop = true;
        Scanner scanner = new Scanner(System.in);
        // - 接收用户输入
        String key = "";
        //2.完成零钱通明细
        // - 零钱通明细的字符串
        String details = "--------零钱通明细--------";
        do {
            System.out.println("========零钱通菜单=========");
            System.out.println("\t\t1 零钱通明细");
            System.out.println("\t\t2 收益  入账");
            System.out.println("\t\t3 消     费");
            System.out.println("\t\t4 退     出");
            System.out.print("请选择(1 - 4):");
            key = scanner.next();
            //使用switch - case判断选择的功能
            switch (key) {
                case "1":
                    System.out.println(details + "\n");
                    break;
                case "2":
                    System.out.println("2 收益  入账");
                    break;
                case "3":
                    System.out.println("3 消     费");
                    break;
                case "4":
                    System.out.println("4 退     出");
                    loop = false;
                    break;
                default:
                    System.out.println("选择有误,请重新选择");
            }
        } while (loop);
        System.out.println("--------退出了零钱通系统--------");
    }
}

3. 收益入账

完成收益入账 完成功能 驱动程序员增加新的变化和代码

  1. 日期
  • Date date = null;//date是java.util.Date类型,表示日期
  • 这里date是指向空,当调用功能3的时候再给它传一个地址,此时date就是调用时的时间了
  • 日期默认是美式的,要改变date的格式
  • 修改时:SimpleDateFormat sdf = new SimpleDateFormat(“yyyy-MM-dd HH:mm”);
  • 调用时:sdf.format(date)
  1. 字符串拼接的思想
  • 定义一个字符串String details = “--------零钱通明细--------”;
  • 当没有零钱明细的时候就只输出"--------零钱通明细--------"
  • 当添加了收益或者消费就把money和balance拼接到details字符串上details += "\n收益入账" + "\t" + money + "\t" + sdf.format(date) + "\t" + balance;
  • 再调用功能一输出details时就会显示详细的明细了System.out.println(details + "\n");

部分代码如下

//3. 完成收益入账 完成功能驱动程序员增加新的变化和代码
double money = 0;//收益
double balance = 0;//余额
/*
- 这里date是指向空,当调用功能3的时候再给它传一个地址,此时date就是调用时的时间了
- 日期默认是美式的,要改变date的格式
- - 修改时:SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
- - 调用时:sdf.format(date)
*/
Date date = null;//date是java.util.Date类型,表示日期
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
do {
System.out.println("========零钱通菜单=========");
System.out.println("\t\t1 零钱通明细");
System.out.println("\t\t2 收益  入账");
System.out.println("\t\t3 消     费");
System.out.println("\t\t4 退     出");
System.out.print("请选择(1 - 4):");
key = scanner.next();
//使用switch - case判断选择的功能
switch (key) {
case "1":
  System.out.println(details + "\n");
  break;
case "2":
  System.out.printf("收益入账金额:");
  //money需要校验范围
  money = scanner.nextDouble();
  balance += money;
  date = new Date();
  //字符串拼接:把收益详细信息拼接到details字符串上
  details += "\n收益入账" + "\t" + money + "\t" + sdf.format(date) + "\t" + balance;
  break;

4. 消费

和收益相似

部分代码

//4. 消费
String note = "";//消费说明
do {
System.out.println("========零钱通菜单=========");
System.out.println("\t\t1 零钱通明细");
System.out.println("\t\t2 收益  入账");
System.out.println("\t\t3 消     费");
System.out.println("\t\t4 退     出");
System.out.print("请选择(1 - 4):");
key = scanner.next();
//使用switch - case判断选择的功能
switch (key) {
case "3":
 System.out.printf("消费金额:");
 //money需要校验范围
 money = scanner.nextDouble();
 System.out.printf("消费说明:");
 note = scanner.next();
 balance -= money;
 date = new Date();
 //字符串拼接:把消费信息拼接到details字符串上
 details += "\n" + note + "\t-" + money + "\t" + sdf.format(date) + "\t余额:" + balance;
 break;

5. 代码改进 - 确认退出(有代码思想在里面)

在这里插入图片描述

代码思想

  1. 建议一段代码,完成一个小功能,尽量不要混在一起
  2. 使用while + break,来处理接收到的数据,必须是y 或者 n (判断条件是true,当满足条件时break)
  3. 退出while后,再判断choice是 y 还是n,就可以决定是否退出
case "4":
  String choice = "";
  //一段代码完成一个小功能,尽量不混在一起
  //while和break结合
  // - 来判断输入内容是否是y/n
  while(true){
      System.out.println("你确定要退出吗?y/n");
      choice = scanner.next();
      //字符串判断相等需要用到equals()方法
      if("y".equals(choice) || "n".equals(choice)){
          break;
      }
  }
  //当用户退出while,进行判断
  if("y".equals(choice)){
      loop = false;
  }
  break;

6. 代码改进 - 判断金额是否合理

在这里插入图片描述

  1. 先完成基本功能,想到改进可以先写在代码旁,后期改进
  2. 找出不正确金额的条件,然后给出提示,就直接break(这样容易维护和审查,找出不正确的然后再有不正确的条件直接添加就好了,如果找正确的还要把正确时做的代码放进去,if中嵌套会很多)
 case "2":
 System.out.printf("收益入账金额:");
 //money需要校验范围
 money = scanner.nextDouble();
 if (money <= 0) {
     System.out.println("收益入账金额 需要 大于0");
     break;
 }
 balance += money;
 date = new Date();
 //字符串拼接:把收益详细信息拼接到details字符串上
 details += "\n收益入账" + "\t+" + money + "\t" + sdf.format(date) + "\t余额:" + balance;
 break;
case "3":
 System.out.printf("消费金额:");
 //money需要校验范围
 money = scanner.nextDouble();
 if(money<=0||money>balance){
     System.out.println("您的消费金额应该在 0 - "+balance);
     break;
 }
 System.out.printf("消费说明:");
 note = scanner.next();
 balance -= money;
 date = new Date();
 //字符串拼接:把消费信息拼接到details字符串上
 details += "\n" + note + "\t-" + money + "\t" + sdf.format(date) + "\t余额:" + balance;
 break;

※将面向过程修改成面向对象(总代码)

在这里插入图片描述

  1. 结构
    在这里插入图片描述
  2. 代码

面向对象的好处:

  1. 把各个功能封装在一起,然后在测试类中调用
  2. 每一个功能对应一个方法
  3. 易于后期扩展,维护

功能类书写

  1. 该类完成零钱通各个功能
  2. 该类包括所有功能的属性以及各功能对应的方法
  3. 使用oop(面向对象编程)
  4. 将各个功能对应一个方法
package com.wpangzi.smallChange.oop;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Scanner;

/*
    1. 该类完成零钱通各个功能
    2. 该类包括所有功能的属性以及各功能对应的方法
    3. 使用oop(面向对象编程)
    4. 将各个功能对应一个方法
 */
public class SmallChangeSysOOP {
    //(属性)
    //1. 先完成显示菜单,并可以选择菜单,给出对应提示
    // - 判断是否继续循环
    boolean loop = true;
    Scanner scanner = new Scanner(System.in);
    // - 接收用户菜单输入
    String key = "";
    //2.完成零钱通明细
    // - 零钱通明细的字符串
    String details = "--------零钱通明细--------";
    //3. 完成收益入账 完成功能驱动程序员增加新的变化和代码
    double money = 0;//收益
    double balance = 0;//余额
    /*
        - 这里date是指向空,当调用功能3的时候再给它传一个地址,此时date就是调用时的时间了
        - 日期默认是美式的,要改变date的格式
        - - 修改时:SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
        - - 调用时:sdf.format(date)
     */
    Date date = null;//date是java.util.Date类型,表示日期
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm");
    //4. 消费
    String note = "";//消费说明

    //(方法)
    //1. 先完成显示菜单,并可以选择菜单,给出对应提示
    public void mainMenu() {
        do {
            System.out.println("========零钱通菜单(OOP)=========");
            System.out.println("\t\t1 零钱通明细");
            System.out.println("\t\t2 收益  入账");
            System.out.println("\t\t3 消     费");
            System.out.println("\t\t4 退     出");
            System.out.print("请选择(1 - 4):");
            key = scanner.next();
            //使用switch - case判断选择的功能
            switch (key) {
                case "1":
                    this.detail();
                    break;
                case "2":
                    this.income();
                    break;
                case "3":
                    this.pay();
                    break;
                case "4":
                    this.exit();
                    break;
                default:
                    System.out.println("选择有误,请重新选择");
            }
        } while (loop);
        System.out.println("--------退出了零钱通系统--------");
    }

    //2. 完成零钱通明细
    public void detail() {
        System.out.println(details + "\n");
    }

    //3. 完成收益入账
    public void income() {
        System.out.printf("收益入账金额:");
        //money需要校验范围
        money = scanner.nextDouble();
        //金额不合理就return
        if (money <= 0) {
            System.out.println("收益入账金额 需要 大于0");
            return;
        }
        balance += money;
        date = new Date();
        //字符串拼接:把收益详细信息拼接到details字符串上
        details += "\n收益入账" + "\t+" + money + "\t" + sdf.format(date) + "\t余额:" + balance;
    }

    //4. 消费
    public void pay() {
        System.out.printf("消费金额:");
        //money需要校验范围
        money = scanner.nextDouble();
        //找出不合理的,然后return退出方法
        if (money <= 0 || money > balance) {
            System.out.println("您的消费金额应该在 0 - " + balance);
            return;
        }
        System.out.printf("消费说明:");
        note = scanner.next();
        balance -= money;
        date = new Date();
        //字符串拼接:把消费信息拼接到details字符串上
        details += "\n" + note + "\t-" + money + "\t" + sdf.format(date) + "\t余额:" + balance;
    }

    //5. 退出程序的代码改进
    public void exit() {
        String choice = "";
        //一段代码完成一个小功能,尽量不混在一起
        //while和break结合
        // - 来判断输入内容是否是y/n
        while (true) {
            System.out.println("你确定要退出吗?y/n");
            choice = scanner.next();
            //字符串判断相等需要用到equals()方法
            if ("y".equals(choice) || "n".equals(choice)) {
                break;
            }
        }
        //当用户退出while,进行判断
        if ("y".equals(choice)) {
            loop = false;
        }
    }
}

package com.wpangzi.smallChange.oop;

//调用oop类中的方法
public class SmallChangeSysAPP {
    public static void main(String[] args) {
        new SmallChangeSysOOP().mainMenu();
    }
}

效果:

在这里插入图片描述

本章作业

1. 类数组,元素顺序根据属性排序

在这里插入图片描述

使用冒泡排序,用年龄或者名字比较,然后交换数组中的对象

package com.wpangzi.homeWork;

public class HomeWork1 {
    public static void main(String[] args) {
        //数组是用方括号的
        Person[] persons = new Person[3];
        persons[0] = new Person("pang", 20, "学生");
        persons[1] = new Person("shou", 27, "学生");
        persons[2] = new Person("men", 22, "教师");
        //排序
        Person person1 = new Person();
        System.out.println("====排序前====");
        for (int i = 0; i < persons.length; i++) {
            System.out.println(persons[i]);
        }
        System.out.println("====排序后====");
        person1.sort(persons);
    }
}

class Person {
    private String name;
    private int age;
    private String job;

    public Person() {
    }

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

    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 getJob() {
        return job;
    }

    public void setJob(String job) {
        this.job = job;
    }

    //对年龄进行冒泡排序
    public void sort(Person[] persons) {
        Person temp = null;
        for (int i = 0; i < persons.length - 1; i++) {
            //内层循环一直是从0开始,
            for (int j = 0; j < persons.length - 1 - i; j++) {
                //年龄从大到小排序
                //名字的话修改成name.length()就好了
                if (persons[j].age < persons[j + 1].age) {
                    temp = persons[j];
                    persons[j] = persons[j + 1];
                    persons[j + 1] = temp;
                }
            }
        }
        for (int i = 0; i < persons.length; i++) {
            System.out.println(persons[i]);
        }
    }

    //重写toString()
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", job='" + job + '\'' +
                '}';
    }
}

效果:
在这里插入图片描述

2. 访问修饰符

在这里插入图片描述

3. 类继承以及方法重写

在这里插入图片描述

package com.wpangzi.homeWork;

public class HomeWork2 {
    public static void main(String[] args) {
        Professor professor = new Professor("jack", 30, "高级", 30000, 1.3);
        professor.introduce();
    }
}

class Teacher{
    private String name;
    private int age;
    private String post;
    private double salary;
    //这里我们分析出来有工资级别
    private double grade;
    //输出教师信息
    public void introduce(){
        System.out.println("name = "+name+" age = "+age+" post = "+post+" salary = "+salary+" grade = "+grade);
    }

    public Teacher(String name, int age, String post, double salary, double grade) {
        this.name = name;
        this.age = age;
        this.post = post;
        this.salary = salary;
        this.grade = grade;
    }

    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 getPost() {
        return post;
    }

    public void setPost(String post) {
        this.post = post;
    }

    public double getSalary() {
        return salary;
    }

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

class Professor extends Teacher{
    public Professor(String name, int age, String post, double salary, double grade) {
        super(name, age, post, salary, grade);
    }
    //重写introduce方法
    public void introduce(){
        System.out.println("教授信息...");
        super.introduce();
    }
}

4. 类继承,重写方法

  1. 写子类时,先分析是否有其特有属性以及父类中没有明确提到但是需要的属性
  2. 基类:属性 + 构造器 + get和set方法 + 自己的方法
  3. 子类:构造器(调用父类)+ 特有属性 + 重写方法/特有方法
  4. 写类时最重要的就是先分析出类的属性 和 方法

在这里插入图片描述
注意set和get方法(很灵活):
若属性不确定时,不用构造器初始化,用set方法来设置,这样可以动态设置属性值–>可以先给属性一个默认值,当需要改变值时,调用set方法来修改
写子类时,若用到父类中private属性,需要用到get方法
在这里插入图片描述
重写代码–>复用父类代码
在这里插入图片描述

5. this和super

注意图片中标识的内容,this:如果本类有该内容就不会找父类的
在这里插入图片描述

6. 子类和父类构造器

创建子类Demo对象,会先调用对应子类Demo的构造器,子类构造器中会默认调用父类的无参构造器
① 子类构造器一定会调用父类的构造器(隐式默认调用无参或者显式调用)
② 若父类中没有无参构造器,就需要在子类中显式调用父类的有参构造器
③ 子类的构造器可以显式的选择要调用的父类构造器
④ 构造器中this和super只存在一个

在这里插入图片描述

7. 方法重写(好好体会)

在这里插入图片描述

这里巧妙使用了父类的deposit方法(父类是核心,扩展时创建一个子类,在父类的基础上进行扩展)

  • 开闭原则:只扩展,不修改
    新增业务是存款手续费需要1元,那么就创建一个它的子类,
    在子类中对父类的内容进行调整super.deposit(amount - 1);,而不修改父类内容(高内聚,低耦合)

main方法测试

public static void main(String[] args) {
        //测试CheckingAccount
        CheckingAccount checkingAccount = new CheckingAccount(1000);
        checkingAccount.deposit(10);//1010 - 1 = 1009
        System.out.println(checkingAccount.getBalance());
        //测试SavingsAccount
        SavingsAccount savingsAccount = new SavingsAccount(1000);
        savingsAccount.deposit(100);
        savingsAccount.deposit(100);
        savingsAccount.deposit(100);
        System.out.println(savingsAccount.getBalance());//1300
        savingsAccount.deposit(100);
        System.out.println(savingsAccount.getBalance());//1399(减了手续费)
        //月初,计时器调用earnMonthlyInterest方法
        System.out.println(savingsAccount.getBalance());//余额+利息
        savingsAccount.deposit(100);}//因为重置了次数,所以免手续费
}

父类(核心类,只扩展不修改)

class BankAccount {
	//属性-余额
    private double balance;
    //构造器
    public BankAccount(double initialBalance) {
        this.balance = initialBalance;
    }
	//get和set方法
    public double getBalance() {
        return balance;
    }
    public void setBalance(double balance) {
        this.balance = balance;
    }
    //存款 - amount就是变动的钱
    public void deposit(double amount) {
        balance += amount;
    }
    //取款
    public void withdraw(double amount) {
        balance -= amount;
    }
}

子类(扩展:收取1元手续费)

class CheckingAccount extends BankAccount {
    //属性
    //方法
    //构造器(把变动的钱传给父类的构造器)
    public CheckingAccount(double initialBalance) {
        super(initialBalance);
    }
    //重写父类存款方法
    @Override
    public void deposit(double amount) {
    /*
         这里巧妙使用了父类的deposit方法(可能父类的deposit方法很复杂,但是我们只需给amount-1)
         开闭原则:只扩展,不修改
         新增业务是存款手续费需要1元,那么就创建一个它的子类,
         在子类中对父类的内容进行调整(对变动的金额-1),而不修改父类内容(高内聚,低耦合)
     */
        super.deposit(amount - 1);
    }
    //重写父类取款方法
    @Override
    public void withdraw(double amount) {
        super.withdraw(amount + 1);
    }
}

子类(扩展:收取1元手续费)

/*
    每个月都有利息产生(earnMonthlyInterest方法被调用)
    并且每个月三次免手续费的存款或取款。
    在earnMonthlyInterest方法中重置交易次数
 */
class SavingsAccount extends BankAccount{
    //属性(自己初始化没用构造器初始化)
    private int cont = 3;//交易次数
    private double rate = 0.01;//利率

    public SavingsAccount(double initialBalance) {
        super(initialBalance);
    }

    public int getCont() {
        return cont;
    }

    public void setCont(int cont) {
        this.cont = cont;
    }

    public double getRate() {
        return rate;
    }

    public void setRate(double rate) {
        this.rate = rate;
    }
    //重写 - 免手续费的逻辑
    @Override
    public void deposit(double amount) {
        if (cont>0){
            super.deposit(amount);//免手续费
        }else{
            super.deposit(amount-1);
        }
        cont--;
    }

    @Override
    public void withdraw(double amount) {
        if (cont>0){
            super.withdraw(amount);
        }else{
            super.withdraw(amount+1);
        }
        cont--;
    }
    //每个月初,统计上个月利息,同时重置免单次数
    public void earnMonthlyInterest(){
        cont=3;
        //把利息加到余额里
        super.deposit(getBalance()*rate);
    }
}

8. 类中重写equals方法(过关斩将的思想)

  1. 判断两个对象是否相同(地址)
  2. 找出不正确的(过关斩将)–>因为找正确的需要写很多嵌套,而找不正确的,找到即可return false终止比较。
    2.1 判断obj是否是SavingsAccount类型或者其子类
    2.2 向下转型,判断各个属性值是否相等即可
  3. 调用equals方法:person1.equals(person2)
public boolean equals(Object obj) {
        //判断两个对象是否相同(地址)
        if (this == obj) {
            return true;
        }
        /*
            找出不正确的(过关斩将)
            判断obj是否是SavingsAccount类型或者其子类
         */
        if (!(obj instanceof SavingsAccount)){//不是的话
            return false;
        }
        //向下转型,因为现在obj的运行类型肯定是savingsAccount或其子类
        //然后判断各个属性值是否相等即可
        SavingsAccount that = (SavingsAccount) obj;
        return cont == that.cont &&
                Double.compare(that.rate, rate) == 0;
    }

9. 向上/下转型,编译/运行类型

在这里插入图片描述

10. == 和 equals的区别

在这里插入图片描述

11. 综合(封装、继承、多态)

在这里插入图片描述
① 方法重写部分
父类共有部分
在这里插入图片描述
子类特有部分
在这里插入图片描述
② 应当分析出还有一个打印信息的方法

  • 提取出信息共有的部分写到父类中
  • 在子类中根据自己的需要对父类的字符串进行拼接或者是调用

在这里插入图片描述
子类中重写该方法(信息的组合)
在这里插入图片描述
测试调用
在这里插入图片描述
③ 第7个问题,对toString方法进行重写,来输出排列后数组元素的信息
在这里插入图片描述
④ 第8个问题 形参是person类型,调用子类特殊方法
在这里插入图片描述

12. 构造器的程序阅读

在这里插入图片描述

13. 什么是多态,多态体现在哪

在这里插入图片描述

14. Java动态绑定机制

在这里插入图片描述
比如讲这个知识点时的例子,

  • 子类要调用方法A,但是子类中没有,所以去父类中找方法A,找到了
  • 方法A中又调用了方法B,此时因为运行类型是子类,所以B方法会先去子类中寻找是否存在,若没有才回去父类中找
  • 所以要切记方法和运行类型是时刻绑定的,方法总是会先去运行类型中查找
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值