JavaSE-第四章

发布于 2022-02-21  140 次阅读


第四章 面向对象

4.1 方法

  • 方法:就是一种功能的体现。方法是可以复用的
  • 格式:

方法的修饰符 返回类型 方法名称(参数){

方法体

}

  • 方法的修饰符:public、private、protected、abstract、static、final...
  • 返回类型:如果方法没有返回值那么使用void修饰,如果有返回类型那么方法的返回值必须是这种类型。
  • 方法名称:符合标识符即可
  • 方法的参数:可以没有或者多个,多个使用逗号分隔符
  • 案例:
public class Demo {
    public static void main(String[] args){
        method();
    }

    //自定义方法
    public static void method(){
        System.out.println("这是一个没有返回和参数的方法");
    }
}
  • 含一个参数的方法
public class Demo {
    public static void main(String[] args){
        method(1);
    }

    public static void method(int key){
        switch(key){
            case 1:
                System.out.println("执行了第一个语句");
                break;
            case 2:
                System.out.println("执行了第二个语句");
                break;
            default:
                System.out.println("执行了其他语句");
                break;
        }
    }
}
  • 含两个参数的方法
public class Demo {
    public static void main(String[] args){
        method(1,2);
    }

    //自定义方法
    public static void method(int m,int n){
        System.out.println(m+n);
    }
}
  • 含有返回值得方法
public class Demo {
    public static void main(String[] args){
        int sum = method(1,2);
        System.out.println(sum);
    }

    //自定义方法
    public static int method(int m,int n){
        return m+n;
    }
}

4.2 方法的重载

4.2.1 重载的条件

  • 在同一个类中
  • 方法的名称相同的
  • 方法的参数的类型、个数、顺序有一个不同即可
  • 与方法的返回类型没有关系

4.2.2 不使用重载

public class Demo {
    public static void main(String[] args){
        int methodInt = methodInt(1,2);
    }

    //因为没有重载,所以增加了记忆方法的困难
    public static int methodInt(int m,int n) {
        return m + n;
    }
    public static double methodDouble(double m,int n){
            return m + n;
        }

    public static long methodLong(long m,int n){
        return m+n;
    }
}

4.2.3 使用重载:减少记忆

public class Demo {
    public static void main(String[] args){
        sum(1,2);
    }


    public static int sum(int m,int n) {
        return m + n;
    }
    public static double sum(double m,int n){
            return m + n;
        }

    public static long sum(long m,int n){
        return m+n;
    }
}

4.2.4 重载的条件

  • 参数的类型不能相同
  • 参数的个数不能相同
  • 参数的类型顺序不能相同

4.3 递归

4.3.1 案例:求10以内的和

public class Demo {
    public static void main(String[] args){
        int sum = 0;
        for (int i = 0; i < 6; i++) {
            sum += i;
        }
        System.out.println(sum);
    }
}

4.3.2 案例:使用递归

  • 自身的调用;就是自己调用自己
public class Demo {
    public static void main(String[] args){
        int sum = rec(5);
        System.out.println(sum);
    }
    //递归
    public static int rec(int i){
        if(i==1)
            return 1;
        return i+rec(i-1);
    }
}
  • 详解
public class Demo {
    public static void main(String[] args){
        int sum = rec(5);
        System.out.println(sum);
    }
    //递归

    /**
     * 返回:5+4+3+2+1
     5+rec(5-1)
     4+rec(4-1)
     3+rec(3-1)
     2+rec(2-1)
     1
     */
    public static int rec(int i){
        if(i==1)
            return 1;
        return i+rec(i-1);
    }
}
  • n的阶层
public static int rec(int n){
    if(n==1)
        return 1;
    return n*rec(n-1);
}

4.4 使用方法完成项目

4.4.1 第一版

import java.util.Scanner;
public class Demo {
    public static void main(String[] args){
        //控制台对象
        Scanner scanner = new Scanner(System.in);
        //跳出循环的标识
        boolean flag = true;
        //提示信息
        System.out.println("1添加;2显示;3删除;4更新;5退出;");
        //循环
        do {
            int key = scanner.nextInt();
            switch (key){
                case 1:
                    System.out.println("请输入一个数字");
                    save(scanner.nextInt());
                    break;
                case 2:
                    findAll();
                    break;
                case 3:
                    System.out.println("请输入一个需要删除的数字");
                    delete(scanner.nextInt());
                    break;
                case 4:
                    System.out.println("请输入需要更新的数字");
                    int i = scanner.nextInt();
                    System.out.println("请输入需要更新的新数字");
                    int n = scanner.nextInt();
                    update(i,n);
                    break;
                case 5:
                    flag = false;
                    break;
            }
        }while(flag);
    }
    //创建数组
    static int[] array = new int[3];

    //获取数据的下标
    public static int findByIndex(int element){
        //通过判断element的值决定查找空位置还是对应元素的下标
        for (int index = 0; index < array.length; index++) {
            if (element == 0) {//获取空位置
                if (array[index] == 0) {
                    return index;
                }
            } else {//查询对应元素的下标
                if (array[index] == element) {
                    return index;
                }
            }
        }
        return -1;
    }

    //添加数据
    public static void save(int el){
            //获取一个保存元素的空位置
            int index = findByIndex(0);
            //判断下标
            if (index!=-1){
                array[index] = el;
                System.out.println("操作成功");
            }else{
                System.out.println("full");
            }
        }

    //显示数据
    public static void findAll(){
            for (int index = 0; index < array.length; index++) {
                int i = array[index];
                if (i!=0) {
                    System.out.println(i);
                }
            }
        }

    //删除数据
    public static void delete(int el){
            //根据元素获取下标
            int index = findByIndex(el);
            //判断
            if (index!=-1){
                array[index] = 0;
                System.out.println("操作成功");
            }else {
                System.out.println("查无此数据,请重新输入");
            }
        }

    //更新数据
    public static void update(int el,int target){
            //根据元组获取下标
            int index = findByIndex(el);
            //判断
            if (index!=-1) {
                array[index] = target;
                System.out.println("操作成功");
            }else{
                System.out.println("查无此数据请重新输入");
            }
        }
}

4.4.2 第二版

import java.util.Scanner;
public class Demo {
    public static void main(String[] args){
        //控制台对象
        Scanner scanner = new Scanner(System.in);
        //跳出循环的标识
        boolean flag = true;
        //提示信息
        System.out.println("1添加;2显示;3删除;4更新;5退出;");
        //循环
        do {
            int key = scanner.nextInt();
            switch (key){
                case 1:
                    save(scanner);
                    break;
                case 2:
                    findAll();
                    break;
                case 3:
                    delete(scanner);
                    break;
                case 4:
                    update(scanner);
                    break;
                case 5:
                    flag = false;
                    break;
            }
        }while(flag);
    }
    //创建数组
    static int[] array = new int[3];

    //获取数据的下标
    public static int findByIndex(int element){
        //通过判断element的值决定查找空位置还是对应元素的下标
        for (int index = 0; index < array.length; index++) {
            if (element == 0) {//获取空位置
                if (array[index] == 0) {
                    return index;
                }
            } else {//查询对应元素的下标
                if (array[index] == element) {
                    return index;
                }
            }
        }
        return -1;
    }

