面向对象(上)

基本概念

  • 成员变量、构造器、方法的构造顺序随意,不会造成不同的影响
  • 带static的方法不能访问非static的方法、变量
  • 如果没有构造器,将调用默认的无参构造,如果定义了显式构造器,默认构造器将失效
  • 构造器就是一种特殊的方法,编译器通过有无定义返回值来区分构造器和方法


    修饰符访问控制表

类和对象

  • 一个类方法的方法体内可以直接调用本类的另一个类方法

    • 不能直接调用对象方法
    • 如果一定要调用,只能在类方法中new一个对象再调用
  • 对象方法的方法体内可以调用本类另一个对象方法

    • 可以加this,也可以省略
    • 如果省略this,就默认this为调用者
  • 可以使用对象来调用类方法、类变量

    • 但是,不推荐这样做
    • 推荐使用类名来调用类方法、类对象
  • 构造器中的this就代表正在创建的对象

  • new对象后,各种部件的创建顺序:

    1. 父类静态变量
    2. 父类静态初始化块
    3. 子类静态变量
    4. 子类静态初始化块
    5. 父类成员变量
    6. 父类初始化块
    7. 父类构造器
    8. 子类成员变量
    9. 子类初始化块
    10. 子类构造器

方法

  • 作为完全的面向对象语言,Java中的方法都不是独立的实体,必须通过类或对象来调用

  • 方法中的参数传递机制:

    • 只有一种方式,值传递
    • 调用方法后,在方法栈区新开辟一个栈区,并复制变量的值(基本类型)或者变量的地址(引用类型)给栈区内的形参
    • 操作基本变量时,只是操作了数值的副本,原先的基本变量值并没有变化
    • 操作引用变量时,传入的是对象地址的副本,实际操作的是对象,因此对象会发生变化,这一点与基本变量不同
  • 一个方法的标识包括以下三个

    • 调用者
    • 方法名
    • 方法的形参
      调用者.方法名(参数1,参数2,……)
    • 返回值和修饰符不是标识
    • 所谓重载,就是调用者和方法名相同,形参不同,与返回值和修饰符没有关系
  • 形参个数可变的方法

    • 最后一个形参的类型后加上三个点...
    • 实际上会在方法体中被组装为数组

变量

  • 分为成员变量、局部变量
  • 如果局部变量和成员变量同名,成员变量会被覆盖,此时可以通过this调用成员变量
    • 一般在构造器中使用较多
    • 方法中应尽量避免
  • 成员变量的内存运行机制:
    执行Person person = new Person()
    1. 首先在堆内存中创造一个类对象,并初始化类变量、执行静态初始化块。
    2. new之后,在堆内存创建一个Person对象,并初始化非静态成员变量,指向字符串常量“张三”
    3. 在栈内存创建person变量,并指向堆内存中的Person对象



隐藏和封装

  • 利用访问控制符,控制变量和方法的隐藏和暴露

    • 外部类只能使用public和default,不能是protected和private
    • public的外部类必须与文件名对应
  • 类的设计应遵循:

    • 高内聚:内部变量、实现方法应隐藏,禁止外界干预
    • 低耦合:只暴露少量方法供外部调用
  • 控制符使用原则

    • 成员变量都应是private,使用setter和getter
    • 工具方法都应private
    • 如果仅在本包内使用,使用default
    • 如果方法仅希望被子类改写,不希望被外界调用,则设为protected

构造器

  • 如果不定义构造器,会调用默认的无参构造器,默认构造器没有参数,不进行任何操作
  • 一旦提供自定义的构造器,默认的无参构造器就会失效
  • 编写参数不同的多个构造器,称为构造器重载
  • 在一个构造器中调用this(参数1,参数2)……,可实现调用另一个构造器。
    • 调用this的语句只能是构造器的第一句

继承

  • extends意为扩展,这个翻译比继承更清晰:子类是父类功能的扩展

    • 子类获得父类的成员变量和方法
    • 父类的构造器会在子类构造器执行前执行
    • 子类可以重新定义父类中已有的实例变量,并覆盖
    • 子类可以重写父类的方法
  • 方法重写的原则:两同两小一大

    • 方法名、形参列表相同
    • 子类方法返回值类型≤父类方法
    • 子类方法抛出异常类型≤父类方法
    • 子类方法的访问权限≥父类方法
  • super相关

    • 在子类中可以使用super调用父类的实例方法
    • 在子类中可以使用父类类名调用父类的类方法
    • 当新建子类实例时,也会为父类的实例变量和方法分配内存,方便使用super调用
  • 子类的构造器总会先调用父类的构造器,分为以下几种情况:

    • 子类构造器的第一句调用了super(参数...),则根据参数调用父类构造器
    • 子类构造器没有使用super,则会隐式调用父类的无参构造器
    • 子类构造器调用了this(),则会调用本类另一个构造器,而另一个构造器总会调用父类构造器
    • 父类有有参构造器,子类无构造器,编译出错

多态

  • 定义:当一个父类变量引用了一个子类对象时,使用父类变量调用的方法,实际运行的是子类的方法,这就是多态

    • 本质是变量的编译时类型运行时类型不同
    • 变量的编译时类型是声明类型
    • 变量的运行时类型是对象类型
  • 发生条件:

    • 子类和父类都存在该方法,执行子类中的方法
    • 子类存在方法,父类不存在,编译错误,因为编译时找不到引用变量的方法
    • 父类存在方法,子类不存在,依然执行子类的方法(从父类继承)
  • 重要:实例变量不具有多态性,使用父类变量调用实例变量,实际调用的是父类对象的实例变量,而不是子类的

    • 一般不会出现,因为大多数实例变量都是private
  • 如果要调用父类中不存在的子类方法,就需要强制类型转换为子类引用


instanceof运算符

  • 一般左边是变量,右边是类,用于判断一个变量指向的对象
Object s = "abc"
s instanceof Object,返回true
s instanceof String,返回true
s instanceof String的子类,返回false
s instanceof Integer,编译出错,String和Integer没有继承关系
  • 一般用于强制类型转换之前,判断一个父类的引用是否指向某一个子类的实例
if(s instanceof String) String str = (String) s;
\\不会出现ClassCast异常,使代码更加健壮

通过组合实现复用

  • 实现类的复用,除了使用继承之外,另一种方式是组合
  • 组合就是把被复用类作为新类的成员变量,在new时传入复用类的实例,这样也能在新类中使用复用类的所有功能
  • 使用场景
    • 在is-a关系时使用继承,例如动物-狗-泰迪
    • 在has-a关系时使用组合,例如电脑-内存/硬盘/cpu

初始化块

  • 修饰符只能是空或static
  • 初始化块和实例变量的初始化一起在构造器执行之前执行
  • 实际上,编译后初始化块的内容会被加入到每个构造器方法体的前面
  • 只有在第一次使用类时对静态块进行初始化,之后就不再调用static块里的内容
  • 使用方法:
    • 初始化块不接受任何参数
    • 各个构造器中重复的语句,如果不调用参数,就适合放在初始化块中
    • 各个构造器中重复的语句,如果要接受参数,适合用this调用其他构造器

你可能感兴趣的:(面向对象(上))