java零基础Ⅰ-- 7.面向对象编程(中级部分)

java零基础Ⅰ-- 面向对象编程(中级部分)

连接视频



Intellij IDEA

IDE (集成开发环境)- IDEA

IDEA介绍

  1. IDEA 全称 Intellij IDEA
  2. 在业界被公认最好的Java开发工具
  3. IDEA是JetBrains公司的产品,总部位于捷克的首都布拉格
  4. 除了支持Java开发,好支持HTML、CSS、PHP、MySQL、Python等

Alt


IDE (集成开发环境)- Eclipse

Eclipse 介绍

  1. Eclipse 是一个开放源代码的,基于Java的可扩展开发平台
  2. 最初是由IBM 公司耗资3000万美金开发的下一代IDE开发环境
  3. 2001年11月贡献给开源社区
  4. Eclipse是目前最优秀的Java开发IDE之一

在这里插入图片描述


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

IDEA的安装

官网:https://www.jetbrains.com/

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

IDEA的基本介绍和使用

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

创建一个java项目 - hello

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
创建Hello类,点击src目录右键,
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

设置字体大小:
File -> Settings -> Editor -> Font

在这里插入图片描述

public class Hello {

    public static void main(String[] args) {
        System.out.printf("hello,idea");
    }
}

点击运行:
在这里插入图片描述

在这里插入图片描述


IDEA使用技巧和经验

设置字体

菜单 File -> sttings

外观字体大小设置
在这里插入图片描述

内容字体大小设置
在这里插入图片描述
字体粗体设置
在这里插入图片描述

设置颜色主题

在这里插入图片描述

字符编码设置

在这里插入图片描述

练习

使用IDEA 开发一个java项目 testpro01,创建一个类MyTools,编译一个方法,可以完成对int数组冒泡排序的功能

public class ArrayTest {

    public static void main(String[] args) {
        MyTools myTools = new MyTools();
        int[] arr = {2,12,-10,22};
        myTools.bubble(arr);
        //输出排序后的arr,引用传递
        System.out.printf("====排序后的arr====\n");
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i] + " ");
        }
        //在idea 当我们 run一个文件时,会先编译成 .class -> 在运行
    }

}
//创建一个类MyTools,编译一个方法,可以完成对int数组冒泡排序的功能
class  MyTools {
    public void bubble(int arr[]){
        int tmp = 0;
        //冒泡排序 从小到大
        for (int i = 0; i < arr.length -1; i++) {//外层循环次数 arr.length-1
            for (int j = 0; j < arr.length - 1 - i; j++) {//
                if(arr[j] > arr[j + 1]){// 交换
                    tmp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = tmp;
                }
            }
        }
    }
}

====排序后的arr====
-10 2 12 22 

在这里插入图片描述

在这里插入图片描述


IDEA常用的快捷键

1、删除当前行,默认是 ctrl + Y 自己配置 ctrl + D 【搜索 Delete Line】
2、复制当前行,自己配置 ctrl + alt + 向下光标 【搜索 Duplicate Line or Selection】
3、补全代码 alt + /
4、添加注释和取消注释 ctrl + / 【第一次是添加注释,第二次是取消注释】

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

在这里插入图片描述
已经存在了,但是可以直接使用ok
在这里插入图片描述
在这里插入图片描述

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

在这里插入图片描述

6、快速格式化代码 ctrl + alt + L 【搜索 Reformat Code】

7、快速运行程序 自定义 alt + R【搜索 Run】

8、生成构造器等 alt + insert 【提供开发效率】
在这里插入图片描述

在这里插入图片描述

9、查看一个类的层级关系 ctrl + H

在这里插入图片描述
生成继承关系图
在这里插入图片描述

10、将光标放在一个方法上,输入 ctrl + B,可以选择定位到哪个类的方法

11、自动的分配变量名,通过 在后面加 .var

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


模板/自定义模板

file -> settings -> editor -> Live templates ->

查看有哪些模板快捷键/可以自己增加模板

自己添加一个模板
在这里插入图片描述
在这里插入图片描述

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



看一个应用场景

现在有两个程序员共同开发一个java项目,程序员xiaoming希望定义一个类取名 Dog,程序员xiaoqiang也想定义一个类也叫 Dog。两个程序员为此还吵了起来,怎么办? ==》 包


包的三大作用

1、区分相同名字的类
2、当类很多时,可以很好的管理类[看Java Api 文档]
3、控制访问范围


包基本语法

package com.zzpedu;

说明:
1、package 关键字,表示打包
2、com.zzpedu; 表示包名


包的本质分析(原理)

包的本质就是 实际上就是创建不同文件夹/目录来保存类文件

示意图:

在这里插入图片描述


快速入门

使用打包技术来解决上面的问题,不同包下Dog类

在这里插入图片描述

package com.use;
import com.xiaoqiang.Dog;

public class Test {
    public static void main(String[] args) {
        Dog dog = new Dog();
        System.out.println(dog);//com.xiaoqiang.Dog@1b6d3586

        com.xiaoming.Dog dog1 = new com.xiaoming.Dog();
        System.out.println(dog1);//com.xiaoming.Dog@4554617c
    }
}

包的命名

命名规则:

只能包含数字、字母、下划线、小圆点,但不能用数字开头不能是关键字或保留字

demo.class.exec1 //错误 class 是关键字
demo.12a //错误 12a 不能用数字开头
demo.abc12.oa //正确

命令规范:
一般是小写字母 + 小圆点
一般是:com.公司名.项目名.业务模块名
比如:com.zzpedu.oa.model; com.zzpedu.oa.controller;

举例:

com.sina.crm.use //用户模块
com.sina.crm.order //订单模块
com.sina.crm.utils //工具类

常用包

一个包下,包含很多的类,java中常用的包有

java.lang.*  //lang包是基本包,默认引入,不需要再引入
java.util.*  //util 包,系统提供的工具包,工具类,使用 Scanner
java.net.*  //网络包,网络开发
java.awt.*  //是做java的界面开发,GUI

如何引入包

com.zzpedu.pkg : //创建包

语法:

import 包;

我们引入一个包的主要目的是要使用该包下的类
比如:import java.util.Scanner; 就只是引入一个类Scanner

import java.util.*; //表示将 java.util 包所有都引入

案例:使用系统提供 Arrays 完成 数组排序

package com.zzpedu.pkg;

//注意:
//我们需要哪个类,就导入哪个类即可,不建议使用 *导入
//import java.util.Scanner;//表示只会引入java.util 包下的 Scanner
//import java.util.*; //表示将 java.util 包下所有类都引入(导入)

import java.util.Arrays;

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] + " ");//-1 2 3 13 20
        }
    }
}


注意事项和使用细节

1、package 的作用是声明当前类所在的包,需要放在类(或者文件)的最上面,一个类中只有一句package

2、import 指令 位置放在package的下面,在类定义前面,可以有多句且没有顺序要求。

//package 的作用是声明当前类所在的包,需要放在类(或者文件)的最上面,
// 一个类中只有一句package
package com.zzpedu.pkg;

//import 指令 位置放在package的下面,在类定义前面,可以有多句且没有顺序要求。
import java.util.Arrays;
import java.util.Scanner;
//..

//类定义
public class PakDetail {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int[] arr = {-1,0};
        Arrays.sort(arr);
    }
}



访问修饰符

基本介绍

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

1、公开级别:用 public 修饰,对外公开
2、受保护级别:用 protected 修饰,对子类和同一个包中的类公开
3、默认级别:没有修饰符号,向同一个包的类公开
4、私有级别:用 private 修饰,只有类本身可以访问,不对外公开。


4种访问修饰符的访问范围

访问级别访问控制修饰符同类/本类同包子类不同包
公开public
受保护protected×
默认没有修饰符××
私有private×××

使用的注意事项

1)修饰符可以用来修饰符类中的属性,成员方法以及类
2)只有默认的和public 才能修饰类!,并且遵循上述访问权限的特点
3)因为还没有学习继承,因此关于子类中的访问权限,以后再讲
4)成员方法的访问规则和属性完全一样

//com.zzpedu.modifier: 需要很多文件来说明(A类,B类,Test类)

package com.zzpedu.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.zzpedu.modifier;

public class B {
    public void say(){
        A a = new A();
        //在同一个包 可以访问 public ,protected 和 默认修饰属性或方法, 不能访问 private 属性或方法
        System.out.println("n1=" + a.n1 +" n2=" + a.n2 + " n3=" + a.n3);
        a.m1();
        a.m2();
        a.m3();
        //a.m4(); //错误的 private 私有的
    }
}
package com.zzpedu.modifier;

public class Test {
    public static void main(String[] args) {
        A a = new A();
        a.m1();//n1=100 n2=200 n3=300 n4=400
        B b = new B();
        b.say();//n1=100 n2=200 n3=300
        a.m2();
        a.m3();
        //a.m4(); //错误的 private 私有的
    }
}
//只有 默认和public 可以修饰类
class Tiger { }
package com.zzpedu.pkg;

import com.zzpedu.modifier.A;

public class Test {
    public static void main(String[] args) {
        A a = new A();
        //在不同包下, 可以访问public 修改的属性或方法
        // 但是不能访问 protected,默认,private修饰的属性或方法
        System.out.println(a.n1);//n1=100
        a.m1();
        //不能访问 a.m2() a.m3() a.m4()
    }
}



面向对象编程三大特征

基本介绍:

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

1. 封装

封装介绍

封装(encapsulation) 就是把抽象的数据 [属性] 和对数据的操作 [方法] 封装在一起,数控被保护在内部,程序的其它部分只有通过被授权的操作 [方法],才能对数据进行操作。


封装的理解和好处

1)隐藏实现细节: 方法(连接数据库) <-- 调用(传入参数…)
2)可以对数据进行验证,保证安全合理

Person{name, age}
Person p = new Person();
p.name = "jack";
p.age = 1220;

封装的实现步骤(三步)

1)将属性进行私有化 【private 修饰】 【不能直接修改属性】
2)通过一个公共的(public)set方法,用于对属性判断并赋值

public void setXxx(类型 参数名){//Xxx 表示某个属性
	//加入数据验证的业务逻辑
	属性 = 参数名;
}

3)提供一个公共的(public)get方法,用于获取属性的值

public 数据类型 getXxx(){//权限判断 Xxx 表示某个属性
	return xx;
}

快速入门案例

看一个案例

那么在java中如何实现这种类似的控制呢?
请大家看一个小程序,不能随便查人的年龄,工资等隐私,并对设置的年龄进行合理的验证。年龄合理就设置,否则给默认年龄,必须在1-120,年龄,工资不能直接查看, name的长度在 2 - 6 字符 之间

public class Person {
	public String name;
	private int age;
	private double salary;
	private String job;
}
package com.zzpedu.encap;

public class Encapsulation01 {
    public static void main(String[] args) {
        //如果要使用快捷键 ctrl + r,需要先配置主类
        //第一次,我们使用鼠标点击形式运行程序,后面就可以使用
        Person person = new Person();
        person.setName("jack");
        person.setAge(30);
        person.setSalary(30000);
        System.out.println(person.info());
        System.out.println(person.getSalary());
    }
}
/*
那么在java中如何实现这种类似的控制呢?
请大家看一个小程序,不能随便查人的年龄,工资等隐私,
并对设置的年龄进行合理的验证。年龄合理就设置,否则给默认年龄,
必须在1-120,年龄,工资不能直接查看, name的长度在 2-6 字符 之间
 */
class Person {
    public String name; //名字公开
    private int age; //age 私有化
    private double salary; //salary 私有化

    //自己写setXxx 和 getXxx 太慢,我们使用快捷键  alt + insert
    //然后根据要求来完善我们的代码
    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;
    }
}

========控制台输出=========
信息为 name=jack age=30 薪水=30000.0
30000.0

将构造器和setXxx结合

看一个案例