    //添加数据
    public static void save(Scanner scanner){
            //获取一个保存元素的空位置
            int index = findByIndex(0);
            //判断下标
            if (index!=-1){
                System.out.println("请输入一个数字");
                array[index] = scanner.nextInt();
                System.out.println("操作成功");
            }else{
                System.out.println("full");
            }
        }

    //显示数据
    public static void findAll(){
            for (int index = 0; index < array.length; index++) {
                int i = array[index];
                if (i!=0) {
                    System.out.println(i);
                }
            }
        }

    //删除数据
    public static void delete(Scanner scanner){
        System.out.println("请输入一个需要删除的数字");
        //根据元素获取下标
        int index = findByIndex(scanner.nextInt());
        //判断
        if (index!=-1){
            array[index] = 0;
            System.out.println("操作成功");
        }else {
            System.out.println("查无此数据,请重新输入");
        }
    }

    //更新数据
    public static void update(Scanner scanner){
        System.out.println("请输入需要更新的数字");
            //根据元组获取下标
            int index = findByIndex(scanner.nextInt());
            //判断
            if (index!=-1) {
                System.out.println("请输入需要更新的新数字");
                array[index] = scanner.nextInt();
                System.out.println("操作成功");
            }else{
                System.out.println("查无此数据请重新输入");
            }
        }
}

4.5 面向过程区别面向对象

  • c语言就是面向过程语言
  • 面向过程的特点:必须了解整个过程,每个步骤的因果关系
  • 面向对象:将现实世界分为不同的对象,把各个对象协作起来
    • 就是面对一个真实存在的
    • 面向对象的特性:实质就是面向抽象
      • 抽象:具有这一类事务的特征和特性
      • 面向对象符合人的思维

4.5.1 面向对象的特性

  • 封装
  • 继承
  • 多态

4.5.2 类与对象的概念

  • 类:对具有共性事物的抽象描述
    • 例如:学生类=学号、姓名、性别、年龄、班级
  • 对象(实例):就是对类的具体化、实例化
    • 例如:学生对象:1001、小灰灰、男、6、3班
  • 以上的类图中都是属性,进一步细化,添加类中的方法
  • 结论:属性源自于一种状态,方法源自于一个功能(动作)
    • 类=属性+方法
    • 类=成员+功能

4.5.3 类的定义

格式:

类的修饰符 class 类名称 extends 父类名称 implements 接口名称{
    类体:属性+方法
}
  • 案例:创建类
public class Student {
    //学号
    int id;
    //姓名
    String name;
    //性别
    String sex;
    //地址
    String address;
    //年龄
    int age;
}
  • 以上都是属性,也成为成员变量
  • 成员变量会默认初始化
  • 注意:局部变量不能默认初始化,必须初始化

4.5.4 对象的创建和使用

  • 使用new关键字创建对象,才可以使用
public class TestDemo {
    public static void main(String[] args) {
        //创建对象:把堆内存中这个对象的内存地址赋值给参数student了
        Student student = new Student();
        System.out.println("内存地址:"+student);
    }
}
  • 属性默认值
  • 创建流程图
  • 具体的默认值
类型默认值
byte0
short0
int0
long0
float0.0
double0.0
booleanfalse
char''
引用类型null

4.5.4.1 可以对成员变量进行赋值

  • 就是把默认值替换了
  • 一个类可以创建N个对象,并且,每个对象的成员值属于当前对象;
  • 只有通过对象才可以修改或者访问这个对象的成员(属性);

4.6 封装性

  • 属性私有化,细节被隐藏掉,安全性提高了。
  • private关键字作用就是把属性保护起来了。
    • 在本类中可以访问
    • 就只能自己使用
    • 就是为了安全,外界无法访问

4.6.1 添加一个方法完成对成员(属性)的操作

  • 添加方法(功能)的目的,就是在使用的时候可以添加各种限制(条件,添加判断);
  • 方法就是对成员操作的
  • 案例
public class TestDemo {
    public static void main(String[] args) {
        Student student = new Student();
        //通过方法把外面的数据添加到对象中的成员上
        student.setAge(20);
        //通过方法把成员的数据获取
        int age = student.getAge();
        System.out.println(age);
        
    }
}

class Student {
    //学号
    private int id;
    //姓名
    private String name;
    //性别
    private String sex;
    //地址
    private String address;
    //年龄
    private int age;

    //添加年龄
    public void setAge(int nl) {
        if (nl > 0 & nl < 300) {
            age = nl;
        } else {
            System.out.println("请添加范围内的数字");
        }
    }
    //获取年龄
    public int getAge() {
        return age;
    }
}

通过上面的案例,避免了直接操作对象的属性,这就是封装;让外界对类内部知道的越少越好;

结论:封装属性;暴露方法

4.7 构造方法(构造器、构造函数)

  • 构造方法就是创建对象的

4.7.1 格式:

  • 构造方法的修饰符:private public protected
  • 构造方法和普通方法一样都可以进行重载
  • 构造方法特点:
    • 与类同名
    • 构造方法不能有任何返回类型,即没有返回值,关键字void也不能加;如果添加了void那么就不是构造方法了,而是普通方法。
    • 任何类都有构造方法,如果没有显示的编写,在创建对象时,系统会分配一个默认的无参数构造方法;如果显示的定义了构造方法,系统就不会创建默认的无参数构造方法了。
  • 4.7.2 案例:无参构造方法
public class TestDemo {
    public static void main(String[] args) {
        Student student = new Student();
    }
}

class Student {
    //系统默认分配了一个无参数的构造方法
}
public class TestDemo {
    public static void main(String[] args) {
        Student student = new Student();
    }
}

class Student {
    //无参数构造方法
    public Student(){
        System.out.println("无参数构造方法");
    }
}
  • 添加void那么就变为普通方法
class Student {
    //创建了一个普通方法
    public void Student(){
        System.out.println("创建了一个普通方法");
    }
}

4.7.3 显示编写构造方法

  • 创建对象时,系统不会在分配默认的构造方法
public class TestDemo {
    public static void main(String[] args) {
        Student student = new Student();
    }
}

class Student {
    //构造方法重载
    
    //无参数构造方法
    public Student(){
        System.out.println("无参数构造方法");
    }
    //有参数构造方法
    public Student(int i){
        System.out.println("有参数构造方法");
    }
}
  • 初始化
public class TestDemo {
    public static void main(String[] args) {
        Student student = new Student(1001, "张三", "男");
    }
}

4.8 对象和引用

4.8.1 java内存的主要划分

4.8.2 内存的表示

  • 主方法main压入栈,就创建一个student对象
public class TestDemo {
    public static void main(String[] args) {
        Student student = new Student(1001, "张三", "男");
    }
}
  • 对student这个对象中的成员赋值
public class TestDemo {
    public static void main(String[] args) {
        Student student = new Student(1001, "张三", "男");
    }
}

class Student {
    //学号
    private int id;
    //姓名
    private String name;
    //性别
    private String sex;

    public Student(int i,String username,String xb){
        id = i;
        name = username;
        sex = xb;
    }
}

4.8.3 当不使用new关键字时,出现问题

4.9 this关键字

  • this表示当前对象
  • 如果堆中有n个对象,会有n个this指向对应的对象

4.9.1 局部变量

  • 当局部变量与成员变量名称相同时,可以使用this加以区分,因为就近原则
public class TestDemo {
    public static void main(String[] args) {
        Student student = new Student();
        student.setName("张三");
    }
}

