(韩顺平)08 面向对象编程(中级部分)(自用)

目录

08 面向对象编程(中级部分)

8.1 IDE(集成开发环境)-IDEA

8.1.1IDEA 介绍

8.2 IDE(集成开发环境)-Eclipse

8.2.1Eclipse 介绍

8.3 IDE(集成开发环境)-IDEA 的使用

8.3.1IDEA 的安装

8.3.2IDEA 的基本介绍和使用

8.3.3IDEA 使用技巧和经验

8.3.4课堂练习

8.3.5IDEA 常用快捷键

8.3.6模板/自定义模板

8.4 包

8.4.1看一个应用场景

8.4.2包的三大作用

8.4.3包基本语法

8.4.4包的本质分析(原理)

8.4.5快速入门

8.4.6包的命名

8.4.7常用的包

8.4.8如何引入包

8.4.9注意事项和使用细节

8.5 访问修饰符

8.5.1基本介绍

8.5.24 种访问修饰符的访问范围(背下来)

8.5.3使用的注意事项

8.6 面向对象编程三大特征

8.6.1基本介绍

8.6.2封装介绍

8.6.4封装的实现步骤 (三步)

8.7 快速入门案例

8.7.1将构造器和 setXxx 结合

8.8 面向对象编程-继承

8.8.1为什么需要继承

8.8.2继承基本介绍和示意图

8.8.3继承的基本语法

8.8.4快速入门案例

8.8.5继承给编程带来的便利

8.8.6继承的深入讨论/细节问题

8.8.7继承的本质分析(重要)

8.8.8课堂练习

8.9 super 关键字

8.9.1基本介绍

8.9.2基本语法

8.9.3super 给编程带来的便利/细节

8.9.4super 和 this 的比较

8.10 方法重写/覆盖(override)

8.10.1 基本介绍

8.10.2 快速入门

8.10.3 注意事项和使用细节

8.11 面向对象编程-多态

8.11.1 先看一个问题

8.11.2 多[多种]态[状态]基本介绍

8.11.3 多态的具体体现

8.11.4 多态快速入门案例

8.11.5 多态注意事项和细节讨论(instanceof重要)

8.11.6 课堂练习

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

8.11.8 多态的应用

8.12 Object 类详解

8.12.1 equals 方法

8.12.2 如何重写 equals 方法

8.12.4 hashCode 方法

8.12.5 toString 方法

8.12.6 finalize 方法

8.13 断点调试(debug)

8.13.1 一个实际需求

8.13.4 断点调试应用案例

8.13.5 断点调试应用案例

8.13.6 断点调试课后练习

8.14 项目-零钱通

8.14.1 项目开发流程

8.14.2 项目需求说明

8.14.3 项目的界面

8.14.4 项目代码实现

8.14.5 项目代码实现改进


08 面向对象编程(中级部分)

8.1 IDE(集成开发环境)-IDEA

8.1.1IDEA 介绍

  1. IDEA 全称 IntelliJ IDEA

  2. 在业界被公认为最好的 Java 开发工具

  3. IDEA 是 JetBrains 公司的产品,总部位于捷克的首都布拉格

  4. 除了支持 Java 开发,还支持 HTML,CSS,PHP,MySQL,Python 等

8.2 IDE(集成开发环境)-Eclipse

8.2.1Eclipse 介绍

  1. Eclipse 是一个开放源代码的、基于 Java 的可扩展开发平台。

  2. 最初是由 IBM 公司耗资 3000 万美金开发的下一代 IDE 开发环境

  3. 2001 年 11 月贡献给开源社区

  4. Eclipse 是目前最优秀的 Java 开发 IDE 之一

8.3 IDE(集成开发环境)-IDEA 的使用

8.3.1IDEA 的安装

  1. 官网: JetBrains: Essential tools for software developers and teams

  2. IDEA 下载后,就可以开始安装。

  3. 看老师给同学们演示如何安装

  4. IDEA 工作界面介绍

8.3.2IDEA 的基本介绍和使用

使用 IDEA 创建 Java 项目(project),看看 IDEA 是如何使用的,IDEA 是以项目的概念,来管理我们的 java 源码的

  1. 创建一个 java 项目 - hello

  2. 5min 练习一下

 public class Hello {
     public static void main(String[] args){
         System.out.println("Hello,idea");
     }
 }

8.3.3IDEA 使用技巧和经验

✓ 设置字体 [如图] 和 颜色主题

菜单 file -> settings

✓ 字符编码设置

8.3.4课堂练习

使用 IDEA 开发一个 java 项目 testpro01,创建一个类 MyTools, 编写一个方法,可以完成对 int 数组冒泡排序的功能 学员练习 , 使用快捷键的开发项目

8.3.5IDEA 常用快捷键

  1. 删除当前行, 默认是 ctrl + Y 自己配置 ctrl + d

  2. 复制当前行, 自己配置 ctrl + alt + 向下光标

  3. 补全代码 alt + /

  4. 添加注释和取消注释 ctrl + / 【第一次是添加注释,第二次是取消注释】

  5. 导入该行需要的类 先配置 auto import , 然后使用 alt+enter 即可

  6. 快速格式化代码 ctrl + alt + L

  7. 快速运行程序 自己定义 alt + R

  8. 生成构造器等 alt + insert [提高开发效率]

  9. 查看一个类的层级关系 ctrl + H [学习继承后,非常有用]

  10. 将光标放在一个方法上,输入 ctrl + B , 可以定位到方法 [学继承后,非常有用]

  11. 自动的分配变量名 , 通过 在后面假 .var [老师最喜欢的]

  12. 还有很多其它的快捷键...

8.3.6模板/自定义模板

  
//sout模板快捷键
         System.out.println();
 ​
         //fori模板
         for (int i = 0; i < ; i++) {
             
         }

8.4 包

8.4.1看一个应用场景

8.4.2包的三大作用

8.4.3包基本语法

8.4.4包的本质分析(原理)

8.4.5快速入门

8.4.6包的命名

8.4.7常用的包

8.4.8如何引入包

 
package com.hspedu.pkg;
 ​
 import java.util.Arrays;
 ​
 //注意:
 //老师建议:我们需要使用到哪个类,就导入哪个类即可,不建议 *导入
 //import java.util.Scanner;//表示只会引入java.util 包下的Scanner
 //import java.util.*;//表示将ava.util 包下的所有类都引入(导入)
 public class Import01 {
 ​
     public static void main(String[] args) {
 ​
         //使用系统提供 Arrays 完成 数组排序
         int[] arr = {-1,20,2,13,3};
         //比如对其进行排序
         //传统方法是,自己编写排序(冒泡)
         //系统是提供了相关的类,可以方便完成 Arrays
         Arrays.sort(arr);
         //输出排序结果
         for (int i = 0; i < arr.length; i++) {
             System.out.print(arr[i] + "\t");
         }
     }
 }

