Java笔记四——java面向对象(封装、继承、多态、抽象)

面向对象基础

创建Person类实例

Person ming = new Person();
在OOP中,class和instance是“模版”和“实例”的关系;
定义class就是定义了一种数据类型,对应的instance是这种数据类型的实例;
class定义的field,在每个instance都会拥有各自的field,且互不干扰;
通过new操作符创建新的instance,然后用变量指向它,即可通过变量来引用这个instance;
访问实例字段的方法是变量名.字段名
指向instance的变量都是引用变量。
在这里插入图片描述

定义方法的语法

修饰符 方法返回类型 方法名(方法参数列表) {
若干方法语句;
return 方法返回值;
}

this变量

在方法内部,可以使用一个隐含的变量this,它始终指向当前实例。因此,通过this.field就可以访问当前实例的字段。

封装

封装是指把一个对象的状态信息(也就是属性)隐藏在对象内部,不允许外部对象直接访问对象的内部信息。但是可以提供一些可以被外界访问的方法来操作属性。

private关键字

在这里插入图片描述

继承

Java使用extends关键字来实现继承:
class Student entends Person{
}
没有明确写extends的类,编译器会自动加上extends Object
Java只允许单继承

  • 子类拥有父类全部的属性和方法(包括Private,但是不能访问)
  • protected关键字可以把字段和方法的访问权限控制在继承树内部(子类可以访问父类的Protected,包括子类的子类也可以访问)

super——表示父类(超类)

  • 子类访问父类字段时,可以用super.fileName
  • 如果父类没有默认的构造方法,子类就必须显式调用super()并给出参数以便让编译器定位到父类的一个合适的构造方法。
    这里还顺带引出了另一个问题:即子类不会继承任何父类的构造方法。子类默认的构造方法是编译器自动生成的,不是继承的。
  • 所以最好写一个没有参数的空构造函数
  • super和this的对比在这里插入图片描述

继承中构造方法的关系

  • 子类中所有的构造方法都会访问父类中空参数的构造方法
  • 子类中每个构造方法的第一条语句默认都是super();
  • 如果父类中没有构造方法

在这里插入图片描述

方法覆(重写)写(override)

参考我的上一篇博客——重载与重写

阻止继承

正常情况下,只要某个class没有final修饰符,那么任何类都可以从该class继承。

从Java 15开始,允许使用sealed修饰class,并通过permits明确写出能够从该class继承的子类名称。

例如,定义一个Shape类:

public sealed class Shape permits Rect, Circle, Triangle {

}

向上转型

把子类类型安全地变为父类类型的赋值
例如
Person p = new Student();

向下转型

把父类类型强制转换为子类类型

Person p1 = new Student(); // upcasting, ok
Person p2 = new Person();
Student s1 = (Student) p1; // ok
Student s2 = (Student) p2; // runtime error! ClassCastException!

如果测试上面的代码,可以发现:

Person类型p1实际指向Student实例,Person类型变量p2实际指向Person实例。在向下转型的时候,把p1转型为Student会成功,因为p1确实指向Student实例,把p2转型为Student会失败,因为p2的实际类型是Person,不能把父类变为子类,因为子类功能比父类多,多的功能无法凭空变出来。

因此,向下转型很可能会失败。失败的时候,Java虚拟机会报ClassCastException。

为了避免向下转型出错,Java提供了instanceof操作符,可以先判断一个实例究竟是不是某种类型:

if (p instanceof Student) {
    // 只有判断成功才会向下转型:
    Student s = (Student) p; // 一定会成功
}

类的设计原则:高内聚低耦合
Java只能单继承,但允许多层继承

static关键字

  1. 修饰成员变量和方法
  2. 随着类的加载而加载,优先于对象存在,被类所有对象共享,可以通过类名调用
  3. 静态方法是没有this关键字的(好理解,this关键字指向当前对象)
  4. 静态方法只能访问静态成员变量和静态成员方法
  5. 内存图:存在于方法区的静态区
  6. 静态变量与成员变量的区别
      静态变量属于类,成员变量属于对象
       静态变量存储于方法区的静态区,成员变量存储于堆区
       内存出现时间不同,静态伴随类,成员变量伴随对象
       调用:静态可以通过类也可以通过对象,成员变量只能通过对象
  7. 推荐用类名.字段名访问静态变量,类名.方法名访问静态方法
    静态方法可以理解为C中的函数