class Student {
    
    //姓名
    private String name;

    public void Student(String name){
        //就把name参数的数值赋值给当前对象的成员变量了
        this.name = name;
    }
} 
  • 自动生成setter和getter方法

4.9.2 可以调用其他的构造方法

  • 注意:调用构造方法时必须在首行

4.9.2.1 这样调用构造方法是错误的,因为,创建了垃圾对象

class Student {
    public Student() {
        System.out.println("无参数");
        //这样调用构造方法是错误的,因为,创建了垃圾对象
        new Student(1);
    }

    public Student(int i) {
        System.out.println("1参数");
    }

    public Student(int i, int n) {
        System.out.println("2参数");
    }
}

4.9.2.2 这样调用是正确的

public class Demo{
    public static void main(String[] args){
        Student student = new Student();
    }
}
class Student {
    public Student() {
        this(10);
        System.out.println("无参数");
    }

    public Student(int i) {
        this(1,2);
        System.out.println("1参数");
    }

    public Student(int i, int n) {
        System.out.println("2参数");
    }
}

4.9.3 this可以调用成员方法

public class TestDemo {
    public static void main(String[] args) {
        Student student = new Student();
        student.show();
    }
}
class Student {
   public void show(){
       System.out.println("show-method");
       this.save();
   }
    public void save(){
        System.out.println("show-method");
    }
}
  • 调用成员方法时也可以不使用this关键字(推荐)
class Student {
   public void show(){
       System.out.println("show-method");
       save();
   }
    public void save(){
        System.out.println("show-method");
    }
}

4.9.4 javaBean

  • bean就是一种特殊的类
  • 含有setter和getter方法
  • 可以看做数据的载体
class Student {
    private int id;
    private String name;
    private String sex;
    private int age;

    public int getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public int getAge() {
        return age;
    }

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

5.0 项目-优化

5.0.1 需求:

  • 对学生信息完成CRUD
  • 使用数组存储学生信息(学生对象)

5.0.2 技术

  • javase+数组+循环
  • 技术分析:
    • 创建一个可以存储学生类型的数组
    • 创建保存学生的方法:save
    • 显示学生的方法:findAll
    • 删除学生的的方法:delete
    • 更新学生的方法:
      • 查询到需要更新的学生:findByid
      • 更新学生的信息:update只更新姓名
    • 根据学生的学号查找在数组中这个学生对应的下标:findByindex

5.0.3 编码实现

import java.util.Scanner;

public class TestDemo {

    //创建一个学生数组
    static Student[] array = new Student[3];

    /***
     * TODO 按照学号查找对应学生的下标,如果sid=0表示需要找空位置完成添加
     * @param sid   表示学号
     * @return  下标
     */
    public static int findByIndex(int sid){
        //循环
        for (int index = 0; index < array.length; index++) {
            //根据下标获取对应的学生对象
            Student student = array[index];
            //判断是否存在
            if (sid==0){
                if (student==null){
                    return index;
                }
            }else{
                if (student!=null && student.getId()==sid){
                    return index;
                }
            }

        }
        return -1;
    }

    /***
     * TODO 保存学生信息
     * @param student   学生对象(地址)
     */
    public static void save(Student student){
        //获取一个空位置
        if (student!=null){
            int index = findByIndex(0);
            if (index!=-1){
                
                array[index] = student;
                System.out.println("操作成功");
            }else{
                System.out.println("满了");
            }
        }else{
            System.out.println("请添加学生信息");
        }
    }

    /**
     *  TODO 显示学生信息
     */
    public static void findAll(){
        //遍历数组中学生的信息
        for (int index = 0; index < array.length; index++) {
            //获取学生对象
            Student student = array[index];
            if (student!=null){
                System.out.println("学号:"+student.getId()+"\t"+
                        "姓名:"+student.getName()+"\t"+
                        "性别:"+student.getSex()+"\t"+
                        "年龄:"+student.getAge()
                );
            }
        }
    }

    /***
     * TODO 删除学生
     * @param sid   学号
     */
    public static void delete(int sid){
        //获取学生在数组中的位置
        int index = findByIndex(sid);
        //判断
        if (index!=-1){
            array[index] = null;
            System.out.println("操作成功");
        }else{
            System.out.println("查无此学生,请重新输入学号");
        }

    }

    public static void update(int sid,String name){
        //按照学号查找对应的下标
        int index = findByIndex(sid);
        //判断
        if (index!=-1) {
            //获取这个学生
            Student student = array[index];
            //修改姓名
            student.setName(name);
            System.out.println("操作成功");
        }else{
            System.out.println("查无此学生,请重新输入学号");
        }

    }

    /***
     * TODO 测试方法
     * @param args
     */
    public static void main(String[] args) {
        //控制台对象
        Scanner scanner = new Scanner(System.in);
        //跳出循环体
        boolean flag = true;
        //提示
        System.out.println("1添加;2显示;3删除;4更新;5退出");
        //循环
        do {
            //key
            int key = scanner.nextInt();
            switch (key){
                case 1://调用方法
                    Student student = new Student();
                    //提示信息
                    System.out.println("请输入学号");
                    student.setId(scanner.nextInt());
                    System.out.println("请输入姓名");
                    student.setName(scanner.next());
                    System.out.println("请输入性别");
                    student.setSex(scanner.next());
                    System.out.println("请输入年龄");
                    student.setAge(scanner.nextInt());
                    //保存
                    save(student);
                    break;
                case 2://调用方法
                    findAll();
                    break;
                case 3:
                    System.out.println("请输入学号");
                    delete(scanner.nextInt());
                    break;
                case 4:
                    System.out.println("请输入学号");
                    int sid = scanner.nextInt();
                    System.out.println("请输入姓名");
                    String name = scanner.next();
                    //更新
                    update(sid,name);
                    break;
                case 5:
                    flag = false;
                    break;
            }
        }while(flag);

    }
}
class Student {
    private int id;         //学号
    private String name;    //学生姓名
    private String sex;     //学生性别
    private int age;        //学生年龄

    public int getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public int getAge() {
        return age;
    }

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

5.0.4 编码实现-优化版

5.0.4.1 扩展

  • 返回字符串
import java.util.UUID;

public class Demo1 {
    public static void main(String[] args){
        //不重复
        String str = UUID.randomUUID().toString();
        System.out.println(str);
    }
}
  • 返回long
import java.util.UUID;
import java.util.zip.CRC32;

public class Demo1 {
    public static void main(String[] args){
        //不重复
        String str = UUID.randomUUID().toString();
        CRC32 crc32 = new CRC32();
        crc32.update(str.getBytes());
        //获取
        long value = crc32.getValue();
        System.out.println(value);
    }
}
  • 实现
import java.util.Scanner;
import java.util.UUID;
import java.util.zip.CRC32;

public class TestDemo {

    //创建一个学生数组
    static Student[] array = new Student[3];

    /***
     * TODO 按照学号查找对应学生的下标,如果sid=0表示需要找空位置完成添加
     * @param sid   表示学号
     * @return  下标
     */
    public static int findByIndex(int sid){
        //循环
        for (int index = 0; index < array.length; index++) {
            //根据下标获取对应的学生对象
            Student student = array[index];
            //判断是否存在
            if (sid==0){
                if (student==null){
                    return index;
                }
            }else{
                if (student!=null && student.getId()==sid){
                    return index;
                }
            }

        }
        return -1;
    }

