写点什么

Java 面向对象精讲【中】

作者:XiaoLin_Java
  • 2021 年 12 月 05 日
  • 本文字数:4557 字

    阅读完需:约 15 分钟

Java 面向对象精讲【中】

简介

📢博客主页:https://www.infoq.cn/profile/F7BF297568FAA8/publish

📢欢迎点赞 👍 收藏 ⭐留言 📝 欢迎讨论!

📢本文由 XiaoLin_Java 原创🙉

📢不要等待机会,而要创造机会。✨

七、继承

面向对象的继承思想,可以解决多个类存在共同代码的问题


  • 被继承的类,称之为父类、基类

  • 继承父类的类,称之为子类,拓展类

  • 父类:存放多个子类共同的字段和方法

  • 子类:存放自己特有的字段和方法

7.1、继承的语法

在程序中,如果一个类需要继承另一个类,此时使用 extends 关键字


public class 子类名 extends 父类名{}
复制代码


注意:Java 中类只支持单继承,不支持多继承,但是支持多重继承。也就是说一个子类只能有一个直接的父类,父类也可以再有父类。一个父类也可以有多个子类


class Student1();class Student2();class Student extend Student1,Student2(){}//错误
复制代码


Object 类是 Java 语言的根类,任何类都是 Object 的子类,要么是直接子类,要么是间接子类

7.2、子类可以继承到父类哪些成员

子类继承父类之后,可以拥有到父类的某一些成员(字段和方法),根据访问修饰符来判断:


  1. 父类中用 public protected 修饰的成员,子类均可以继承

  2. 如果父类和子类在同一个包中,使用缺省访问修饰的成员,此时子类可以继承到

  3. 如果父类中的成员用 private 修饰,子类继承不到,因为 private 只能在奔本类中访问

  4. 父类的构造器,子类也无法继承,因为构造器必须和类名相同

7.3、方法的重写

当子类存在一个和父类一模一样的方法时,我们就称之为子类覆盖了父类的方法,也称之为重写。那么我们就可以在子类方法体中,重写编写逻辑代码


方法调用的顺序为:通过对象调用方法时,先在子类中查找有没有对应的方法,若存在就执行子类的,若子类不存在就执行父类的,如果父类也没有,报错。


方法重写需要注意的点:


  1. private 修饰的方法不能被子类所继承,也就不存在重写的概念

  2. 实例方法签名必须相同(方法签名=方法名+方法参数列表)

  3. 子类方法的返回值类型和父类的返回值类型相同或者是其子类

  4. 子类方法中声明抛出的异常小于或者等于父类方法声明抛出的异常类型

  5. 子类方法的访问权限比父类的访问权限更大或者相等

  6. 一般开发都是直接拷贝父类的方法定义粘贴到子类中,重新编写子类的方法体

7.4、super 关键字

在子类中的某一个方法中需要去调用父类中被覆盖的方法,此时得使用 super 关键字。


如果调用被覆盖的方法不使用 super 关键字,此时调用的是本类中的方法。super 关键字表示父类对象的意思

7.5、抽象

抽象方法用 abstract 来修饰方法,被 abstract 修饰的方法具备两个特征:


  1. 该方法没有方法体

  2. 要求子类必须覆盖该方法

7.5.1、抽象方法

使用 abstract 修饰的方法,称为抽象方法


public abstract 返回类型 方法名(参数);
复制代码


抽象方法的特点:


  1. 使用 abstract 修饰,没有方法体,留给子类去覆盖

  2. 抽象方法必须定义在抽象类或者接口中

7.5.2、抽象类

使用 abstract 修饰的类,称为抽象类


public abstract class 类名{}
复制代码


抽象类的特点:


  1. 抽象类不能创建对象,调用没有方法体的抽象方法没有任何意义

  2. 抽象类中可以同时拥有抽象方法和普通方法

  3. 抽象类必须有子类才有意义,子类必须覆盖父类的抽象方法,否则子类也得作为抽象类

7.6、Object 类和常用方法

Object 本身表示对象的意思,是 Java 中的根类,要么是一个类的直接父类,要么就是一个类的间接父类。任何类都直接(间接)继承了 Object 类


class  A{}  //等价于class  A extends Object{}
复制代码


因为所有类都是 Object 类的子类, 所有类的对象都可以调用 Object 类中的方法,经常使用的方法有