8.4.9注意事项和使用细节

 //package的作用是声名当前类所在的包,需要放在类(或者文件的最上面)的最上面,
 // 一个类中最多只有一句package
 ​
 package com.hspedu.pkg;
 ​
 //import指令 位置放在package的下面,在类定义面前,可以有多聚且没有顺序要求
 import java.util.Arrays;
 import java.util.Scanner;
 //arr
 ​
 //类定义
 public class PkgDetail {
     public static void main(String[] args) {
         Scanner scanner = new Scanner(System.in);
         int[] arr = {0,-1};
         Arrays.sort(arr);
     }
 }

8.5 访问修饰符

8.5.1基本介绍

java 提供四种访问控制修饰符号,用于控制方法和属性(成员变量)的访问权限(范围):

  1. 公开级别:用 public 修饰,对外公开

  2. 受保护级别:用 protected 修饰,对子类和同一个包中的类公开

  3. 默认级别:没有修饰符号,向同一个包的类公开.

  4. 私有级别:用 private 修饰,只有类本身可以访问,不对外公开.

8.5.24 种访问修饰符的访问范围(背下来)

8.5.3使用的注意事项

 package com.hspedu.modifier;
 ​
 public class A {
 ​
     //四个属性,分别使用不同的访问修饰符来修饰
     public int n1 = 100;
     protected int n2 = 200;
     int n3 = 300;
     private int n4 = 400;
     public void m1(){
         //在同一类中,可以访问public protected 默认 private 修饰属性和方法
         System.out.println("n1=" + n1 + " n2=" + n2 + " n3=" + n3 + " n4=" + n4);
     }
     protected void m2(){ }
     void m3(){ }
     private void m4(){ }
     public void hi(){
         //在同一类中,可以访问public protected 默认 private 修饰属性和方法
         m1();
         m2();
         m3();
         m4();
     }
 }
 ​
 package com.hspedu.modifier;
 ​
 public class B {
     public void say(){
         A a = new A();
         //在同一个包下,可以访问public ,protected 和 默认,不能访问private 属性或方法
         System.out.println("n1=" + a.n1 + " " + a.n2 + " " + a.n3);
         a.m1();
         a.m2();
         a.m3();
         //a.m4();错误的
     }
 }
 ​
 package com.hspedu.modifier;
 ​
 public class Test {
     public static void main(String[] args) {
         A a = new A();
         a.m1();
         B b = new B();
         b.say();
     }
 }
 ​
 //只有 默认和public 可以修饰类
 class Tiger{ }
 ​

8.6 面向对象编程三大特征

8.6.1基本介绍

面向对象编程有三大特征:封装、继承和多态。

8.6.2封装介绍

8.6.3封装的理解和好处

8.6.4封装的实现步骤 (三步)

8.7 快速入门案例

✓ 看一个案例

 
package com.hspedu.encap;
 ​
 public class Encapsulation01 {
 ​
     public static void main(String[] args) {
         //如果要使用快捷键 alt+r, 需要先配置主类
         //第一次,我们使用鼠标点击形式运算程序,后面就可以用
         Person person = new Person();
         person.setName("Jack");
         person.setAge(30);
         person.setSalary(30000);
         System.out.println(person.info());
         System.out.println(person.getSalary());
 ​
         //如果我们使用构造器指定属性
         Person smith = new Person("Smitth",2000,5000);
         System.out.println("====smith的信息====");
         System.out.println(smith.info());
     }
 }
 /*
 那么在 java 中如何实现这种类似的控制呢?
 请大家看一个小程序(com.hspedu.encap: Encapsulation01.java), 不能随便查看人的年龄,工资等隐私,
 并对设置的年龄进行合理的验证。年龄合理就设置,否则给默认
 年龄, 必须在 1-120, 年龄, 工资不能直接查看 , name 的长度在 2-6 字符 之间
 */
 ​
 class Person {
     public String name;//名字公开
     private int age;//age 私有化
     private double salary;//薪水私有化
 ​
     public void say(int n,String name){
 ​
     }
     //构造器 alt+insert
     public Person() {
     }
     //有三个属性的构造器
     public Person(String name, int age, double salary) {
 //        this.name = name;
 //        this.age = age;
 //        this.salary = salary;
         //我们可以将set方法写在构造器中,这样仍然可以验证
         setName(name);
         setAge(age);
         setSalary(salary);
     }
     //自己写 setXxx 和 getXxx 太慢,我们使用快捷键
     //然后根据要求来完善我们的代码.
     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=" + name + " age=" + age + " 薪水=" + salary;
     }
 }

8.7.1将构造器和 setXxx 结合

 看一个案例

//有三个属性的构造器
    public Person(String name, int age, double salary) {
//        this.name = name;
//        this.age = age;
//        this.salary = salary;
        //我们可以将set方法写在构造器中,这样仍然可以验证
        setName(name);
        setAge(age);
        setSalary(salary);
    }

8.7.2课堂练习

package com.hspedu.encap;

/**
 * 创建程序,在其中定义两个类:Account 和 AccountTest 类体会 Java 的封装性。
 * Account 类要求具有属性:姓名(长度为 2 位 3 位或 4 位)、余额(必须>20)、
 * 密码(必须是六位), 如果不满足,则给出提示信息,并给默认值(程序员自己定)
 * 通过 setXxx 的方法给 Account 的属性赋值。
 * 在 AccountTest 中测试
 */
public class Account {
    //为了封装,将三个属性设置为private
    private String name;
    private double balance;
    private String pwd;

    //提供两种构造器
    public Account() {
        //设置无参构造器是因为写了有参构造器,覆盖了默认无参构造器,现在显性无参构造器
    }

    public Account(String name, double balance, String pwd) {
        setName(name);
        setBalance(balance);
        setPwd(pwd);
    }

    public String getName() {
        return name;
    }
    //姓名(长度为 2 位 3 位或 4 位)
    public void setName(String name) {
        if(name.length() > 1 && name.length() < 5) {
            this.name = name;
        }else{
            System.out.println("名字的长度不对,需要(2-4)个字符,默认名字");
            this.name = "无名人";
        }
    }

    public double getBalance() {
        return balance;
    }
    //余额(必须>20)
    public void setBalance(double balance) {
        if(balance > 20) {
            this.balance = balance;
        }else{
            System.out.println("账户余额错误,需要大于20,默认余额");
            this.balance = 20;
        }
    }

    public String getPwd() {
        return pwd;
    }
    //密码(必须是六位)
    public void setPwd(String pwd) {
        if(pwd.length() == 6) {
            this.pwd = pwd;
        }else{
            System.out.println("密码长度有误,需要6位,默认密码");
            this.pwd = "000000";
        }
    }
    //写一个方法,返回属性信息
    public String info() {
       //可以增加权限的校验
        return "姓名为" + name + " 余额=" + balance + " 密码=" +pwd;
    }
}
package com.hspedu.encap;

public class AccountTest {
    public static void main(String[] args) {
        Account smith = new Account("李华", 96000, "000000");
        System.out.println(smith.info());

    }
}

8.8 面向对象编程-继承

8.8.1为什么需要继承

8.8.2继承基本介绍和示意图