    /***
     * TODO 保存学生信息
     * @param student   学生对象(地址)
     */
    public static void save(Student student){
        //获取一个空位置
        if (student!=null){
            int index = findByIndex(0);
            if (index!=-1){
                array[index] = student;
                System.out.println("操作成功");
            }else{
                System.out.println("满了");
            }
        }else{
            System.out.println("请添加学生信息");
        }
    }

    /**
     *  TODO 显示学生信息
     */
    public static void findAll(){
        //遍历数组中学生的信息
        for (int index = 0; index < array.length; index++) {
            //获取学生对象
            Student student = array[index];
            if (student!=null){
                System.out.println("学号:"+student.getId()+"\t"+
                        "姓名:"+student.getName()+"\t"+
                        "性别:"+student.getSex()+"\t"+
                        "年龄:"+student.getAge()
                );
            }
        }
    }

    /***
     * TODO 删除学生
     * @param sid   学号
     */
    public static void delete(int sid){
        //获取学生在数组中的位置
        int index = findByIndex(sid);
        //判断
        if (index!=-1){
            array[index] = null;
            System.out.println("操作成功");
        }else{
            System.out.println("查无此学生,请重新输入学号");
        }

    }

    public static void update(int sid,String name){
        //按照学号查找对应的下标
        int index = findByIndex(sid);
        //判断
        if (index!=-1) {
            //获取这个学生
            Student student = array[index];
            //修改姓名
            student.setName(name);
            System.out.println("操作成功");
        }else{
            System.out.println("查无此学生,请重新输入学号");
        }

    }

    public static long getValue(){
        //不重复
        String str = UUID.randomUUID().toString();
        CRC32 crc32 = new CRC32();
        crc32.update(str.getBytes());
        //获取
        long value = crc32.getValue();
        return value;
    }

    /***
     * TODO 测试方法
     * @param args
     */
    public static void main(String[] args) {
        //控制台对象
        Scanner scanner = new Scanner(System.in);
        //跳出循环体
        boolean flag = true;
        //提示
        System.out.println("1添加;2显示;3删除;4更新;5退出");
        //循环
        do {
            //key
            int key = scanner.nextInt();
            switch (key){
                case 1://调用方法
                    Student student = new Student();
                    //提示信息
                    student.setId((int)getValue());
                    System.out.println("请输入姓名");
                    student.setName(scanner.next());
                    System.out.println("请输入性别");
                    student.setSex(scanner.next());
                    System.out.println("请输入年龄");
                    student.setAge(scanner.nextInt());
                    //保存
                    save(student);
                    break;
                case 2://调用方法
                    findAll();
                    break;
                case 3:
                    System.out.println("请输入学号");
                    delete(scanner.nextInt());
                    break;
                case 4:
                    System.out.println("请输入学号");
                    int sid = scanner.nextInt();
                    System.out.println("请输入姓名");
                    String name = scanner.next();
                    //更新
                    update(sid,name);
                    break;
                case 5:
                    flag = false;
                    break;
            }
        }while(flag);

    }
}
class Student {
    private int id;         //学号
    private String name;    //学生姓名
    private String sex;     //学生性别
    private int age;        //学生年龄

    public int getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public int getAge() {
        return age;
    }

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

5.1 静态关键字static

5.1.1 修饰

  • 可以修饰变量、方法、代码块
  • 特点:
    • 使用static修饰的可以直接加载执行,所以,static修饰的变量也成为类变量
    • static修饰的代码块称为静态代码块,JVM加载类的时候,会执行静态代码块中的内容

5.1.2 采用静态变量实现累加器

  • 没有采用静态变量,而是成员变量,所以不会实现累加器的效果
  • 因为,成员变量属于当前对象,不会被共享
public class Demo1 {
    public static void main(String[] args){
        //创建对象
        Student student1 = new Student();
        Student student2 = new Student();
        //获取成员变量
        System.out.println(student1.getCount());
        System.out.println(student2.getCount());
    }
}

class Student{
    //成员变量(属性)
    private int count;

    //构造方法
    public Student(){
        count++;
    }

    //获取属性
    public int getCount(){
        return count;
    }
}
  • 采用静态变量完成累加器的功能
public class Demo1 {
    public static void main(String[] args){
        //创建对象
        Student student1 = new Student();
        Student student2 = new Student();
        //获取静态变量
        System.out.println(student1.getCount());
        System.out.println(student2.getCount());
    }
}

class Student{
    //静态变量
    private static int count;

    //构造方法
    public Student(){
        count++;
    }

    //获取属性
    public int getCount(){
        return count;
    }
}
  • static声明的变量通过该类new出的对象,都可以"共享",都可以通过对象访问
  • 类也可以直接访问,所以,我们称为"类变量"
  • 类直接调用
public class Demo1 {
    public static void main(String[] args){
        System.out.println(Student.count);
    }
}

class Student{
    //静态变量
    public static int count;
}

5.1.3 笔试题

5.1.3.1 静态方法中是否可以访问实例变量、实例方法或this关键字?

答:不能,因为,静态修饰的在类加载器就初始了,而对象这个时候还不存在

  • 我们可以改善一下代码
    • 把方法修改为static修饰
  • 使用对象调用
  • 不能在静态中直接使用this

5.1.3.2 静态方法的初始化顺序

  • 就是按照static修改的顺序执行
public class Demo1 {

    static{
        System.out.println(1);
    }

    static{
        System.out.println(2);
    }

    static{
        System.out.println(3);
    }

    public static void main(String[] args){
        System.out.println(4);
    }
}
  • 输出为1234,因为,主方法需要压栈后才执行,静态块可以直接执行
public class Demo1 {
    public static void main(String[] args){
        System.out.println(4);
    }

    static{
        System.out.println(1);
    }

    static{
        System.out.println(2);
    }

    static{
        System.out.println(3);
    }
}

5.1.3.3 执行的顺序

  • 1静态块 2语句块 3构造方法
public class Demo1 {
    public static void main(String[] args){
        //创建对象时,1静态块 2语句块 3构造方法
        Student student = new Student();
    }
}

class Student{
    //静态变量
    public Student(){
        System.out.println("构造方法");
    }
    //静态块
    static{
        System.out.println("静态块");
    }
    //语句块
    {
        System.out.println("语句块");
    }
}

5.2 设计模式

  • 设计模式:就是一种可以反复利用的解决方案;
  • 设计模式是1995年由GOF4人组提出
  • 设计模式:23种
  • 从结构上分为:
    • 创建型
    • 结构型
    • 行为型

5.2.1 单例模式

  • 单例模式:就是单实例(一个对象)的一种模式,也称为"反模式"
  • 优缺点:
    • 优点:堆中只创建一个对象,使用堆内存少,成本低
    • 缺点:因为对象是被共享的,所以,有安全问题。
  • 创建单例模式:
    • 构造方法私有化
    • 创建一个私有的、静态的、本类型对象
    • 创建一个公有的、静态的、返回本类型的方法
    • 自律模式
public class Demo1 {
    public static void main(String[] args){
        Student student1 = Student.getInstance();
        Student student2 = Student.getInstance();
        //比较两个对象的内存地址
        System.out.println(student2 == student1);
    }
}
//单例模式:饿汉式:推荐使用
class Student{
    //创建一个私有的、静态的对象
    private static Student student = new Student();
    //私有化
    private Student(){}

