拨开荷叶行,寻梦已然成。仙女莲花里,翩翩白鹭情。
IMG-LOGO
主页 文章列表 【JAVA SE】三万字终极魔典 面向物件编程深度讲解(包+继承+多型+抽象类+界面 全面剖析)

【JAVA SE】三万字终极魔典 面向物件编程深度讲解(包+继承+多型+抽象类+界面 全面剖析)

白鹭 - 2022-03-01 2110 0 0

本章目录

  • 温馨提示
  • 开篇介绍( 知识点比较多 务必耐心看完!)
  • 本章重点
  • 正文开始
    • 1. 包
      • 1.1 汇入包中的类
      • 1.2 静态汇入
      • 1.3 将类放到包中
      • 1.4 包的访问权限控制
      • 1.5 常见的系统包
    • 2. 继承
      • 2.1 了解继承
      • 2.2 语法规则(比较枯燥)
      • 2.3 子类的构造方法
      • 2.4 super 和 this 的区别
      • 2.5 访问权限
      • 2.6 更复杂的继承关系
      • 2.7 final 关键字
    • 3. 多型
      • 3.1 向上转型
        • 知识点补充:父类参考访问成员
      • 3.2 动态系结
        • 知识点补充1:在构造方法中呼叫重写的方法
        • 知识点补充2:静态系结
      • 3.3 方法重写
      • 3.4 重写和多载的区别(重新整理)
      • 3.5 向下转型
      • 3.6 理解多型
    • 4. 抽象类
      • 4.1 了解抽象类
      • 4.2 语法规则
      • 4.3 抽象类的作用
    • 5. 界面
      • 5.1 了解界面及简单语法规则
      • 5.2 实作多个界面及其他语法规则
      • 5.3 界面之间的继承
      • 5.4 界面使用实体
        • Comparable界面
        • Comparator界面
        • Cloneable界面及深拷贝和浅拷贝
      • 5.5 抽象类和界面的区别
  • 全文结束

温馨提示

大家好我是Cbiltps,在我的博客中如果有难以理解的句意难以用文字表达的重点,我会有配图,所以我的博客配图非常重要!!!

而且很多知识在代码的注释里,所以代码注释也非常重要!!!

这篇文章前后逻辑顺序非常重要,一定要从前往后看,慢慢看!!!

开篇介绍( 知识点比较多 务必耐心看完!)

等后面我会写关于类和物件的博客,其实在学习本篇博客之前一定要类和物件有一定的了解!就是大家先学习类和物件的博客,然后再看这篇面向物件文章,你就会有一个阶梯式的理解

紧接着,在这篇文章的尾部,我会展开一个简单且知识点全面的一个关于面向物件的训练(最后面直接贴链接),大家拭目以待吧!学习完后你就会对面向物件有一个初步的认识

还有一点:这篇文章横向拓展比较多,全程三万多字!大家在学习的时候会看到丰富的知识点,覆写面也比较全!

那么,今天这篇博客的知识点在小米阿里巴巴百度VIVO腾讯携程贝壳美团头条网易京东滴滴等公司常考!!!

本章重点

  • 继承
  • 多型
  • 抽象类
  • 界面

正文开始


1. 包


(package) 是组织类的一种方式,使用包的主要目的是保证类的唯一性

上面的话晦涩难懂,我们先不要纠结这个,直接举例子演示(看下面)!

1.1 汇入包中的类

比如说打印阵列的时候就汇入了包中的类:

package com.company;

import java.util.Arrays;//呼叫了util这个包中的Arrays这个类

public class Main {

    public static void main(String[] args) {
	    int[] array = {1,2,3,4,5};
        System.out.println(Arrays.toString(array));//用字串的方式打印阵列
    }
}

注意一个问题: toString这个方法是由类名(Arrays)呼叫的,所以这个方法就是一个静态方法按住CTRL后鼠标点进去,进入Arrays.java档案,如下图)!
在这里插入图片描述
所以所有通过类名直接呼叫的方法就一定是静态方法,我们不用管这个方法是怎么执行的,这个是官方写好的,直接呼叫即可!

