Java 面向对象

前言

  • 面向对象的程序设计:程序设计是通过对象对程序进行设计,对象代表一个实体,实体可以清楚地被识别。

  • Java 作为一种面向对象语言,支持以下基本概念

    • 对象
    • 封装
    • 继承
    • 多态
    • 抽象
    • 实例
    • 方法
    • 消息解析
  • 面向对象的特征

    • 封装,最常见的是把属性私有化封装在一个类里面,只能通过方法去访问。
    • 继承,子类继承父类,从而继承了父类的方法和属性。
    • 多态,多态分操作符的多态和类的多态。类的多态指父类引用指向子类对象,并且有继承,有重写。
    • 抽象,比如一个类,抽象出了一些属性和方法,使得开发过程中更加易于理解。
  • Java 面向对象设计

  • Java 面向对象 快速入门

1、类

  • 类:类是一个模板,它描述一类对象的行为和状态。类可以看成是创建 Java 对象的模板。

  • 一个类可以拥有多个方法。

1.1 类变量

  • 一个类可以包含以下类型变量

    • 成员变量
    • 局部变量
    • 类变量(静态变量)
  • 成员变量

    • 成员变量是定义在类中,方法体之外的变量。成员变量可以被类中方法、构造方法和特定类的语句块访问。
    • 在创建实例时创建类的成员变量,并且当对象被销毁时被销毁。
    • 未显式分配值的所有成员变量在声明期间自动分配一个初始值。成员变量的初始化值取决于成员变量的类型。













































元素类型 初始值
byte 0
short 0
int 0
long 0L
float 0.0f
double 0.0d
char “\u0000”
boolean false
对象引用 null
  • 局部变量

    • 在方法、构造方法或者语句块中定义的变量被称为局部变量。
    • 变量声明和初始化都是在方法中,方法结束后,变量就会自动销毁。
  • 类变量(静态变量)

    • 类变量也声明在类中,方法体之外,但必须声明为 static 类型。
    • 静态变量在类加载时初始化。

1.2 Object 类

  • Object 类是所有类的父类。声明一个类的时候,默认是继承了 Object

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    package java.lang;

    /**
    * Class {@code Object} is the root of the class hierarchy.
    * Every class has {@code Object} as a superclass. All objects,
    * including arrays, implement the methods of this class.
    *
    * @author unascribed
    * @see java.lang.Class
    * @since JDK1.0
    */
    public class Object {

    private static native void registerNatives();
    static {
    registerNatives();
    }

    public final native Class<?> getClass();

    public native int hashCode();

    public boolean equals(Object obj) {
    return (this == obj);
    }

    protected native Object clone() throws CloneNotSupportedException;

    public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

    public final native void notify();

    public final native void notifyAll();

    public final native void wait(long timeout) throws InterruptedException;

    public final void wait(long timeout, int nanos) throws InterruptedException {
    if (timeout < 0) {
    throw new IllegalArgumentException("timeout value is negative");
    }

    if (nanos < 0 || nanos > 999999) {
    throw new IllegalArgumentException(
    "nanosecond timeout value out of range");
    }

    if (nanos > 0) {
    timeout++;
    }

    wait(timeout);
    }

    public final void wait() throws InterruptedException {
    wait(0);
    }

    protected void finalize() throws Throwable { }
    }
    1
    2
    3
    4
    // 继承 Object
    public class Hello extends Object {

    }
  • Object 类提供了一些常用的方法

关键字 说明
toString 返回当前对象的字符串表达
finalize 垃圾回收的时候调用,是由虚拟机 JVM 调用的,一旦一个对象被回收,它的 finalize() 方法就会被调用
equals 判断两个对象的内容是否相同
等号 ==
这不是 Object 的方法,但是用于判断两个对象是否相同,更准确的讲,用于判断两个引用,是否指向了同一个对象
hashCode 返回一个对象的哈希值
getClass 返回一个对象的类对象
wait
notify
notifyAll
线程同步相关方法
  • final, finally, finalize 的区别

    • final 修饰类,方法,基本类型变量,引用的时候分别有不同的意思。
      • 修饰类 表示该类不能被继承。
      • 修饰方法 表示该方法不能被重写。
      • 修饰基本类型变量 表示该变量只能被赋值一次。
      • 修饰引用 表示该引用只有一次指向对象的机会。
    • finally 是用于异常处理的场面,无论是否有异常抛出,都会执行。
    • finalize 是 Object 的方法,所有类都继承了该方法。当一个对象满足垃圾回收的条件,并且被回收的时候,其 finalize() 方法就会被调用。
  • 垃圾回收

    • GC 是 Garbage Collection 的缩写,即垃圾回收。
    • 这里所谓的垃圾,指的是那些不再被使用的对象,JVM 的垃圾回收机制使得开发人员从无聊、容易犯错的手动释放内存资源的过程中解放出来。
    • 开发人员可以更加专注的进行业务功能的开发,而资源回收的工作交由更加专业的垃圾回收机制自动完成。