class Person {
    public String name; //名字公开
    private int age; //age 私有化
    private double salary; //salary 私有化

    //构造器 快捷键 alt + insert
    public Person() {
    }
    //还有一种开发方式,就是将构造方法和serXxx结合
    //有三个属性的构造器
    public Person(String name, int age, double salary) {
//        this.name = name;
//        this.age = age;
//        this.salary = salary;
        //我们可以将set方法写在构造器中,这样仍然可以验证
        setName(name);//等价于 this.setName(name)
        setAge(age);
        setSalary(salary);
    }
	....
}

练习

创建程序,在其中定义两个类:Account和AccountTest类体会Java的封装性。
1、Account类要求具有属性:姓名(长度为2位3位或者4位)、余额(必须>20)、密码(必须是六位),如果不满足,则给出提示信息,并给默认值(自定义)
2、通过setXxx的方法给Account 的属性赋值
3、在AccountTest中测试

提示知识点:

String name = “”;
int len = name.length(); //字符串的长度

package com.zzpedu.encap;

/**
 * 创建程序,在其中定义两个类:Account和AccountTest类体会Java的封装性。
 * 1、Account类要求具有属性:姓名(长度为2位3位或者4位)、余额(必须>20)、密码(必须是六位),
 * 如果不满足,则给出提示信息,并给默认值(自定义)
 * 2、通过setXxx的方法给Account 的属性赋值
 * 3、在AccountTest中测试
 */
public class Account {
    //为了封装,将3个属性设置为private
    private String name;
    private double balance;//余额
    private String pwd;
    //通过两个构造器
    public Account() {
    }
    public Account(String name, double balance, String pwd) {
        this.setBalance(balance);
        this.setPwd(pwd);
        this.setName(name);
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        //姓名(长度为2位3位或者4位)
        if (name.length() >= 2 && name.length() <= 4) {
            this.name = name;
        } else {
            System.out.println("姓名要求(长度为2位3位或者4位),默认值 无名");
            this.name = "无名";
        }

    }

    public double getBalance() {
        return balance;
    }

    public void setBalance(double balance) {
        //余额(必须>20)
        if (balance > 20) {
            this.balance = balance;
        } else {
            System.out.println("余额(必须>20),默认为0");
        }
    }
    public String getPwd() {
        return pwd;
    }
    public void setPwd(String pwd) {
        //密码(必须是六位)
        if (pwd.length() == 6) {
            this.pwd = pwd;
        } else {
            System.out.println("密码(必须是六位),默认密码为 000000");
            this.pwd = "000000";
        }
    }
    //显示账号信息
    public String showInfo() {
        //可以增加一个权限的效验 对密码
        return "账号信息 name=" + name + " 余额=" + balance + " 密码=" + pwd;
//        if(){
//            return "账号信息 name=" + name + " 余额=" + balance + " 密码=";
//        }else {
//            return "你无权限查看....";
//        }
    }
}
package com.zzpedu.encap;

public class AccountTest {
    public static void main(String[] args) {
        //创建Account
        Account account = new Account();
        account.setName("jack111");
        account.setBalance(10);
        account.setPwd("12346");
        System.out.println(account.showInfo());
    }
}
==========控制台输出===========
姓名要求(长度为23位或者4位),默认值 无名
余额(必须>20,默认为0
密码(必须是六位),默认密码为 000000
账号信息 name=无名 余额=0.0 密码=000000



2. 继承

为什么需要继承

一个小问题,还是看个程序,提出代码复用

我们编写两个类,一个是Pupil类(小学生),一个是Graduate(大学毕业生)。
问题:两个类的属性和方法很多是相同的,怎么把?

package com.zzpedu.extend_;
//小学生 -> 模拟小学生考试的情况
public class Pupil {
    public String name;
    public int age;
    private double score;//成绩

    public void setScore(double score) {
        this.score = score;
    }
    public void testing(){
        System.out.println("小学生 " + name + " 正在考小学数学...");
    }
    public void show(){
        System.out.println("学生名=" + name + " 年龄=" + age + " 成绩=" + score);
    }
}
package com.zzpedu.extend_;
//大学生类 -> 模拟大学生考试的简单情况
public class Graduate {
    public String name;
    public int age;
    private double score;//成绩

    public void setScore(double score) {
        this.score = score;
    }
    public void testing(){//和Pupil不一样
        System.out.println("大学生 " + name + " 正在考大学数学...");
    }
    public void show(){
        System.out.println("学生名=" + name + " 年龄=" + age + " 成绩=" + score);
    }
}
package com.zzpedu.extend_;

public class Extends01 {
    public static void main(String[] args) {
        Pupil pupil = new Pupil();
        pupil.name = "银角大王";
        pupil.age = 10;
        pupil.testing();
        pupil.setScore(60);
        pupil.show();
        System.out.println("================");
        Graduate graduate = new Graduate();
        graduate.name = "金角大王";
        graduate.age = 22;
        graduate.testing();
        graduate.setScore(100);
        graduate.show();
    }
}

========控制台输出==============
小学生 银角大王 正在考小学数学...
学生名=银角大王 年龄=10 成绩=60.0
================
大学生 金角大王 正在考大学数学...
学生名=金角大王 年龄=22 成绩=100.0

==> 继承(代码复用性)


继承基本介绍和示意图

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

示意图

在这里插入图片描述


继承的基本语法

class 子类 extends 父类{
}

1)子类就会自动拥有父类的属性和方法
2)父类又叫 超类,基类
3)子类有叫派生类


快速入门案例

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

package com.zzpedu.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 show(){
        System.out.println("学生名=" + name + " 年龄=" + age + " 成绩=" + score);
    }
}
package com.zzpedu.extend_.improve_;
//小学生 -> 模拟小学生考试的情况
//让 Pupil 继承 Student类
public class Pupil extends Student{
    public void testing(){
        System.out.println("小学生 " + name + " 正在考小学数学...");
    }
}
package com.zzpedu.extend_.improve_;
//大学生类 -> 模拟大学生考试的简单情况
//让 Graduate 继承 Student类
public class Graduate extends Student{
    public void testing(){//和Pupil不一样
        System.out.println("大学生 " + name + " 正在考大学数学...");
    }
}
package com.zzpedu.extend_.improve_;

public class Extends01 {
    public static void main(String[] args) {
        Pupil pupil = new Pupil();
        pupil.name = "银角大王~";
        pupil.age = 11;
        pupil.testing();
        pupil.setScore(50);
        pupil.show();
        System.out.println("================");
        Graduate graduate = new Graduate();
        graduate.name = "金角大王~";
        graduate.age = 23;
        graduate.testing();
        graduate.setScore(80);
        graduate.show();
    }
}
============控制台输出=========
小学生 银角大王~ 正在考小学数学...
学生名=银角大王~ 年龄=11 成绩=50.0
================
大学生 金角大王~ 正在考大学数学...
学生名=金角大王~ 年龄=23 成绩=80.0

继承给编程带来的便利

1)代码的复用性提高了
2)代码的扩展性和维护性提高了


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

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

package com.zzpedu.extend_;

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

    public Base(){//无参构造器
        System.out.println("父类Base()构造器被调用...");
    }
    //父类提供一个public的访问private属性,返回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");
    }
    //提供public方法调用test400
    public void callTest400(){
        test400();
    }
}
package com.zzpedu.extend_;

public class Sub extends Base{//子类
    public Sub(){
    	super();//默认调用父类的无参构造器 可写可不写 默认的
        System.out.println("子类Sub()构造器被调用...");
    }
    public void sayOk(){//子类方法
        //非私有属性和方法可以在子类直接访问
        //但是私有属性和方法不能在子类直接访问
        System.out.println(n1 + " " + n2 + " " + n3 );
        test100();
        test200();
        test300();
        //test400();//不能直接访问private属性和方法
        //要通过父类提供公共的方法去访问
        System.out.println("n4=" + getN4());
        callTest400();
    }
}
package com.zzpedu.extend_;

public class ExtendsDetail {
    public static void main(String[] args) {
        Sub sub = new Sub();
        sub.sayOk();
    }
}
=======控制台输出======
父类Base()构造器被调用...
子类Sub()构造器被调用...
100 200 300
test100
test200
test300
n4=400
test400

子类继承了所有的属性和方法 debug
在这里插入图片描述

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

package com.zzpedu.extend_;

public class ExtendsDetail {
    public static void main(String[] args) {
        Sub sub = new Sub();//创建了子类对象 sub
    }
}
=======控制台输出======
父类Base()构造器被调用...
子类Sub()构造器被调用...

3、当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器,如果父类没有提供无参构造器,则必须在子类的构造器中用 super 去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不通过

public class Base {//父类
    public Base(){//无参构造器
        System.out.println("父类Base()构造器被调用...");
    }
}
public class Sub extends Base{//子类
    public Sub(){
        //默认调用父类的无参构造器 可写可不写 默认的
        super();
        System.out.println("子类Sub()构造器被调用...");
    }
    //当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器
    public Sub(String name){
        //do nothing...
        System.out.println("子类Sub(String name)构造器被调用...");
    }
 }
 
package com.zzpedu.extend_;

public class ExtendsDetail {
    public static void main(String[] args) {
        System.out.println("====第一个对象==");
        Sub sub = new Sub();//创建了子类对象 sub
        System.out.println("====第二个对象==");
        Sub sub2 = new Sub("jack");
    }
}
=======控制台输出======
====第一个对象==
父类Base()构造器被调用...
子类Sub()构造器被调用...
====第二个对象==
父类Base()构造器被调用...
子类Sub(String name)构造器被调用...
public class Base {//父类
//    public Base(){//无参构造器
//        System.out.println("父类Base()构造器被调用...");
//    }
    public Base(String name,int age){//有参构造器
        System.out.println("父类Base(String name,int age)构造器被调用...");
    }
 }
 public class Sub extends Base{//子类
    public Sub(){
        //默认调用父类的无参构造器 可写可不写 默认的
        //super();
        //如果父类没有提供无参构造器,则必须在子类的构造器中用 super
        // 去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不通过
        super("smith",20);
        System.out.println("子类Sub()构造器被调用...");
    }
    //当创建子类对象时,不管使用子类的哪个构造器,默认情况下总会去调用父类的无参构造器
    public Sub(String name){
        //如果父类没有提供无参构造器,则必须在子类的构造器中用 super
        // 去指定使用父类的哪个构造器完成对父类的初始化工作,否则,编译不通过
        super("tom",30);
        //do nothing...
        System.out.println("子类Sub(String name)构造器被调用...");
    }
 }
 package com.zzpedu.extend_;

public class ExtendsDetail {
    public static void main(String[] args) {
        System.out.println("====第一个对象==");
        Sub sub = new Sub();//创建了子类对象 sub
        System.out.println("====第二个对象==");
        Sub sub2 = new Sub("jack");
    }
}
=======控制台输出======
====第一个对象==
父类Base(String name,int age)构造器被调用...
子类Sub()构造器被调用...
====第二个对象==
父类Base(String name,int age)构造器被调用...
子类Sub(String name)构造器被调用...

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

public class Base {//父类
    public Base(){//无参构造器
        System.out.println("父类Base()构造器被调用...");
    }
    public Base(String name,int age){//有参构造器
        System.out.println("父类Base(String name,int age)构造器被调用...");
    }
    public Base(String name){//有参构造器
        System.out.println("父类Base(String name)构造器被调用...");
    }
}
public class Sub extends Base{//子类
    public Sub(String name,int age){
        //1. 调用父类的无参构造器,如下 或者 什么都不写 默认调用父类的无参构造器
        //super();//父类的无参构造器,显示调用
        //2. 调用父类的 Base(String name) 构造器
        //super("zzp");
        //3. 调用父类的 Base(String name,int age) 构造器
        super("king",20);
        System.out.println("子类Sub(String name,int age)构造器被调用...");
    }
}
public class ExtendsDetail {
    public static void main(String[] args) {
        Sub sub = new Sub("king",50);
    }
}
=======控制台输出======
父类Base(String name,int age)构造器被调用...
子类Sub(String name,int age)构造器被调用...

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

