前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >第七章 面向对象编程(进阶)

第七章 面向对象编程(进阶)

作者头像
捞月亮的小北
发布2023-12-01 08:47:00
1470
发布2023-12-01 08:47:00
举报
文章被收录于专栏:捞月亮的小北
image
image

1. this 关键字

1.1 this 的含义

  • 在 Java 中,this 关键字不算难理解,它的作用和其词义很接近。
    • 它在方法(准确的说是实例方法或非 static 的方法)内部使用,表示调用该方法的对象
    • 它在构造器内部使用,表示该构造器正在初始化的对象。
  • this 可以调用的结构:成员变量、方法和构造器

1.2 什么时候用 this

  1. 实例方法或构造器中使用当前对象的成员 使用 this 来区分成员变量和局部变量 ​
image
image

​​

  1. 同一个类中构造器互相调用 this 可以作为一个类中构造器相互调用的特殊格式。
    • this():调用本类的无参构造器
    • this(实参列表):调用本类的有参构造器

注意:

  • 不能出现递归调用。比如,调用自身构造器。
    • 推论:如果一个类中声明了 n 个构造器,则最多有 n - 1 个构造器中使用了"this(形参列表)"
  • this()和 this(实参列表)只能声明在构造器首行。
    • 推论:在类的一个构造器中,最多只能声明一个"this(参数列表)"

2. 面向对象特征二 继承(Inheritance)

2.1 继承性的理解

生活上:财产的继承、颜值的继承

代码层面:

  • 自上而下:定义了一个类 A,在定义另一个类 B 时,发现类 B 的功能与类 A 相似,考虑类 B 继承于类 A
  • 自下而上:定义了类 B,C,D 等,发现 B、C、D 有类似的属性和方法,则可以考虑将相同的属性和方法进行抽取,封装到类 A 中,让类 B、C、D 继承于类 A,同时,B、C、D 中的相似的功能就可以删除了。

有了继承性以后:

  • 子类就获取到了父类中声明的所有的属性和方法。
  • 但是,由于封装性的影响,可能子类不能直接调用父类中声明的属性或方法。
  • 子类在继承父类以后,还可以扩展自己特有的功能(体现:增加特有的属性、方法)
  • extends:延展、扩展、延伸
  • 子类和父类的理解,要区别于集合和子集
  • 不要为了继承而继承。在继承之前,判断一下是否有 is a 的关系。

默认的父类:

Java 中声明的类,如果没有显式的声明其父类时,则默认继承于 java.lang.Object

补充说明:

  • Java 是支持多层继承。
  • 概念:直接父类、间接父类
  • Java 中的子父类的概念是相对的。
  • Java 中一个父类可以声明多个子类。反之,一个子类只能有一个父类(Java 的单继承性)

2.2 继承性的好处

  • 继承的出现减少了代码冗余,提高了代码的复用性。
  • 继承的出现,更有利于功能的扩展。
  • 继承的出现让类与类之间产生了is-a​ 的关系,为多态的使用提供了前提。
    • 继承描述事物之间的所属关系,这种关系是:is-a​ 的关系。可见,父类更通用、更一般,子类更具体。

2.3 继承的格式

代码语言:javascript
复制
class A{
    //属性、方法
}

class B extends A{

}

继承中的基本概念:

  • 类 A: 父类、superClass、超类、基类
  • 类 B: 子类、subClass、派生类

3. 方法的重写

3.1 方法重写的要求

  1. 子类重写的方法必须​ 和父类被重写的方法具有相同的方法名称​、参数列表​。
  2. 子类重写的方法的返回值类型不能大于​ 父类被重写的方法的返回值类型。(例如:Student < Person)。

注意:如果返回值类型是基本数据类型和 void,那么必须是相同

  1. 子类重写的方法使用的访问权限不能小于​ 父类被重写的方法的访问权限。(public > protected > 缺省 > private)

注意:① 父类私有方法不能重写 ② 跨包的父类缺省的方法也不能重写

  1. 子类方法抛出的异常不能大于父类被重写方法的异常

此外,子类与父类中同名同参数的方法必须同时声明为非 static 的(即为重写),或者同时声明为 static 的(不是重写)。因为 static 方法是属于类的,子类无法覆盖父类的方法。

3.2 为什么需要方法的重写?

子类在继承父类以后,就获取了父类中声明的所有的方法。但是,父类中的方法可能不太适用于子类,换句话说,子类 需要对父类中继承过来的方法进行覆盖、覆写的操作。