1.3 抽象类

  • 抽象类:在类中声明一个方法,这个方法没有实现体,是一个 “空” 方法,这样的方法就叫抽象方法,使用修饰符 abstract 修饰,当一个类有抽象方法的时候,该类必须被声明为抽象类。

  • 在 Java 中,我们使用抽象类来定义抽象概念。抽象概念必须有一些抽象方面。例如,抽象概念是 Shape,而抽象方面是如何计算面积。抽象概念在 Java 中变成抽象类,抽象方面成为抽象方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // 用 abstract 修饰抽象类,不能够直接被实例化,子类会被要求实现抽象方法
    public abstract class Hero {

    String name;
    float hp;
    float armor;
    int moveSpeed;

    public abstract void attack(); // 抽象方法 attack,Hero 的子类会被要求实现 attack 方法
    }
  • 抽象类可以没有抽象方法,一旦一个类被声明为抽象类,就不能够被直接实例化。

    1
    2
    3
    4
    5
    6
    7
    // 用 abstract 修饰抽象类,不能够直接被实例化
    public abstract class Hero {
    String name;
    float hp;
    float armor;
    int moveSpeed;
    }
  • abstract class(抽象类)和 interface(接口)的区别

    • 相同

      • 抽象类和接口都可以有实体方法。
    • 使用方式

      • 抽象类只能够通过继承被使用。子类只能继承一个抽象类,不能继承多个。
      • 接口必须通过实现被使用。子类可以实现多个接口。
    • 实现方法

      • 抽象类不仅可以提供抽象方法,也可以提供实现方法。
      • 接口只能提供抽象方法,不能提供实现方法。但是在 Java8 版本开始,接口可以提供实现方法了,前提是要在方法前加一个 default 修饰符。
    • 属性定义

      • 抽象类可以定义
        • public, protected, package, private
        • 静态和非静态属性
        • final 和非 final 属性
      • 但是接口中声明的属性,只能是以下类型,即便没有显式的声明
        • public
        • 静态
        • final 的
  • 抽象类是否可实现(implements)接口?

    • 可以,比如 MouseAdapter 鼠标监听适配器 是一个抽象类,并且实现了 MouseListener 接口。
  • 抽象类是否可继承实体类(concrete class)?

    • 可以,所有抽象类,都继承了 Object。

1.4 内部类(嵌套类)

  • 在任何类外部声明的类是顶级类。内部类(嵌套类)是声明为其他类或作用域的成员的类。

  • 内部类(嵌套类)分为四种

  • 非静态内部类

    • 可以直接在一个类里面定义,只有一个外部类对象存在的时候,才有意义。可以直接访问外部类的实例属性和方法。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      public class Hero {
      private String name; // 姓名
      float hp; // 血量
      float armor; // 护甲
      int moveSpeed; // 移动速度

      // 非静态内部类 // 只有一个外部类对象存在的时候,才有意义:战斗成绩只有在一个英雄对象存在的时候才有意义
      class BattleScore {
      int kill;
      int die;
      int assit;

      public void legendary() {
      if (kill >= 8)
      System.out.println(name + "超神!"); // 可以直接访问外部类的实例属性和方法 name
      else
      System.out.println(name + "尚未超神!");
      }
      }
      }
    • 只有一个外部类对象存在的时候,才有意义,new 外部类().new 内部类();

      1
      2
      3
      4
      5
      6
      7
      8
      9
      public static void main(String[] args) {
      Hero garen = new Hero();
      garen.name = "盖伦";

      // 实例化内部类
      BattleScore score = garen.new BattleScore(); // BattleScore 对象只有在一个英雄对象存在的时候才有意义,所以其实例化必须建立在一个外部类对象的基础之上
      score.kill = 9;
      score.legendary();
      }
  • 静态内部类

    • 在一个类里面声明一个静态内部类,static 修饰,不需要一个外部类的实例为基础,可以直接实例化。不可以访问外部类的实例属性和方法。
    • 除了可以访问外部类的私有静态成员外,静态内部类和普通类没什么大的区别。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      public class Hero {
      public String name;
      protected float hp;

      private static void battleWin() {
      System.out.println("battle win");
      }

      // 静态内部类
      static class EnemyCrystal { // 敌方的水晶
      int hp = 5000;

      // 如果水晶的血量为 0,则宣布胜利
      public void checkIfVictory() {
      if (hp == 0) {
      Hero.battleWin();
      System.out.println(name + " win this game"); // 不能直接访问外部类的对象属性
      }
      }
      }
      }
    • 不需要一个外部类的实例为基础,可以直接实例化,new 外部类.静态内部类();

      1
      2
      3
      4
      5
      6
      public static void main(String[] args) {

      // 实例化静态内部类
      Hero.EnemyCrystal crystal = new Hero.EnemyCrystal();
      crystal.checkIfVictory();
      }