public class Sub extends Base{//子类
    public Sub(String name,int age){
        //super 在使用时,必须在构造器第一行(super只能在构造器中使用)
        super("king",20);
        System.out.println("子类Sub(String name,int age)构造器被调用...");
    }
}

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

public class Sub extends Base{//子类
    public Sub(String name,int age){
        //super() 和 this() 都只能放在构造器第一个行,因此这两个方法不能共存在一个构造器
        super("king",20);
        //this("zzp");//不允许 super() 和 this() 只能存在一个
        System.out.println("子类Sub(String name,int age)构造器被调用...");
    }
}

7、java所有类都是Object类的子类,Object 是所有类的基类(超类)

在这里插入图片描述

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

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

public class Base extends ToBase{//父类}..
public class Sub extends Base{//子类}...

public class ExtendsDetail {
    public static void main(String[] args) {
        Sub sub = new Sub("king",50);
    }
}
=======控制台输出======
构造器ToBase() 被调用...
父类Base(String name,int age)构造器被调用...
子类Sub(String name,int age)构造器被调用...

9、子类最大继承一个父类(指直接继承类),即java中是单继承机制
思路:如何让A类继承B类和C类

A类继承B类 --> B类继承C类
在这里插入图片描述

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

Person is a Music?
Person Music
Music extends Person//不合理

Animal
Cat extends Animal//合理
// An highlighted block
var foo = 'bar';

继承的本质分析(重要)

案例

我们看一个案例来分析继承父类,创建子类对象时,内存中到底发送了什么?【当子类对象创建好后,建立查找关系

class GrandPa{
	String name = "大头爷爷";
	String hobby = "旅游";
}
class Father extends GrandPa{
	String name = "大头爸爸";
	int age= 39;	
}
class Son extends Father {
	String name = "大头儿子";
}
Son son = new Son();
son.name = ?;
son.age = ?;
son.hobby = ?;
package com.zzpedu.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);//大头儿子
        // private修饰的不能直接访问 报错 注: GrandPa.age不会访问 程序在Father.age 跳出
        //System.out.println(son.age);//39
        System.out.println(son.getAge());//39
        System.out.println(son.hobby);//旅游
    }
}
class GrandPa{//爷爷类
        String name = "大头爷爷";
        String hobby = "旅游";
        public int age = 100;
}
class Father extends GrandPa{//父类
    String name = "大头爸爸";
    private int age= 39;
    public int getAge() {
        return age;
    }
}
class Son extends Father {//子类
    String name = "大头儿子";
}
==========控制台输出==========
大头儿子
39
旅游

子类创建的内存布局

在这里插入图片描述


练习

1、案例1

class A{
	A(){System.out.println("a");}
	A(String name){System.out.println("a name");}
}
class B extends A{
	B(){ this("abc");System.out.println("b");}
	B(String name){System.out.println("b name");}//分析有默认的super()
}
main函数中:B b = new B(); 会输出什么?
package com.zzpedu.extend_.exercise;

public class ExtendsExercise01 {
    public static void main(String[] args) {
        B b = new B();// a, b name, b
    }
}
class A{
    A(){System.out.println("a");}
    A(String name){System.out.println("a name");}
}
class B extends A{
    B(){ this("abc");System.out.println("b");}
    B(String name){
        //默认有 super();
        System.out.println("b name");
    }
}
==========控制台输出==========
a
b name
b

2、案例2

class A {
	public A(){
		System.out.println("我是A类");
	}
}
class B extends A {//B类 继承A类
	public B(){
		System.out.println("我是B类的无参构造器");
	}
	public B(String name){
		System.out.println(name + "我是B类的有参构造器");
	}
}
class C extends B {//C类 继承B类
	public C(){
		System.out.println("我是C类的无参构造器");
	}
	public C(String name){
		super("haha");
		System.out.println(name + "我是C类的有参构造器");
	}
}
//main方法中: C c  = new C();输出什么内容
package com.zzpedu.extend_.exercise;

public class ExtendsExercise02 {
    public static void main(String[] args) {
        C c = new C();// 我是A类 haha我是B类的有参构造器 hello我是C类的有参构造器 我是C类的无参构造器
        System.out.println("-------------");
        C c2 = new C("cc");// 我是A类 haha我是B类的有参构造器 cc我是C类的有参构造器
    }
}
class A {//A类
    public A(){
        System.out.println("我是A类");
    }
}
class B extends A {//B类 继承A类
    public B(){
        System.out.println("我是B类的无参构造器");
    }
    public B(String name){
        //有默认的 super();
        System.out.println(name + "我是B类的有参构造器");
    }
}
class C extends B {//C类 继承B类
    public C(){
        this("hello");
        System.out.println("我是C类的无参构造器");
    }
    public C(String name){
        super("haha");
        System.out.println(name + "我是C类的有参构造器");
    }
}
==========控制台输出==========
我是A类
haha我是B类的有参构造器
hello我是C类的有参构造器
我是C类的无参构造器
-------------
我是A类
haha我是B类的有参构造器
cc我是C类的有参构造器

Process finished with exit code 0

3、案例3

编写Computer类,包含CPU、内存、硬盘等属性,getDetails方法用于返回Computer的详细信息
编写PC子类,继承Computer类,添加特有属性【品牌brand】
编写NotePad子类,继承Computer类,添加特有属性【演示color】
编写Test类,在main方法创建PC和NotePad对象,分别给对象中特有的属性赋值,以及从Computer类继承的属性赋值,并使用方法打印输出信息。

package com.zzpedu.extend_.exercise;

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

    public Computer(String cpu, int memory, int disk) {
        this.cpu = cpu;
        this.memory = memory;
        this.disk = disk;
    }
    //返回Computer信息
    public String getDetails(){
        return "cpu=" + cpu + " memory=" + memory + " disk=" +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.zzpedu.extend_.exercise;
//编写PC子类,继承Computer类,添加特有属性【品牌brand】
public class PC extends Computer{

    private String brand;
    //这里IDEA 根据继承规则,自动把构造器的调用写好
    //这里也体现:继承设计基本思想,父类的构造器完成父类属性初始化
    //  子类的构造器完成子类属性初始化
    public PC(String cpu, int memory, int disk, String brand) {
        super(cpu, memory, disk);
        this.brand = brand;
    }
    public void printInfo(){
        System.out.println("PC信息如下");
        //System.out.println(getCpu() + getMemory() + getDisk());
        //调用父类的getDetails()方法,得到相关属性信息
        System.out.println(getDetails() + " brand=" + brand);
    }
    public String getBrand() {
        return brand;
    }
    public void setBrand(String brand) {
        this.brand = brand;
    }
}
package com.zzpedu.extend_.exercise;
//编写NotePad子类,继承Computer类,添加特有属性【演示color】
public class NotePad extends Computer{
    private String color;

    public NotePad(String cpu, int memory, int disk, String color) {
        super(cpu, memory, disk);
        this.color = color;
    }
    public void printInfo(){
        System.out.println("NotePad信息如下");
        //System.out.println(getCpu() + getMemory() + getDisk());
        //调用父类的getDetails()方法,得到相关属性信息
        System.out.println(getDetails() + " color=" + color);
    }
    public String getColor() {
        return color;
    }
    public void setColor(String color) {
        this.color = color;
    }
}
package com.zzpedu.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("intel",16,500,"IBM");
        pc.printInfo();
        NotePad notePad = new NotePad("pad",8,128,"白色");
        notePad.printInfo();
    }
}
=======控制台输出============
PC信息如下
cpu=intel memory=16 disk=500 brand=IBM
NotePad信息如下
cpu=pad memory=8 disk=128 color=白色



super关键字

基本介绍

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


基本语法

1、访问父类的属性,但是不能访问父类的private属性
案例

super.属性名

2、访问父类的方法,不能访问父类的private方法
案例

super.方法名(参数列表);

3、方法父类的构造器;

super(参数列表); 只能放在构造器的第一句,只能出现一句!

package com.zzpedu.super_;

public class A {
    //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 test100(){
    }
    protected void test200(){
    }
    void test300(){
    }
    private void test400(){
    }
}
package com.zzpedu.super_;

public class B extends A{
    //访问父类的属性,但是不能访问父类的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方法
    }
    //方法父类的构造器; super(参数列表); 只能放在构造器的第一句,只能出现一句!
    public B(){
        //super();
        //super("jack");
        super("jack",10);
    }
}

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

1、调用父类的构造器的好处(分工明确,父类属性由父类初始化,子类的属性由子类初始化

2、方子类中有父类中的成员(属性和方法)重名时,为了访问父类的成员必须通过super。如果没有重名,使用super、this、直接访问是一样的效果!

package com.zzpedu.super_;

public class A {
    public int n1 = 100;
    public void cal(){
        System.out.println("A类的cal()方法...");
    }
}
package com.zzpedu.super_;

public class B extends A{
    public int n1 = 888;

    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类
        // 提示:如果查找方法过程中,找到了,但不能访问(private), 则报错 cannot access
        // 如果查找方法过程中,没有找到,则提示方法不存在
        //cal();//第一种
        //this.cal();//第二种 ==> 等价于 cal();//第一种

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

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

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

public class SuperDetail {
    public static void main(String[] args) {
        B b = new B();
        b.sum();
    }
}
=======控制台输出=====
B类的sum()方法...
A类的cal()方法...
888
888
100



3、super的访问不限于直接父类,如果爷爷类和本类中有同名的成员,也可以使用super去访问爷爷的成员;如果多个基类(上级类)中都有同名的成员,使用super访问遵循就近原则。 A -> B -> C,当然也要遵守访问权限的相关规则

public class Base {//父类是Object
    public int n1 = 999;
    public int age = 111;
    public void cal(){
        System.out.println("Base类的cal()方法...");
    }
}
public class A extends Base{
//    public int n1 = 100;
//    private void cal(){
//        System.out.println("A类的cal()方法...");
//    }
}
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();
    }
}
package com.zzpedu.super_;

public class SuperDetail {
    public static void main(String[] args) {
        B b = new B();//子类对象
        b.test();
    }
}
=======控制台输出=====
super.n1=999
Base类的cal()方法...

super和this的比较

No.区别点thissuper
1访问属性访问本类中属性,如果本类
没有此属性则从父类中继承
从父类开始查找属性
2调用方法访问本类的方法,如果本类
没有此方法则从父类继承查找
直接从父类开始查找方法
3调用构造器调用本类构造器,必须放在
构造器的首行
调用父类的构造器,必须放在
子类构造器的首行
4特殊表示当前对象子类中访问父类对象



方法重写/覆盖(override)

基本介绍

简单来说:方法覆盖(重写)就是子类有一个方法,和父类的某个方法的名称、返回类型、参数一样,那么我们就说子类的这个方法覆盖了父类的那个方法


快速入门

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

class Dog extends Animal{
	public void cry(){
		System.out.println("小狗汪汪叫...");
	}
}
package com.zzpedu.override_;

public class Animal {
    public void cry(){
        System.out.println("动物叫唤...");
    }
}
public class Dog extends Animal{
    //1. 因为Dog 是 Animal的子类
    //2. Dog的 cry方法 和 Animal的 cry定义形式一样(名称、返回类型、参数一样)
    //3. 这时我们就说 Dog的cry方法,重写了Animal的cry方法
    public void cry(){
        System.out.println("小狗汪汪叫...");
    }
}
public class Override01 {
    public static void main(String[] args) {
        //演示方法重写的情况
        Dog dog = new Dog();
        dog.cry();
    }
}
=======控制台输出====
小狗汪汪叫...

注意事项和使用细节

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