7.6.1、boolean equals(Object obj)

boolean equals(Object obj):拿当前调用该方法的对象和参数 obj 做比较


在 Object 类中的 equals 方法和“ == ”符号相同,都是比较对象是否是同一个的存储地址。


public class ObjectDemo {  public static void main(String[] args) {    //创建Person对象p1    Person p1 = new Person();    //创建Person对象p2    Person p2 = new Person();        //比较p1和p2的内存地址是否相同    boolean ret1 = p1 == p2;    boolean ret2 = p1.equals(p2);    System.out.println(ret1);  //false    System.out.println(ret2);  //false  }}
复制代码

7.6.2、toString 方法

toString 表示把对象中的字段信息转换为字符串格式


打印对象其实就是打印对象的 toString()方法,但是 toString()方法默认打印的是对象的 hashCode 的值


com._04_object.Person@15db9742  
复制代码


所以一般我们都会重写 toString()方法


  public String toString() {    return "Person [name=" + name + ", age=" + age + "]";  }
复制代码

7.7、== 符号详解

每一次使用 new 关键字,都表示在堆内存中创建一块新的内存空间


  • 如果是基本数据类型:比较的是两个值是否相等

  • 如果是对象数据类型:比较的是是否为同一块内存地址

八、多态

8.1、接口

​ 接口是一种约定规范,是多个抽象方法的集合。仅仅只是定义了应该有哪些功能,本身不实现功能,至于每个功能具体怎么实现,就交给实现类完成。


​ 接口中的方法是抽象方法,并不提供功能实现,体现了规范和实现相分离的思想,也体现了组件之间低耦合的思想。接口仅仅提供方法的定义,却不提供方法的代码实现


​ 所谓耦合度,表示组件之间的依赖关系。依赖关系越多,耦合性越强,同时表明组件的独立性越差,在开发中往往提倡降低耦合性,可提高其组件独立性,举一个很简单的例子:


  • 集成显卡:显卡和主板焊死在一起,显卡坏了,只能换主板

  • 独立显卡:显卡和主板相分离,显卡插到主板上即可,显卡坏了,只换显卡,不用换主板

8.1.1、接口的定义

接口可以认为是一种特殊的类,但是定义类的时候使用 class 关键字,定义接口使用 interface 关键字,接口中的方法都是公共的抽象方法


public  interface  接口名{    //抽象方法1();    //抽象方法2();    //抽象方法2();}  
复制代码


注意:从 Java 8 开始, Java 支持在接口中定义有实现的方法


public interface IMan {  public abstract void walk();//抽象方法        default void defaultMethod(){        System.out.println("有默认实现的方法, 属于对象");    }    static void defaultMethod(){        System.out.println("有默认实现的方法, 属于类");    }}
复制代码


类可以继承类,但是只能单继承的,接口也可以继承接口,但是却可以继承多个接口,也就是说一个接口可以同时继承多个接口

8.1.2、接口实现类

和抽象类一样,接口是不可以创建对象的,如果想实现接口的功能,就必须定义一个类去实现接口,并且覆盖接口中的所有方法,这个类称为实现类,这种类和接口的关系称为实现关系


public class 类名 implements 接口名{  //覆盖接口中抽象方法}
复制代码


  • 接口:定义多个抽象方法,仅仅定义有哪些功能,不做任何实现

  • 实现类:实现接口,重写接口中的抽象方法,完成具体功能的实现

8.2、多态

在继承关系,是一种”is A”的关系,也就说子类是父类的一种特殊情况


public class Animal{}public class Dog extends Animal{}public class Cat extends Animal{}
复制代码


此时我们可以认为猫和狗都是一种特殊动物,那么可以使用动物类型来表示猫或者狗


Dog   dog   =   new Dog();    //创建一只狗对象,赋给子类类型变量Animal  animal    =  new Cat();      //创建一只猫对象,赋给父类类型变量
复制代码


此时对象 animal 具有两种类型:


  1. 编译类型:声明对象时候的类型(Animal)