    //创建一个公有的、静态的、返回本类型
    public static Student getInstance(){
        return student;
    }

    //成员方法
    public void method(){
        System.out.println("成员方法");
    }
}
  • 单例模式
    • 饿汉式:推荐使用
    • 懒汉式:加锁--变为自旋锁
  • 可以调用成员方法
public class Demo1 {
    public static void main(String[] args){
        Student student1 = Student.getInstance();
        //可以通过单例对象调用成员方法了
        student1.method();
    }
}
//单例模式:饿汉式
class Student{
    //创建一个私有的、静态的对象
    private static Student student = new Student();
    
    //私有化
    private Student(){}

    //创建一个公有的、静态的、返回本类型
    public static Student getInstance(){
        return student;
    }

    //成员方法
    public void method(){
        System.out.println("成员方法");
    }
}

5.3 优化项目

5.3.1 分类完成功能

  • 使用数组模拟数据库:可以创建一个单独的类
  • 实现功能CRUD:独立的类完成
  • 前端:独立类完成

5.3.2 开发

开发流程图:开发就是这个过程的倒过程

5.3.2.1 创建实体类:即javaBean

public class Student {
    private int id;
    private String username;
    private String sex;
    private int age;

    public int getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public int getAge() {
        return age;
    }

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

5.3.2.2 创建数据库类:就是管理数组的

public class DataBaseYgm {
    //构造方法私有化
    private DataBaseYgm(){}

    //创建对象
    private static DataBaseYgm dataBaseYgm = new DataBaseYgm();

    //创建方法
    public static DataBaseYgm getInstance(){
        return dataBaseYgm;
    }

    //创建数据库
    private Student[] students = new Student[3];
    //返回数组的方法:成员方法
    public Student[] getStudents(){
        return students;
    }
}

5.3.2.3 业务类service

import java.util.Scanner;

//业务类
public class StudentService {
    //创建控制台
    Scanner scanner = new Scanner(System.in);
    //依赖数据库
    private Student[] array = DataBaseYgm.getInstance().getStudents();

    /**
     * 获取对应下标
     * @param sid 学号
     * @return 对应的下标
     */
    public int findByIndex(int sid){
        //获取学生对象
        for (int index = 0; index<array.length; index++){
            Student student = array[index];
            //判断
            if (sid == 0){
                if (student == null) return index;
            }else{
                if (student!=null && student.getId() == sid ){
                    return index;
                }
            }
        }
        return -1;
    }
    //保存学生
    public void save(Student student){
        if (student!=null){
            //获取一个空位置
            int index = findByIndex(0);
            if (index!=-1){
                array[index] = student;
            }else{
                System.out.println("满了");
            }
        }else{
            System.out.println("请输入学生信息");
        }
    }

    /**
     * TODO 查询所有学生
     */
    //查询学生
    public Student findUid(int id) {
        int index = findByIndex(id);
        //判断
        if (index != -1) {
            return array[index];
        } else {
            return null;
        }
    }
    /**
     * TODO 删除学生
     * @param sid 学号
     */
    //删除学生
    public void delete(int sid){
        //获取学生在数组中的位置
        int index = findByIndex(sid);
        //判断
        if (index != -1){
            array[index] = null;
            System.out.println("操作成功");
        }else{
            System.out.println("无此学生信息,请重新输入");
        }
    }
    //更新学生
    public void update(Student student) {
        if (student != null) {
            int index = findByIndex(student.getId());
            //判断
            if (index != -1) {
                array[index] = student;
            }else{
                System.out.println("查无此学生");
            }
        } else {
            System.out.println("请输入学生信息");
        }
    }
    //显示
    public void findAll(){
        //加强for循环
        for (Student student : array) {
            if (student!=null) {
                System.out.println("id="+student.getId()+"\t username="+
                        student.getUsername()+"\t sex="+
                        student.getSex()+"\t age="+
                        student.getAge());
            }
        }
    }
}

5.3.2.4 前端控制类

  • action:负责接收用户的数据,并且封装到实体类对象中
  • 前端
import java.util.Scanner;

public class StudentAction {

    //依赖于业务层
    private  StudentService service = new StudentService();

    //保存学生
    public void saveStudent(Scanner scanner){
        //创建对象
        Student student = new Student();
        student.setId(StudentUtils.getKetInt());
        System.out.println("请输入姓名");
        student.setUsername(scanner.next());
        System.out.println("请输入性别");
        student.setSex(scanner.next());
        System.out.println("请输入年龄");
        student.setAge(scanner.nextInt());
        service.save(student);
        //提示
        System.out.println("操作成功");
    }
    //显示学生
    public void show(){
        service.findAll();
    }

    //删除学生
    public void deleteStudent(Scanner scanner){
        System.out.println("请输入学号");
        service.delete(scanner.nextInt());
    }
    //更新学生
    public void updateStudent(Scanner scanner){
        //提示
        System.out.println("请输入学号");
        Student student = new Student();
        student.setId(scanner.nextInt());
        System.out.println("请输入新姓名");
        student.setUsername(scanner.next());
        System.out.println("请输入新性别");
        student.setSex(scanner.next());
        System.out.println("请输入新年龄");
        student.setAge(scanner.nextInt());
        //调用更新
        service.update(student);
        System.out.println("操作成功");
    }
}

5.3.2.5 工具类

  • 提供一个不重复的id
  • 提供一个日期
import java.time.LocalDateTime;
import java.util.UUID;
import java.util.zip.CRC32;

public class StudentUtils {
    //构造方法私有化
    private StudentUtils(){}

    //静态方法
    public static String getKetString(){
        return UUID.randomUUID().toString();
    }
    //静态方法
    public static int getKetInt(){
        CRC32 crc32 = new CRC32();
        crc32.update(UUID.randomUUID().toString().getBytes());
        long value = crc32.getValue();

        //转换
        return (int) value;
    }

    //日期
    public static String getDateString(){
        return LocalDateTime.now().toString();
    }
}

5.3.2.6 前端类

  • 前端
import java.util.Scanner;

public class StudentFront {
    public static void main(String[] args){
        //依赖于action层
        StudentAction action = new StudentAction();
        //控制台
        Scanner scanner = new Scanner(System.in);
        //循环
        boolean isRunning = true;
        System.out.println("1添加;2显示;3删除;4更新;5退出;");
        do{
            int key = scanner.nextInt();
            switch(key){
                case 1:
                    action.saveStudent(scanner);
                case 2:
                    action.show();
                    break;
                case 3:
                    action.deleteStudent(scanner);
                    break;
                case 4:
                    action.updateStudent(scanner);
                    break;
                case 5:
                    isRunning = false;
                    break;
            }
        }while(isRunning);
    }
}

5.4 类的继承

5.4.1 继承:

  • 实现了代码重用率;例如:A继承B,那么,A就拥有B的所有特性;
  • java中的类是单继承性
  • 继承的关键字extends
  • 继承优先
  • 案例:class A extends B{}

5.4.2 代码重用率

public class Demo {
    public static void main(String[] args) {
        Student student = new Student();
        //可以直接调用父类对象的方法
        student.setAge(11);
    }
}

class Student extends Person{
    private int sid;

    public int getSid() {
        return sid;
    }

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

class User extends Person{
    private int sid;