继承可以解决代码复用,让我们的编程更加靠近人类思维.当多个类存在相同的属性(变量)和方法时,可以从这些类中 抽象出父类,在父类中定义这些相同的属性和方法,所有的子类不需要重新定义这些属性和方法,只需要通过 extends 来 声明继承父类即可。画出继承的示意图

8.8.3继承的基本语法

8.8.4快速入门案例

我们对 Extends01.java 改进,使用继承的方法,请大家注意体会使用继承的好处

package com.hspedu.extend;

public class Extends01 {
    //编写一个main方法
    public static void main(String[] args) {
        Pupil pupil = new Pupil();
        pupil.name = "银角大王";
        pupil.age = 10;
        pupil.testing();
        pupil.setScore(60);
        pupil.shouInfo();

        System.out.println("==========");
        Graduate graduate = new Graduate();
        graduate.name = "金角大王";
        graduate.age = 22;
        graduate.testing();
        graduate.setScore(100);
        graduate.shouInfo();
    }
}

package com.hspedu.extend.improve_;

//父类,是Pupil 和Graduate的父类
public class Student {
    //共有属性
    public String name;
    public int age;
    private double score;
    public void setScore(double score) {
        this.score = score;
    }
    //共有方法
    public void shouInfo(){
        System.out.println("学生名 " + name + " 年龄" + age + " 成绩" + score);
    }
}

package com.hspedu.extend.improve_;
//让Pupil 继承 Student类
public class Pupil extends Student{
    public void testing(){
        System.out.println("大学生" + name + " 正在考大学数学。。");
    }
}

package com.hspedu.extend.improve_;

public class Graduate extends Student{
    public void testing(){
        System.out.println("大学生" + name + " 正在考大学数学。。");
    }
}

8.8.5继承给编程带来的便利

  • 代码的复用性提高了

  • 代码的扩展性和维护性提高了

8.8.6继承的深入讨论/细节问题

  1. 子类继承了所有的属性和方法,非私有的属性和方法可以在子类直接访问, 但是私有属性和方法不能在子类直接访 问,要通过父类提供公共的方法去访问

  2. 子类必须调用父类的构造器, 完成父类的初始化

  3. 当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用 super 去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不会通过(怎么理解。) [举例说明]

  4. 如果希望指定去调用父类的某个构造器,则显式的调用一下 : super(参数列表)

  5. super 在使用时,必须放在构造器第一行(super() 只能在构造器中使用)

  6. super() 和 this() 都只能放在构造器第一行,因此这两个方法不能共存在一个构造器

  7. java 所有类都是 Object 类的子类, Object 是所有类的基类.

  8. 父类构造器的调用不限于直接父类!将一直往上追溯直到 Object 类(顶级父类)

  9. 子类最多只能继承一个父类(指直接继承),即 java 中是单继承机制。 思考:如何让 A 类继承 B 类和 C 类? 【A 继承 B, B 继承 C】

  10. 不能滥用继承,子类和父类之间必须满足 is-a 的逻辑关系

package com.hspedu.extend;

import com.hspedu.extend.improve_.Sub;

public class ExtendsDetail {
    public static void main(String[] args) {
//        System.out.println("====第1个对象======");
//        Sub sub = new Sub();//创建了子类对象 sub
//        System.out.println("====第2个对象======");
//        Sub sub2 = new Sub("jack");//创建了子类对象 sub2
        System.out.println("====第3个对象======");
        Sub sub3 = new Sub("king",10);//创建了子类对象 sub3
        //sub.sayOk();
    }
}

package com.hspedu.extend.improve_;

public class TopBase {//父类是 Object
    public TopBase(){
        //super(); Object 的无参构造器
        System.out.println("构造器 TopBase() 被调用..");//1
    }
}


package com.hspedu.extend.improve_;

public class Base extends TopBase{//父类
    //4个属性
    public int n1 = 100;
    protected int n2 = 200;
    int n3 = 300;
    private int n4 = 400;

    public Base() { //无参构造器

        System.out.println("父类 Base()构造器被调用....");
    }
    public Base(String name, int age) {//有参构造器
    //默认 super()
        System.out.println("父类 Base(String name, int age)构造器被调用....");
    }
    public Base(String name) {//有参构造器

        System.out.println("父类 Base(String name)构造器被调用....");
    }
    //父类提供一个 public 的方法,返回了 n4
    public int getN4() {

        return n4;
    }
    public void test100() {

        System.out.println("test100");
    }
    protected void test200() {

        System.out.println("test200");
    }
    void test300() {

        System.out.println("test300");
    }
    private void test400() {

        System.out.println("test400");
    }
    //call
    public void callTest400() {

        test400();
    }
}


package com.hspedu.extend.improve_;

//输入ctrl + H 可以看到类的继承关系
public class Sub extends Base{//子类

    public Sub(String name,int age){
        //1.如若要调用父类的无参构造器,如下或者 什么都不写,默认就是调用super()
        //super();//父类的无参构造器
        //2.如若要调用父类的Base(String name)构造器
        //super("hsp");
        //2.如若要调用父类的Base(String name,int age)构造器
        super("king",20);
        System.out.println("子类sub(String name,int age)构造器被调用");
        //super("king",20);错误,对super的调用必须是构造器中的第一个语句

    }
    public Sub(){//无参构造器
        //super();//默认调用父类的无参构造器
        super("smith",10);
        System.out.println("子类sub()构造器被调用");
    }
    //当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器
    public Sub(String name){
        super("tom",30);
        //do nothing...
        System.out.println("子类Sub(String name)构造器被调用");
    }
    public void sayOk(){//子类方法
        //非私有的属性和方法可以在子类直接访问
        //但私有的属性和方法不能在子类直接访问
        System.out.println(n1 + " " + n2 + " " + n3);
        test100();
        test200();
        test300();
        //test400();错误
        //要通过父类提供公共的方法去访问
        System.out.println("n4=" + getN4());
        callTest400();//

    }
}

8.8.7继承的本质分析(重要)

✓ 案例

package com.hspedu.extend;

/**
 * 讲解继承的本质
 */
public class ExtendsTheory {
    public static void main(String[] args) {
        Son son = new Son();//内存的布局
        //?-> 这时请大家注意,要按照查找关系来返回信息
        //(1)首先看子类是否有该属性
        //(2)如果子类有这个属性,并且可以访问,则返回信息
        //(3)如果子类没有这个属性,就看父类有没有该属性(如果父类有该属性,并且可以访问,就返回信息。。)
        //(4)如果父类没有就按(3)的规则,继续找上级父类,直到Object。。。
        System.out.println(son.name);//返回就是大头儿子
        System.out.println(son.getAge());//返回就是39
        System.out.println(son.hobby);//返回就是旅游
    }
}
class GrandPa { //爷类
    String name = "大头爷爷";
    String hobby = "旅游";

}
class Father extends GrandPa {//父类
    String name = "大头爸爸";
    private int age = 39;

    public int getAge() {
        return age;
    }
}
class Son extends Father { //子类
    String name = "大头儿子";
}