1、子类的方法的形参列表、方法名称,要和父类方法的形参列表、方法名称完全一样。

2、子类方法的返回类型和父类方法返回类型一样,或者是父类的类型的子类,比如:返回类型是 Object,子类方法返回类型是String

public Object getInfo(){…} //父类方法
public String getInfo(){…} //子类方法

3、子类方法不能缩小父类的访问权限
public > protected > 默认 > private

void sayOk(){…} //父类方法
public void sayOk(){…} //子类方法

package com.zzpedu.override_;

public class Animal {

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


public class Dog extends Animal{
    //细节:子类方法的返回类型和父类方法返回类型一样,
    //  或者是父类的类型的子类,比如:返回类型是 Object,
    //                          子类方法返回类型是String
    public String m1(){
        return null;
    }
    //这里Object 不是 Animal.m2()方法返回值String的子类,因此编译错误
//    public Object m2(){
//        return null;
//    }
    public BBB m3(){
        return null;
    }
    //细节:子类方法不能缩小父类的访问权限
    // public --> protected --> 默认 --> private
    public void eat(){

    }
}
class AAA { }
class BBB extends AAA{ }

练习

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

名称范围方法名形参列表返回类型修饰符
重载(overload)本类必须一样类型,个数或者顺序
至少有一个不同
无要求无要求
重写(override)父子类必须一样相同子类重写的方法,
返回的类型和父类返回类型一致,
或者是其子类
子类方法不能缩小父类方法的访问范围

2、
1)编写一个Person类,包括属性/private(name,age),构造器,方法say(返回自我介绍的字符串)。
2)编写一个Student类,继承Person类,增加id,score属性/private,以及构造器,定义say方法(返回自我介绍的信息)
3)在main中,分别创建Person和Student对象,调用say方法输出自我介绍

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

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public String say(){
        return "name=" + name + " age=" + age;
    }
}
package com.zzpedu.override_;
//编写一个Student类,继承Person类,增加id,score属性/private,
// 以及构造器,定义say方法(返回自我介绍的信息)
public class Student extends Person{
    private int id;
    private double score;

    public Student(String name, int age, int id, double score) {
        super(name, age);//这里会调用父类的构造器
        this.id = id;
        this.score = score;
    }
    //say
    public String say(){//这里体现super的一个好处,代码复用。
        return super.say() + " id=" + id + " score=" + score;
    }
}
package com.zzpedu.override_;

public class OverrideExercise {
    public static void main(String[] args) {
        //在main中,分别创建Person和Student对象,调用say方法输出自我介绍
        Person person = new Person("jack",10);
        System.out.println(person.say());

        Student student = new Student("smith",20,123,100);
        System.out.println(student.say());
    }
}
========控制台输出======
name=jack age=10
name=smith age=20 id=123 score=100.0



3. 多态

问题描述:

请编写一个程序,Master类 中有一个 feed(食物)方法,可以完成 主人给动物喂食物的信息

在这里插入图片描述

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

package com.zzpedu.poly_;

public class Food {
    private String name;

    public Food(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
public class Fish extends Food{
    public Fish(String name) {
        super(name);
    }
}
public class Bone extends Food{
    public Bone(String name) {
        super(name);
    }
}
package com.zzpedu.poly_;

public class Animal {
    private String name;

    public Animal(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
public class Cat extends Animal{
    public Cat(String name) {
        super(name);
    }
}
public class Dog extends Animal{
    public Dog(String name) {
        super(name);
    }
}
package com.zzpedu.poly_;

public class Master {
    private String name;

    public Master(String name) {
        this.name = name;
    }
    //主人给小狗 喂食 骨头
    public void feed(Dog dog,Bone bone){
        System.out.println("主人:" + name + " 给 " + dog.getName() + " 吃 " + bone.getName());
    }
    //主人给小猫 喂食 黄花鱼
    public void feed(Cat cat,Fish fish){
        System.out.println("主人:" + name + " 给 " + cat.getName() + " 吃 " + fish.getName());
    }
    //如果动物越多,食物很多
    //===> feed 方法很多。不利于管理和维护
    //Pig -> Rice
    //Tiger -> meat ...
}
package com.zzpedu.poly_;

public class Poly01 {
    public static void main(String[] args) {
        Master tom = new Master("汤姆");
        Dog dog = new Dog("大黄");
        Bone bone = new Bone("大棒骨");

        tom.feed(dog,bone);

        Cat cat = new Cat("小花猫");
        Fish fish = new Fish("黄花鱼");
        tom.feed(cat,fish);
    }
}
=====控制台输出=====
主人:汤姆 给 大黄 吃 大棒骨
主人:汤姆 给 小花猫 吃 黄花鱼

传统的方法带来的问题是什么?如何解决?
问题是:代码的复用性不高,而且不利于代码维护
解决方案:引出我们讲解的 多态


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

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


多态的具体体现

1、方法的多态

重写和重载就体现多态

package com.zzpedu.poly_;

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() 方法被调用...");
    }
}
=====控制台输出=====
30
60
A say() 方法被调用...
B say() 方法被调用...

2、对象的多态(重点,核心)
重要的几句话:
(1)一个对象的编译类型和运行可以不一致
(2)编译类型在定义对象时,就确定了,不能改变
(3)运行类型是可以变化的
(4)编译类型看定义时 = 号 的左边,运行类型看 = 号的 右边

案例:

Animal animal = new Dog(); 【animal 编译类型Animal,运行类型是Dog】
animal = new Cat();【animal 的运行类型变成 Cat,编译类型仍然是 Animal】
package com.zzpedu.poly_.objectpoly_;

public class Animal {
    public void cry(){
        System.out.println("Animal cry() 动物在叫...");
    }
}
public class Cat extends Animal{
    public void cry() {
        System.out.println("Cat cry() 小猫在叫...");
    }
}
public class Dog extends Animal{
    public void cry() {
       System.out.println("Dog cry() 小狗在叫...");
    }
}
package com.zzpedu.poly_.objectpoly_;

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

        //animal 编译类型就是 Animal,运行类型 是就 Dog
        Animal animal = new Dog();
        //因为运行时,就执行到该行时,animal运行类型是Dog,所以cry就是Dog的cry
        animal.cry();//Dog cry() 小狗在叫...

        //animal 编译类型就是 Animal,运行类型 是就 Cat
        animal = new Cat();
        animal.cry();//Cat cry() 小猫在叫...
    }
}
=====控制台输出=====
Dog cry() 小狗在叫...
Cat cry() 小猫在叫...

多态快速入门

使用多态即机制来解决主人喂食物的问题

public class Master {
    private String name;

    public Master(String name) {
        this.name = name;
    }
    //使用多态机制,可以统一的管理主人喂食物的问题
    //animal 编译类型是Animal,可以指向(接收)Animal子类的对象
    //food by类型是Food,可以指向(接收)Food子类的对象
    public void feed(Animal animal,Food food){
        System.out.println("主人:" + name + " 给 " + animal.getName() + " 吃 " + food.getName());
    }
}

多态注意事项和细节讨论

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

多态的向上转型

1)本质:父类的引用指向了子类的对象
2)语法:父类类型 引用 = new 子类类型();
3)特点:编译类型看左边,运行类型看右边。
      可以调用父类中的所有成员(需遵守访问权限)
      不能调用子类中特有成员;
     最终运行效果看子类(运行类型)的具体实现!

package com.zzpedu.poly_.detail_;

public class Animal {
    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,你好!");
    }
}
public class Cat extends Animal{
    public void eat() {//方法重写
        System.out.println("猫吃鱼");
    }
    public void catchMouse(){//Cat特有的方法
        System.out.println("猫捉老鼠");
    }
}
package com.zzpedu.poly_.detail_;

public class PolyDetail {
    public static void main(String[] args) {
        //向上转型:父类的引用指向了子类的对象
        //语法:父类类型 引用 = new 子类类型();
        Animal animal = new Cat();
        Object obj = new Cat();//可以吗? 可以 Object 也是 Car的父类

        //向上转型调用方法的规则如下:
        //(1)可以调用父类中的所有成员(需遵守访问权限)
        //(2)但是不能调用子类中特有成员;
        //(#)因为在编译阶段,能调用哪些成员,是由编译类型来决定的
        //animal.catchMouse();//错误

        //($)最终运行效果看子类(运行类型)的具体实现!
        //   即调用方法时,按照从子类(运行类型)开始查找方法,然后调用,
        animal.eat();//猫吃鱼
        animal.run();//跑
        animal.show();//hello,你好!
        animal.sleep();//睡觉
        
        System.out.println("Ok~");
    }
}
========控制台输出=========
猫吃鱼
跑
hello,你好!
睡觉
Ok~

多态的向下转型

1)语法:子类类型 引用名 = (子类类型)父类引用;
2)只能强转父类的引用,不能强转父类的对象
3)要求父类的引用必须指向的是当前目标类型的对象
4)当向下转型后,可以调用子类类型中所以成员

package com.zzpedu.poly_.detail_;

public class Dog extends Animal{//Dog是Animal的子类
}
package com.zzpedu.poly_.detail_;

public class PolyDetail {
    public static void main(String[] args) {
        //希望调用 Cat的catchMouse()方法
        Animal animal = new Cat();
        //多态的向下转型
        //(1)语法:子类类型 引用名 = (子类类型)父类引用
        //Cat 编译类型是Cat,运行类型是Cat
        Cat cat = (Cat) animal;
        cat.catchMouse();//猫捉老鼠

        //(2)要求父类的引用必须指向的是当前目标类型的对象
        //Dog dog = (Dog) animal;//编译不报错 运行报错 ClassCastException
    }
}
========控制台输出=========
猫捉老鼠

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

package com.zzpedu.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;//属性
}
==========控制台输出================
10
20

instanceOf 比较操作符,用于判断对象的运行类型是否为XX类型或者XX类型的子类

package com.zzpedu.poly_.detail_;

public class PolyDetail03 {
    public static void main(String[] args) {
        BB bb = new BB();
        //instanceOf 比较操作符,用于判断对象的运行类型是否为XX类型或者XX类型的子类
        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{}//子类
==========控制台输出================
true
true
true
true
false
true

练习

1、请说出下面每条语言,哪些是正确的,哪里是错误的,为什么?

public class PolyExercise01{
	public static void main(String[] args){
		double d = 13.4;//ok
		long l = (long)d;//ok 强转
		System.out.println(l);//13
		int in = 5;//ok
		boolean b = (boolean)in;//错误 boolean -> int
		Object obj = "Hello";//ok,向上转型
		String objStr = (String)obj;//ok,向下转型
		System.out.println(objStr);// Hello

		Object objPri = new Integer(5);//ok,向上转型
		//错误ClassCastExcetpion,指向Integer的父类引用,转成String
		String str = (String)objPri;
		Integer str1 = (Integer)objPri;//ok,向下转型
	}
}

2、

class Base{//父类
	int count = 10;
	public void display(){
		System.out.println(this.count);
	}
}
class Sub extends Base{//子类
	int count = 20;
	public void display(){
		System.out.println(this.count);
	}
}
public class PolyExercise02{//主类
	public static void main(String[] args){
		Sub s = new Sub();
		System.out.println(s.count);//20 看编译类型Sub
		s.display();//20 看运行类型 Sub
		Base b = s;//向上转型
		System.out.println(b == s);//true   b = s
		System.out.println(b.count);//10 看编译类型 Base
		b.display();//20 看运行类型 Sub
	}
}

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

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

class A {//父类
	public int i = 10;
	public int sum(){
		return getI() + 10;
	}
	public int sum1(){
		return i + 10;
	}
	public int getI(){
		return i;
	}
}
class B extends A{//子类
	public int i = 20;
	public int sum(){
		return i + 20;
	}
	public int getI(){
		return i;
	}
	public int sum1(){
		return i + 10;
	}
}
//main方法中
A a = new B();//向上转型
System.out.println(a.sum());//40 看运行类型 B
System.out.println(a.sum1());//30 看运行类型

java的动态绑定机制
1、当调用对象方法的时候,该方法会和对象的内存地址/运行类型绑定
2、当调用对象属性时,没有动态绑定机制,哪里声明,那里使用

package com.zzpedu.poly_.dynamc_;

public class DynamicBinding {
    public static void main(String[] args) {
        //a 的编译类型是 A,运行类型是 B
        A a = new B();//向上转型
        System.out.println(a.sum());//30
        System.out.println(a.sum1());//20
    }
}
class A {//父类
    public int i = 10;
    //动态绑定机制:
    //(1)当调用对象方法的时候,该方法会和对象的内存地址/运行类型绑定
    public int sum(){//父类sum()
        return getI() + 10;//20 + 10 --> getI() 先找子类的getI()
    }
    //(2)当调用对象属性时,没有动态绑定机制,哪里声明,那里使用
    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;
//    }
}
==========控制台输出================
30
20

多态的应用

1)多态数组

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

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

package com.zzpedu.poly_.polyarr_;

public class Person {//父类
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public String say(){//返回名字和年龄
        return name + "\t" + age;
    }
    //setXX.getXX...
}

public class Student extends Person{//子类
    private double score;
    public Student(String name, int age, double score) {
        super(name, age);
        this.score = score;
    }
    //重写父类say方法
    public String say() {
        return "学生 " + super.say() + " score=" + score;
    }
    //setXX.getXX...
}

public class Teacher extends Person{//子类
    private double salary;

