11、面向对象(中级)

一、包

1、三个作用

  • 区分相同名字的类
  • 当类很多时,可以很好地管理
  • 控制访问范围

2、基本语法

package 包名;

(1)包的本质是创建不同的文件夹来保存类文件

(2)包的创建:new——》package——》com.包名

(3)包的命名:只能包含数字,字母,下划线,小圆点,不能以数字开头,不能用关键字和保留字

(4)命名规范:小写字母 + 小圆点com.公司名.项目名.业务模块名

3、常用包

(1)java.lang.___ Java基本包

(2)java.util.___ 系统工具包

(3)java.net.___ 网络开发包

(4)java.awt.___ 界面开发包、GUI

4、如何引入包

(1)引入所有包:import java.util.*

(2)引入特定包:import java.util.Scanner

5、包的使用细节

(1)package的作用是声明当前类所属的包,需要放在该类文件的最上面,最多有一个package语句

(2)import指令位置在package之下,在类定义之前,可以有多个且无序

6、访问修饰符

用于控制方法、属性的访问权限

访问级别 访问修饰控制符 同类 同包 子类 不同包
公开 public:对外公开
受保护 protected:对子类和同一个包中的类公开
默认
私有 private:不对外公开,类内部使用

(1)修饰符可以用来修饰类中的属性,成员方法,以及类

(2)只有默认和public才能修饰类

(3)成员方法于属性访问规则一致

二、面向对象三大特征

graph LR 面向对象三大特征 --> 封装encapsulation 面向对象三大特征 --> 继承extends 面向对象三大特征 --> 多态poly

1、封装(encapsulation)

(1)把抽象的数据(属性)和对数据的操作(方法)封装在一起,数据保护在内部,程序的其他部分只有被授权的操作(方法)才能执行

作用:

  • 隐藏实现细节
  • 可以对数据进行验证,保证安全合理

(2)封装实现步骤(三步)

  • 第一步:将属性私有化private,使外部不能修改属性
private String name;
  • 第二步:提供一个公共的(public)set方法,对属性判断并赋值
public void setName(类型 参数名) {
    //加入验证逻辑
    属性 = 参数名;
}
  • 第三步:提供一个公共的(public)get方法,用于获取属性的值
public String getName() {
    //权限判断
    return name;
}

(3)构造器于set方法的融合使用

在封装的情况下,使用构造器将跳过构造器直接对属性进行改变,因此需要将set方法在构造器中调用

public Person(String name) {
    setName(name);
}

2、继承(extends)

利用特定对象之间的共有属性,解决代码的复用

(1)父类和子类

当多个类存在相同的属性和方法时,可以从中抽象出父类,在父类中定义这些相同的属性和方法,子类则不需要重新定义这些方法和属性

graph TB 图形类 --> 三角形类 图形类 --> 四边形类 图形类 --> ... 图形类 --> N边形类 三角形类 --> 等边三角形 三角形类 --> 等腰三角形 四边形类 --> 正方形 四边形类 --> 平行四边形 N边形类 --> 不规则图形
  • 父类也称为基类、超类(super类)
  • 图形类和三角形类为父类和子类的关系,三角形类和等边三角形类之间为父类和子类的关系

(2)语法

class 子类 extends 父类 {
    
}

(3)细节

  • 子类继承了所有的属性和方法,但私有属性和方法不能在子类直接访问,要通过父类提供的公共方法去访问
  • 子类必须调用父类的构造器,完成父类的初始化
super();//默认调用父类的无参构造器
  • 当创建子类对象时,不管使用哪类构造器都会调用父类的无参构造器,若父类没有提供无参构造器 ,若父类没有提供无参构造器,则必须在子类中用super去指定使用父类中的特定构造器完成父类的初始化,否则编译不通过
super("name", age);
  • 若需指定构造器,须调用super(实参列表)
  • super();应位于构造器代码块的第一行
  • super关键字只能在构造器中使用
  • thissuper不能共存
  • Java所有类都是Object类的子类
  • 父类构造器的调用不限于直接父类,将向上追溯至Object类(顶级父类)
  • 子类最多直接继承一个父类(单继承机制)
  • 不能滥用继承,子类和父类之间必须满足is-a的关系,即子类是一个父类的逻辑关系
//例子
class A {
    A() {
        System.out.prinln("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");
    }
}

问:B b = new B();输出什么?
结果:
	a
    b name
    b
分析:
    (1)B b = new B();——建立一个引用b,并指向一个B类对象,同时调用B类的无参构造器
    (2)执行B类无参构造器中的this("abc");——》有this,则无super,则调用本类的有参构造器B(String name)
    (3)执行子类构造器时,若无指定,默认super();调用父类的无参构造器A()
	(4)A()——》输出——a——》返回至 B(String name)
    (5)输出——b name ——》返回至 B()
    (6)输出——b

(4)继承的本质

当子类对象创建好后,建立查找关系

类的属性存储于堆中,各自为独立空间——在执行过程中,会将所有相关的父类,子类加载至方法区

(5)总结

  • 注意几个要素
    • 父子关系
    • 属性
    • 构造器
    • 封装
    • 封装和构造器的融合
    • 方法
  • 子类实例化时,构造器的使用,即super的使用
  • 子类实例化时,实参列表的使用,需要结合父类和子类的构造器传参
  • 父类构造器完成父类的初始化,子类完成子类的初始化