✓ 子类创建的内存布局

8.8.8课堂练习

  1. 案例 1 ExtendsExercise01.java

  1. 案例 2 ExtendsExercise02.java

    package com.hspedu.extend.exercise;
    
    public class ExtendsExercise02 {
        public static void main(String[] args) {
            C c = new C();
        }
    }
    
    class A {//A 类
    
        public A() {
            System.out.println("我是 A 类");
        }
    }
    
    class B extends A { //B 类,继承 A 类 //main 方法中: C c =new C(); 输出么内容? 3min
        public B() {
            System.out.println("我是 B 类的无参构造");
        }
    
        public B(String name) {
            System.out.println(name + "我是 B 类的有参构造");
        }
    }
    
    class C extends B { //C 类,继承 B 类
        public C() {
            this("hello");
            System.out.println("我是 c 类的无参构造");
        }
    
        public C(String name) {
            super("hahah");
            System.out.println("我是 c 类的有参构造");
        }
    }

    执行结果:

  2. 案例 3 ExtendsExercise03.java

    编写 Computer 类,包含 CPU、内存、硬盘等属性,getDetails 方法用于返回 Computer 的详细信息

    编写 PC 子类,继承 Computer 类,添加特有属性【品牌 brand】

    编写 NotePad 子类,继承 Computer 类,添加特有属性【color】

    编写 Test 类,在 main 方法中创建 PC 和 NotePad 对象,分别给对象中特有的属性赋值,以及从 Computer 类继承的 属性赋值,并使用方法并打印输出信息

package com.hspedu.extend.exercise;

//编写 Computer 类,包含 CPU、内存、硬盘等属性,getDetails 方法用于返回 Computer 的详细信息
public class Computer {
    private String cpu;
    private int memory;
    private int disk;
    public Computer(){//无参构造器

    }
    public Computer(String cpu,int memory,int disk){//有参构造器
        this.cpu = cpu;
        this.memory = memory;
        this.disk = disk;
    }
    public String getDetails(){//返回 Computer 的详细信息
        return "CPU is" + cpu + " 内存大小 is" + memory + " 硬盘大小 is" + disk;
    }

    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;
    }
}

package com.hspedu.extend.exercise;

//编写 PC 子类,继承 Computer 类,添加特有属性【品牌 brand】
public class PC extends Computer {
    private String brand;

    public PC() {

    }
    //这里IDEA 根据继承的规则,自动把构造器的调用写好
    //这里也体现:继承设计的基本思想,父类的构造器完成父类属性初始化
    //子类的构造器完成子类属性初始化
    public PC(String cpu, int memory, int disk, String brand) {
        super(cpu, memory, disk);
        this.brand = brand;
    }

    public String getBrand() {
        return brand;
    }

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

    public void printInfo1() {
        System.out.println("PC 信息=");
// System.out.println(getCpu() + getMemory() + getDisk());
//调用父类的 getDetails 方法,得到相关属性信息..
        System.out.println(getDetails() + " brand=" + brand);
    }
}

package com.hspedu.extend.exercise;

public class NotePad extends Computer{
    private String color;
    public NotePad(){//无参构造器

    }

    public NotePad(String cpu, int memory, int disk, String color) {
        super(cpu, memory, disk);
        this.color = color;
    }
    public void printInfo2() {
        System.out.println("NotePad 信息=");
// System.out.println(getCpu() + getMemory() + getDisk());
//调用父类的 getDetails 方法,得到相关属性信息..
        System.out.println(getDetails() + " coolor=" + color);
    }
}

package com.hspedu.extend.exercise;

/*
编写 Computer 类,包含 CPU、内存、硬盘等属性,getDetails 方法用于返回 Computer 的详细信息
编写 PC 子类,继承 Computer 类,添加特有属性【品牌 brand】
编写 NotePad 子类,继承 Computer 类,添加特有属性【color】//同学们自己写。
编写 Test 类,在 main 方法中创建 PC 和 NotePad 对象,分别给对象 中特有的属性赋值,
以及从 Computer 类继承的属性赋值,并使用方法并打印输出信息
*/
public class ExtendsExercise03 {
    public static void main(String[] args) {
        PC pc = new PC("AMD", 1024, 2048, "LEGION");
        NotePad notePad = new NotePad("Inter",1024,3072,"red");
        pc.printInfo1();
        notePad.printInfo2();
    }
}

8.9 super 关键字

8.9.1基本介绍

super 代表父类的引用,用于访问父类的属性、方法、构造器

8.9.2基本语法

代码

package com.hspedu.super_;

public class Base {//父类是Object

    public int n1 = 999;
    public int age = 111;
    public void cal(){
        System.out.println("Base 类的 cal() 方法...");
    }
    public void eat(){
        System.out.println("Base类的eat()......");
    }
}


package com.hspedu.super_;

public class A extends Base {
    //4 个属性
    //public int n1 = 100;
    protected int n2 = 200;
    int n3 = 300;
    private int n4 = 400;

    public A() {
    }

    public A(String name) {
    }

    public A(String name, int age) {
    }

    public void cal() {
        System.out.println("A 类的 cal() 方法...");
    }

    public void test100() {
    }

    protected void test200() {
    }

    void test300() {
    }

    private void test400() {
    }
}

package com.hspedu.super_;

public class B extends A{

    public int n1 = 888;

    //编写测试方法
    public void test() {
    //super 的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用 super 去访问爷爷类的成员;
    // 如果多个基类(上级类)中都有同名的成员,使用 super 访问遵循就近原则。A->B->C
        System.out.println("super.n1=" + super.n1);
        super.cal();
    }

    //访问父类的属性 , 但不能访问父类的 private 属性 [案例]super.属性名
    public void hi(){
        System.out.println(super.n1 + " " + super.n2 + " " + super.n3);
    }
    //访问父类的方法,不能访问父类的 private 方法 super.方法名(参数列表);
    public void ok(){
        super.test100();
        super.test200();
        super.test300();
        //super.test400();//不能访问父类private方法
    }
    public void cal() {
        System.out.println("B 类的 cal() 方法...");
    }
    public void sum() {
        System.out.println("B 类的 sum()");
        //希望调用父类-A 的 cal 方法
        //这时,因为子类 B 没有 cal 方法,因此我可以使用下面三种方式

        //找 cal 方法时(cal() 和 this.cal()),顺序是:
        // (1)先找本类,如果有,则调用
        // (2)如果没有,则找父类(如果有,并可以调用,则调用)
        // (3)如果父类没有,则继续找父类的父类,整个规则,就是一样的,直到 Object 类
        // 提示:如果查找方法的过程中,找到了,但是不能访问, 则报错, cannot access
        // 如果查找方法的过程中,没有找到,则提示方法不存在
        //cal();
        this.cal();//等价cal

        //找cal方法(super.call()) 的顺序是直接查找父类,其他的规则一样
        //super.cal();

        //演示访问属性的规则
        //n1 和this.n1 的规则是
        //(1)先找本类,如果有,则调用
        //(2)如果没有,则找父类(如果有,并可以调用,则调用)
        //(3)如果父类没有,则继续找父类的父类,整个规则,就是一样的,直到 Object 类
        //提示:如果查找方法的过程中,找到了,但是不能访问, 则报错, cannot access
        //         如果查找方法的过程中,没有找到,则提示方法不存在
        System.out.println(n1);
        System.out.println(this.n1);

        //找n1(super.n1) 的顺序是直接查找父类属性,其他的规则一样
        System.out.println(super.n1);//100
    }