    public Teacher(String name, int age, double salary) {
        super(name, age);
        this.salary = salary;
    }
    //重写父类say方法
    public String say() {
        return "老师 " + super.say() + " salary=" + salary;
    }
    //setXX.getXX...
}
package com.zzpedu.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("jack",20);
        persons[1] = new Student("tom",18,100);
        persons[2] = new Student("smith",19,80.8);
        persons[3] = new Teacher("scott",30,20000);
        persons[4] = new Teacher("king",50,50000);
        //循环遍历多态数组,调用say方法
        for (int i = 0; i < persons.length; i++) {
            //perons[i] 编译类型是 Person ,运行类型是 根据时间情况由JVM来判断
            System.out.println(persons[i].say());//动态绑定机制
        }
    }
}
===========控制台输出======
jack	20
学生 tom	18 score=100.0
学生 smith	19 score=80.8
老师 scott	30 salary=20000.0
老师 king	50 salary=50000.0

应用升级:如何调用子类特有的方法,比如 Teacher 有一个 teach,Student 有一个 study 怎么调用?

public class Student extends Person{//子类
    //特有方法
    public void study(){
        System.out.println("学生 " + getName() + " 正在学习....");
    }
}
public class Teacher extends Person{//子类
    //特有方法
    public void teach(){
        System.out.println("老师 " + getName() + " 正在讲课...");
    }
}
package com.zzpedu.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("jack",20);
        persons[1] = new Student("tom",18,100);
        persons[2] = new Student("smith",19,80.8);
        persons[3] = new Teacher("scott",30,20000);
        persons[4] = new Teacher("king",50,50000);
        //循环遍历多态数组,调用say方法
        for (int i = 0; i < persons.length; i++) {
            //perons[i] 编译类型是 Person ,运行类型是 根据时间情况由JVM来判断
            System.out.println(persons[i].say());//动态绑定机制
            //这里使用 instanceof + 向下转型
            if(persons[i] instanceof Student){//判断persons[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("你的类型有误,请检查...");
            }
        }
    }
}
=============控制台输出======================
jack	20
学生 tom	18 score=100.0
学生 tom 正在学习....
学生 smith	19 score=80.8
学生 smith 正在学习....
老师 scott	30 salary=20000.0
老师 scott 正在讲课...
老师 king	50 salary=50000.0
老师 king 正在讲课...

2)多态参数

方法定义的形参类型为父类类型,实参类型允许为子类类型

应用实例1:前面的主人喂动物

应用实例2:

定义员工类Employee,包含姓名和月工资[private],以及计算年工资getAnnual的方法,普通员工和经理继承了员工,经理类多了奖金bonus属性和管理manage方法,普通员工类多了work方法,普通员工和经理类要求分别重写getAnnual方法

测试类中添加一个方法showEmpAnnual(Employee e),实现获取任何员工对象的年工资,并在main方法中调用该方法[e.getAnnual()]

测试类中添加一个方法,testWork,如果是普通员工,则调用work方法,如果是经理,则调用manage方法

package com.zzpedu.poly_.polyparameter_;

public class Employee {//父类
    private String name;
    private double salary;

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

    //setXX,getXX...
}
public class Manager extends Employee{//子类
    private double bonus;

    public Manager(String name, double salary, double bonus) {
        super(name, salary);
        this.bonus = bonus;
    }
    public void manage(){
        System.out.println("经理 " + getName() + " is managing");
    }
    //重写获取年薪的方法
    public double getAnnual() {
        return super.getAnnual() + bonus;
    }
    //setXX,getXX...
}

public class Worker extends Employee{//子类
    public Worker(String name, double salary) {
        super(name, salary);
    }
    public void work(){
        System.out.println("普通员工 " + getName() + " is working");
    }
    //重写父类的getAnnual方法
    public double getAnnual() {//因为普通员工没有其他收入,则直接调用父类方法
        return super.getAnnual();
    }
}
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("不做处理...");
        }
    }
}
========控制台输出========
30000.0
260000.0
普通员工 tom is working
经理 milan is managing



Object类详解

在这里插入图片描述

equals方法

== 和 equals 的对比

==是一个比较运算符

1、==:即可以判断基本类型,有可以判断引用类型

2、==:如果判断基本类型,判断的是值是否相等。

实例:int i =10; double d = 10.0;

3、==:如果判断引用类型,判断的是地址是否相等,即判断是不同一个对象

package com.zzpedu.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
        int num1 = 10;
        double num2 = 10.0;
        System.out.println(num1 == num2);//true
    }
}
class A extends B{}
class B{}

IDEA 如何查看 JDK 源码:

1、一般来说 IDEA 配置好 JDK 以后,jdk 的源码也就自动配置好了
2、如果没有的话,点击菜单 File -> Project Structure -> SDKs -> Sourcepath 然后点击右侧绿色的加号

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

3、在需要查看某个方法源码时,将光标放在该方法,输入 ctrl + b 即可
或者在该方法上,点击右键 -> go to -> Declaration or …

在这里插入图片描述

4、equals:是Object类中的方法,只能判断引用类型,

5、默认判断是地址是否相等,子类中往往重写该方法,用于判断内容是否相等。比如:Integer,String

源码:

package java.lang;
public class Object {
	//即Object 的equals 方法默认就是比较对象的地址是否相等
	// 也就是判断两个对象是不是同一个对象。
	public boolean equals(Object obj) {
        return (this == obj);
    }
}
package java.lang;
public final class Integer extends Number implements Comparable<Integer> {
	
	// 从源码可以看到 Integer 也重写了Object的equals方法,
	//变成了判断两个值是否相等
    public boolean equals(Object obj) {
        if (obj instanceof Integer) {
            return value == ((Integer)obj).intValue();
        }
        return false;
    }
}
package java.lang;
public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    //String类的 equals方法
    //把Object的equals方法重写了,变成了比较两个字符串值是否相同
	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
    }
}
Integer n1 = new Integer(1000);
Integer n2 = new Integer(1000);
System.out.println(n1 == n2);//false
System.out.println(n1.equals(n2));//true

String str1 = new String("zzpedu");
String str2 = new String("zzp");
System.out.println(str1 == str2);//false
System.out.println(str1.equals(str2));//true

如何重写equals方法

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

package com.zzpedu.object_;

public class EqualsExercise01 {
    public static void main(String[] args) {
        Person person1 = new Person("jack",20,'男');
        Person person2 = new Person("jack",20,'男');

        System.out.println(person1.equals(person2));//true
    }
}
//应用实例:判断两个Person类对象的内容是否相等,
// 如果两个Person对象的各个属性值都一样,则返回true,反之false
class Person {//extends Object
    private String name;
    private int age;
    private char gender;

    public Person(String name, int age, char gender) {
        this.name = name;
        this.age = age;
        this.gender = gender;
    }
    //重写Object 的 equals方法
    public boolean equals(Object obj) {
        //判断如果比较的两个对象时是同一个对象,则直接返回true
        if(this == obj){
            return true;
        }
        //类型判断
        if(obj instanceof Person){//是Person,我们才比较
            //进行 向下转型,因为需要得到obj的 各个属性
            Person p = (Person) obj;
            return this.name.equals(p.name) && this.age == p.age && this.gender == p.gender;
        }
        //如果不是Person,则直接返回false
        return false;
    }
    //setXx getXx..
}

练习

1、

class Person{
	public String name;
}


Person p1 = new Person();
p1.name = "zzpedu";

Person p2 = new Person();
p2 = "zzpedu";

System.out.println(p1 == p2);//false
System.out.println(p1.name.equals(p2.name));//true
System.out.println(p1.equals(p2));//false

String s1 = new String("asdf");

String s2 = new String("asdf");
System.out.println(s1.equals(s2));//true
System.out.println(s1 == s2);//false

2、

int it = 65;
float fl = 65.0f;
System.out.println("65和65.0f是否相等?" + (it == fl));//true
char ch1 = 'A'; char ch2 = 12;
System.out.println("65和 'A' 是否相等?" + (it == ch1));//true
System.out.println("12和 ch2 是否相等?" + (12 == ch2));//true

String str1 = new String("hello");
String str2 = new String("hello");
System.out.println("str1 和 str2是否相等?"  + (str1 == str2));//false

System.out.println("str1 是否equals str2?"  + (str1.equals(str2)));//true
System.out.println("hello"  == new java.sql.Date());//编译报错

hashCode方法

在这里插入图片描述

1)提高具有哈希结构的容器的效率!
2)两个引用,如果指向的是同一个对象,则哈希值肯定一样的!
3)两个引用,如果指向的是不同的对象,则哈希值是不一样的
4)哈希值主要根据地址号来的!不能完全将哈希值等价于地址。

package com.zzpedu.object_;

public class HashCode_ {
    public static void main(String[] args) {
        AA aa = new AA();
        AA aa2 = new AA();
        //两个引用,如果指向的是不同的对象,则哈希值是不一样的
        System.out.println("aa.hashCode()=" + aa.hashCode());
        System.out.println("aa2.hashCode()=" + aa2.hashCode());
        //两个引用,如果指向的是同一个对象,则哈希值肯定一样的!
        AA aa3 = aa;
        System.out.println("aa3.hashCode()=" + aa3.hashCode());
    }
}
class AA{}
=============控制台输出=============
aa.hashCode()=460141958
aa2.hashCode()=1163157884
aa3.hashCode()=460141958

toString方法

在这里插入图片描述

基本介绍
默认返回:全类名 + @ + 哈希值的十六进制(hashCode),【查看Object 的 toString方法】
子类往往重写toString方法,用于返回对象的属性信息

源码:

package java.lang;
public class Object {
	//Object 的toString方法 源码
	//(1)getClass().getName() 类的全类名(包名 + 类名)
	//(2)Integer.toHexString(hashCode()) 将该对象的hashCode值转换成十六进制字符串
    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
}
package com.zzpedu.object_;

public class ToString {
    public static void main(String[] args) {
        Monster monster = new Monster("小妖怪", "巡山的", 1000);
        System.out.println(monster.toString() + " hashcode=" + monster.hashCode());
    }
}
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;
    }
}
=============控制台输出=============
com.zzpedu.object_.Monster@1b6d3586 hashcode=460141958

重写toString方法,打印对象或拼接对象时,都会自动调用该对象的toString形式。案例:Monster[name, job, sal]