举例(银行账户):

代码语言:javascript
复制
class Account{//账户
    double balance;//余额

    //取钱
    public void withdraw(double amt){
        //判断balance余额是否够amt取钱的额度
    }
}

class CheckAccount extends Account{ //信用卡
    double protectedBy;//透支额度


    public void withdraw(double amt){
        //判断balance余额是否够amt取钱的额度
        //如果不够,还可以考虑从protectedBy额度里取
    }
}

class AccountTest{
    public static void main(String[] args){
        CheckAccount acct = new CheckAccount();
        acct.withdraw(); //执行的是子类重写父类的方法
    }
}

3.3 区分方法的重载(overload)与重写(override / overwrite)

重载:“两同一不同”

重写:继承以后,子类覆盖父类中同名同参数的方法

4. 关键字 super

super 的理解:父类的

在 Java 类中使用 super 来调用父类中的指定操作:

  • super 可用于访问父类中定义的属性
  • super 可用于调用父类中定义的成员方法
  • super 可用于在子类构造器中调用父类的构造器

注意:

  • 尤其当子父类出现同名成员时,可以用 super 表明调用的是父类中的成员
  • super 的追溯不仅限于直接父类
  • super 和 this 的用法相像,this 代表本类对象的引用,super 代表父类的内存空间的标识

4.1 为什么需要 super?

  • 举例 1:子类继承父类以后,对父类的方法进行了重写,那么在子类中,是否还可以对父类中被重写的方法进行调用? 可以!
  • 举例 2:子类继承父类以后,发现子类和父类中定义了同名的属性,是否可以在子类中区分两个同名的属性? 可以!

4.2 如何调用?

使用 super 关键字即可。

4.3 super 调用构造器

① 子类继承父类时,不会继承父类的构造器。只能通过“super(形参列表)”的方式调用父类指定的构造器。

② 规定:“super(形参列表)”,必须声明在构造器的首行。

③ 我们前面讲过,在构造器的首行可以使用"this(形参列表)",调用本类中重载的构造器, 结合 ②,结论:在构造器的首行,"this(形参列表)" 和 "super(形参列表)"只能二选一。

④ 如果在子类构造器的首行既没有显示调用"this(形参列表)",也没有显式调用"super(形参列表)", 则子类此构造器默认调用"super()",即调用父类中空参的构造器。

⑤ 由 ③ 和 ④ 得到结论:子类的任何一个构造器中,要么会调用本类中重载的构造器,要么会调用父类的构造器。 只能是这两种情况之一。

⑥ 由 ⑤ 得到:一个类中声明有 n 个构造器,最多有 n-1 个构造器中使用了"this(形参列表)", 则剩下的那个一定使用"super(形参列表)"。

我们在通过子类的构造器创建对象时,一定在调用子类构造器的过程中,直接或间接的调用到父类的构造器。 也正因为调用过父类的构造器,我们才会将父类中声明的属性或方法加载到内存中,供子类对象使用。

4.4 super 调用属性、方法

子类继承父类以后,我们就可以在子类的方法或构造器中,调用父类中声明的属性或方法。(满足封装性的前提下)

如何调用呢?需要使用"super."的结构,表示调用父类的属性或方法。

一般情况下,我们可以考虑省略"super."的结构。但是,如果出现子类重写了父类的方法或子父类中出现了同名的属性时, 则必须使用"super."的声明,显式的调用父类被重写的方法或父类中声明的同名的属性。

4.5 this 和 super

this:当前对象

  • 在构造器和非静态代码块中,表示正在 new 的对象
  • 在实例方法中,表示调用当前方法的对象

super:引用父类声明的成员

2、this 和 super 的使用格式

  • this
    • this.成员变量:表示当前对象的某个成员变量,而不是局部变量
    • this.成员方法:表示当前对象的某个成员方法,完全可以省略 this.
    • this()或 this(实参列表):调用另一个构造器协助当前对象的实例化,只能在构造器首行,只会找本类的构造器,找不到就报错
  • super
    • super.成员变量:表示当前对象的某个成员变量,该成员变量在父类中声明的
    • super.成员方法:表示当前对象的某个成员方法,该成员方法在父类中声明的
    • super()或 super(实参列表):调用父类的构造器协助当前对象的实例化,只能在构造器首行,只会找直接父类的对应构造器,找不到就报错