    访问父类的构造器(这点前面用过):super(参数列表);只能放在构造器的第一句,只能出现一句!
    public B(){
        //super();
        //super("jack",10);
        //super("jack");
    }
}

package com.hspedu.super_;

import com.hspedu.extend.improve_.Base;

public class Super01 {
    public static void main(String[] args) {
        B b = new B();//子类对象
        //b.sum();
        b.test();

    }
}

8.9.3super 给编程带来的便利/细节

代码, 看前面的案例即可:

8.9.4super 和 this 的比较

8.10 方法重写/覆盖(override)

8.10.1 基本介绍

8.10.2 快速入门

代码

package com.hspedu.override_;

public class Animals {
    public void cry() {
        System.out.println("动物叫唤..");
    }

    public Object m1(){
        return null;
    }

    public String m2(){
        return null;
    }
    public AAA m3(){
        return null;
    }
    protected void eat(){

    }
}

package com.hspedu.override_;

public class Dog extends Animals{
    //老师解读
    //1.因为Dog 是 Animals子类
    //2.Dog的 cry方法和Animals的 cry定义形式一样(名称、返回类型、参数)
    //3.这时我们就说 Dog的cry方法,重写了Animals的cry方法
    public void cry() {
        System.out.println("小狗汪汪叫..");
    }
    //细节:子类的返回方法和父类方法返回类型一样
    //     或者是父类返回类型的子类VT比如 父类 返回类型是Object,
    //     子类方法返回类型是String
    public String m1(){
        return null;
    }
    //这里Object 不是 String的子类,因此编译错误
//    public Object m2(){
//        return null;
//    }

//    public BBB m3(){
//
//    }
    //细节:子类方法不能缩小父类方法的访问权限
    //public > protected > 默认 > private
    public void eat(){

    }
}

class AAA{

}
class BBB extends AAA{

}

package com.hspedu.override_;

public class Override01 {
    public static void main(String[] args) {
        //演示方法的情况
        Dog dog = new Dog();
        dog.cry();//ctl + b

    }

}

8.10.3 注意事项和使用细节

方法重写也叫方法覆盖,需要满足下面的条件

8.10.4 课堂练习

✓ 题 1

请对方法的重写和重载做一个比较

✓ 题 2

  1. 编写一个 Person 类,包括属性/private(name、age),构造器、方法 say(返回自我介绍的字符串)。

  2. 编写一个 Student 类,继承 Person 类,增加 id、score 属性/private,以及构造器,定义 say 方法(返回自我介绍的信息)。

  3. 在 main 中,分别创建 Person 和 Student 对象,调用 say

package com.hspedu.override_;
//编写一个 Person 类,包括属性/private(name、age),构造器、方法 say(返回自我介绍的字符串
public class Person {
    private String name;
    private int age;

    public Person() {//无参构造器
    }

    public Person(String name, int age) {//有参构造器
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    public String say(){
        return "名字是" + name + " 年龄是" + age;
    }
}

package com.hspedu.override_;
//编写一个 Student 类,继承 Person 类,增加 id、score 属性/private,以及构造器,定义 say 方法(返回自我介绍的信息)。
public class Student extends Person{
    private String id;
    private int score;

    public Student() {//无参构造器
    }

    public Student(String name, int age, String id, int score) {//有参构造器
        super(name, age);//调用父类构造器
        this.id = id;
        this.score = score;
    }

    public String getId() {
        return id;
    }

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

    public int getScore() {
        return score;
    }

    public void setScore(int score) {
        this.score = score;
    }

    public String say(){//这里体现super的一个好处,代码复用
        return super.say() + " id是" + id + " 成绩是" + score;
    }
}

package com.hspedu.override_;

public class OverrideExercise {
    public static void main(String[] args) {
        Person person = new Person("Jim",10);
        System.out.println(person.say());
        Student amy = new Student("Amy", 11, "123456", 98);
        System.out.println(amy.say());

    }
}

8.11 面向对象编程-多态

8.11.1 先看一个问题

· 使用传统的方法来解决(private 属性)

· 传统的方法带来的问题是什么? 如何解决?

问题是: 代码的复用性不高,而且不利于代码维护

解决方案: 引出我们要讲解的多态

8.11.2 多[多种]态[状态]基本介绍

方法或对象具有多种形态。是面向对象的第三大特征,多态是建立在封装和继承基础之上的。

8.11.3 多态的具体体现

  1. 方法的多态 PloyMethod.java

    重写和重载就体现多态 [案例说明:]

package com.hspedu.poly_.objpoly_;

public class PolyMethod {
    public static void main(String[] args) {
//方法重载体现多态
        A a = new A();
//这里我们传入不同的参数,就会调用不同 sum 方法,就体现多态
        System.out.println(a.sum(10, 20));
        System.out.println(a.sum(10, 20, 30));
//方法重写体现多态
        B b = new B();
        a.say();
        b.say();
    }
}
class B { //父类
    public void say() {
        System.out.println("B say() 方法被调用...");
    }
}
class A extends B {//子类
    public int sum(int n1, int n2){//和下面 sum 构成重载
        return n1 + n2;
    }
    public int sum(int n1, int n2, int n3){
        return n1 + n2 + n3;
    }
    public void say() {
        System.out.println("A say() 方法被调用...");
    }
}
  1. 对象的多态 (核心,困难,重点)

代码

package com.hspedu.poly_.objpoly_;

public class Animals {
    public void cry(){
        System.out.println("Animals cry() 动物在叫....");
    }
}

package com.hspedu.poly_.objpoly_;

public class Cat extends Animals{
    @Override
    public void cry() {
        System.out.println("Cat cry()小猫喵喵叫...");
    }
}

package com.hspedu.poly_.objpoly_;

public class Dog extends Animals{
    @Override
    public void cry() {
        System.out.println("Dong cry()小狗汪汪叫...");
    }
}

package com.hspedu.poly_.objpoly_;

public class PolyObject {
    public static void main(String[] args) {
        //体现对象多态特点

        //animals 编译类型就是 Animals,运行类型 Dog
        Animals animals = new Animals();
        //因为运行时,这时即执行到该行时,animals运行类型是Dog的cry
        animals.cry();//小狗汪汪叫...

        //animals 编译类型就是 Animals,运行类型 Cat
        animals = new Cat();
        animals.cry();//小猫喵喵叫...
    }
}

8.11.4 多态快速入门案例

使用多态的机制来解决主人喂食物的问题,走代码。 Poly01.java

8.11.5 多态注意事项和细节讨论(instanceof重要)

com.hspedu.poly.detail 包 : PolyDetail.java

多态的前提是:两个对象(类)存在继承关系

多态的向上转型

多态向下转型

package com.hspedu.poly_.detail_;

public class Animals {
    String name = "动物";
    int age = 10;