package com.zzpedu.object_;

public class ToString {
    public static void main(String[] args) {
        Monster monster = new Monster("小妖怪", "巡山的", 1000);
        System.out.println(monster.toString() + " hashcode=" + monster.hashCode());
    }
}
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 +
                '}';
    }
}
=============控制台输出=============
Monster{name='小妖怪', job='巡山的', sal=1000.0} hashcode=460141958

当直接输出一个对象时,toString 方法会被默认的调用

System.out.println(monster); 就会默认调用 monster.toString()


finalize方法

在这里插入图片描述

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

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

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

package com.zzpedu.object_;

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

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



断点调试(debug)

一个实际需求

1、在开发中,新手程序员在查找错误时,这时老程序员就会温馨提示,可以断点调试,一步一步的看源码执行过程,从而发现错误所在

2、重要提示:在断点调试 过程中,是运行状态,是以对象的 运行类型来执行的。

A extends B; B b = new A(); b.xx();


断点调试介绍

1、断点调试是指在程序的某一行设置一个断点,调试时,程序运行到这一行就会停住,然后你可以一步一步往下调试,调试过程中可以看各个变量当前的值,出错的话,调试到出错的代码即显示错误,停下。进行分析从而找出这个Bug

2、断点调试是程序员必须掌握的技能。

3、断点调试也能帮助我们查看java底层源代码的执行过程,提高程序员的Java水平。


断点调试的快捷键:

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

F7:跳入方法内
F8:逐行执行代码
shift + F8:跳出方法

在这里插入图片描述


断点调试应用案例

案例1

看一下变量的变化情况等

package com.zzpedu.debug_;

public class Debug01 {
    public static void main(String[] args){
        //演示逐行执行 快捷键 F8
        int sum = 0;
        for(int i = 0; i < 5; i++){
            sum += i;
            System.out.println("i =" + i);
            System.out.println("sum =" + sum);
        }
        System.out.println("退出for...");
    }
}

截图 + 说明 F8 逐行执行代码…

下断点
在这里插入图片描述
debug运行:
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
控制台输出:
在这里插入图片描述

在这里插入图片描述

案例2

package com.zzpedu.debug_;

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

截图 + 说明

在这里插入图片描述
再按 F8 退出 控制台输出错误信息
在这里插入图片描述

案例3
演示如何追踪源码,看看java设计者是怎么实现的。
小技巧:将光标放在某个变量上,可以看到最新的数据

package com.zzpedu.debug_;

import java.util.Arrays;

public class Debug03 {
    public static void main(String[] args){
        //debug 源码
        int arr[] = {1, -1, 10, -20, 100};
        //排序 追人sort源码 F7(跳入)
        Arrays.sort(arr);
        for(int i = 0; i < arr.length; i++){
            System.out.print(arr[i] + "\t");
        }
    }
}

截图 + 说明 F7(跳入)

在这里插入图片描述
如果进入不了
1.使用强制进入 (force step into) --> alt + shift + F7

在这里插入图片描述
2、配置一下
Setting --> Build,Execution,Deployment --> Debugger --> Stepping

把 Do bot step into the classes中的 java.*, javax.* 取消勾选,其他随意

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

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

案例4

演示如何直接执行到下一个断点 F9 resume
小技巧:断点可以在debug过程中,动态的下断点

package com.zzpedu.debug_;

import java.util.Arrays;
//演示执行到下一个断点,同时支持动态的下断点
public class Debug04 {
    public static void main(String[] args){
        //debug 源码
        int arr[] = {1, -1, 10, -20, 100};
        //排序 追人sort源码 F7(跳入)
        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");
    }
}

截图 + 说明

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述


断点调试练习

1、使用断点调试的方法,追踪下一个对象创建的过程。
Person[name, age, 构造器]

package com.zzpedu.debug_;

//debug对象创建的过程,加深对调试的理解
public class DebugExercise {
    public static void main(String[] args) {
        //创建对象的流程
        //(1)加载 Person对象
        //(2)初始化
        // 2.1 默认初始化
        // 2.2 显示初始化
        // 2.3 构造器初始化
        //(3)返回对象的地址
        Person jack = new Person("jack", 20);
        System.out.println(jack);
    }
}
class Person{
    private String name;
    private int age;

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

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

在这里插入图片描述
加载类
在这里插入图片描述

在这里插入图片描述
构造器初始化

在这里插入图片描述

在这里插入图片描述
进入toString方法

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

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

2、我们使用断点调试,查看动态绑定机制的如何工作

在这里插入图片描述

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

在这里插入图片描述

在这里插入图片描述





作业

1、定义一个Person类 {name,age,job},初始化Person 对象数组,有3个person对象,并按照 age 从 大到小 进排序,使用冒泡排序

package com.zzpedu.homework;

public class Homework01 {
    public static void main(String[] args) {
        //初始化Person 对象数组 有3个person对象
        Person[] persons = new Person[3];
        persons[0] = new Person("jack",10,"JavaEE工程师");
        persons[1] = new Person("tom",50,"大数据工程师");
        persons[2] = new Person("mary",35,"PHP工程师");

        //输出当前对象数组
        for (int i = 0; i < persons.length; i++) {
            System.out.println(persons[i]);//默认调用对象的 toString方法
        }

        //使用冒泡排序
        Person person = null;//临时变量,用于交换
        for (int i = 0; i < persons.length - 1; i++) {//外出循环
            for (int j = 0; j < persons.length -1 -i; j++) {//内层循环
                //并按照 age 从 大到小 进排序,如果前面的人的年龄小age < 后面人的年龄,即交换
                if(persons[j].getAge() < persons[j+1].getAge()){
                   person =  persons[j+1];
                   persons[j + 1] = persons[j];
                   persons[j] = person;
                }
            }
        }
        System.out.println("=====排序后的对象数组====");
        for (int i = 0; i < persons.length; i++) {
            System.out.println(persons[i]);//默认调用对象的 toString方法
        }
    }
}
/*
定义一个Person类 {name,age,job},初始化Person 对象数组,
有3个person对象,并按照 age 从 大到小 进排序,使用冒泡排序
*/
class Person{
    private String name;
    private int age;
    private String job;

    public Person(String name, int age, String job) {
        this.name = name;
        this.age = age;
        this.job = job;
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", job='" + job + '\'' +
                '}';
    }
    public int getAge() {
        return age;
    }
}
===============控制台输出===============
Person{name='jack', age=10, job='JavaEE工程师'}
Person{name='tom', age=50, job='大数据工程师'}
Person{name='mary', age=35, job='PHP工程师'}
=====排序后的对象数组====
Person{name='tom', age=50, job='大数据工程师'}
Person{name='mary', age=35, job='PHP工程师'}
Person{name='jack', age=10, job='JavaEE工程师'}

2、写出四种访问修饰符和各自访问权限

访问级别访问控制修饰符同类/本类同包子类不同包
公开public
受保护protected×
默认没有修饰符××
私有private×××

3、编写老师类

(1)要求有属性 “姓名name”,“年龄age”,“职称post”,“基本工资salary”

(2)编写业务方法,introduce(),实现输出一个教师的信息。

(3)编写教师类的三个子类:教授类(Professor),副教授类,讲师类。工资级别分别是:教授为1.3、副教授为1.2、讲师类1.1。在三个子类里面都重写父类的introduce()方法

(4)定义并初始化一个老师对象,调用业务方法,实现对象基本信息的后台打印

package com.zzpedu.homework;

/*
 * (1)要求有属性 “姓名name”,“年龄age”,“职称post”,“基本工资salary”
 * (2)编写业务方法,introduce(),实现输出一个教师的信息。
 */
public class Teacher {
    private String name;
    private int age;
    private String post;
    private double salary;
    //这里我们在增加一个工资级别
    private double grade;

    public Teacher(String name, int age, String post, double salary, double grade) {
        this.name = name;
        this.age = age;
        this.post = post;
        this.salary = salary;
        this.grade = grade;
    }
    public void introduce(){
        System.out.println("name: " + name + " age: " + age + " post: " + post
            + " salary: " + salary + " grade: " + grade);
    }
}
//子类
public class Professor extends Teacher {

    public Professor(String name, int age, String post, double salary, double grade) {
        super(name, age, post, salary, grade);
    }

    @Override
    public void introduce() {
        System.out.println(" 这是教授的信息 ");
        super.introduce();
    }
}

public class Homework03 {
    public static void main(String[] args) {
        Professor professor = new Professor("贾宝玉",10,"高级职称",30000,1.3);
        professor.introduce();
    }
}
=======控制台输出=======
 这是教授的信息 
name: 贾宝玉 age: 10 post: 高级职称 salary: 30000.0 grade: 1.3

4、通过继承实现员工工资打印功能

父类:员工类(Employee)
子类:部门经理类(Manager)、普通员工类(Worker)

(1) 部门经理工资=1000 + 当日工资*天数*等级 (1.2) =》奖金 + 基本工资

(2) 普通员工工资= 当日工资*天数*等级 (1.0) =》 基本工资

(3) 员工属性:姓名、单日工资,工作天数

(4) 员工方法(打印工资)

(5) 普通员工及部门经理都是员工子类,需要重写打印工资方法

(6) 定义并初始化普通员工对象,调用打印工资方法输出工资,定义并初始化部门经理对象,调用打印工资方法输出工资

package com.zzpedu.homework;

public class Employee {
    //属性
    //员工属性:姓名、单日工资,工作天数
    private String name;
    private double daySal;
    private int workDays;
    //分析出还有一个属性 等级
    private double grade;

    //方法[构造器 getter setter]
    public Employee(String name, double daySal, int workDays, double grade) {
        this.name = name;
        this.daySal = daySal;
        this.workDays = workDays;
        this.grade = grade;
    }
    // 打印工资方法
    //方法名 void printSal()
    public void printSal(){
        System.out.println(name + " 工资=" + (daySal * workDays * grade));
    }
    public String getName() {
        return name;
    }
    public double getDaySal() {
        return daySal;
    }
    public int getWorkDays() {
        return workDays;
    }
    public double getGrade() {
        return grade;
    }
}
public class Manager extends Employee{
    //特有属性 奖金
    private double bonus;
    //创建Manager对象时,奖金是多少并不是确定的,因此在构造器中,不给bonus
    //,可以通过setBonus
    public Manager(String name, double daySal, int workDays, double grade) {
        super(name, daySal, workDays, grade);
    }
    //方法:重写父类的 printSal
    @Override
    public void printSal() {
        //因为经理的工资计算方式和Employee不一样,所以我们重写
        System.out.println("经理 " + getName() + " 工资=" +
                (bonus + getDaySal() * getWorkDays() * getGrade()));
    }
    public void setBonus(double bonus) {
        this.bonus = bonus;
    }
}
public class Worker extends Employee{
    //分析普通员工没有特有的属性
    public Worker(String name, double daySal, int workDays, double grade) {
        super(name, daySal, workDays, grade);
    }
    //方法:重写父类的 printSal
    @Override
    public void printSal() {//因为普通员工和Employee输出工资情况一样,所以直接调用父类的printSal()
        System.out.print("普通员工 ");//自己的输出信息
        super.printSal();//调用父类的方法,复用代码
    }
}
public class Homework04 {
    public static void main(String[] args) {
        Manager manager = new Manager("刘备",100,20,1.2);
        //设置奖金
        manager.setBonus(3000);
        //打印经理的工资情况
        manager.printSal();
        Worker worker = new Worker("关羽",80,10,1.0);
        worker.printSal();
    }
}
=======控制台输出=======
经理 刘备 工资=5400.0
普通员工 关羽 工资=800.0

5、设计父类 - 员工类。子类:工人类(Worker),农民类(Peasant),教师类(Teacher),科学类(Scientist),服务生(Waiter)类等。

(1) 其中工人,农民,服务生只有基本工资 sal
(2) 教师除基本工资外,还有课酬(元/天) classDay,classSal
(3) 科学家除基本工资外,还有年终奖 bonus
(4) 编写一个测试类,将各种类型的员工的全年工资打印出来

package com.zzpedu.homework.homework5;

public class Employee {//父类
    //属性
    private String name;
    private double sal;
    //分析有一个带薪的月份 13薪,15薪,12薪
    private int salMonth = 12;//默认12个月
    //方法
    public Employee(String name, double sal) {
        this.name = name;
        this.sal = sal;
    }
    //打印全年工资
    public void printSal(){
        System.out.println(name + " 年工资=" + (sal * salMonth));
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public double getSal() {
        return sal;
    }
    public void setSal(double sal) {
        this.sal = sal;
    }
    public int getSalMonth() {
        return salMonth;
    }
    public void setSalMonth(int salMonth) {
        this.salMonth = salMonth;
    }
}
public class Worker extends Employee{//子类
    // 其中工人,农民,服务生只有基本工资 sal
    public Worker(String name, double sal) {
        super(name, sal);
    }
    @Override
    public void printSal() {
        System.out.print("工人 ");
        super.printSal();//使用父类的printSal()方法
    }
}
public class Peasant extends Employee{//子类
    // 其中工人,农民,服务生只有基本工资 sal
    public Peasant(String name, double sal) {
        super(name, sal);
    }
    @Override
    public void printSal() {
        System.out.print("农民 ");
        super.printSal();//使用父类的printSal()方法
    }
}
public class Waiter extends Employee{//子类
    public Waiter(String name, double sal) {
        super(name, sal);
    }
    @Override
    public void printSal() {
        System.out.print("服务生 ");
        super.printSal();//使用父类的printSal()方法
    }
}
public class Teacher extends Employee{//子类
    //特有属性
    private int classDay;//一年上课次数
    private double classSal;//课时费
    public Teacher(String name, double sal) {
        super(name, sal);
    }
    //方法-重写printSal
    @Override
    public void printSal() {//老师不能复用super.printSal()
        System.out.print("老师 ");
        System.out.println(getName() + " 年工资=" +
                (getSal() * getSalMonth() + classSal * classDay));
    }