那么,它在哪个包底下呢?

在上面打开档案(Arrars.java)的最前面就可以看到的,如下
在这里插入图片描述
我们可以找到这个档案(Arrars.java)的路径:
在这里插入图片描述
关于上面的package关键字是什么,一会说!而且再看下面一点点(Arrars.java中)还有import关键字
在这里插入图片描述
然后提出疑问:importpackage有什么区别?

  • package 是你运行程序后生成的.class档案全部放在了你所定义的包中,便于以后呼叫管理,
  • import 则是在撰写程序的时候需要呼叫某个包中的类,

也就是说,如果要用到一些Java类别库里面代码的时候,我们都需要import来汇入的!

然后再举一个汇入例子:

//汇入方式1
package com.company;

import java.util.Date;//在这里汇入

public class Main {

    public static void main(String[] args) {
        Date date = new Date();
    }
}
//汇入方式2
package com.company;

public class Main {

    public static void main(String[] args) {
        java.util.Date date = new java.util.Date();//这汇入也可以,但是比较麻烦,推荐方式1
    }
}

记住:只能汇入一个具体的类,不能汇入一个具体的包
在这里插入图片描述

然后紧接着下一个问题:我们看到有人是这样汇入包的,这里的*是什么意思?

import java.util.*;
  • * 是一个通配符:意思是汇入这个包底下所有的类

疑问:util下面有很多的类,难道一下子全部汇入了吗?

不是的,Java处理的时候,需要谁,它才会拿谁

C语言里面,通过include关键字汇入之后,会把头档案里面的内容全部拿过来

但是,这样子(import java.util.*;)汇入的范围太广了,你有可能把握不住,有时候会有冲突(如下),建议汇入具体的类名
在这里插入图片描述
但是,假设你想用另一个包底下的Date,就识别不了了(看下图):
在这里插入图片描述
解决方法就是:使用完整的类名汇入
在这里插入图片描述

//代码组织如下:
package com.company;

import java.util.*;
import java.sql.*;
//以上两个包中都有Date类,为了避免冲突,使用如下操作

public class Main {

    public static void main(String[] args) {
        java.util.Date date = new java.util.Date();//使用完整的类名汇入
    }
}

1.2 静态汇入

一般情况下,静态汇入用的非常的少,了解即可!

package com.company;

import static java.lang.System.*;
import static java.lang.Math.*;

public class Main {

    public static void main(String[] args) {
        out.println("123");//静态汇入写起来方便
        out.println(max(12, 23));//但是,不提倡这样写,不方便阅读,稀奇古怪
    }
}

1.3 将类放到包中

废话不多说,直接上步骤

  1. 新建一个包
    在这里插入图片描述
  2. 创建类
    在这里插入图片描述

同时可以看到磁盘上的目录结构已经被 IDEA 自动创建出来了
在这里插入图片描述

基本规则(注意以下几点):

  • 在档案的最上方加上一个 package 陈述句指定该代码在哪个包中
  • 包名必须是小写的
  • 包名需要尽量指定成唯一的名字, 通常会用公司的域名的颠倒形式(例如:com.xxxxx.www)
  • 包名要和代码路径相匹配,例如创建com.cbiltps的包, 那么会存在一个对应的路径com/cbiltps来存盘代码
  • 如果一个类没有 package 陈述句, 则该类被放到一个默认包中

经过上面的步骤后,回传第一节的第一句话 (package) 是组织类的一种方式,使用包的主要目的是保证类的唯一性

如何保证类的唯一性呢?
其实就是磁盘上的同一目录下(同一个包下)只能有一个同名的档案(类),并且包名不一样了,路径也不一样,也就互相不干扰了!

1.4 包的访问权限控制

包访问权限:顾名思义,就是只能在当前包中使用

举个例子:当你的成员变量不加任何的访问修饰限定词的时候,就是包访问权限

由于在不同的包中展示,不方便直接代码展示,所以就截图展示了(比较乱,见谅!)
在这里插入图片描述
由此可见,cbiltps包无法访问company包中的val值,所以包访问权限只能在当前包中使用