    public int getSid() {
        return sid;
    }

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

class Person{
    private String username;
    private String sex;
    private int age;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
  • 当创建子类对象时,父类对象先被创建出来(无父即无子),子类会默认创建父类的构造方法
public class Demo {
    public static void main(String[] args) {
        Student student = new Student();
    }
}
class Student extends Person{

    public Student(){
        System.out.println("子类构造方法");
    }
}

class Person{
    public Person(){
        System.out.println("父类构造方法");
    }
}

5.4.3 类的重写(覆盖)-都是对方法

  • 重载
    • 在同一个类中
    • 方法名称相同
    • 只跟方法的参数:个数、顺序、类型不同即可
    • 与方法的修饰符和返回类型无关
  • 重写
    • 必须是子类和父类有继承关系
    • 覆盖只能出现在子类中
    • 在子类中被覆盖的方法,必须与父类的方法"完全"一样,也就是说方法名称、返回类型、方法参数,要完全一致
    • 子类方法的访问权限不能小于父类方法的访问权限
    • 子类不能抛出比父类更多的异常,但是,可以抛出父类异常的子异常
    • 父类的静态方法不能被子类覆盖
    • 父类的私有方法也不能被覆盖
    • 覆盖针对的是"方法"而非"属性"
    • 重写就是改变了父类的行为

5.4.3.1 案例:

public class Demo {
    public static void main(String[] args) {
        Animal cat = new Cat();
        cat.move();
    }
}

class Animal {
    public void move(){
        System.out.println("动物移动");
    }
}

class Cat extends Animal{
    public void move(){
        System.out.println("小猫在移动..");
    }
}

通过上面的例子,可以看到,父类指向子类的引用,子类改变了父类的行为

多态就是多种形态,overload(重载)是多态的一种,属于编译期绑定;override(重写)是运行期间绑定的(后期绑定)

多态存在的条件:

  1. 有继承
  2. 有覆盖
  3. 父类指向子类的引用

5.4.3.2 对静态方法覆盖

public class Demo {
    public static void main(String[] args) {
        Animal cat = new Cat();
        cat.move();
    }
}

class Animal {
    public static void move(){
        System.out.println("动物移动");
    }
}

class Cat extends Animal{
    public static void move(){
        System.out.println("小猫在移动..");
    }
}
  • 静态方法不产生覆盖

5.4.3.3 成员方法可以被覆盖,但是成员变量不能被覆盖

public class Demo {
    public static void main(String[] args) {
        Animal cat = new Cat();
        cat.move();
        System.out.println(cat.name);
    }
}

class Animal {
    public String name = "动物";
    public void move(){
        System.out.println("动物移动");
    }
}

class Cat extends Animal{
    public String name = "动物";
    public void move(){
        System.out.println("小猫在移动..");
    }
}

5.5 super关键字

5.5.1 作用

  • super指的是父类对象
  • 调用父类的构造方法
  • 调用父类的成员方法

注意:super只能应用于成员方法和构造方法中,不能应用在static静态方法中,如果在构造方法中使用(和this相同)必须放在首行。

5.5.2 为什么会有super关键字

  • 因为子类创建对象时必须调用父类的构造方法,先把父类构造完成,因为子类依赖于父类;无父即无子;
  • 子类有时也调用父类的成员方法;
  • 特殊注意:我们在很多的时候创建子类对象时,并不显示的编写构造方法的,那么,默认调用的就是父类的无参数构造方法
  • 特别注意:构造方法可以被重载,不可以被重写

5.5.3 案例:

5.5.3.1 调用父类的默认构造方法

public class Demo {
    public static void main(String[] args) {
        //创建子类对象
        Emp emp = new Emp();
    }
}

class Person{
    public Person(){
        System.out.println("父类的无参数构造方法");
    }
}

class Emp extends Person{
    public Emp(){
        //super();
        System.out.println("子类的无参数构造方法");
    }
}
  • 先创建父类对象然后创建子类对象

5.5.3.2 调用指定的构造方法

public class Demo {
    public static void main(String[] args) {
        //创建子类对象
        Emp emp = new Emp();
    }
}

class Person{
    //构造方法重载
    public Person(){
        System.out.println("父类的无参数构造方法");
    }
    public Person(int i){
        System.out.println("父类1参数构造方法");
    }
    public Person(int i,int n){
        System.out.println("父类2参数构造方法");
    }

}

class Emp extends Person{
    public Emp(){
        super(1,2);
        System.out.println("子类的无参数构造方法");
    }
}
  • 调用父类的2个参数的构造方法

5.5.3.3 笔试题

5.5.3.4 笔试题

public class Demo {
    public static void main(String[] args) {
        //创建子类对象
        Emp emp = new Emp(10);
    }
}

class Person{
    //构造方法重载
    public Person(){
        System.out.println(1);
    }

    public Person(int i){
        System.out.println(2);
    }

    static{
        System.out.println(3);
    }

    {
        System.out.println(4);
    }

}

class Emp extends Person{
    public Emp(){
        System.out.println(5);
    }

    public Emp(int i){
        System.out.println(6);
    }

    static{
        System.out.println(7);
    }

    {
        System.out.println(8);
    }
}
  • 输出:374186

5.6 final

  • final表示最终的,不能改变的

5.6.1 修饰

  • 采用final修饰的类不能被继承了
  • 采用final修饰的变量:
    • 基本类型:变量的值不能改变
    • 引用类型:引用的地址不能改变,但是被指向的对象中的属性是可以修改的
  • 采用final修饰的方法不能被覆盖了
  • 采用final修饰的变量必须显示初始化
  • 构造方法不能被final修饰
  • 使用static final修饰的是"常量",名称要求"全部"大写

5.6.2 案例:

5.6.2.1 final的类不能被继承

5.6.2.2 final修饰的方法不能被覆盖

5.6.2.3 final修饰基本类型变量

  • 常量
  • 必须初始化

5.6.2.4 final修饰引用类型地址不能被改变

  • 可以修改对象中属性
public class Demo {
    public static void main(String[] args) {
        final Person person = new Person();
        //修改属性
        person.setUsername("张三");
        person.setUsername("李四");
    }
}

class Person{
    private String username;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }
}

5.7 抽象类

  • 重点:抽象类不能创建对象,但是,有构造方法因为子类在创建对象时必须调用父类的构造方法。

5.7.1 抽象类

  • 使用abstract修饰的类即为抽象类
  • 使用abstract修饰的方法即为抽象方法
  • 抽象类中可以有抽象方法也可以没有
  • 如果类中有抽象方法,那么这个类一定是抽象类
  • 抽象方法不能使用final修饰
  • 子类继承了抽象类,那么必须实现所有的抽象方法
  • 如果子类继承了抽象父类,并且,没有实现抽象方法,那么这个子类也一定是一个抽象类

5.7.2 抽象类的作用

  • 在抽象类中可以添加公用的代码(就是相同的代码)
  • 在抽象类中可以约束

5.7.3 案例:

5.7.3.1 抽象父类

public class Demo {
    public static void main(String[] args) {
        new Emp();
    }
}

abstract class Person{
    private int id;
    private String username;

    public Person(){
        System.out.println("------------>>");
    }
    public Person(int i){}

    public int getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }
}

class Emp extends Person{}
  • 子类创建对象时也调用了父类的构造方法

5.7.3.2 在抽象父类中添加约束