    public void sleep() {
        System.out.println("睡");
    }

    public void run() {
        System.out.println("跑");
    }
    public void eat(){
        System.out.println("吃");
    }
    public void show(){
        System.out.println("hello,你好");
    }
}

package com.hspedu.poly_.detail_;

public class Cat extends Animals{
    public void eat(){//方法重写
        System.out.println("猫吃鱼");
    }
    public void catchMouse(){//Cat 特有方法
        System.out.println("猫抓老鼠");
    }
}

package com.hspedu.poly_.detail_;

public class Dog extends Animals{//是animals子类
}

package com.hspedu.poly_.detail_;

public class PolyDetail {
    public static void main(String[] args) {

        //向上转型: 父类的引用指向子类的对象
        //语法:父类类型引用名 = new 子类类型();
        Animals animals  = new Cat();

        //Object obj = new Cat();//可以,Object 也是 Cat的父类

        //向上转型调用方法的规则如下:
        //(1)可以调用父类中的所有成员(须遵守访问权限)
        //(2)但是不能调用子类中的特有的成员
        //(3)因为在编译阶段,能调用那些成员,是由编译类型决定的
        //  animals.catchMouse();//错误
        //(4)最终运行效果看子类的具体实现,即调用方法时,按照从子类开始查找方法
        //  然后调用,规则和前面我们讲的方法调用规则一致
        animals.eat();//猫吃鱼
        animals.run();//跑
        animals.show();//hello,你好
        animals.sleep();//睡

        //老师希望,可以调用Cat的catchMouse()方法
        //多态的向下转型
        //(1)语法:子类类型 引用名 =(子类类型)父类引用;

        //问一个问题? cat 的编译类型 Cat,运行类型是 Cat
        Cat cat = (Cat) animals;
        cat.catchMouse();//猫抓老鼠
        //(2)要求父类的引用必须指向的是当前目标类型的对象
        //  Dog dog = (Dog) animals;//错
    }
}

✓ 属性没有重写之说!属性的值看编译类型 PolyDetail02.java

package com.hspedu.poly_.detail_;

public class PolyDetail02 {
    public static void main(String[] args) {
        //属性没有重写之说!属性的值看编译类型
        Base base = new Sub();//向上转型
        System.out.println(base.count);// ? 看编译类型 10
        Sub sub = new Sub();
        System.out.println(sub.count);// 20
    }
}

class Base { //父类
    int count = 10;//属性
}
class Sub extends Base {//子类
    int count = 20;//属性
}

✓ instanceOf 比较操作符,用于判断对象的运行类型是否为 XX 类型或 XX 类型的子类型【举例说明】PolyDetail03.java

package com.hspedu.poly_.detail_;

public class PolyDetail03 {
    public static void main(String[] args) {
        BB bb = new BB();
        System.out.println(bb instanceof BB);//true
        System.out.println(bb instanceof AA);//true

        //aa 编译类型 AA, 运行类型是BB
        AA aa = new BB();
        System.out.println(aa instanceof AA);//true
        System.out.println(aa instanceof BB);//true

        Object obj = new Object();
        System.out.println(obj instanceof AA);//false
        String str = "hello";
        //System.out.println(str instanceof AA);
        System.out.println(str instanceof Object);//true
    }
}

class AA{}//父类
class BB extends AA{}//子类

8.11.6 课堂练习

请说出下面的每条语言,哪些是正确的,哪些是错误的,为什么? 2min 后老师评讲

PolyExercise02.java 3min

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

Java 重要特性: 动态绑定机制

1.当调用对象对象方法时,该方法会和该对象的内存地址/运行类型绑定

2.当调用对象属性时,没有动态绑定机制,哪里声名,那里使用

package com.hspedu.poly_.dynamic_;

public class DynamicBinding {
    public static void main(String[] args) {
        //a 的编译类型 A, 运行类型 B
        A a = new B();//向上转型
        System.out.println(a.sum());//?40 -> 30
        System.out.println(a.sum1());//?30-> 20
    }
}

class A {//父类
    public int i = 10;
    //动态绑定机制:
    public int sum() {//父类 sum()
        return getI() + 10;//20 + 10
    }
    public int sum1() {//父类 sum1()
        return i + 10;//10 + 10,
    }
    public int getI() {//父类 getI
        return i;
    }
}
class B extends A {//子类
    public int i = 20;
    // public int sum() {
// return i + 20;
// }
    public int getI() {//子类 getI()
        return i;
    }
// public int sum1() {
// return i + 10;
// }
}

8.11.8 多态的应用

  1. 多态数组 com.hspedu.poly.polyarr 包 PloyArray.java

    数组的定义类型为父类类型,里面保存的实际元素类型为子类类型

    应用实例:现有一个继承结构如下:要求创建 1 个 Person 对象、2 个 Student 对象和 2 个 Teacher 对象, 统一放在数组 中,并调用每个对象

    say 方法.

    应用实例升级:如何调用子类特有的方法,比如

    Teacher 有一个 teach , Student 有一个 study

    怎么调用?

package com.hspedu.poly_.polyarr_;

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

    public Person() {//无参构造器
    }

    public Person(String name, int age) {//有参构造器
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

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

    public String say(){
        return  name + "\t" + age;
    }
}

package com.hspedu.poly_.polyarr_;

public class Teacher extends Person {
    private double salary;

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

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }
    //重写父类的say方法
    @Override
    public String say() {
        return "老师是 " + super.say() + " salary" + salary;
    }

    //特有方法
    public void teach(){
        System.out.println("老师 " + getName() + " 正在授java课");
    }
}

package com.hspedu.poly_.polyarr_;

public class Student extends Person {
    private double score;

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

    public double getScore() {
        return score;
    }

    public void setScore(double score) {
        this.score = score;
    }
    //重写父类say

    @Override
    public String say() {
        return "学生是 " + super.say() + " score" + score;
    }
    //特有的方法
    public void study(){
        System.out.println("学生 " + getName() + " 正在学java");
    }
}

package com.hspedu.poly_.polyarr_;

public class PolyArray {


    public static void main(String[] args) {
        //应用实例:现有一个继承结构如下:要求创建 1 个 Person 对象、
        // 2 个 Student 对象和 2 个 Teacher 对象, 统一放在数组中,并调用每个对象 say 方法

        Person[] persons = new Person[5];
        persons[0] = new Person("marry",20);
        persons[1] = new Student("jack",18,100);
        persons[2] = new Student("smith",19,30.1);
        persons[3] = new Teacher("scott",30,20000);
        persons[4] = new Teacher("king",50,25000);

        //循环遍历多态数组,调用say
        for (int i = 0; i < persons.length; i++) {
            //提示:person[i] 编译类型是 Person ,运行类型是根据实际情况由JVM来判断
            System.out.println(persons[i].say());//动态绑定机制
            //这里大家聪明,使用类型判断 + 向下转型
            if(persons[i] instanceof Student){//判断person[i]的运行类型是不是Student
                Student student = (Student) persons[i];//向下转型
                student.study();
                //也可以使用一条语句((Student)persons[i]).study();
            } else if(persons[i] instanceof Teacher){
                Teacher teacher = (Teacher)persons[i];
                teacher.teach();
            }else if(persons[i] instanceof Person){

            }else{
                System.out.println("你的类型有误,请自己检查...");
            }
        }
    }
}
  1. 多态参数

