Java基础第三天【2】

该文档是:Luo学习Java笔记...(该文章有点长分为三部分)

博客连接:https://www.loveuluo.cn

日期:2020-10-24

6. 面向对象三大特征之继承

6.1 继承有什么用

在10.3 super内存图中也可详细了解到父类子类的关系!

image-20201227171046237

没用继承的代码:

// 分析以下程序存在什么问题?代码臃肿。代码没有得到重复利用。
public class ExtendsTest01{
    public static void main(String[] args){
        // 创建普通账户
        Account act = new Account();
        act.setActno("1111111");
        act.setBalance(10000);
        System.out.println(act.getActno() + ",余额" + act.getBalance());

        // 创建信用账户
        CreditAccount ca = new CreditAccount();
        ca.setActno("2222222");
        ca.setBalance(-10000);
        ca.setCredit(0.99);
        System.out.println(ca.getActno() + ",余额" + ca.getBalance() + ",信誉度" + ca.getCredit());
    }
}

// 银行账户类
// 账户的属性:账号、余额
class Account{
    // 属性
    private String actno;
    private double balance;

    // 构造方法
    public Account(){
    
    }
    public Account(String actno, double balance){
        this.actno = actno;
        this.balance = balance;
    }

    // setter and getter
    public void setActno(String actno){
        this.actno = actno;
    }
    public String getActno(){
        return actno;
    }
    public void setBalance(double balance){
        this.balance = balance;
    }
    public double getBalance(){
        return balance;
    }
}

// 其它类型的账户:信用卡账户
// 账号、余额、信誉度
class CreditAccount{
    // 属性
    private String actno;
    private double balance;
    private double credit;

    // 构造方法
    public CreditAccount(){
    
    }

    // setter and getter方法
    public void setActno(String actno){
        this.actno = actno;
    }
    public String getActno(){
        return actno;
    }
    public void setBalance(double balance){
        this.balance = balance;
    }
    public double getBalance(){
        return balance;
    }

    public void setCredit(double credit){
        this.credit = credit;
    }
    public double getCredit(){
        return credit;
    }
}

使用了继承之后的代码:

// 使用继承机制来解决代码复用问题。
// 继承也是存在缺点的:耦合度高,父类修改,子类受牵连。
public class ExtendsTest02{
    public static void main(String[] args){
        // 创建普通账户
        Account act = new Account();
        act.setActno("1111111");
        act.setBalance(10000);
        System.out.println(act.getActno() + ",余额" + act.getBalance());

        // 创建信用账户
        CreditAccount ca = new CreditAccount();
        ca.setActno("2222222");
        ca.setBalance(-10000);
        ca.setCredit(0.99);
        System.out.println(ca.getActno() + ",余额" + ca.getBalance() + ",信誉度" + ca.getCredit());
    }
}

// 银行账户类
// 账户的属性:账号、余额
class Account{ // 父类
    // 属性
    private String actno;
    private double balance;

    // 构造方法
    public Account(){
    
    }
    public Account(String actno, double balance){
        this.actno = actno;
        this.balance = balance;
    }

    // setter and getter
    public void setActno(String actno){
        this.actno = actno;
    }
    public String getActno(){
        return actno;
    }
    public void setBalance(double balance){
        this.balance = balance;
    }
    public double getBalance(){
        return balance;
    }
}

// 其它类型的账户:信用卡账户
// 账号、余额、信誉度
class CreditAccount extends Account{ //子类

    // 属性
    private double credit;

    // 构造方法
    public CreditAccount(){
    
    }

    public void doSome(){
        //错误: actno 在 Account 中是 private 访问控制
        //System.out.println(actno);
        // 间接访问
        //System.out.println(this.getActno());
        System.out.println(getActno());
    }

    // setter and getter方法
    public void setCredit(double credit){
        this.credit = credit;
    }
    public double getCredit(){
        return credit;
    }    
}

6.2 继承的相关特性

image-20201227173401153

6.3 通过子类对象调用继承过来的方法

image-20201227174508964

6.4 实际开发什么时候可以使用继承?

image-20201227174915896

6.5 println方法的解释

image-20201227175242767

代码解释:

image-20201227175834517

6.6 Object的toString方法

image-20201227191051383

代码测试:

public class ExtendsTest05 {

    // ExtendsTest05默认继承Object
    // ExtendsTest05类当中是有toString()方法
    // 不过toString()方法是一个实例方法,需要创建对象才能调用。
    /*
    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
    */
    