  • 添加一个抽象方法,子类必须要实现
abstract class Person{
    //抽象方法
    public abstract void show();
    public abstract void save();
    //实现的方法
    public void update(){
        System.out.println("实现的方法");
    }
}

class Emp extends Person{
    @Override
    public void show() {
        System.out.println("必须实现抽象方法");
    }

    @Override
    public void save() {
        System.out.println("必须实现抽象方法");
    }
}

5.7.3.3 抽象父类不能使用final修饰

  • 因为这样很矛盾(这两个关键字)

5.7.3.4 抽象方法不能使用final修饰

  • 因为这样很矛盾(这两个关键字)

5.7.3.5 抽象类中的抽象方法没有被实现,那么,子类也是抽象类

public class Demo {
    public static void main(String[] args) {
        new Emp();
    }
}

abstract class Person{
    //抽象方法
    public abstract void show();
}

abstract class Emp extends Person{
   public abstract void save();
}
//最终的子类必须实现所有的抽象方法
class User extends Emp{

    @Override
    public void show() {
        
    }

    @Override
    public void save() {

    }
}

5.8 接口

  • 接口就是标准,关键字是interface
  • 面向抽象编程,就是面向接口编程

5.8.1 接口分类

5.8.1.1 普通接口:

  • 接口中的方法都是public abstract
  • 接口中的方法可以实现
  • 接口中的的变量都是常量public static final
  • 接口不能实例化,接口中没有构造方法的概念
  • 接口间可以实现多继承,但是,接口之间不能实现
  • 实现接口的关键字implements
  • 类可以实现多个接口,并且,需要实现接口中所有的抽象方法

5.8.1.2 函数式接口:

  • 使用标签:FunctionInterface修饰的
  • 接口中可以有实现的方法了
  • 接口中只能有且只有一个必须实现的方法

5.8.2 创建接口

5.8.2.1 编写接口

public interface StudentManager {
    public abstract void save();
    public static final int ABC = 100;
    //实现的方法:使用default关键字就是让你来覆盖的
    default void delete(){
        System.out.println("这是一个删除方法");
    }
}
  • 编写接口时可以省略public abstarct或者static final
public interface StudentManager {
    void save();
    int ABC = 100;
}

5.8.2.2 接口中的变量都是常量,所以不能修改

5.8.2.3 接口之间不能实例化

5.8.2.4 接口间可以多继承

interface A{
    void method1();
}
interface B{
    void method2();
}
interface C extends A,B{}

5.8.2.5 接口间不能实现

5.8.2.6 接口只能通过类实现,并且,需要实现所有的抽象方法

interface A{
    void method1();
}
interface B {
    void method2();
}
interface C extends A,B{}
//实现接口
class CImpl implements C{

    @Override
    public void method1() {

    }

    @Override
    public void method2() {

    }
}

5.8.2.7 一个类可以同时实现多个接口

interface A{
    void method1();
}
interface B {
    void method2();
}
//实现接口

class CImpl implements A,B{

    @Override
    public void method1() {

    }

    @Override
    public void method2() {

    }
}

5.8.2.8 即有父类也要实现接口,继承优先

//父类
class Person{}
interface A{
    void method1();
}
interface B {
    void method2();
}
//实现接口

class CImpl extends Person implements A,B{

    @Override
    public void method1() {

    }

    @Override
    public void method2() {

    }
}

5.8.2.9 笔试题:抽象类可以实现接口

  • 无非就是添加接口中的抽象方法
interface A{
    void show();
}

//父类
abstract class Person implements A{

}
//实现抽象方法
class CImpl extends Person{
    @Override
    public void show() {

    }
}

5.8.3 接口进一步应用

5.8.3.1 接口的本质

  • 本质就是抽象
  • 采用接口可以很明确的声明了所提供的服务
  • 解决了类的单继承问题
  • 重要:实现了可插拔性

5.8.3.2 开发一个系统需要使用两种数据库:mysql、oracle

  • 第一种:不使用接口
public class Demo {
    public static void main(String[] args) {
        //使用mysql数据库
        StudentMysql mysql = new StudentMysql();
        mysql.add();
        
        //使用oracle数据库
        StudentOracle oracle = new StudentOracle();
        oracle.save();
    }
}

//操作mysql
class StudentMysql{
    public void add(){
        System.out.println("保存数据-mysql");
    }

    public void show(){
        System.out.println("显示数据-mysql");
    }
}

//操作oracle
class StudentOracle{
    public void save(){
        System.out.println("保存数据-oracle");
    }

    public void findAll(){
        System.out.println("显示数据-oracle");
    }
}
  • 代码不能灵活使用,当我们的需求改变了需要改动大量代码的,这样会导致代码重构,因为项目没有扩展性。
  • 第二种:使用接口
public class Demo {
    public static void main(String[] args) {
        //使用mysql数据库
        IStudent iStudent = new StudentOracleImpl();//new StudentMysqlImpl();
        iStudent.save();
        iStudent.delete();
        iStudent.update();
    }
}

//定义标准
interface IStudent{
    void save();
    void delete();
    void update();
}

//操作mysql
class StudentMysqlImpl implements IStudent{
    public void save(){
        System.out.println("保存数据-mysql");
    }

    public void delete(){
        System.out.println("删除数据-mysql");
    }

    public void update(){
        System.out.println("更新数据-mysql");
    }
}

//操作oracle
class StudentOracleImpl implements IStudent{
    public void save(){
        System.out.println("保存数据-oracle");
    }

    public void delete(){
        System.out.println("删除数据-oracle");
    }

    public void update(){
        System.out.println("更新数据-oracle");
    }
}
  • 体现了扩展性和灵活性:
    • 只要符合标准都可以随时更换
    • 面向接口编程(因为,我们以后做每一件事情都要遵守标准和规范)

5.9 类之间的关系

5.9.1 泛化关系:类和类之间的继承及接口之间的继承

5.9.2 实现关系:类对接口的实现

5.9.3 关联关系:

  • 类与类之间的连接,一个类作为另一个类的属性和方法使用,例如:实例变量(成员属性)

5.9.4 聚合关系

  • 聚合关系是关联关系的一种,是较强的一种关联关系;例如:汽车和轮胎,一个代表整体,一个代表局部,但是,符合汽车的标准即可,不需要指定轮胎的厂家

5.9.5 合成关系:

  • 合成关系是关联关系的一种,比如聚合关系强的一种关联关系;例如:人和四肢

5.9.6 依赖关系:是比关联关系弱的一种关系

6.0 is-a、is-like-a、has-a

6.0.1 is-a:接口中有的方法,类中必须实现

interface A{
    void method();
}

class AImpl implements A{

    @Override
    public void method() {
        
    }
}

6.0.2 is-like-a:接口中的有的方法,类中必须实现,并且,类中也有自己的方法

interface A{
    void method();
}

class AImpl implements A{

    @Override
    public void method() {

    }
    public void method2(){
        
    }
}

6.0.3 has-a:一个类作为另一个类的属性使用

class B{}

class AImpl{
    private B b;
}

6.1 Object类

  • Object类是所有类的父类(基类、超类)
  • 自定义类不显示的继承Object,那么,默认的基类就是Object

6.1.1 案例