1.5 匿名类

  • 匿名类指的是在声明一个类的同时实例化它,使代码更加简洁精练。

  • 通常情况下,要使用一个接口或者抽象类,都必须创建一个子类。有的时候,为了快速使用,直接实例化一个抽象类,并 “当场” 实现其抽象方法。

  • 既然实现了抽象方法,那么就是一个新的类,只是这个类,没有命名。这样的类,叫做匿名类。

  • 匿名类本质上就是在继承其他类,实现其他接口。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 抽象类
    public abstract class Hero {
    String name;
    float hp;
    float armor;
    int moveSpeed;

    public abstract void attack();
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    public static void main(String[] args) {

    ADHero adh = new ADHero();

    adh.attack();
    System.out.println(adh); // 通过打印 adh,可以看到 adh 这个对象属于 ADHero 类

    // 创建 匿名类
    Hero h = new Hero() {

    @Override
    public void attack() { // 使用一个抽象类,当场实现 attack 抽象方法
    System.out.println("新的进攻手段");
    }
    };
    h.attack();

    System.out.println(h); // 通过打印 h,可以看到 h 这个对象属于 Hero$1 这么一个系统自动分配的类名,这个类就是匿名类。
    }
  • 在匿名类中使用外部的局部变量,外部的局部变量必须修饰为 final

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public static void main(String[] args) {

    // 在匿名类中使用外部的局部变量,外部的局部变量必须修饰为 final
    final int damage = 5;

    Hero h = new Hero() {
    @Override
    public void attack() {
    System.out.printf("新的进攻手段,造成 %d 点伤害", damage);
    }
    };
    }
  • 在 jdk8 中,已经不需要强制修饰成 final 了,如果没有写 final,不会报错,因为编译器偷偷的帮你加上了看不见的 final

1.6 本地类

  • 本地类可以理解为有名字的匿名类

    1
    2
    3
    4
    5
    6
    7
    8
    9
    // 抽象类
    public abstract class Hero {
    String name;
    float hp;
    float armor;
    int moveSpeed;

    public abstract void attack();
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public static void main(String[] args) {

    // 创建 本地类
    class SomeHero extends Hero { // 与匿名类的区别在于,本地类有了自定义的类名
    @Override
    public void attack() {
    System.out.println(name + " 新的进攻手段");
    }
    }

    SomeHero h = new SomeHero();
    h.name = "地卜师";
    h.attack();
    }
  • 内部类与匿名类不一样的是,内部类必须声明在成员的位置,即与属性和方法平等的位置。

  • 本地类和匿名类一样,直接声明在代码块里面,可以是主方法,for 循环里等等地方。

1.7 UML 图

  • UML 图:Unified Module Language,统一建模语言,可以很方便的用于描述类的属性,方法,以及类和类之间的关系。

    • 类图
    • 接口图
    • 继承关系图
    • 实现关系图

2、对象

  • 对象:对象是类的一个实例,有状态和行为。例如,一条狗是一个对象,它的状态有:颜色、名字、品种;行为有:摇尾巴、叫、吃等。

  • 现在让我们深入了解什么是对象。看看周围真实的世界,会发现身边有很多对象,车,狗,人等等。所有这些对象都有自己的状态和行为。

  • 拿一条狗来举例,它的状态有:名字、品种、颜色,行为有:叫、摇尾巴和跑。

  • 对比现实对象和软件对象,它们之间十分相似。

  • 软件对象也有状态和行为。软件对象的状态就是属性,行为通过方法体现。

  • 在软件开发中,方法操作对象内部状态的改变,对象的相互调用也是通过方法来完成。

2.1 创建对象

  • 对象是根据类创建的。在 Java 中,使用关键字 ​new​ 来创建一个新的对象。创建对象需要以下三步:

    • 声明:声明一个对象,包括对象名称和对象类型。
    • 实例化:使用关键字 ​new​ 来创建一个对象。
    • 初始化:使用 ​new​ 创建对象时,会调用构造方法初始化对象。
  • 实例

    1
    2
    3
    4
    5
    6
    7
    public class Puppy {

    // 这个构造器仅有一个参数:name
    public Puppy(String name) {
    System.out.println("Puppy Name is :" + name );
    }
    }
    1
    2
    3
    4
    5
    public static void main (String []args) {

    // 下面的语句将创建一个 Puppy 对象
    Puppy myPuppy = new Puppy("tommy");
    }

2.2 对象转型

  • 对象转型:指当引用类型和对象类型不一致的时候,才需要进行类型转换。类型转换有时候会成功,有时候会失败。

  • 通常情况下,引用类型和对象类型是一样的。

    • 引用类型
    • 对象类型
  • 转换方式

    • 子类转父类 (向上转型) :所有的子类转换为父类,都是说得通的。说得通,就可以转。
    • 父类转子类 (向下转型) :有的时候行,有的时候不行,所以必须进行强制转换。
    • 没有继承关系的两个类,互相转换 :一定会失败。
    • 实现类转换成接口 (向上转型) :转换是能成功的。
    • 接口转换成实现类 (向下转型) :转换会失败。
  • instanceof:判断一个引用所指向的对象。

    1
    2
    // 判断引用 h1 指向的对象,是否是 ADHero 类型
    System.out.println(h1 instanceof ADHero);

3、属性

  • Java 类的属性分为二种

    • 类属性
    • 对象属性
  • 类属性

    • 当一个属性被 static 修饰的时候,就叫做类属性,又叫做静态属性。
    • 当一个属性被声明成类属性,那么所有的对象,都共享一个值。
  • 对象属性

    • 又叫实例属性,非静态属性。
    • 不同对象的 对象属性 的值都可能不一样。
  • 访问类属性

    • 有两种方式,这两种方式都可以访问类属性,访问即修改和获取,但是建议使用第二种 类.类属性 的方式进行,这样更符合语义上的理解。

    • 对象.类属性

      1
      teemo.copyright
    • 类.类属性

      1
      Hero.copyright

3.1 属性初始化

  • 对象属性初始化有三种

    • 声明该属性的时候初始化
    • 构造方法中初始化
    • 初始化块

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      public class Hero {
      public String name = "some hero"; // 声明该属性的时候初始化
      protected float hp;
      float maxHP;

      {
      maxHP = 200; // 初始化块
      }

      public Hero() {
      hp = 100; // 构造方法中初始化
      }
      }
  • 类属性初始化有二种

    • 声明该属性的时候初始化
    • 静态初始化块

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      public class Hero {
      public String name;
      protected float hp;
      float maxHP;

      public static int itemCapacity = 8; // 声明的时候 初始化

      static {
      itemCapacity = 6; // 静态初始化块 初始化
      }
      }
  • 初始化顺序

    • 对于静态变量、静态初始化块、变量、初始化块、构造器,它们的初始化顺序依次是(静态变量、静态初始化块)>(变量、初始化块)> 构造器。

4、方法

  • Java 类的方法分为二种

    • 类方法
    • 对象方法
  • 类方法

    • 又叫做静态方法。被 static 修饰的方法。
    • 访问类方法,不需要对象的存在,直接就访问。
    • 如果一个方法,没有调用任何对象属性,那么就可以考虑设计为类方法。
  • 对象方法

    • 又叫实例方法,非静态方法。
    • 访问一个对象方法,必须建立在有一个对象的前提的基础上。
    • 如果方法里访问了对象属性,那么这个方法,就必须设计为对象方法。
  • 类方法有几个限制

    • 它们只能调用其他静态方法。
    • 它们只能访问静态数据。
    • 他们不能以任何方式引用 this 或 super。
  • 访问类方法

    • 和访问类属性一样,调用类方法也有两种方式,这两种方式都可以调用类方法,但是建议使用第二种 类.类方法 的方式进行,这样更符合语义上的理解。

    • 对象.类方法

      1
      garen.battleWin();
    • 类.类方法

      1
      Hero.battleWin();

4.1 构造方法

  • 通过一个类创建一个对象,这个过程叫做实例化,实例化是通过调用构造方法(又叫做构造器)实现的。

  • 构造方法方法名和类名一样(包括大小写),没有返回类型,实例化一个对象的时候,必然调用构造方法。

  • 每个类都有构造方法。如果没有显式地为类定义构造方法,Java 编译器将会为该类提供一个默认无参的构造方法。

  • 一旦提供了一个有参的构造方法,同时又没有显式的提供一个无参的构造方法,默认的无参的构造方法就失效了。

  • 在创建一个对象的时候,至少要调用一个构造方法。一个类可以有多个构造方法。

  • 和普通方法一样,构造方法也可以重载

  • 子类不能继承父类的构造方法,所以就不存在重写父类的构造方法。

  • 无参构造方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class Hero {
    String name;
    float hp;
    float armor;
    int moveSpeed;

    // 无参构造方法
    public Hero() { // 方法名和类名一样(包括大小写),没有返回类型
    System.out.println("实例化一个对象的时候,必然调用构造方法");
    }
    }
  • 有参构造方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class Hero {
    String name;
    float hp;
    float armor;
    int moveSpeed;

    // 有参构造方法
    public Hero(String heroname) { // 如果没有显式的提供一个无参的构造方法,那么默认的无参构造方法就失效了
    name = heroname;
    }
    }
  • 构造方法重载

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    public class Hero {
    String name;
    float hp;
    float armor;
    int moveSpeed;

    // 无参构造方法
    public Hero() {

    }

    // 带一个参数的构造方法
    public Hero(String heroname) {
    name = heroname;
    }

    // 带两个参数的构造方法
    public Hero(String heroname, float herohp) {
    name = heroname;
    hp = herohp;
    }
    }

4.2 抽象方法

  • 抽象方法:在类中声明一个方法,这个方法没有实现体,是一个 “空” 方法,这样的方法就叫抽象方法,使用修饰符 abstract 修饰。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public abstract class Hero {

    String name;
    float hp;
    float armor;
    int moveSpeed;

    // 抽象方法 attack,Hero 的子类会被要求实现 attack 方法
    public abstract void attack();
    }
  • 实现 抽象方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class ADHero extends Hero {

    public void physicAttack() {
    System.out.println("进行物理攻击");
    }

    // 实现 抽象方法
    @Override
    public void attack() {
    physicAttack();
    }
    }
  • 抽象方法不可同时是 static 的,不可同时是 synchronized

4.3 方法重载

  • 方法重载(Overload):指的是方法名一样,但是参数不一样。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    public class ADHero extends Hero {

    public void attack() {
    System.out.println(name + " 进行了一次攻击 ,但是不确定打中谁了");
    }

    public void attack(Hero h1) {
    System.out.println(name + "对" + h1.name + "进行了一次攻击 ");
    }

    public void attack(Hero h1, Hero h2) {
    System.out.println(name + "同时对" + h1.name + "和" + h2.name + "进行了攻击 ");
    }
    }
  • 可变数量的参数 的方法

    1
    2
    3
    4
    5
    6
    7
    // 可变数量的参数,在方法里,使用操作数组的方式处理参数 heros 即可
    public void attack(Hero... heros) {

    for (int i = 0; i < heros.length; i++) {
    System.out.println(name + " 攻击了 " + heros[i].name);
    }
    }
  • Overload(重载)的方法是否可以改变返回值的类型

    • 可以,重载其实本质上就是完全不同的方法,只是恰好取了相同的名字。

4.4 方法重写(覆盖)

  • 方法重写:子类可以继承父类的 对象方法,在继承后,重复提供该方法,就叫做方法的重写,又叫覆盖 Override

    1
    2
    3
    4
    5
    6
    7
    8
    public class Item {
    String name;
    int price;

    public void effect() {
    System.out.println("物品使用后,可以有效果");
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    public class LifePotion extends Item {

    // 重写父类方法
    @Override
    public void effect() {
    System.out.println("血瓶使用后,可以回血");
    }
    }
  • Overload(重载)和 Override(重写)的区别

    • Overload 是方法重载的意思,指的是在同一个类里面,方法名一样,但是参数不一样。
    • Override 是方法重写的意思,指的是子类继承了父类的某个方法后,重新又写了一遍。

4.5 方法隐藏

  • 方法隐藏:与重写类似,就是子类覆盖父类的 类方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    public class Hero {
    public String name;
    protected float hp;

    // 类方法,静态方法,通过类就可以直接调用
    public static void battleWin(){
    System.out.println("hero battle win");
    }
    }
    1
    2
    3
    4
    5
    6
    7
    public class ADHero extends Hero {

    // 方法隐藏,隐藏父类的 battleWin 方法
    public static void battleWin(){
    System.out.println("ad hero battle win");
    }
    }

4.6 传参

  • 变量有两种类型,基本类型和类类型。参数也是变量,所以传参分为,基本类型传参和类类型传参。

  • 基本类型传参

    • 赋值,在方法内,无法修改方法外的基本类型参数。
  • 类类型传参

    • 引用,类类型又叫引用,在方法内,可以修改方法外的引用类型参数。

5、引用

  • 引用:如果一个变量的类型是 类类型,而非基本类型,那么该变量又叫做引用。

  • 一个引用,同一时间,只能指向一个对象。

    1
    2
    3
    4
    5
    public static void main(String[] args) {

    Hero garen = new Hero();
    garen = new Hero();
    }
  • 多个引用,同一时间,可以指向同一个对象。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public static void main(String[] args) {

    Hero h1 = new Hero(); // 使用一个引用来指向这个对象
    Hero h2 = h1; // h2 指向 h1 所指向的对象
    Hero h3 = h1;
    Hero h4 = h1;
    Hero h5 = h4;

    // h1, h2, h3, h4, h5 五个引用,都指向同一个对象
    }

6、继承

  • 继承:子类拥有父类的属性和方法。继承使用关键字 extends

    1
    2
    3
    4
    public class Item {
    String name;
    int price;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    // Weapon 不继承
    public class Weapon { // 独立设计 name 和 price 属性,同时多了一个属性 damage 攻击力
    String name;
    int price;
    int damage;
    }

    // Weapon 继承自 Item
    public class Weapon extends Item { // 虽然自己没有设计 name 和 price,但是通过继承 Item 类,也具备了 name 和 price 属性
    int damage;
    }

7、多态

  • 多态:同一个类型,调用同一个方法,却能呈现不同的状态。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    public class Item {
    String name;
    int price;

    public void effect() {
    System.out.println("物品使用后,可以有效果");
    }
    }

    public class LifePotion extends Item {

    public void effect(){
    System.out.println("血瓶使用后,可以回血");
    }
    }

    public class MagicPotion extends Item {

    public void effect(){
    System.out.println("蓝瓶使用后,可以回魔法");
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public static void main(String[] args) {

    // 父类(接口)引用指向子类对象
    Item i1 = new LifePotion();
    Item i2 = new MagicPotion();

    // 调用的方法有重写
    System.out.print("i1 是 Item 类型,执行 effect 打印:");
    i1.effect(); // 输出:血瓶使用后,可以回血

    System.out.print("i2 也是 Item 类型,执行 effect 打印:");
    i2.effect(); // 输出:蓝瓶使用后,可以回魔法
    }
  • 操作符的多态:同一个操作符在不同情境下,具备不同的作用。

    • + 可以作为算数运算,也可以作为字符串连接。
  • 类的多态 条件

    • 父类(接口)引用指向子类对象。
    • 调用的方法有重写。

8、包

  • :主要用来对类和接口进行分类。当开发 Java 程序时,可能编写成百上千的类,因此很有必要对类和接口进行分类。把比较接近的类,规划在同一个包下。使用关键字 package

  • 使用同一个包下的其他类,直接使用即可。

  • 使用其他包下的类,必须 import 包名.类名

  • Import 语句

    • 在 Java 中,如果给出一个完整的限定名,包括包名、类名,那么 Java 编译器就可以很容易地定位到源代码或者类。​
    • Import​ 语句就是用来提供一个合理的路径,使得编译器可以找到某个类。
  • 实例

    1
    2
    3
    4
    5
    6
    7
    8
    package charactor;          // 在最开始的地方声明该类所处于的包名

    import property.Weapon; // Weapon 类在其他包里,使用必须进行 import
    import java.io.*;

    public class Hero {

    }

9、接口

  • 接口:接口就像是一种约定,实现某个接口,就相当于承诺了某种约定。

  • 定义接口

    • 接口在语法上使用关键字 interface
    • 声明一个方法,但是没有方法体,是一个 “空” 方法。

      1
      2
      3
      4
      public interface AD {

      public void physicAttack(); // 声明一个 “空” 方法
      }
  • 实现接口

    • 实现在语法上使用关键字 implements
    • 实现某个接口,就相当于承诺了某种约定。

      1
      2
      3
      4
      5
      6
      7
      public class Hero implements AD {                   // 实现接口

      @Override
      public void physicAttack() { // 实现了 AD 这个接口,就必须提供 AD 接口中声明的方法
      System.out.println("进行物理攻击");
      }
      }
  • abstract class(抽象类)和 interface(接口)的区别

    • 相同

      • 抽象类和接口都可以有实体方法。
    • 使用方式

      • 抽象类只能够通过继承被使用。子类只能继承一个抽象类,不能继承多个。
      • 接口必须通过实现被使用。子类可以实现多个接口。
    • 实现方法

      • 抽象类不仅可以提供抽象方法,也可以提供实现方法。
      • 接口只能提供抽象方法,不能提供实现方法。但是在 Java8 版本开始,接口可以提供实现方法了,前提是要在方法前加一个 default 修饰符。
    • 属性定义

      • 抽象类可以定义
        • public, protected, package, private
        • 静态和非静态属性
        • final 和非 final 属性
      • 但是接口中声明的属性,只能是以下类型,即便没有显式的声明
        • public
        • 静态
        • final 的
  • 接口是否可继承接口?

    • 可以,比如 List 就继承了接口 Collection。
  • 抽象类是否可实现(implements)接口?

    • 可以,比如 MouseAdapter 鼠标监听适配器 是一个抽象类,并且实现了 MouseListener 接口。
  • 抽象类是否可继承实体类(concrete class)?

    • 可以,所有抽象类,都继承了 Object。

9.1 接口默认方法

  • 接口默认方法:JDK8 新特性,指的是接口也可以提供具体方法了,而不像以前,只能提供抽象方法,这个方法有实现体,并且被声明为了 default

    1
    2
    3
    4
    5
    6
    7
    8
    public interface Mortal {
    public void die();

    // 接口默认方法,声明为 default
    default public void revive() {
    System.out.println("本英雄复活了");
    }
    }
  • 引入了默认方法后,原来的类,不需要做任何改动,并且还能得到这个默认方法。

10、修饰符

  • 下表显示了功能和修改器的所有可能组合。yes 表示我们可以使用该修饰符来控制对应实体的访问。
修饰符 变量 方法 构造函数 代码块
public yes yes yes yes no
protected no yes yes yes no
empty accessor yes yes yes yes yes
private no yes yes yes no
final yes yes yes no no
abstract yes no yes no no
static no yes yes no yes
native no no yes no no
transient no yes no no no
volatile no yes no no no
synchronized no no yes no yes

10.1 基本修饰符

10.1.1 static

  • static 表示 “全局” 或者 “静态” 的意思,用来修饰成员变量和成员方法,也可以形成静态 static 代码块。

  • 被 static 修饰的成员变量和成员方法独立于该类的任何对象。也就是说,它不依赖类特定的实例,被类的所有实例共享。

  • 只要这个类被加载,Java 虚拟机就能根据类名在运行时数据区的方法区内定找到他们。因此,static 对象可以在它的任何对象创建之前访问,无需引用任何对象。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public class Hero() {

    // 静态属性
    private static int intValue;

    // 静态方法
    public static void aStaticMethod() {
    }

    // JVM 在运行 main 方法的时候可以直接调用而不用创建实例
    public static void main(String[] args) {
    }
    }
  • 用 static 修饰的代码块表示静态代码块,当 Java 虚拟机加载类时,就会执行该代码块。是在类中独立于类成员的 static 语句块,可以有多个,位置可以随便放,它不在任何的方法体内。

  • 如果 static 代码块有多个,JVM 将按照它们在类中出现的先后顺序依次执行它们,每个代码块只会被执行一次。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    public class Test {
    private static int a;
    private int b;

    public void f() {
    System.out.println("hello");
    }

    // 静态代码块
    static {
    Test.a = 3;

    Test t = new Test();
    t.f();
    t.b = 1000;
    }

    // 静态代码块
    static {
    Test.a = 4;
    }
    }
  • static 修饰 类、属性、方法、代码块 的时候分别有不同的意思。


























修饰类型 说明
静态类
属性 类属性,又叫做静态属性
方法 类方法,又叫做静态方法
代码块 表示静态代码块,当 Java 虚拟机加载类时,就会执行该代码块

10.1.2 final

  • final 修饰一个变量,有很多种说法,比如不能改变等等。

  • 准确的描述是 当一个变量被 final 修饰的时候,该变量只有一次赋值的机会。

  • final 修饰的变量在方法中,可以先初始化再赋值。但是如果是全局变量,必须在初始化的时赋值,不然会报错。

    1
    2
    // 全局变量
    public final int itemTotalnumber = 6;
  • static 和 final 一块用来修饰成员变量和成员方法,可简单理解为 “全局常量”。

  • 对于变量,表示一旦给值就不可修改,并且通过类名可以访问。

  • 对于方法,表示不可覆盖,并且可以通过类名直接访问。

    1
    2
    // 全局常量
    public static final int HOURS_OF_DAY = 24;
  • final 修饰 类、方法、基本类型变量、引用 的时候分别有不同的意思。


























修饰类型 说明
该类不能够被继承
方法 该方法不能够被重写
引用 该引用只有一次指向对象的机会
基本类型变量 该变量只有一次赋值机会

10.1.3 abstract

  • abstract 修饰抽象类、抽象方法。

10.1.4 this

  • this 这个关键字,相当于普通话里的 “我”,this 即代表当前对象。
  • 可以在任何方法中使用来引用当前对象。

  • 通过 this 访问属性

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    public class Hero {
    String name;
    float hp;

    // 参数名和属性名一样,在方法体中,只能访问到参数 name
    public void setName1(String name) {
    name = name;
    }

    // 为了避免 setName1 中的问题,参数名不得不使用其他变量名
    public void setName2(String heroName) {
    name = heroName;
    }

    // 通过 this 访问属性
    public void setName3(String name) {

    // name 代表的是参数 name,this.name 代表的是属性 name
    this.name = name;
    }
    }
  • 通过 this 调用其他的构造方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    public class Hero {
    String name;
    float hp;

    // 带一个参数的构造方法
    public Hero(String name) {
    this.name = name;
    }

    // 带两个参数的构造方法
    public Hero(String name,float hp) {
    this.hp = hp;

    // 在一个构造方法中,调用另一个构造方法
    this(name);
    }
    }

10.1.5 super

  • super 父类。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class Hero {
    String name;
    float hp;
    float armor;
    int moveSpeed;

    public void useItem(Item i) {
    System.out.println("hero use item");
    i.effect();
    }
    }
  • 实例化子类,父类的构造方法一定会被调用,并且是父类构造方法先调用,子类构造方法会默认调用父类的无参的构造方法。

    1
    2
    3
    4
    public ADHero(String name) {
    super(name); // 子类显式调用父类带参构造方法
    System.out.println("AD Hero 的构造方法");
    }
  • 调用父类属性。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    public class ADHero extends Hero {
    int moveSpeed = 400;

    public int getMoveSpeed() {
    return this.moveSpeed;
    }

    public int getMoveSpeed2() {
    return super.moveSpeed; // 调用父类属性
    }
    }
  • 调用父类方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    public class ADHero extends Hero {

    int moveSpeed = 400;

    public int getMoveSpeed() {
    return this.moveSpeed;
    }

    public int getMoveSpeed2() {
    return super.moveSpeed;
    }

    // 重写 useItem,并在其中调用父类的 userItem 方法
    public void useItem(Item i) {
    System.out.println("adhero use item");
    super.useItem(i);
    }
    }

10.2 访问修饰符

  • 访问修饰符的使用

    • 属性通常使用 private 封装起来。
    • 方法一般使用 public 用于被调用。
    • 会被子类继承的方法,通常使用 protected
    • package 用的不多,一般新手会用 package,因为还不知道有修饰符这个东西。
  • 成员变量有四种修饰符

修饰符 说明
private 私有的
package/friendly/default 不写,没有修饰符即代表 package/friendly/default
protected 受保护的
public 公共的
  • 作用域
修饰符 自身 同包子类 不同包子类 同包类 其他类
private 访问 不能继承 不能继承 不能访问 不能访问
package 访问 继承 不能继承 访问 不能访问
protected 访问 继承 继承 访问 不能访问
public 访问 继承 继承 访问 访问
  • 作用范围最小原则

    • 简单说,能用 private 就用 private,不行就放大一级,用 package,再不行就用 protected,最后用 public
    • 这样就能把数据尽量的封装起来,没有必要露出来的,就不用露出来了。

11、运算符

  • Java 提供了运行时运算符 instanceof 来检查对象的类类型。

  • 判断一个引用所指向的对象。

    1
    2
    // 判断引用 h1 指向的对象,是否是 ADHero 类型
    System.out.println(h1 instanceof ADHero);
文章目录
  1. 1. 前言
  2. 2. 1、类
    1. 2.1. 1.1 类变量
    2. 2.2. 1.2 Object 类
    3. 2.3. 1.3 抽象类
    4. 2.4. 1.4 内部类(嵌套类)
    5. 2.5. 1.5 匿名类
    6. 2.6. 1.6 本地类
    7. 2.7. 1.7 UML 图
  3. 3. 2、对象
    1. 3.1. 2.1 创建对象
    2. 3.2. 2.2 对象转型
  4. 4. 3、属性
    1. 4.1. 3.1 属性初始化
  5. 5. 4、方法
    1. 5.1. 4.1 构造方法
    2. 5.2. 4.2 抽象方法
    3. 5.3. 4.3 方法重载
    4. 5.4. 4.4 方法重写(覆盖)
    5. 5.5. 4.5 方法隐藏
    6. 5.6. 4.6 传参
  6. 6. 5、引用
  7. 7. 6、继承
  8. 8. 7、多态
  9. 9. 8、包
  10. 10. 9、接口
    1. 10.1. 9.1 接口默认方法
  11. 11. 10、修饰符
    1. 11.1. 10.1 基本修饰符
      1. 11.1.1. 10.1.1 static
      2. 11.1.2. 10.1.2 final
      3. 11.1.3. 10.1.3 abstract
      4. 11.1.4. 10.1.4 this
      5. 11.1.5. 10.1.5 super
    2. 11.2. 10.2 访问修饰符
  12. 12. 11、运算符
隐藏目录