1.5 常见的系统包

  1. java.lang:系统常用基础类(String、Object)
    此包从JDK1.1后自动汇入,不需要import进行汇入
  2. java.lang.reflect:java反射编程包,
  3. java.net:进行网络编程开发包,
  4. java.sql:进行数据库开发的支持包,
  5. java.util:是java提供的工具程序包,(集合类等) 非常重要
  6. java.io:I/O编程开发包,

2. 继承


在学习本小节之前先来复习一下知识点:面向物件的基本特征(共四点 先说两点)

  • 封装:不必要公开的资料成员和方法,使用private关键字修饰(为了安全性!
  • 继承:对共性的抽取,使用extends进行处理(代码可以重复使用!

2.1 了解继承

代码中创建的类, 主要是为了抽象现实中的一些事物(包含属性和方法)

有的时候客观事物之间就存在一些关联关系, 那么在表示成类和物件的时候也会存在一定的关联

例如, 设计一个类表示动物(直接上代码):

class Animal {
    //动物的名字和年龄都是共有的!
    public String name;
    public int age;

    //包括吃也是共有的行为!
    public void eat() {
        System.out.println(name + "eat()");
    }
}

class Dog extends Animal {
    //抽取完后大大减少了代码量!
}

class Bird extends Animal {
    public String wing;

    public void fly() {
        System.out.println(name+"fly()" + age);
    }
}

此时,Animal 这样被继承的类, 我们称为父类基类超类,对于像 DogBird 这样的类,我们称为子类派生类

从逻辑上讲,DogBird 都是一种 Animal (is - a 语意);

和现实中的儿子继承父亲的财产类似,子类也会继承父类的栏位和方法,以达到代码重用的效果,
在这里插入图片描述
extends 英文原意指 “扩展”,而我们所写的类的继承,也可以理解成基于父类进行代码上的 “扩展”;

例如我们写的 Bird 类, 就是在 Animal 的基础上扩展出了 fly 方法,

2.2 语法规则(比较枯燥)

基本语法:

class 子类 extends 父类 {
}

注意

  • 使用 extends 指定父类

  • Java 中一个子类只能继承一个父类 (而C++/Python等语言支持多继承)
    在这里插入图片描述

  • 子类会继承父类的所有 public 的栏位和方法

  • 对于父类的 private 的栏位和方法,子类中是无法访问
    在这里插入图片描述

  • 子类的实体中,也包含着父类的实体,可以使用 super 关键字得到父类实体的参考

上面简单的总结后看另外一个问题:
在这里插入图片描述
得出结论:子类和父类栏位同名的情况下优先访问的是子类的 !

而它的存储器图是这样子的:
在这里插入图片描述

2.3 子类的构造方法

看一个问题:根据上面的表示动物类的代码中添加父类的构造方法后为什么会产生下面的错误?
在这里插入图片描述
根据上面的报错,我们得到下面的结论:

  • 父类构造方法不能被子类继承
  • 子类的构造方法中必须要呼叫父类的构造方法

而且注意:呼叫父类构造方法的时候

  • 子类通过 super(自变量串列)呼叫父类构造方法,呼叫super的陈述句必须放在子类构造方法的第一行
  • 若子类构造方法中没有显示呼叫父类构造方法,则系统默认呼叫父类无参的构造方法;若父类中没有定义无自变量的构造方法,编译出错

所以,按照下面的样子改即可:
在这里插入图片描述
上面的知识点明白之后,我们补充完主类后,画一下存储器图加强理解):

//主类的代码如下:
public class TestDemo {
    public static void main(String[] args) {
        Dog dog = new Dog("哈士奇",19);
        System.out.println(dog.name);
        dog.eat();

        Bird bird = new Bird("喜鹊",18,"我要的飞翔");
        System.out.println(bird.name);
        bird.eat();
        bird.fly();
    }
}

在这里插入图片描述

2.4 super 和 this 的区别

在之前的学习中已经遇见 superthis 两个关键字,博主根据自身所学及博客参考做出如下总结:

super: 可以理解为父类物件的参考(是依赖物件的),不能出现在静态环境(包括:static变量,static方法,static陈述句块)中因为 static 不依赖物件)!

  • super(); //呼叫父类的构造方法
  • super.func(); //呼叫父类的普通方法
  • super.data; //呼叫父类的成员属性