3、super的使用

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

  • 访问构造器的非私有属性super.属性
  • 访问父类非私有方法super.方法名(实参列表)
  • 在子类构造器中访问父类构造器super(实参列表)——只有一句且放在第一行

(2)super的好处

  • 调用父类构造器,使分工明确,父类属性由父类初始化,子类属性由子类初始化
  • 当子类与父类中成员(属性和方法)重名时,为了访问父类成员,必须通过super,否则会输出子类对应的属性。若无重名h,superthis和直接访问的效果一致
  • super的访问不限于直接父类,使用super访问遵循就近原则

属性和方法的调用

默认情况下,是从子类到父类寻找相关属性和方法,子类若有该属性或方法,且可调用,则调用子类的属性和方法,若没有,则向上回溯至父类寻找同名的属性和方法,若有但不可调用,则会报错

(3)super和this的比较

区别点 this super
访问属性(方法) 访问本类中的属性,若无... 访问父类中的属性
调用构造器 调用本类构造器 调用父类构造器
特殊 表示当前对象 子类中访问父类对象

4、方法重写/覆盖(overload)

(1)概念:若子类有一个方法,父类的某个方法的名称、返回类型、参数一样,则称子类的这个方法覆盖了父类的方法,也称方法重写了

(2)注意事项

  • 子类方法名和参数要和父类完全一样(因为调用方法时就依据方法名,因为重载,也会依据参数列表)
  • 返回类型:子类方法与父类方法一样,或是父类返回类型的子类(返回类型精度:子类 小于等于 父类
  • 子类方法不能缩小父类方法的访问权限(访问权限:子类 大于等于 父类

5、多态(poly)

方法和对象具有多种形态,多态建立在封装和继承的基础上,多态提高代码复用性,有利于代码维护

5.1多态的具体表现

(1)方法的多态

  • 方法重载的多态(本类)
  • 方法重写的多态(父子类)

(2)对象的多态

  • 一个对象的编译类型和运行类型可以不一致
//父类和子类
class Animal {}
class Dog extends Animal {}

Animal animal = new Dog();
//一个父类的引用可以指向一个子类的对象(的地址)
//上面语句中,animal的编译类型为Animal父类,运行类型为Dog子类
//一个名为animal的Animal类引用,指向了其子类Dog类的一个对象的地址——向上转型
//语义上:一个子类一定可以称为是一个父类
//内存上:只是地址的引用,其对象实际上还是子类

5.2 注意事项

  • 本质:父类的一个引用指向了子类的一个对象

  • 语法:父类类型 引用名 = new 子类类型();

  • 编译类型看左边,运行类型看右边

    调用成员变量看编译类型的属性,调用成员方法看运行类型的方法

    在运行时,实际上参与运行的还是父类引用所指向的子类对象

  • 可以调用父类中的所有成员(在访问权限的限制下)

  • 不能调用子类中特有的成员属性和方法

  • 最终执行效果看运行类型的具体实现

5.3 多态的向下转型

多态向下转型的基础在于已经向上转型,即父类的引用已经指向了子类的一个对象,此时,可以向下转型,将父类的引用换成子类的引用指向子类的对象

  • 语法:子类类型 新引用名 = (子类类型) 父类引用
  • 只能强制父类的引用,不能强制父类的对象
  • 当前父类的引用必须指向当前目标类型的对象
  • 可以调用子类类型中的所有成员

5.4 子类的重写问题

(1)属性没有重写之说,属性的值看编译类型,编译类型确定了继承中向上回溯的起点

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

aa instanceOf XX;

5.5动态绑定机制

(1)属性在哪里声明,就在哪里使用

(2)方法的动态绑定机制

当调用对象方法时,该方法和该对象的内存地址/运行类型绑定,即在运行时,实际上参与运行的还是父类引用所指向的子类对象

5.6 多态的应用

  • 多态数组

概念:数组的定义类型为父类类型,保存的实际元素类型为子类类型

  • 多态参数

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

6、“==”和equals方法

(1)“==”是一个比较运算符

  • 既可以判断基本类型,有可以判断引用类型
  • 判断基本类型时,判断值是否相等
  • 判断引用类型时,判断地址是否相等,即判断是否为同一对象

(2)equals方法

  • equals方法是Object类中的方法,只能判断引用类型
  • 默认判断地址是否相等,子类一般重写了该方法,用于判断内容是否相等

7、hashCode()方法

六个结论

  • 提高具有哈希结构的容器的效率

  • 两个引用,若指向同一个对象,则哈希值是一样的

  • 两个引用,若指向不同对象,则哈希值是不一样的

  • 哈希值主要根据地址号写的,但是不能完全将哈希值等价于地址

  • (集合中讲)

8、toString()方法

  • 默认返回:全类名 + @ + 哈希值的十六进制
  • 子类往往重写toString方法,用于返回对象的属性信息——使用alt + insert即可重写
  • 当直接输出对象时,toString方法被默认调用
System.out.println(person);
等价于==>
person.toString();

9、finalize方法

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

(2)回收机制:当某个对象没有任何引用时,则JVM认为此对象为垃圾对象,会使用垃圾回收机制来销毁该对象,销毁前,会调用finalize方法

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

注:实际开发中几乎不会运用

posted @ 2021-12-07 22:13  DarkSki  阅读(32)  评论(0编辑  收藏  举报