Javajavase

面向对象的理解

  • 面向对象和面向过程的区别
    • 都是编程思想
    • 面向过程强调的是“怎么做”,面向过程的程序,是以函数为基本单位
    • 面向对象强调的是“谁来做”,面向对象是关注的是对象的个体,以类为基本单位,重点在于类的设计,某个数据、行为是在哪个类中描述更合适
  • 好处
    • 解决中大型的问题,使用面向对象代码简化了
    • 以人类的思维方式来思考问题,比较好理解
  • 思考步骤
    • 分析里面涉及到几个类,分别是什么
    • 抽取每个类中的属性和方法,再加以定义
    • 面向过程正常调用和执行

类与对象

  • 类是对象的设计模板,对象是类的实例
  • 区别:类是抽象的,对象是具体的、实际存在的
  • 关系:类是由对象总结或抽取出来的一个概念,是对象的所属类型,对象是通过类创建出来的具体的实体

  • 对现实世界中,具有共同特性的某一类事物的抽象的描述,在Java中使用一个类来描述一类事物
  • 组成

    • 现实世界的事物
    • 属性:事物的信息描述,对应类中的成员变量
    • 行为:事物的功能,对应类中的成员方法
      1. 修饰符 class 类名 {
      2. 属性列表
      3. 构造器列表
      4. 方法列表
      5. }
  • 定义类的步骤

    • 定义类(考虑修饰符、类名)
    • 编写类的属性(考虑修饰符、属性类型、属性名、初始化值)
    • 编写类的方法(考虑修饰符、返回值类型、方法名、形参等)

对象

  • 对象是类的一个具体的个体,是类的一个实例(instance)
  • 创建

    1. 类名 对象名 = new 类名();
    • 匿名对象

匿名对象只能使用一次

  1. new Person().shout();
  • 使用**对象名.对象成员**的方式访问对象成员(包括属性和方法)
  • 类的访问机制
    • 一个类中访问:类中的方法可以直接访问类中的成员变量(例外:static方法访问非static编译不通过)
    • 不同类中访问:先创建要访问类的对象,再用对象访问类中定义的成员
  • 对象的生命周期
    • 从创建对象到被垃圾回收器回收

对象的内存结构

虚拟机栈:将局部变量存储在栈结构中

  • 存放方法中的基本类型变量
  • 存放方法中的引用类型变量的引用(地址号)
  • 栈描述的是方法执行的内存模型。每个方法被调用都会创建一个栈帧(存储局部变量操作数方法出口等)
  • JVM为每个线程创建一个栈,用于存放该线程执行方法的信息(实际参数、局部变量等)
  • 栈是一个连续的内存空间,由系统自动分配,速度快
  • 栈属于线程私有,不能实现线程间的共享
  • 存储特性是“先进后出,后进先出”

堆:存放对象本身,对象的属性(非static的)加载在堆空间中

  • 堆用于存储创建好的对象和数组
  • 堆是一个不连续的内存空间,分配灵活,速度慢
  • JVM只有一个堆,被所有线程共享

方法区

  • JVM只有一个方法区,被所有线程共享
  • 方法区实际也是堆,只是用于存储类、常量相关的信息!
  • 用来存放程序中永远不变或唯一的内容。(类信息(Class对象)、静态变量、字符串常量等)

image.png
内存图.png内存举例.png
image.png
创建一个对象占多大堆空间?
markword 包含对象信息 8字节,其中包含所信息
类型指针 表明所属类,既指向其class对象
实例数据 即成员属性
对齐 对象长度必须是8字节的整数倍,对齐用于补齐


抽象类

抽象类

在父类中明确子类应该包含某些方法,但是在父类中又不能给出具体的实现,父类中只能把这个方法声明为抽象方法,包含抽象方法的类必须是抽象类

  1. 修饰符 abstract class 抽象类的名称 {
  2. 类的成员列表
  3. }
  1. abstract class 几何图形类 {
  2. public abstract double getArea();
  3. }
  4. class extends 几何图形类 {
  5. private double 半径;
  6. public double getArea() {
  7. return Math.PI * 半径 * 半径;
  8. }
  9. }
  10. class 矩形 extends 几何图形类 {
  11. private double 长;
  12. private double 宽;
  13. public double getArea() {
  14. return * 宽;
  15. }
  16. }

原因

  • 从逻辑角度:几何图形类中,应该包含所有几何图形共同的特征。那么所有几何图形,就应该包含 “获取面积”的功能
  • 语法角度:通过“几何图形”类对圆对象,矩形对象等的多态引用,应该是可以调用“获取面积”的方法,如果父类中没有声明该方法,就无法调用,无法实现多态引用