this: 可以理解为指向本物件的指标,它代表当前物件名(在程序中易产生二义性之处,应使用 this指明当前物件;如果函式的形参类中的成员资料同名,这时需用 this指明成员变量名)!

  • this(); //呼叫本类中另一种形成的构造方法

注意点与区别总结:

  • super();this();区别是:super();从子类中呼叫父类的构造方法,this();在同一类内呼叫其它方法
  • super();this();均需放在构造方法内第一行
  • 有时候 thissuper 不能同时出现在一个建构式里面,因为 this 必然会呼叫其它的建构式,其它的建构式必然也会有 super 陈述句的存在,所以在同一个建构式里面有相同的陈述句,就失去了陈述句的意义,编译器也不会通过
  • this();super();都指的是物件,所以,均不可以在 static环境中使用(包括:static变量,static方法,static陈述句块)

在这里插入图片描述

2.5 访问权限

Java中对于栏位方法共有四种访问权限

  • private: 类内部能访问, 类外部不能访问
  • 默认(也叫包访问权限): 类内部能访问, 同一个包中的类可以访问, 其他类不能访问
  • protected: 类内部能访问, 子类和同一个包中的类可以访问, 其他类不能访问
  • public : 类内部和类的呼叫者都能访问

重要的是下面的范围图:
在这里插入图片描述

private关键字(补充的一点):

这里有一个问题

父类的 private 修饰的成员变量是否被继承了?

这个问题的答案有点模糊,有的书上说是继承了的;

但有的书上说不是,建议我们采用没有没继承的答案!

原因看下面:

//父类
class A {
    private int a;//使用private修饰
}
//子类 这段代码是错
class B extends A { //继承
    public void func() {
        System.out.println(this.a);//这里无法访问就说是没有没继承的!
    }
}

这个知识点先就写到这里,后期有需要的话经过百度之后有可能会补充!

大家也可以在评论区自由发挥!

protected关键字:

刚才我们发现,如果把栏位设为 private,子类不能访问;

但是设成 public,又违背了我们 “封装” 的初衷;

两全其美的办法就是 protected 关键字,

  • 对于类的呼叫者来说,protected 修饰的栏位和方法是不能访问
  • 对于类的 子类同一个包的其他类 来说,protected 修饰的栏位和方法是可以访问

什么时候下用哪一种呢?

我们希望类要尽量做到 “封装”,即隐藏内部实作细节,只暴露出必要的信息给类的呼叫者,

因此我们在使用的时候应该尽可能的使用比较严格的访问权限,

例如如果一个方法能用 private,就尽量不要用 public

另外,还有一种简单粗暴的做法:将所有的栏位设为 private, 将所有的方法设为public

不过这种方式属于是对访问权限的滥用,还是更希望同学们能写代码的时候认真思考,该类提供的栏位方法到底给 “谁” 使用(是类内部自己用,还是类的呼叫者使用,还是子类使用),

2.6 更复杂的继承关系

这里有个不成文的规则:继承层次最好不要超过三个!

时刻牢记,我们写的类是现实事物的抽象,而我们真正在公司中所遇到的项目往往业务比较复杂,可能会涉及到一
系列复杂的概念,都需要我们使用代码来表示,所以我们真实项目中所写的类也会有很多,类之间的关系也会更加
复杂,

但是即使如此,我们并不希望类之间的继承层次太复杂,一般我们不希望出现超过三层的继承关系,如果继承层
次太多,就需要考虑对代码进行重构了,

如果想从语法上进行限制继承,就可以使用 final 关键字 !

2.7 final 关键字

曾经我们学习过 final 关键字,修饰一个变量或者栏位的时候,表示常量 (不能修改)

final int a = 10;
a = 20; //编译出错

final 关键字也能修饰(叫做密封类),此时表示被修饰的类就不能被继承 !

final 关键字的功能是限制类被继承,“限制” 这件事情意味着 “不灵活”,