  • 相当于

6.1.2 toString方法

public class Demo {
    public static void main(String[] args) {
        Person person = new Person();
        System.out.println(person.toString());
    }
}

class Person{
    @Override
    public String toString() {
        return super.toString();
    }
}
  • 源代码
public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
  • 注意:在输出对象的参数时候,调用或不调用toString方法默认都在执行toString方法:

"getClass().getName() + "@" + Integer.toHexString(hashCode());"

6.1.2.1 我们在调用对象的时候,都习惯把toString方法"覆盖"

public class Demo {
    public static void main(String[] args) {
        Person person = new Person();
        person.setUsername("张三");
        person.setSex("男");
        person.setAge(11);
        //输出
        System.out.println(person.toString());
    }
}

class Person{
    private String username;
    private String sex;
    private int age;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public int getAge() {
        return age;
    }

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

    @Override
    public String toString() {
        return "Person{" +
                "username='" + username + '\'' +
                ", sex='" + sex + '\'' +
                ", age=" + age +
                '}';
    }
}

6.1.3 finalize方法

  • 垃圾回收器(Garbage Collection) GC
    • 当对象不在使用了,那么,GC会将其回收
    • 垃圾回收在后台运行,我们没有办法命令立刻回收,但是,可以通知
    • 垃圾回收前必须调用的方法就是finalize,建议不使用这个方法
  • 案例:
public class Demo {
    public static void main(String[] args) {
        //创建对象
        Person person = new Person();
        //不使用了
        person = null;
        //通知垃圾回收器
        System.gc();
    }
}

class Person{
    @Override
    protected void finalize() throws Throwable {
        System.out.println("垃圾回收前调用了");
    }
}
  • 扩展
public class Demo {
    public static void main(String[] args) {
        //创建对象
        Person person = new Person();
        //使用对象后
        person = null;
        
    }
}

在使用完后是否需要把对象设置为null?
    答:不需要设置为null,因为,在方法中的参数都是在压栈时存在,弹栈时不存在;

6.1.4 ==与equals方法

6.1.4.1 等号“==”

  • 基本类型:比较的是"值"
  • 引用类型:比较的是"址"(内存地址)

案例1:基本类型

public class Demo extends Object{
    public static void main(String[] args) {
        //创建对象
        Person person = new Person();
        //使用对象后
        person = null;
        
    }
}

案例2:引用类型

public class Demo {
    public static void main(String[] args) {
        //创建对象
        Person person = new Person();
        person.setId(1001);
        person.setUsername("张三");
        //创建对象
        Person person1 = new Person();
        person1.setId(1001);
        person1.setUsername("张三");

        //比较
        System.out.println("person="+person);
        System.out.println("person1="+person1);
        System.out.println(person==person1);
    }
}

class Person{
    private int id;
    private String username;

    public int getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }
}

案例3:引用类型--进一步完善

  • equals方法默认使用的是"=="
jdk的源代码
public boolean equals(Object obj) {
    return (this == obj);
}
  • 需要把引用类型中的值比较一下:id和username的"值"相同,那么,就认为同一个人
  • String的源代码
public boolean equals(Object anObject) {
    if (this == anObject) {
        return true;
    }
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}
  • 覆盖Object中的equals方法
public class Demo {
    public static void main(String[] args) {
        //创建对象
        Person person = new Person();
        person.setId(1001);
        person.setUsername("张三");
        //创建对象
        Person person1 = new Person();
        person1.setId(1001);
        person1.setUsername("张三");

        //比较
        System.out.println(person==person1);
        System.out.println(person.equals(person1));
    }
}

class Person{
    private int id;
    private String username;

    public int getId() {
        return id;
    }

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

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    @Override
    public boolean equals(Object anObject) {
        //如果两个对象的地址相同就是true
        if (this == anObject) {
            return true;
        }
        //判断是否为指定的类型
        if (anObject instanceof Person) {
            Person another = (Person)anObject;
            //判断id和username
            if (id==another.getId() && username.equals(((Person) anObject).getUsername())){
                return true;
            }
        }
        return false;
    }
}
  • instanceof判断类型使用的
  • 注意:强制类型转换
    • 将父类转为子类"向下转型",是不安全的
    • 将子类转为父类"向上转型",是安全的

6.2 访问权限

修饰符类的内部同一个包子类(不同包)任何位置
publicYYYY
privateYNNN
protectedYYYN
defaultYYNN

6.3 内部类

6.3.1 分类

  • 实例内部类:必须持有外部的引用(实例)
  • 静态内部类:不需要持有外部的引用(实例)
  • 局部内部类:在方法中使用

6.3.2 实例内部类

  • 必须持有外部的引用(实例):就是内外两个对象都创建
  • 内部不能定义static成员,只能是实例成员
  • 案例:第一种方式
public class Demo {
    public static void main(String[] args) {
        //使用内部类
        A a = new A();
        a.show();   //调用自己的成员方法

        //再创建内部的对象
        A.B b = a.new B();
        b.save();
    }
}

class A{
    //成员方法
    public void show(){
        System.out.println("A--method");
    }

    class B{//内部类
        public void save(){
            System.out.println("B--method");
            //可以调用外部的成员方法
            show();
        }
    }
}
  • 第二种方式
public class Demo {
    public static void main(String[] args) {
        //直接创建对象
        A.B b = new A().new B();
        b.save();
    }
}

class A{
    //成员方法
    public void show(){
        System.out.println("A--method");
    }

    class B{//内部类
        public void save(){
            System.out.println("B--method");
            //可以调用外部的成员方法
            show();
        }
    }
}

6.3.3 静态内部类

  • 不需要持有外部的引用
  • 静态内部类可以访问外部的静态变量,如果需要访问外部的成员变量必须通过外部的对象方法
  • 案例:
public class Demo {
    public static void main(String[] args) {
        //不需要外部创建对象,直接创建内部对象即可
        A.B b = new A.B();
        b.save();
        //可以直接调用静态方法
        System.out.println(A.B.i);
    }
}

class A{

    //静态方法
    public static void show(){
        System.out.println("A--method");
    }

    static class B{//内部类
        public static int i =100;
        public void save(){
            System.out.println("B--method");
            //可以调用外部的成员方法
            show();
        }
    }
}

6.3.4 局部内部类

  • 就是在方法中创建的类;和局部变量的使用是相同的
public class Demo {
    public static void main(String[] args) {
        //创建对象,调用成员方法
        new A().method(100);
    }
}

class A{
    
    public void method(int i){
        //相当于自定义了一种只在方法内使用的类型
        class B{
            public void save(){
                System.out.println("----->>"+i);
            }
        }
        //在方法内使用
        B b = new B();
        b.save();
    }
}

6.3.5 扩展:匿名内部类

  • 接口作为方法的匿名使用
public class Demo {
    public static void main(String[] args) {
        B b = new B();
        //调用方法,匿名实现接口中的方法
        Object exec = b.exec(new A() {
            @Override
            public Object callBack() {
                return "匿名实现";
            }
        });

        System.out.println(exec);
    }
}

interface A{
    Object callBack();
}

class B{
    //成员方法
    public Object exec(A a){
        return a.callBack();
    }
}
  • 使用lambda函数
public class Demo {
    public static void main(String[] args) {
        A a = ()->"lambda函数方式实现";
        //调用
        Object back = a.callBack();
        System.out.println(back);
    }
}

interface A{
        Object callBack();
}


我从未觉得繁琐,说浪漫些,我很爱你。