注意

  • 此类不能实例化
  • 抽象类中一定有构造器,便于子类实例化时调用(涉及:子类对象实例化的全过程)
  • 开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作
  • 包含抽象方法的类就是抽象类。通过abstract方法定义规范,然后要求子类必须定义具体实现
  • 通过抽象类,我们就可以做到严格限制子类的设计,使子类之间更加通用
  • 应用:模板设计模式

    抽象方法

    1. 修饰符 abstract 返回值类型 方法名(形参列表);
  • 抽象方法只有方法的声明,没有方法体

  • 包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法的
  • 若子类重写了父类中的所有的抽象方法后,此子类方可实例化
  • 若子类没有重写父类中的所有的抽象方法,则此子类得是一个抽象类,需要使用abstract修饰

注意

  • 不能用abstract修饰变量、代码块、构造器
  • 不能用abstract修饰私有方法、静态方法、final的方法、final的类

接口

  • 接口的本质是契约、标准、规范

为什么要声明接口

  • 解决Java的单继承

例如:多功能的交通工具
既具有飞机的飞行功能,
又具有船的水上航行功能
又具有车的路上行驶功能

  • 多个不相关的类可能具有共同的行为特征,这样的行为特征,就可以提取成一个接口

继承只能表示is-a的关系
类与类之间的关系还有has-a,like-a关系
例如:
飞机有飞的能力
小鸟有飞的能力

飞机与小鸟之间没有is-a的关系
例如:
学生对象可比较
商品对象可比较
。。。
学生与商品没有is-a的关系,可比较行为可以抽取成接口
设计理念区别

  • 抽象类,被继承体现的是:“is a”的关系,抽象类中定义的是该继承体系的共性功能
  • 接口,被实现体现的是:“like a”的关系,接口中定义的是该继承体系的扩展功能

定义

  1. 修饰符 interface 接口名 {
  2. }
  • 访问修饰符只能是public或缺省
  • 接口使用interface来定义
  • 接口名和类名采用相同命名机制
  • extends:接口可以多继承
  • 常量:接口中的属性只能是全局常量,由**public static final**修饰,不写也是
  • 方法:接口中的方法只能是:**public abstract**,不写也是

    1. 修饰符 class 子类 extends 父类 implements 接口名1, 接口名2... {
    2. }

    要点

  • Java中,接口和类是并列的两个结构

  • 如何定义接口:定义接口中的成员
    • JDK7及以前:只能定义全局常量和抽象方法
    • JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、default方法
  • 接口中不能定义构造器的!意味着接口不可以实例化
  • Java开发中,接口通过让类去实现implements的方式来使用
    • 如果实现类覆盖了接口中的所有抽象方法,则此实现类就可以实例化
    • 如果实现类没有覆盖接口中所有的抽象方法,则此实现类仍为一个抽象类
  • Java类可以实现多个接口 —->弥补了Java单继承性的局限性
  • 接口与接口之间可以多继承

接口的使用

  • 接口使用上也满足多态性
  • 实际上就是定义了一种规范
  • 开发中,体会面向接口编程
    • 面向接口编程是面向对象编程的一部分。 为什么需要面向接口编程? 软件设计中最难处理的就是需求的复杂变化,需求的变化更多的体现在具体实现上。 我们的编程如果围绕具体实现来展开就会陷入”复杂变化”的汪洋大海中,软件也就不能最终实现。我们必须围绕某种稳定的东西开展,才能以静制动,实现规范的高质量的项目。 接口就是规范,就是项目中最稳定的东东! 面向接口编程让我们把握住真正核心的东西,使实现复杂多变的需求成为可能。通过面向接口编程,而不是面向实现类编程,可以大大降低程序模块间的耦合性,提高整个系统的可扩展性和可维护性。面向接口编程的概念比接口本身的概念要大得多。 设计阶段相对比较困难,在你没有写实现时就要想好接口,接口一变就乱套了,所以设计要比实现难! 接口语法本身非常简单,但是如何真正使用?这才是大学问。我们需要后面在项目中反复使用,大家才能体会到

接口Java8新特性

  • 接口中定义的静态方法,只能通过“接口.方法”来调用
  • 通过实现类的对象,可以调用接口中的默认方法
  • 如果实现类重写了接口中的默认方法,调用时,仍然调用的是重写以后的方法
  • 如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的默认方法, 那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法 —->类优先原则
  • 如果实现类实现的多个接口中定义了同名同参数的默认方法, 那么在实现类没有重写此方法的情况下,报错

—->接口冲突。这就需要我们必须在实现类中重写此方法

  • 子类(或实现类)的方法中调用父类、接口中被重写的方法
    • **super.默认方法();**
    • **接口名.super.默认方法();**

面试题

  1. interface A {
  2. int x = 0;
  3. }
  4. class B {
  5. int x = 1;
  6. }
  7. class C extends B implements A {
  8. public void pX() {
  9. System.out.println(x); // 🔴编译不通过,因为x不明确,方法有类优先原则,属性没有
  10. System.out.println(super.x);// 1
  11. System.out.println(A.x); // 0
  12. }
  13. }