package com.hspedu.poly_.polyparameter_;

public class Employee {
    private String name;
    private double salary;
    public Employee() {
    }

    public Employee(String name, double salary) {
        this.name = name;
        this.salary = salary;
    }
    //得到年工资的方法
    public double getAnnual(){
        return 12 * salary;
    }

    public String getName() {
        return name;
    }

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

    public double getSalary() {
        return salary;
    }

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

package com.hspedu.poly_.polyparameter_;

public class Worker extends Employee{
    public Worker(String name, double salary) {
        super(name, salary);
    }
    public void work(){
        System.out.println("普通员工 " + getName() + " is working");
    }

    @Override
    public double getAnnual() {//因为普通员工二u其他收入,则直接调用父类方法
        return super.getAnnual();
    }
}

package com.hspedu.poly_.polyparameter_;

public class Manager extends Employee{
    private double bonus;

    public Manager(String name, double salary, double bonus) {
        super(name, salary);
        this.bonus = bonus;
    }

    public double getBonus() {
        return bonus;
    }

    public void setBonus(double bonus) {
        this.bonus = bonus;
    }
    public void manage(){
        System.out.println("经理 " + getName() + " is managing");
    }

    //重写获取年薪方法
    @Override
    public double getAnnual() {
        return super.getAnnual() + bonus;
    }
}

package com.hspedu.poly_.polyparameter_;

public class PolyParameter {
    public static void main(String[] args) {
        Worker tom = new Worker("tom", 2500);
        Manager milan = new Manager("milan", 5000, 200000);
        PolyParameter polyParameter = new PolyParameter();
        polyParameter.showEmpAnnual(tom);
        polyParameter.showEmpAnnual(milan);

        polyParameter.testWork(tom);
        polyParameter.testWork(milan);
    }

    //showEmpAnnual(Employee e)
    //实现获取任何员工对象的年工资,并在 main 方法中调用该方法 [e.getAnnual()]
    public void showEmpAnnual(Employee e){
        System.out.println(e.getAnnual());//动态绑定机制,
    }
    //添加一个方法,testWork,如果是普通员工,则调用 work 方法,如果是经理,则调用 manage 方法
    public void testWork(Employee e){
        if(e instanceof Worker){
            ((Worker) e).work();//有向下转型操作
        }else if(e instanceof Manager){
            ((Manager) e).manage();//有向下转型操作
        }else{
            System.out.println("do nothing...");
        }
    }
}

8.12 Object 类详解

8.12.1 equals 方法

✓==和 equals 的对比 [面试题]

package com.hspedu.Object_;

public class Equals01 {
    public static void main(String[] args) {
        A a = new A();
        A b = a;
        A c = b;
        System.out.println(a == c);//true
        System.out.println(b == c);//true
        B bObj = a;
        System.out.println(bObj == c);//true

        //equals 方法,源码怎么查看。
        //把光标放在equals方法,直接输入ctrl + b

        /*
        //带大家
        public boolean equals(Object anObject) {
        if (this == anObject) {//如果是同一个对象
            return true;//返回 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;//如果两个字符串的所有字符都相等,则返回 true
            }
        }
        return false;//如果比较的不是字符串,则直接返回 false
    }
         */
        "hello".equals("abc");

        //看看Object类的 equals 是
        /*
        //即Object 的equals 方法默认就是比较对象地址是否相同
        //也就判断两个对象是不是同一个对象
        public boolean equals(Object obj) {
        return (this == obj);
    }
         */
        /*
        //从源码可以看出 Integer 也重写了Object的equals方法,
        //变成了判断两个值是否相同
        public boolean equals(Object obj) {
        if (obj instanceof Integer) {
            return value == ((Integer)obj).intValue();
        }
        return false;
    }
         */
        Integer integer1 = new Integer(5);
        Integer integer2 = new Integer(5);
        System.out.println(integer1 == integer2);//false
        System.out.println(integer1.equals(integer2));//true

        String str1 = new String("hspedu");
        String str2 = new String("hspedu");
        System.out.println(str1 == str2);//false
        System.out.println(str1.equals(str2));//false
    }
}
class B { }
class A extends B{ }

8.12.2 如何重写 equals 方法

  1. 应用实例: 判断两个 Person 对象的内容是否相等,如果两个 Person 对象的各个属性值都一样,则返回 true,反之 false。

EqualsExercise01.java

package com.hspedu.Object_;

import jdk.nashorn.internal.ir.CallNode;

public class EqualsExercise01 {
    public static void main(String[] args) {
        Person person0 = new Person("amy", 16, '女');
        Person person1 = new Person("amy", 16, '女');
//        Person person1 = new Person("Jim", 15, '男');
        System.out.println(person0.equals(person1));
    }
}

//判断两个 Person 对象的内容是否相等,
//如果两个 Person 对象的各个属性值都一样,则返回 true,反之 false
class Person{//extends Object
    private String name;
    private int age;
    private char gender;
    //重写Object 的equals方法
    public boolean equals(Object obj){
        //判断如果比较的两个对象是同一个对象,则直接返回 true
        if(this == obj){
            return true;
        }
        //类型判断
        if(obj instanceof Person){//instanceof判断的是是运行类型,obj编译类型是Object,运行类型是Person
            //进行 向下转型,因为我需要得到obj的各个属性
            //向下转型后可以调用子类类型的所有成员
            Person p = (Person)obj;
            return this.name.equals(p.name) && this.age == p.age && this.gender == p.gender;
//            if(this.name != ((Person) obj).name){//判断条件错了,只能判断两个name的地址
//                return false;
//            }else if(this.age != ((Person) obj).age){
//                return false;
//            }else if(this.gender != ((Person) obj).gender){
//                return false;
//            }else{
//                 return true;
//            }
        }else{//如果不是Person,则直接返回false
            return false;
        }
    }
    public Person(String name, int age, char gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }

    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 char getGender() {
        return gender;
    }

    public void setGender(char gender) {
        this.gender = gender;
    }
}
  1. 习题三

8.12.4 hashCode 方法

★ 6 个小结:

  1. 提高具有哈希结构的容器的效率!

  2. 两个引用,如果指向的是同一个对象,则哈希值肯定是一样的!

  3. 两个引用,如果指向的是不同对象,则哈希值是不一样的

  4. 哈希值主要根据地址号来的!, 不能完全将哈希值等价于地址

  5. 案例演示[HashCode_.java]: obj.hashCode() [测试:A obj1 = new A(); A obj2 = new A(); A obj3 = obj1]