    public int getClassDay() {
        return classDay;
    }
    public void setClassDay(int classDay) {
        this.classDay = classDay;
    }
    public double getClassSal() {
        return classSal;
    }
    public void setClassSal(double classSal) {
        this.classSal = classSal;
    }
}
public class Scientist extends Employee{//子类
    //特有属性
    private double bonus;//年终奖
    public Scientist(String name, double sal) {
        super(name, sal);
    }
    //重写年工资打印
    @Override
    public void printSal() {
        System.out.print("科学家 ");
        System.out.println(getName() + " 年工资=" + (getSal() * getSalMonth() + bonus));
    }
    public double getBonus() {
        return bonus;
    }
    public void setBonus(double bonus) {
        this.bonus = bonus;
    }
}
public class Homework05 {
    public static void main(String[] args) {
        Worker jack = new Worker("jack",1000);
        jack.setSalMonth(15);//灵活的修改带薪月份
        jack.printSal();
        Peasant smith = new Peasant("smith",2000);
        smith.printSal();
        //老师测试
        Teacher zzp = new Teacher("zzp", 2000);
        zzp.setClassDay(360);
        zzp.setClassSal(500);
        zzp.printSal();
        //科学家
        Scientist scientist = new Scientist("重男", 2000);
        scientist.setSalMonth(15);
        scientist.setBonus(100000);
        scientist.printSal();

        Waiter waiter = new Waiter("tom",5000);
        waiter.setSalMonth(13);
        waiter.printSal();
    }
}
=======控制台输出=======
工人 jack 年工资=15000.0
农民 smith 年工资=24000.0
老师 zzp 年工资=204000.0
科学家 重男 年工资=130000.0
服务生 tom 年工资=65000.0

6、 在父类和子类中通过this和super都可以调用哪些属性和方法,假定 Grand、Father 和 Son 在同一个包

class Grand{//超类
	String name = "AA";
	private int age = 100;
	public void g1(){}
}
class Father extends Grand{//父类
	String id = "001";
	private double score;
	public void f1(){
		//super可以访问哪些成员(属性和方法)?指Grand类并且不是private修饰的
		super.name; 
		super.g1();
		//this可以访问哪些成员?先本类后父类
		this.id;
		this.score;
		this.f1();
		this.name;
		this.g1();
	}
}

class Son extends Father{//子类
	String name = "BB";
	public void g1(){}
	private void show(){
		//super可以访问哪些成员(属性和方法)?
		super.id;
		super.f1();
		super.name; 
		super.g1();
		//this可以访问哪些成员?
		this.name;
		this.g1();
		this.show();
		this.id;
		this.f1();
	}
}

7、写出程序结果

class Test{//父类
	String name = "Rose";
	Test(){
		System.out.println("Test");
	}
	Test(String name){
		this.name = name;
	}
}
class Demo extends Test{//子类
	String name = "Jack";
	Demo(){
		super();
		System.out.println("Demo");
	}
	Demo(String s){
		super(s);
	}
	public void test(){
		System.out.println(super.name);
		System.out.println(this.name);
	}
	public static void main(String[] args){
		new Demo().test();//匿名对象
		new Demo("john").test();//匿名对象
	}
}
结果:
Test
Demo
Rose
Jack
-------
john
Jack
package com.zzpedu.homework;

public class Homework07 {
    public static void main(String[] args){
        new Demo().test();//匿名对象
        new Demo("john").test();//匿名对象
    }
}
class Test{//父类
    String name = "Rose";
    Test(){
        System.out.println("Test");//1 Test
    }
    Test(String name){//name = john
        this.name = name;//这里把父类的 name 修改成 john
    }
}
class Demo extends Test{//子类
    String name = "Jack";
    Demo(){
        super();
        System.out.println("Demo");//2 Demo
    }
    Demo(String s){
        super(s);
    }
    public void test(){
        System.out.println(super.name);//3 Rose 5 john
        System.out.println(this.name);//4 Jack  6 Jack
    }
}
====控制台输出=========
Test
Demo
Rose
Jack
john
Jack

8、扩展如下的BankAccount类

class BankAccount{
	private double balance;//余额
	public BankAccount(double initialBalance){
		this.balance = initialBalance;
	}
	//存款
	public void deposit(double amount){
		balance += amount;
	}
	//取款
	public void withdraw(double amount){
		balance -= amount;
	}
	//setter gettter..方法
}

要求:
(1) 在上面类的基础上扩展 新类 CheckingAccount 对每次存款和取款都收取1美元的手续费
(2) 扩展前一个练习的BankAccount类,新类SavingsAccount每个月都有利息产生(earnMonthlyInterest 方法被调用),并且有每月三此免手续费的存款或取款。在earnMonthlyInterest方法中重复设置交易次数

package com.zzpedu.homework;

/*
扩展前一个练习的BankAccount类,新类SavingsAccount每个月都有利息产生(earnMonthlyInterest 方法被调用),
并且有每月三此免手续费的存款或取款。在earnMonthlyInterest方法中重复设置交易次数
 */
public class SavingsAccount extends BankAccount{
    //分析
    //新增加属性
    private int count = 3;
    private double rate = 0.01;//利率