5. 面向对象特征之三 : 多态

如何理解多态 :

  • 理解:理解为一个事物的多种形态。

生活举例:

女朋友:我想养一个宠物。 孩子:我想要一个玩具。 老板:张秘书,安排一个技术科的同事,跟我一起下周出差。

5.1 多态的形式和体现

5.1.1 对象的多态性:

多态性,是面向对象中最重要的概念,在 Java 中的体现:**对象的多态性:父类的引用指向子类的对象**

格式:(父类类型:指子类继承的父类类型,或者实现的接口类型)

代码语言:javascript
复制
父类类型 变量名 = 子类对象;

举例:

代码语言:javascript
复制
Person p = new Student();

Object o = new Person();//Object类型的变量o,指向Person类型的对象

o = new Student(); //Object类型的变量o,指向Student类型的对象

对象的多态:在 Java 中,子类的对象可以替代父类的对象使用。所以,一个引用类型变量可能指向(引用)多种不同类型的对象

5.1.2 多态的理解

Java 引用变量有两个类型:编译时类型​ 和运行时类型​。编译时类型由声明​ 该变量时使用的类型决定,运行时类型由实际赋给该变量的对象​ 决定。简称:编译时,看左边;运行时,看右边。

  • 若编译时类型和运行时类型不一致,就出现了对象的多态性(Polymorphism)
  • 多态情况下,“看左边”:看的是父类的引用(父类中不具备子类特有的方法) ** “看右边”:看的是子类的对象(实际运行的是子类重写父类的方法)**

多态的使用前提:① 类的继承关系 ② 方法的重写

5.2 多态的好处与弊端

弊端:

  • 在多态的场景下,我们创建了子类的对象,也加载了子类特有的属性和方法。但是由于声明为父类的引用, 导致我们没有办法直接调用子类特有的属性和方法。

好处:

  • 极大的减少了代码的冗余,不需要定义多个重载的方法。

举例:

代码语言:javascript
复制
class Account{
    public void withdraw(){} //取钱
}

class CheckAccount extends Account{ //信用卡
    //存在方法的重写
    public void withdraw(){} //取钱
}
class SavingAccount extends Account{ //储蓄卡
    //存在方法的重写
}

class Customer{
    Account account;

    public void setAccount(Account account){
        this.account = account;
    }

    public Account getAccount(){
        return accout;
    }

}


class CustomerTest{
    main(){
        Customer cust = new Customer();
        cust.setAccount(new CheckAccount());

        cust.getAccount().withdraw();

    }
}

5.3 向上转型和向下转型

首先,一个对象在 new 的时候创建是哪个类型的对象,它从头至尾都不会变。即这个对象的运行时类型,本质的类型用于不会变。但是,把这个对象赋值给不同类型的变量时,这些变量的编译时类型却不同。

为什么要类型转换:

因为多态,就一定会有把子类对象赋值给父类变量的时候,这个时候,在编译期间​,就会出现类型转换的现象。

但是,使用父类变量接收了子类对象之后,我们就不能调用​ 子类拥有,而父类没有的方法了。这也是多态给我们带来的一点"小麻烦"。所以,想要调用子类特有的方法,必须做类型转换,使得编译通过​。

image
image

  • 向上转型:当左边的变量的类型(父类) > 右边对象/变量的类型(子类),我们就称为向上转型
    • 此时,编译时按照左边变量的类型处理,就只能调用父类中有的变量和方法,不能调用子类特有的变量和方法了
    • 但是,运行时,仍然是对象本身的类型,所以执行的方法是子类重写的方法体。
    • 此时,一定是安全的,而且也是自动完成的

  • 向下转型:当左边的变量的类型(子类)<右边对象/变量的编译时类型(父类),我们就称为向下转型
    • 此时,编译时按照左边变量的类型处理,就可以调用子类特有的变量和方法了
    • 但是,运行时,仍然是对象本身的类型
    • 不是所有通过编译的向下转型都是正确的,可能会发生 ClassCastException,为了安全,可以通过 isInstanceof 关键字进行判断

5.4 如何向上或向下转型

向上转型:自动完成

向下转型:(子类类型)父类变量

代码语言:javascript
复制
package com.atguigu.polymorphism.grammar;