  2. 运行类型:对象真实的类型(Cat)


当编译类型和运行类型不一致的时候,多态就产生了


所谓的多态就是表示一个对象有多种形态,简单来说就是同一引用类型,由于引用的实例不同,对同一方法产生的结果也不同


Person person = null;person = new Man();  //person此时表示Man类型person = new Woman();//person此时表示Woman类型
复制代码


多态一定建立在方法重写或者实现之上,可以是继承关系(类和类),也可以是实现关系(接口和实现类),在开发中,一般都指接口和实现类之间的关系,多态在在开发中有两种定义格式

8.2.1、操作继承关系(开发中不是很多)

父类引用变量指向于子类对象,调用方法时实际调用的是子类的方法


父类 变量名 = new 子类();变量名.方法();
复制代码


Animal 类:


public class Animal {  public void shout() {    System.out.println("动物...叫...");  }}
复制代码


Cat 类:


public class Cat extends Animal{  public void shout() {    System.out.println("猫叫...");  }}
复制代码


Test 类:


public class AnimalDemo{    public static void main(String[] args){        Animal animal = new Cat();        animal.shout();    }}
复制代码


运行结果:


猫叫
复制代码

8.2.2、操作实现关系

接口 变量名 = new 实现类();变量名.方法();
复制代码


ISwimable 接口:


public interface ISwimable{    void swim();}
复制代码


Fish 类:


public class Fish implements ISwimable{}    public void swim(){        Sysout.out.println("鱼在游")    }}
复制代码


Test 类:


public class Fish implements{   public static void main(String[] args){       ISwimable fish = new Fish();       fish.swim();   }}
复制代码


结果:


鱼在游泳
复制代码

8.2.3、多态中方法调用问题

如果我们把子类对象赋值给父类对象


Animal animal = new Cat();animal.shout();
复制代码


那么 animal 对象在调用 shout 方法的时候,他调用的是子类自己的方法还是父类的方法呢?



我们可以看得出,在编译时期,JVM 会先去找父类的方法,如果找到了就去找子类的同名方法,如果子类也有就运行子类的方法,否则就运行父类的方法,如果连父类的方法都没有找到的话,直接编译失败失败

8.2.4、类型转换和 instanceof 运算符

8.2.4.1、自动类型转换

自动类型转换:把子类对象赋给父类变量(多态)


Animal a = new Dog();Object  obj = new Dog();  //Object是所有类的根类
复制代码

8.2.4.2、强制类型转换

把父类类型对象赋给子类类型变量(前提:该对象的真实类型应该是子类类型)


当需要调用子类特有的方法时,必须经过强制类型转换,不过有一个要求:父类必须是真实的子类类型才可以转换


Animal a = new Dog();Dog  d = (Dog) a;//正确Cat c = (Cat) a;//错误,真实类型为Dog
复制代码

8.2.4.3、instanceof 运算符

判断该对象是否是某一个类的实例


语法格式


boolean b = 对象A   instanceof  类B;  //判断 A对象是否是 B类的实例?如果是,返回true
复制代码

8.2.4.4、多态总结

面向接口编程,体现的就是多态,其好处:把实现类对象赋给接口类型变量,屏蔽了不同实现类之间的实现差异,从而可以做到通用编程

8.3、面试题

接口和抽象类的区别


  1. 接口中所有的方法隐含的都是抽象的,但是抽象类中可以同时包含抽象方法和普通方法以及静态常量

  2. 类可以实现很多个接口,但是只能继承一个抽象类

  3. 类如果要实现一个接口,那么他必须要实现接口声明的所有方法,但是类可以不实现抽象类中的所有方法,但是这个类必须是抽象类

  4. 接口中不存在构造方法,因为接口的成员变量都是 static final 变量,是在编译的时候就完成了初始化操作了,无需通过构造方法来进行初始化操作,而抽象类必须有构造方法

  5. 抽象类和接口的概念的区别:

  6. 抽象类是从一些类中抽取他们的共有的属性,方法的修饰符可以是 public 或者是 protected 以及缺省,抽象类注重对类本身的抽象,抽象方法没有方法体,仅仅是声明了该方法,让继承他的子类去实现

  7. 接口主要是对类的行为抽象,接口也可以有变量和方法,但是变量以及方法的修饰符必须是 public static final(缺省时会默认加上)和 public abstract(缺省时默认也是这个)

发布于: 2 小时前阅读数: 5
用户头像

XiaoLin_Java

关注

问啥啥都会,干啥啥不行。 2021.11.08 加入

问啥啥都会,干啥啥不行。CSDN原力作者,掘金优秀创作者。

评论

发布
暂无评论
Java 面向对象精讲【中】