在编程中,灵活往往不见得是一件好事,灵活可能意味着更容易出错

而我们常见的String类就是final修饰的:
在这里插入图片描述


3. 多型


从字面上理解,就是一种事物多种形态,

但是,面试官问的时候不能这样回答!

了解多型需要一个程序!

3.1 向上转型

根据上面的继承关系,我们进行探讨:

public static void main(String[] args) {
        /*Dog dog = new Dog("旺财",20);
        Animal animal = dog;*/
        
        //把上面的代码简化一下
        Animal animal = new Dog("旺财",23);//向上转型   
        //其实就是:父类参考 参考 子类物件
    }

知识点补充:父类参考访问成员

学到这里我在添加一个知识点(这一个知识点作为 继承访问的补充),

然后我们再次重新展示一下代码(为了方便我直接把知识点写写进了代码里,请大家注意查收):

//Animal类
class Animal {
    public String name = "动物";
    public int age;
    protected int count;

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

    public void eat() {
        System.out.println(name+" eat()");
    }
}
//Bird类 继承于 Animal
lass Bird extends Animal {
    public String wing;
    public String name = "鸟类";

    public Bird(String name, int age, String wing) {
        super(name, age);
        this.wing = wing;
    }

    public void fly() {
        System.out.println(super.name + "fly()");
    }
}
//主类的主方法
public static void main(String[] args) {

        Animal animal2 = new Bird("wuya",12,"wuya fly!");//这里发生向上转型
        animal2.eat();//可以呼叫eat方法

        System.out.println(animal2.name);//这里其实访问的是父类的name

//        注意了:重点知识在这里!!!
//        System.out.println(animal2.wing); 无法访问的
//        因为animal的型别是Animal型别
//        意思就是:通过父类参考,只能访问父类自己的成员!

    }

什么情况下会发生向上转型?

  1. 直接赋值(就是上面的情况)
public static void main(String[] args) {
        /*Dog dog = new Dog("旺财",20);
        Animal animal = dog;*/
        
        //把上面的代码简化一下
        Animal animal = new Dog("旺财",23);//向上转型   
        //其实就是:父类参考 参考 子类物件
    }
  1. 作为函式的自变量
public class TestDemo2 {

    public static void func(Animal animals) {

    }

    public static void main(String[] args) {

        Dog dog = new Dog("金毛",20);
        func(dog);//在这里发生向上转型
    }
}
  1. 作为方法的回传值
 public static Animal fun2(Animal animalss) {
        Dog dog = new Dog("huahua",23);
        return dog;//在这里发生向上转型
    }

3.2 动态系结

Java中有两种多型运行时多型(动态系结)编译时多型(静态系结)

编译时多型就是多载来实作的,根据你给的自变量以及个数的不同,来推汇出你呼叫那个函式!

运行时多型是怎样的呢?往下看…

首先一个问题:
在这里插入图片描述
因为此时发生了动态系结!

发生动态系结两个条件:

  • 父类参考 参考 子类物件
  • 通过这个父类参考,呼叫父类和子类同名的覆写方法

大家注意了:动态系结多型的基础 !!!

这个时候是不是还是有点不明白这个动态是啥意思,接着往下看…

那就来看看下面的字节码档案:

在此之前,我们来看看Java中如何打开反汇编代码?
在这里插入图片描述
我们来看一下这个反汇编代码(main 方法):
在这里插入图片描述
我们看到反汇编代码中呼叫的是 Animal.eat; (父类的 eat 方法),但是运行的时候呼叫的为啥是 dog.eat(); (参考的子类物件的 eat 方法)?
在这里插入图片描述
在这里,在编译的时候不能够确定此时呼叫谁的方法;在运行的时候才知道呼叫谁的方法

这个叫运行时系结,也叫动态系结!

知识点补充1:在构造方法中呼叫重写的方法

直接看代码:

//Animal类
class Animal {
    public String name = "动物";
    public int age;

    public Animal(String name,in
							
标签:

0 评论

发表评论

您的电子邮件地址不会被公开。 必填的字段已做标记 *