    public static void main(String[] args){

        // 分析这个代码可以执行吗?
        //ExtendsTest05.toString();

        // 先new对象
        ExtendsTest05 et = new ExtendsTest05();
        String retValue = et.toString();

        // 2f92e0f4 可以“等同”看做对象在堆内存当中的内存地址。
        // 实际上是内存地址经过“哈希算法”得出的十六进制结果。
        System.out.println(retValue); // ExtendsTest05@2f92e0f4

        // 创建对象
        Product pro = new Product();

        String retValue2 = pro.toString();
        System.out.println(retValue2); // Product@5305068a

        // 以上两行代码能否合并为一行!!!可以
        System.out.println(pro.toString()); //Product@5305068a

        // 如果直接输出“引用”呢???????
        System.out.println(pro); //Product@5305068a

        System.out.println(100);
        System.out.println(true);
        // Product@5305068a
        System.out.println(pro); // println方法会自动调用pro的toString()方法。
    }
}

class Product{
    /*
    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
    */
}

结论:

image-20201227191152700

7. 面向对象之方法的覆盖

7.1 方法覆盖初体验

image-20201227192831022

7.2 满足什么条件构成方法覆盖

image-20201227195953214

代码:

public class OverrideTest02{
    public static void main(String[] args){
        Bird b = new Bird();
        b.move();
        b.sing(1000); //Animal sing....

        Cat c = new Cat();
        c.move();
    }
}

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

    public void sing(int i){
        System.out.println("Animal sing....");
    }
}

class Bird extends Animal{

    // 对move方法进行方法覆盖,方法重写,override
    // 最好将父类中的方法原封不动的复制过来。(不建议手动编写)
    // 方法覆盖,就是将继承过来的那个方法给覆盖掉了。继承过来的方法没了。
    public void move(){
        System.out.println("鸟儿在飞翔!!!");
    }

    //protected表示受保护的。没有public开放。
    // 错误:正在尝试分配更低的访问权限; 以前为public
    /*
    protected void move(){
        System.out.println("鸟儿在飞翔!!!");
    }
    */

    //错误:被覆盖的方法未抛出Exception
    /*
    public void move() throws Exception{
        System.out.println("鸟儿在飞翔!!!");
    }
    */

    // 分析:这个sing()和父类中的sing(int i)有没有构成方法覆盖呢?
    // 没有,原因是,这两个方法根本就是两个完全不同的方法。
    // 可以说这两个方法构成了方法重载吗?可以。
    public void sing(){
        System.out.println("Bird sing.....");
    }
}

class Cat extends Animal{

    // 方法重写
    public void move(){
        System.out.println("猫在走猫步!!!");
    }
}

7.3 方法覆盖的经典案例

//方法覆盖比较经典的案例
//一定要注意:方法覆盖/重写的时候,建议将父类的方法复制粘贴,这样比较保险。
public class OverrideTest03{
    public static void main(String[] args){
        // 创建中国人对象
        // ChinaPeople p1 = new ChinaPeople("张三");// 错误原因:没有这样的构造方法
        ChinaPeople p1 = new ChinaPeople();
        p1.setName("张三");
        p1.speak();

        // 创建美国人对象
        // AmericPeople p2 = new AmericPeople("jack"); // 错误原因:没有这样的构造方法
        AmericPeople p2 = new AmericPeople();
        p2.setName("jack");
        p2.speak();
    }
}

// 人
class People{
    // 属性
    private String name;
    // 构造
    public People(){}
    public People(String name){
        this.name = name;
    }
    //setter and getter
    public void setName(String name){
        this.name = name;    
    }
    public String getName(){
        return name;
    }
    // 人都会说话
    public void speak(){
        System.out.println(name + "....");
    }
}

// 中国人
class ChinaPeople extends People{

    // 中国人说话是汉语
    // 所以子类需要对父类的speak()方法进行重写
    public void speak(){
        System.out.println(this.getName() + "正在说汉语");
    }
}


// 美国人
class AmericPeople extends People{
    // 美国人说话是英语
    // 所以子类需要对父类的speak()方法进行重写
    public void speak(){
        System.out.println(getName() + " speak english!");
    }
}

7.4 覆盖toString方法

image-20201227201014322

代码:

public class OverrideTest04{
    public static void main(String[] args){
        // 创建一个日期对象
        MyDate t1 = new MyDate();
        // 调用toString()方法(将对象转换成字符串形式。)
        // 问:你对这个输出结果满意吗?不满意,希望输出:xxxx年xx月xx日
        // 重写MyDate的toString()方法之前的结果
        //System.out.println(t1.toString()); //MyDate@28a418fc 

        // 重写MyDate的toString()方法之后的结果
        System.out.println(t1.toString());

        // 大家是否还记得:当输出一个引用的时候,println方法会自动调用引用的toString方法。
        System.out.println(t1);

        MyDate t2 = new MyDate(2008, 8, 8);
        System.out.println(t2); //2008年8月8日

        //创建学生对象
        Student s = new Student(1111, "zhangsan");
        // 重写toString()方法之前
        //System.out.println(s); //Student@87aac27
        // 重写toString()方法之后
        // 输出一个学生对象的时候,可能更愿意看到学生的信息,不愿意看到对象的内存地址。
        System.out.println(s.toString());
        System.out.println(s);
    }
}