  6. 后面在集合,中 hashCode 如果需要的话,也会重写, 在讲解集合时,老韩在说如何重写 hashCode()

package com.hspedu.Object_;

public class HashCode_ {
    public static void main(String[] args) {
        AA aa = new AA();
        AA aa1 = new AA();
        AA aa2 = aa;
        System.out.println("aa.hashCode()=" + aa.hashCode());//460141958
        System.out.println("aa1.hashCode()=" + aa1.hashCode());//1163157884
        System.out.println("aa2.hashCode()=" + aa2.hashCode());//460141958
    }
}
class AA{}

8.12.5 toString 方法

  1. 基本介绍

    默认返回:全类名+@+哈希值的十六进制,【查看 Object 的 toString 方法】

    子类往往重写 toString 方法,用于返回对象的属性信息

  2. 重写 toString 方法,打印对象或拼接对象时,都会自动调用该对象的 toString 形式

    案例演示:Monster [name, job, sal] 案例: ToString_.java

  3. 当直接输出一个对象时,toString 方法会被默认的调用, 比如 System.out.println(monster); 就会默认调用monster.toString()

package com.hspedu.Object_;

public class ToString_ {
    public static void main(String[] args) {

        /*
         Object的toString()源码
         (1)getClass().getName() 类的全类名(包名+类名)
         (2)Integer.toHexString(hashCode()将对象的hashCode值转成16进制字符串
         public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
         */

        Monster monster = new Monster("小妖怪", "巡山的", 1000);monster.toString();
        System.out.println(monster.toString() + " hashcode=" + monster.hashCode());
        System.out.println("==当直接输出一个对象时,toString 方法会被默认的调用==");
        System.out.println(monster);
    }
}

class Monster{
    private String name;
    private String job;
    private double sal;

    public Monster(String name, String job, double sal) {
        this.name = name;
        this.job = job;
        this.sal = sal;
    }

    //重写toString方法,输出对象的属性
    //使用快捷键即可 alt+insert -> toString

    @Override
    public String toString() {//默认重写后,一般是把对象的属性输出,当然程序员也可以自己定制
        return "Monster{" +
                "name='" + name + '\'' +
                ", job='" + job + '\'' +
                ", sal=" + sal +
                '}';
    }
}

8.12.6 finalize 方法

  1. 当对象被回收时,系统自动调用该对象的 finalize 方法。子类可以重写该方法,做一些释放资源的操作【演示】

  2. 什么时候被回收:当某个对象没有任何引用时,则 jvm 就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来 销毁该对象,在销毁该对象前,会先调用 finalize 方法。

  3. 垃圾回收机制的调用,是由系统来决定(即有自己的 GC 算法), 也可以通过 System.gc() 主动触发垃圾回收机制,测 试:Car [name]

    老韩提示: 我们在实际开发中,几乎不会运用 finalize , 所以更多就是为了应付面试.

package com.hspedu.Object_;

//演示 Finalize的用法
public class Finalize_ {
    public static void main(String[] args) {
        Car bmw = new Car("宝马");
        //这时 car对象就是一个垃圾,垃圾回收器就会回收(销毁)对象,在销毁对象前,会调用该对象的finalize方法
        //,程序员就可以在 finalize中,写自己的业务逻辑代码(比如释放资源:数据库连接,或者打开文件..)
        //,如果程序员不重写 finalize,那么就会调用 Object类的finalize,及默认处理
        //,如果程序员重写了finalize,就可以实现自己的逻辑
        bmw = null;
        System.gc();//主动调用垃圾回收器
        System.out.println("程序退出了....");
    }
}
class Car{
    private String name;

    public Car(String name) {
        this.name = name;
    }
    //重写finalize
    //快捷键就可以生成

    @Override
    protected void finalize() throws Throwable {
        System.out.println("我们销毁 汽车" + name);
        System.out.println("释放了某些资源...");
    }
}

8.13 断点调试(debug)

8.13.1 一个实际需求

8.13.2 断点调试介绍

8.13.3 断点调试的快捷键

F7(跳入) F8(跳过) shift+F8(跳出) F9(resume,执行到下一个断点)

F7:跳入方法内

F8: 逐行执行代码.

shift+F8: 跳出方法

8.13.4 断点调试应用案例

看几段代码,演示调试过程

8.13.5 断点调试应用案例

  1. 案例 1 com.hspedu.debug_ 包 Debug01.java

    看一下变量的变化情况等

package com.hspedu.debug_;

public class Debug01 {
    public static void main(String[] args) {
        //演示逐行执行代码
        int sum = 0;
        for (int i = 0; i < 5; i++) {
            sum += i;
            System.out.println("i=" + i);
            System.out.println("sum=" + i);
        }
        System.out.println("退出 for....");
    }
}
  1. 案例 2

    看一下数组越界的异常 Debug02.jav

package com.hspedu.debug_;

public class Debug02 {
    public static void main(String[] args) {
        int[] arr = {1, 10, -1};
        for (int i = 0; i <= arr.length; i++) {
            System.out.println(arr[i]);
        }
        System.out.println("退出 for");
    }
}
  1. 案例 3

    演示如何追源码,看看 java 设计者是怎么实现的。(提高编程思想)。

    小技巧:将光标放在某个变量上,可以看到最新的数据。 Debug03.java

package com.hspedu.debug_;

import java.util.Arrays;

public class Debug03 {
    public static void main(String[] args) {
        int[] arr = {1, -1, 10, -20 , 100};
        //我们看看 Arrays.sort 方法底层实现.->Debug
        Arrays.sort(arr);
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + "\t");
        }

    }
}
  1. 案例 4

    演示如何直接执行到下一个断点 F9 resume。

    小技巧: 断点可以在 debug 过程中,动态的下断点

package com.hspedu.debug_;

import java.util.Arrays;
//演示执行到下一个断点,同时支持动态的下断点
public class Debug04 {
    public static void main(String[] args) {
        int[] arr = {1, -1, 10, -20 , 100};
//我们看看 Arrays.sort 方法底层实现.->Debug
        Arrays.sort(arr);
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + "\t");
        }
        System.out.println("hello100");
        System.out.println("hello200");
        System.out.println("hello300");
        System.out.println("hello400");
        System.out.println("hello500");
        System.out.println("hello600");
        System.out.println("hello700");
    }
}

8.13.6 断点调试课后练习

8.14 项目-零钱通

8.14.1 项目开发流程

8.14.2 项目需求说明

使用 Java 开发 零钱通项目 , 可以完成收益入账,消费,查看明细,退出系统等功能.

8.14.3 项目的界面

化繁为简.

  1. 先完成显示菜单,并可以选择

  2. 完成零钱通明细.

  3. 完成收益入账

  4. 消费

  5. 退出

8.14.4 项目代码实现

编写文件 SmallChangeSys.java 完成基本功能 (过程编程)

老师提示:先使用过程编程,后面改成 OOP 版本,请小伙伴体会 OOP 编程带来的好处

8.14.5 项目代码实现改进

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值