    public SavingsAccount(double initialBalance) {
        super(initialBalance);
    }
    public void earnMonthlyInterest(){//每个月初,我们统计上个月的利息,同时将count=3
        count = 3;
        super.deposit(getBalance() * rate);
    }
    @Override
    public void deposit(double amount) {//存款
        //判断是否还可以免手续费
        if(count > 0){
            super.deposit(amount);
        }else {
            super.deposit(amount - 1 );//手续费 1块钱转入银行
        }
        count--;//减去一次
    }
    @Override
    public void withdraw(double amount) {//取款
        //判断是否还可以免手续费
        if(count > 0){
            super.withdraw(amount);
        }else {
            super.withdraw(amount + 1 );//手续费 1块钱转入银行
        }
        count--;
    }
    public int getCount() {
        return count;
    }
    public void setCount(int count) {
        this.count = count;
    }
    public double getRate() {
        return rate;
    }
    public void setRate(double rate) {
        this.rate = rate;
    }
}
/*
 *在上面类的基础上扩展 新类 CheckingAccount 对每次存款和取款都收取1美元的手续费
 */
public class CheckingAccount extends BankAccount{//新的账号
    //没有新的属性
    public CheckingAccount(double initialBalance) {
        super(initialBalance);
    }
    @Override
    public void deposit(double amount) {//存款
        super.deposit(amount - 1);//巧妙的使用父类的deposit
        //1 块钱转入银行的账号
    }
    @Override
    public void withdraw(double amount) {//取款
        super.withdraw(amount + 1);
        //1 块钱转入银行的账号
    }
}
public class Homework08 {
    public static void main(String[] args) {
        CheckingAccount checkingAccount = new CheckingAccount(1000);
        checkingAccount.deposit(10);//1010 -1 = 1009
        checkingAccount.withdraw(9);//1009 - 9 - 1 = 999
        System.out.println(checkingAccount.getBalance());
        System.out.println("-------------------------");
        //测试SavingsAccount
        SavingsAccount savingsAccount = new SavingsAccount(1000);
        savingsAccount.deposit(100);
        savingsAccount.deposit(100);
        savingsAccount.deposit(100);
        System.out.println(savingsAccount.getBalance());//1300.0
        savingsAccount.deposit(100);
        System.out.println(savingsAccount.getBalance());//1399.0
        //月初,定时器自动调用一下 earnMonthlyInterest
        savingsAccount.earnMonthlyInterest();
        System.out.println(savingsAccount.getBalance());//1399.0 + 1399.0* 0.01 = 1399.0 + 13.99 = 1412.99
        savingsAccount.withdraw(100);//免手续费
        System.out.println(savingsAccount.getBalance());//1312.99 免手续费
        savingsAccount.withdraw(100);//免手续费
        savingsAccount.withdraw(100);//免手续费
        System.out.println(savingsAccount.getBalance());//1112.99
        savingsAccount.deposit(100);//扣手续费
        System.out.println(savingsAccount.getBalance());//1211.99
    }
}
========控制台输出========
999.0
-------------------------
1300.0
1399.0
1412.99
1312.99
1112.99
1211.99

9、设计一个Point类,其x和y坐标可以通过构造器提供。提供一个子类LabeledPoint,其构造器接受一个标签值和x,y坐标,比如:new LabeledPoint(“Black”, 1929, 230.07),写出对应的构造器即可

package com.zzpedu.homework;

public class Point {
    private double x;
    private double y;
    public Point(double x, double y) {
        this.x = x;
        this.y = y;
    }
}
public class LabeledPoint extends Point{
    //特有属性
    private String label;
    public LabeledPoint(String label, double x, double y) {
        super(x, y);
        this.label = label;
    }
}
public class Homework09 {
    public static void main(String[] args) {
        new LabeledPoint("Black", 1929, 230.07);
    }
}

10、编写Doctor类 {name, age, job, gender, sal} 相应的getter()和setter()方法,5个参数的构造器,重写父类(Object)的equals()方法:
public boolean equals(Object obj)
并判断测试类中创建两个对象是否相等。相等就是判断属性是否相同

package com.zzpedu.homework;

public class Doctor {
    //属性 {name, age, job, gender, sal}
    private String name;
    private int age;
    private String job;
    private char gender;
    private double sal;
    public Doctor(String name, int age, String job, char gender, double sal) {
        this.name = name;
        this.age = age;
        this.job = job;
        this.gender = gender;
        this.sal = sal;
    }
    //重写父类(Object)的equals()方法:
    public boolean equals(Object obj){
        if(this == obj){//判断两个比较对象是否相同
            return true;
        }
        //判断obj是否是 Doctor类型或者其子类
        if(! (obj instanceof Doctor)){//不是的话
            return false;
        }
        //向下转型,因为obj的运行类型是Doctor或者是其子类型
        Doctor doctor = (Doctor) obj;
        return this.name.equals(doctor.name) && this.age==doctor.age &&
                this.gender==doctor.gender && this.job.equals(doctor.getJob()) &&
                this.sal==doctor.sal;
    }
    //相应的getter()和setter()方法
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public int getAge() { return age; }
    public void setAge(int age) { this.age = age; }
    public String getJob() { return job; }
    public void setJob(String job) { this.job = job; }
    public char getGender() { return gender; }
    public void setGender(char gender) { this.gender = gender; }
    public double getSal() { return sal; }
    public void setSal(double sal) { this.sal = sal; }
}
public class Homework10 {
    public static void main(String[] args) {
        Doctor doctor1 = new Doctor("jack",20,"牙科医生",'男',20000);
        Doctor doctor2 = new Doctor("jack",20,"牙科医生",'男',20000);

        System.out.println(doctor1.equals(doctor2));//true
    }
}

11、现有Person类,里面有方法run、eat,Student类继承了Person类,并重写了run方法,自定义study方法,试写出对象向上转型和向下转型的代码,并写出各自都可以调用哪些方法,并写出方法输出什么?

class Person{//父类
	public void run(){
		System.out.println("person run");
	}
	public void eat(){
		System.out.println("person eat");
	}
}
class Student extends Person{//子类
		public void run(){
		System.out.println("student run");
	}
	public void study(){
		System.out.println("student study...");
	}
}
//向上转型 父类的引用指向子类对象
Person p = new Student();
p.run();//先找Student的run student run
p.eat();//子类没有 person eat
//向下转型:把指向子类对象的父类引用,转成指向子类对象的子类引用
Student stu = (Student) p;
stu.run();//student run
stu.study();//student study...
stu.eat();//person eat

12、说出 == 和 equals 的区别

名称概念用于基本类型用于引用类型
==比较运算符可以,判断值是否相等可以,判断两个对象是否相等
equalsObject类的方法,
Java类都可以使用equals
不可以可以,默认是判断两个对象是否相等,
但是子类往往重写该方法,比较对象的属性是否相等,比如(String,Integer)

13、打印效果如下:

老师的信息:
姓名:张飞
年龄:30
性别:男
工龄:5
我承诺,我会认真教课。
王飞爱玩象棋
----------------------------------
学生的信息:
姓名:小明
年龄:15
性别:男
学号:00023102
我承诺,我会好好学习
小明爱玩足球

题目描述:
(1)做一个Student类,Student类有名称(name),性别(sex),年龄(age),学号(stu_id),做合理封装,通过构造器在创建对象时将4个属性赋值

(2)写一个Teacher类,Teacher类有名称(name),性别(sex),年龄(age),工龄(work_age),做合理封装,通过构造器在创建对象时将4个属性赋值

(3)抽取一个父类Person类,将共同属性和方法放到Person类

(4)学生需要有学习的方法(study),在方法里写 “我承诺,我会好好学习。”

(5)教师需要有教学的方法(teach),在方法里写上 “我承诺,我会认真教学。”

(6)学生和教师都有玩的方法(play),学生会玩的是足球,老师玩的是象棋,此方法返回字符串,分别返回"xx爱玩足球"和"xx爱玩象棋"(其中xx分别代表学生和老师的姓名)。因为玩的方法名称都是一样的,所以要求此方法定义在父类中,子类实现重写

应当分析出,我们需要打印信息的方法,printInfo()

package com.zzpedu.homework.homework13;

/*
抽取一个父类Person类,将共同属性和方法放到Person类
 */
public class Person {//父类
    //属性
    private String name;
    private char sex;
    private int age;
    //构造器
    public Person(String name, char sex, int age) {
        this.name = name;
        this.sex = sex;
        this.age = age;
    }
    //编写一个 play方法,把共有的输出内容写到父类
    public String play(){
        return name + "爱玩";
    }
    //返回一个基本信息
    /*
    姓名:张飞
    年龄:30
    性别:男
     */
    public String basicInfo(){
        return "姓名: " + name + "\n年龄: " + age + "\n性别: " + sex;
    }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public char getSex() { return sex; }
    public void setSex(char sex) { this.sex = sex; }
    public int getAge() { return age; }
    public void setAge(int age) { this.age = age; }
}
/*
Teacher类有名称(name),性别(sex),年龄(age),工龄(work_age),
做合理封装,通过构造器在创建对象时将4个属性赋值
 */
public class Teacher extends Person{//子类
    //属性
    private int work_age;
    public Teacher(String name, char sex, int age, int work_age) {
        super(name,sex,age);
        this.work_age = work_age;
    }
    @Override
    public String play() {
        return super.play() + "象棋";
    }
    //教师需要有教学的方法(teach),在方法里写上 “我承诺,我会认真教学。
    public void teach(){
        System.out.println("我承诺,我会认真教学。");
    }
    //输出信息的方法
    public void printInfo(){
        System.out.println("老师的信息:");
        System.out.println(super.basicInfo());
        System.out.println("工龄: " + work_age);
        teach();//组合
        System.out.println(play());
    }
    public int getWork_age() { return work_age; }
    public void setWork_age(int work_age) { this.work_age = work_age; }
}
/*
Student类有名称(name),性别(sex),年龄(age),学号(stu_id),
做合理封装,通过构造器在创建对象时将4个属性赋值
 */
public class Student extends Person {//子类
    //属性
    private String stu_id;
    //构造器
    public Student(String name, char sex, int age, String stu_id) {
        super(name,sex,age);
        this.stu_id = stu_id;
    }
    @Override
    public String play() {
        return super.play() + "足球";
    }
    //学生需要有学习的方法(study),在方法里写 “我承诺,我会好好学习。
    public void study(){
        System.out.println("我承诺,我会好好学习。");
    }
    //编写一个输出信息的方法,这样体现封装
    public void printInfo(){
        System.out.println("学生的信息:");
        System.out.println(super.basicInfo());
        System.out.println("学号: " + stu_id);
        study();//组合
        System.out.println(play());
    }
    public String getStu_id() { return stu_id; }
    public void setStu_id(String stu_id) { this.stu_id = stu_id; }
}
package com.zzpedu.homework.homework13;

public class Homework13 {
    public static void main(String[] args) {
        //测试老师
        Teacher teacher = new Teacher("张飞",'男',30,5);
        teacher.printInfo();
        System.out.println("--------------------");
        //测试
        Student student = new Student("小明",'男',15,"00023102");
        student.printInfo();//封装
    }
}
===========控制台输出=============
老师的信息:
姓名: 张飞
年龄: 30
性别: 男
工龄: 5
我承诺,我会认真教学。
张飞爱玩象棋
----------------
学生的信息:
姓名: 小明
年龄: 15
性别: 男
学号: 00023102
我承诺,我会好好学习。
小明爱玩足球

(7)定义多态数组,里面保存2个学生和2个教师,要求按年龄从高到底排序,

(8)定义方法,形参为Person类型,功能:调用学生的study或教师的teach方法

public class Person {//父类
	//...
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", sex=" + sex +
                ", age=" + age +
                '}';
    }	
}
public class Teacher extends Person{//子类
	//...
	@Override
    public String toString() {
        return "Teacher{" +
                "work_age=" + work_age +
                '}' + super.toString();
    }
}
public class Student extends Person {//子类
	//...
	@Override
    public String toString() {
        return "Student{" +
                "stu_id='" + stu_id + '\'' +
                '}' + super.toString();
    }
}
public class Homework13 {
    public static void main(String[] args) {
       //定义多态数组,里面保存2个学生和2个教师,要求按年龄从高到底排序
        Person[] persons = new Person[4];
        persons[0] = new Student("jack",'男',10,"0001");
        persons[1] = new Student("mary",'女',20,"0002");
        persons[2] = new Teacher("smith",'男',36,5);
        persons[3] = new Teacher("scott",'男',26,1);
        Homework13 homework13 = new Homework13();
        homework13.bubbleSort(persons);
        System.out.println("======排序后的对象数组======");
        for (int i = 0; i < persons.length; i++) {
            System.out.println(persons[i]);
        }
        //遍历数组,调用test方法
        System.out.println("======------======");
        for (int i = 0; i < persons.length; i++) {
            homework13.test(persons[i]);
        }
    }
    //定义方法,形参为Person类型,功能:调用学生的study或教师的teach方法
    //分析这里会使用到向下转型和类型判断
    public void test(Person p){
        if(p instanceof Student){//p 的运行类型如果是 Student
            ((Student) p).study();
        }else if(p instanceof Teacher){
            ((Teacher) p).teach();
        }else {
            System.out.println("do nothing...");
        }
    }

    //方法,完成年龄从高到底排序
    public void bubbleSort(Person[] persons){
        Person tmp = null;
        for (int i = 0; i < persons.length - 1; i++) {
            for (int j = 0; j < persons.length - 1 - i; j++) {
                if(persons[j].getAge() < persons[j + 1].getAge()){
                    tmp = persons[j + 1];
                    persons[j + 1] = persons[j];
                    persons[j] = tmp;
                }
            }
        }
    }
}
===========控制台输出=============
======排序后的对象数组======
Teacher{work_age=5}Person{name='smith', sex=, age=36}
Teacher{work_age=1}Person{name='scott', sex=, age=26}
Student{stu_id='0002'}Person{name='mary', sex=, age=20}
Student{stu_id='0001'}Person{name='jack', sex=, age=10}
======------======
我承诺,我会认真教学。
我承诺,我会认真教学。
我承诺,我会好好学习。
我承诺,我会好好学习。

14、程序阅读, 在main 方法中,执行: C c = new C(); 输出什么内容?

class A {//超类
	public A(){
		System.out.println("我是A类");
	}
}
class B extends A{//父类
	public B(){
		System.out.println("我是B类的无参构造");
	}
	public B(String name){
		System.out.println(name + "我是B类的有参构造");
	}
}
class C extends B{//子类
	public C(){
		this("hello");
		System.out.println("我是C类的无参构造");
	}
	public C(String name){
		super("haha");
		System.out.println(name + "我是C类的有参构造");
	}
}
C c = new C(); 输出如下
我是A类
haha我是B类的有参构造
hello我是C类的有参构造
我是C类的无参构造

15、什么是多态,多态具体体现有哪些?(可举例说明)

多态:方法或者对象具有多种形态,是OOP的第三大特征,
	 是建立在封装和继承基础之上的
多态具体体现
1.方法多态
(1)重载体现多态 (2)重写体现多态
2.对象多态
(1)对象的编译类型和运行类型可以不一致,编译类型在定义时,就确定,不能变化
(2)对象的运行类型是可以变化的,可以通过getClass() 来查看运行类型
(3)编译类型看定义时 = 号的左边,运行类型看 = 号 右边
3.举例说明
public class Homework15 {
    public static void main(String[] args) {
        AAA obj = new BBB();//向上转型
        AAA b1 = obj;
        System.out.println("obj的运行类型:" + obj.getClass());//BBB
        obj = new CCC();//向上转型
        System.out.println("obj的运行类型:" + obj.getClass());//CCC
        obj = b1;
        System.out.println("obj的运行类型:" + obj.getClass());//BBB
    }
}
class AAA{ //超类
}
class BBB extends AAA{//父类
}
class CCC extends BBB{//子类
}
==========控制台输出=============
obj的运行类型:class com.zzpedu.homework.BBB
obj的运行类型:class com.zzpedu.homework.CCC
obj的运行类型:class com.zzpedu.homework.BBB

16、java的动态绑定机制是什么?

1.当调用对象的方法时,该方法会和对象的内存地址/运行类型绑定
2.当调用对象的属性时,没有动态绑定机制,哪里声明,哪里使用
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值