// 日期类
class MyDate {
    private int year;
    private int month;
    private int day;
    public MyDate(){
        this(1970,1,1);
    }
    public MyDate(int year,int month,int day){
        this.year = year;
        this.month = month;
        this.day = day;
    }
    public void setYear(int year){
        this.year = year;
    }
    public int getYear(){
        return year;
    }
    public void setMonth(int month){
        this.month = month;
    }
    public int getMonth(){
        return month;
    }
    public void setDay(int day){
        this.day = day;
    }
    public int getDay(){
        return day;
    }

    // 从Object类中继承过来的那个toString()方法已经无法满足我业务需求了。
    // 我在子类MyDate中有必要对父类的toString()方法进行覆盖/重写。
    // 我的业务要求是:调用toString()方法进行字符串转换的时候,
    // 希望转换的结果是:xxxx年xx月xx日,这种格式。
    // 重写一定要复制粘贴,不要手动编写,会错的。
    public String toString() {
        return year + "年" + month + "月" + day + "日";
    }
}

class Student{
    int no;
    String name;
    public Student(int no, String name){
        this.no = no;
        this.name = name;
    }
    // 重写  方法覆盖
    public String toString() {
        return "学号:" + no + ",姓名:" + name;
    }
}

7.5 方法覆盖总结

image-20201227201108182

8. 面向对象三大特性之多态(非常重要)

8.1 向上向下转型和多态

理解:

image-20201228092458240

父类子类:

Animal类:

image-20201228093040982

Bird类:

image-20201228093050226

Cat类:

image-20201228093104189

Dog类:

image-20201228095043986

8.1.1 通过向上转型对多态理解

测试代码:

image-20201228094437822

8.1.2 通过向下转型对多态理解

测试代码:

image-20201228094900645

8.1.3 instanceof 运算符

image-20201228103339347

8.1.4 为什么要instanceof 判断

为什么要instanceof 判断?:

image-20201228104908606

因为:

例如这时候另一个程序员要调用我写的test()方法:

image-20201228105434381

而我不知道他要传入的是cat还是bird,但肯定是个animal:

image-20201228105507789

8.1.5 向上向下转型和多态总结

image-20201228110319811

8.2 多态在开发中的作用

8.2.1 例子:主人喂养宠物案例

没有使用多态的情况:

主人类代码:

image-20201228142107778

分析:

image-20201228142207232

经过分析之后修改代码增加多态:

添加一个猫或者狗等等的宠物父类:

image-20201228142313208

主人类代码(修改为使用宠物类):

image-20201228143203747

测试类(例如此时主人又想喂养鹦鹉了好处就是不需要修改Master类了,直接添加一个鹦鹉类继承Pet):

鹦鹉类(继承Pet类重写eat方法):

image-20201228142456925

狗类(同理继承Pet类):

image-20201228143928826

测试代码:

image-20201228143943990

8.2.2 多态在开发中的作用总结

image-20201228145132636

9. 学会多态后方法覆盖的深入

9.1 静态方法不存在方法覆盖

理解:

image-20201228150157010

代码:

9.2 私有方法无法覆盖

image-20201228150614640

9.3 总结

image-20201228150949302

10. 面相对象之super关键字

10.1 super概述

理解:

image-20201228152835646

代码:

image-20201228152909677

10.2 子类构造方法执行时必然调用父类构造方法

理解:

image-20201228153431765

代码:

image-20201228153511082

10.3 super(实参)的用法和内存图(重要)

super代表的是“当前对象(this)”的“父类型特征”。

理解:

代码:

虽然例如new new CreditAccount("1111", 10000.0, 0.999)在构造方法执行过程中一连串调用了父类的构造方法,父类的构造方法又继续向下调用它的父类的构造方法,但是实际上对象只创建了一个。只不过在创建的过程中有一些特征是父类的特征,父类型的特征相当于已经继承过来了,所以属于自己的了,详情查看内存图!!!

image-20201228160016779

测试程序的内存图(重要):

image-20201228164111535

10.4 super关键字小测试(重要)

代码:

image-20201228163537966

内存图(答案是一样):

image-20201228181404327

10.5 什么时候不能省略super

理解:

代码:

image-20201228171336822

内存图:

image-20201228171359763

10.6 super使用时后边必须要有个.

image-20201228172546502

10.7 使用super调用父类方法

理解:

image-20201228182620103ai

代码:

image-20201228182640710

10.8 总结super

image-20201228182712513

最后修改:2020 年 12 月 28 日 06 : 36 PM
如果觉得我的文章对你有用,请随意赞赏