public class ClassCastTest {
    public static void main(String[] args) {
        //没有类型转换
        Dog dog = new Dog();//dog的编译时类型和运行时类型都是Dog

        //向上转型
        Pet pet = new Dog();//pet的编译时类型是Pet,运行时类型是Dog
        pet.setNickname("小白");
        pet.eat();//可以调用父类Pet有声明的方法eat,但执行的是子类重写的eat方法体
//        pet.watchHouse();//不能调用父类没有的方法watchHouse

	// 向下转型
        Dog d = (Dog) pet;
        System.out.println("d.nickname = " + d.getNickname());
        d.eat();//可以调用eat方法
        d.watchHouse();//可以调用子类扩展的方法watchHouse

        Cat c = (Cat) pet;//编译通过,因为从语法检查来说,pet的编译时类型是Pet,Cat是Pet的子类,所以向下转型语法正确
        //这句代码运行报错ClassCastException,因为pet变量的运行时类型是Dog,Dog和Cat之间是没有继承关系的
    }
}

5.5 instanceof 关键字

instanceof 的使用:

  1. 建议在向下转型之前,使用 instanceof 进行判断,避免出现类型转换异常
  2. 格式: a instanceOf A : 判断对象 a 是否是类 A 的实例。
  3. 如果 a instanceOf A 返回 true,则: a instanceOf superA 返回也是 true。其中,A 是 superA 的子类。

为了避免 ClassCastException 的发生,Java 提供了instanceof​ 关键字,给引用变量做类型的校验。如下代码格式:

代码语言:javascript
复制
//检验对象a是否是数据类型A的对象,返回值为boolean型
对象a instanceof 数据类型A
  • 说明:
    • 只要用 instanceof 判断返回 true 的,那么强转为该类型就一定是安全的,不会报 ClassCastException 异常。
    • 如果对象 a 属于类 A 的子类 B,a instanceof A 值也为 true。
    • 要求对象 a 所属的类与类 A 必须是子类和父类的关系,否则编译错误。

代码:

代码语言:javascript
复制
package com.atguigu.polymorphism.grammar;

public class TestInstanceof {
    public static void main(String[] args) {
        Pet[] pets = new Pet[2];
        pets[0] = new Dog();//多态引用
        pets[0].setNickname("小白");
        pets[1] = new Cat();//多态引用
        pets[1].setNickname("雪球");

        for (int i = 0; i < pets.length; i++) {
            pets[i].eat();

            if(pets[i] instanceof Dog){
                Dog dog = (Dog) pets[i];
                dog.watchHouse();
            }else if(pets[i] instanceof Cat){
                Cat cat = (Cat) pets[i];
                cat.catchMouse();
            }
        }
    }
}

6. Object 类的使用

java.lang.Object​ 是类层次结构的根类,即所有其它类的父类。每个类都使用 Object​ 作为超类。

image
image

7. 面试题

==和 equals 的区别:

  • == 既可以比较基本类型也可以比较引用类型。对于基本类型就是比较值,对于引用类型就是比较内存地址
  • equals 的话,它是属于 java.lang.Object 类里面的方法,如果该方法没有被重写过默认也是==;我们可以看到 String 等类的 equals 方法是被重写过的,而且 String 类在日常开发中用的比较多,久而久之,形成了 equals 是比较值的错误观点。
  • 具体要看自定义类里有没有重写 Object 的 equals 方法来判断。
  • 通常情况下,重写 equals 方法,会比较类中的相应属性是否都相等。
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-06-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. this 关键字
    • 1.1 this 的含义
      • 1.2 什么时候用 this
      • 2. 面向对象特征二 继承(Inheritance)
        • 2.1 继承性的理解
          • 2.2 继承性的好处
            • 2.3 继承的格式
            • 3. 方法的重写
              • 3.1 方法重写的要求
                • 3.2 为什么需要方法的重写?
                  • 3.3 区分方法的重载(overload)与重写(override / overwrite)
                  • 4. 关键字 super
                    • 4.1 为什么需要 super?
                      • 4.2 如何调用?
                        • 4.3 super 调用构造器
                          • 4.4 super 调用属性、方法
                            • 4.5 this 和 super
                            • 5. 面向对象特征之三 : 多态
                              • 5.1 多态的形式和体现
                                • 5.1.1 对象的多态性:
                                • 5.1.2 多态的理解
                              • 5.2 多态的好处与弊端
                                • 5.3 向上转型和向下转型
                                  • 5.4 如何向上或向下转型
                                    • 5.5 instanceof 关键字
                                    • 6. Object 类的使用
                                    • 7. 面试题
                                    领券
                                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档