final关键字

  1. 修饰类,类不能被继承
  2. 修饰变量,变量就变成了“常量”,只能被赋值一次(可以在构造函数中初始化final字段)
  3. 修饰方法,方法不能被重写

多态

多态具有一个非常强大的功能,就是允许添加更多类型的子类实现功能扩展,却不需要修改基于父类的代码。(通过重写等方式)

多态前提和体现

  1. 有继承有重写
  2. 父类引用指向子类对象

重写(覆写)Object方法

因为所有的class最终都继承自Object,而Object定义了几个重要的方法:

  • toString():把instance输出为String;
  • equals():判断两个instance是否逻辑相等;
  • hashCode():计算一个instance的哈希值。
    必要的时候可以重写这几个方法,如下
class Person {
    ...
    // 显示更有意义的字符串:
    @Override
    public String toString() {
        return "Person:name=" + name;
    }

    // 比较是否相等:
    @Override
    public boolean equals(Object o) {
        // 当且仅当o为Person类型:
        if (o instanceof Person) {
            Person p = (Person) o;
            // 并且name字段相同时,返回true:
            return this.name.equals(p.name);
        }
        return false;
    }

    // 计算hash:
    @Override
    public int hashCode() {
        return this.name.hashCode();
    }
}

接口和抽象类

抽象类

抽象类特点
  1. 抽象类和抽象方法必须用abstract关键字修饰
    格式:
    abstract class 类名{}
    publict abstract void eat();
  2. 抽象类不一定有抽象方法,有抽象方法的一定是抽象类
  3. 抽象类不能被实例化(new的时候编译报错)
  4. 抽象类的子类要么是抽象类,要么就要重写抽象类中所有的抽象方法
抽象类的成员特点
  1. 成员变量可以是常量也可以是变量
  2. 有构造方法,但是不能通过构造方法实例化,构造方法的作用是用于子类访问父类数据的初始化
  3. 抽象方法不能有具体实现,但是成员方法也可以有非抽象方法

abstrac关键字不能和private、final、static共存
容易理解,抽象类就是要让子类来继承实现的,抽象类不能实例化,上面三个关键字与这种理念冲突

面向抽象编程

抽象类Person,具体类Student
Person s = new Student();
s.run();

尽量引用高层类型,避免引用实际子类型的方式,称之为面向抽象编程。
面向抽象编程的本质就是:
上层代码只定义规范(例如:abstract class Person);
不需要子类就可以实现业务逻辑(正常编译);
具体的业务逻辑由不同的子类实现,调用者并不关心。

接口

用interface定义接口
格式:interfac 接口名{}

interface Person{
	void run();
	String getName();
}

interface抽象极了,不能有任何字段(成员变量等),接口定义的所有方法默认都是public abstract,所以不用写

接口特点
  • 具体的class实现接口(用implements表示)
    格式:clas 类名 implements 接口名{}
    举个例子
class Student implements Person {
    private String name;

    public Student(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        System.out.println(this.name + " run");
    }

    @Override
    public String getName() {
        return this.name;
    }
}
  • 接口的子类
    要么是抽象类,要么实现接口中所有的抽象方法
  • 一个类可以实现多个interface
class Student implements Person, Hello { // 实现了两个interface
    ...
}
  • 接口成员特点
    成员变量只能是常量,默认修饰符public static final
    没有构造方法
    成员方法只能是抽象方法,默认public abstract

抽象类和接口的比较

在这里插入图片描述
default的介绍可以参考博文
链接: https://blog.csdn.net/qq_27093465/article/details/84565771.

在这里插入图片描述

  • 一个interface可以继承自另一个interface。interface继承自interface使用extends,例如
interface Hello {
    void hello();
}

interface Person extends Hello {
    void run();
    String getName();
}

继承关系

合理设计interface和abstract class的继承关系,可以充分复用代码。一般来说,公共逻辑适合放在abstract class中,具体逻辑放到各个子类,而接口层次代表抽象程度。可以参考Java的集合类定义的一组接口、抽象类以及具体子类的继承关系:
在这里插入图片描述
在使用的时候,实例化的对象永远只能是某个具体的子类,但总是通过接口去引用它,因为接口比抽象类更抽象

List list = new ArrayList(); // 用List接口引用具体子类的实例
Collection coll = list; // 向上转型为Collection接口
Iterable it = coll; // 向上转型为Iterable接口
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值