面向对象

1
2
3
4
5
6
7
8
9
10
11
public class 类名 {
成员变量(代表属性,一般是名词)

成员方法(代表行为,一般是动词)

构造器

代码块

内部类
}
  • 例如

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public class test1.Phone {
    // 属性(成员变量)
    String brand;
    double price;

    // 行为(方法)
    public void call() {
    ...
    }
    public void playGame() {
    ...
    }
    }

类的对象

  • 类名 对象名 = new 类名();

  • 例如: Phone p = new Phone();

成员变量

  • 修饰符 数据类型 变量名称 = 初始化值

    一般无需指定初始化值,存在默认值。

封装

对象代表什么,就得封装对应的数据,并提供数据对应的行为

  • 例如:
    人画圆: 两个类–人、圆 画行为则属于圆类人关门: 两个类–人、门 关门行为则属于门类

  • 我们可以把一些零散的数据封装成一个对象, 以后传递参数的时候, 只要传递一个整体就可以了, 不需要管这些零散的数据

  • 例如:

    • 可避免以后要新增检查的信息

      1
      2
      User userInfo = new User(username, password, null, null);
      checkUserInfo(userInfo);
    • 无法避免

      1
      checkUserInfo(username, password);

构造方法

1
2
3
4
5
public class 类名() {
修饰符 类名(参数) {
方法体;
}
}
  • 作用: 给成员变量初始化

  • 特点

    1. 方法名与类名相同,大小写也要一致
    2. 没有返回值类型,连void都没有
    3. 没有具体的返回值 (不能由return带回结果数据)
  • 执行实际
    1. 创建对象的时候由虚拟机调用,不能手动调用构造方法
    2. 每创建一次对象,就会调用一次构造方法

  • 注意事项

    • 构造方法的定义:

      1. 如果没有定义构造方法,系统将给出一个默认的无参数构造方法
      2. 如果定义了构造方法,系统将不再提供默认的构造方法
    • 构造方法的重载:

      ​ 带参构造方法和无参构造方法,两者方法名相同,但是参数不同,这叫做构造方法的重载

    • 推荐的使用方式:

      ​ 无论是否使用,都手动书写无参和带全部参数的构造方法

      • 例如:

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        public class Student {
        private String name;
        private int age;
        public Student() {
        ...(空参构造)
        }
        public Student(String name, int age) {
        ...(带参构造)
        }
        }

标准的JavaBean类

  • 类名需要见名知意

  • 成员变量使用private修饰

  • 提供至少两个构造方法

  • 成员方法

    • 提供每一个成员变量对应的setXXX()/getXXX()Alt + Insert 或 右键PTG_to_JavaBean 自动补全
    • 如果还有其他行为,也需要写上

对象内存图

  • Java内存分配

    • 栈: 方法运行时所进的内存,变量也是在这里

    • 堆: new出来的东西会在这块内存中开辟空间并产生地址

    • 方法区: 字节码文件(包含类中所有的成员变量和成员方法的信息)加载时进入的内存

    • 本地方法栈

    • 寄存器

  • 一个对象的内存图:
    Student s = new Student()

    1. 加载class文件 (把Student类的字节码文件加载到内存中)
    2. 申明局部变量 (对s申明)
    3. 在堆内存中开辟一个空间 (new开辟空间)
      1. 默认初始化/显示初始化
      2. 构造方法初始化
    4. 将堆内存中的地址值赋值给左边的局部变量 (即s是一个地址)
  • 两个对象的内存图:

    Student s = new Student()

    Student s1 = new Student()

    • 此时不需要再加载Student.class文件,其他步骤一样

    • 注意: 一个方法执行完毕后要出栈(main也是方法),方法出栈后指向堆的指针消失,堆中的地址没有变量指向,成为垃圾,也消失

this

  • this的作用: 区分局部变量和成员变量

  • this的本质: 代表所在方法调用者的地址值

  • 在非静态方法的参数中, 隐藏了变量this, 该this是由虚拟机赋值的

成员和局部变量

区别 成员变量 局部变量
类中位置不同 类中, 方法外 方法内, 方法申明上
初始化值不同 有默认初始值 没有默认初始值, 使用之前需要完成赋值
内存位置不同 堆内存 栈内存
生命周期不同 随着对象的创建而存在, 随着对象的消失而消失 随着方法的调用而存在, 随着方法的运行结束而消失
作用域 整个类中有效 当前方法中有效
  • 例如

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    public class Student {
    // 成员变量
    private String name;
    private int age;
    }
    public class Test {
    public static void main(String[] args) {
    // 局部变量
    int a = 10;
    new Student();
    }
    }

static

  • static表示静态, 是Java中的一个修饰符, 可以修饰成员变量, 成员方法

  • 静态变量: 被static修饰的成员变量

    • 特点:
      • 被该类所有对象共享
      • 不属于对象, 属于类
      • 随着类的加载而加载, 优先于对象出现
    • 调用方式:
      • 类名调用 (推荐)
      • 对象名调用
    • image-20250118233639912
  • 静态方法: 被static修饰的成员方法, 叫做静态方法

    • 特点:

      • 多用在测试类和工具类中

        • 工具类: 帮助我们做一些事情的, 但是不描述任何事物的类
          • 类名见名知意
          • 私有化构造方法
          • 方法定义为静态
        1
        2
        3
        4
        5
        6
        7
        8
        public class ArrUtil {
        private ArrUtil() {}

        public static int getMax(...) {...}
        public static int getMin(...) {...}
        public static int getSum(...) {...}
        public static int getAvg(...) {...}
        }
      • Javabean类中很少会用

    • 调用方式:

      • 类名调用 (推荐)
      • 对象名调用
  • 注意事项:

    • 静态方法只能访问静态变量和静态方法
    • 非静态方法可以访问所有
    • 静态方法中没有this关键字

继承

  • public class 子类 extends 父类 {}

  • 好处

    • 可以把多个子类中重复的代码抽取到父类中, 提高代码的复用性
    • 子类可以在父类的基础上, 增加其他的功能, 使子类更强大
  • 什么时候用继承?

    • 当类与类之间存在相同的内容, 并满足子类是父类中的一种, 就可以考虑使用继承, 来优化代码
  • 特点

    • Java只支持单继承, 不支持多继承, 但支持多层继承
    • 每一个类都直接或间接继承与Object类
  • 子类能继承父类中的哪些内容?

    构造方法 成员变量 成员方法
    非私有 不能 能加入虚方法表的, 能
    private 不能 能, 但不能用 不能
    • 只有父类中的虚方法才能被子类继承, 并不是一级一级向上访问

      image-20250118233655755成员变量的访问特点

    • 就近原则: 先在局部位置找, 本类成员位置找, 父类成员位置找, 逐级往上

  • 成员方法的访问特点

    • 就近原则

    • 方法重写

      • 当父类的方法不能满足子类现在的需求时, 需要进行方法重写
      • 方法声明与父类一致
      • @Override 重写注释
    • 方法重写的本质

      image-20250118233705905

    • 注意事项

      • 子类重写父类方法时, 访问权限子类必须大于等于父类 (空着不写 < protected < public)
      • 子类重写父类方法时, 返回值类型子类必须小于等于父类
      • 重写方法尽量与父类保持一致
      • 只有被添加到虚方法表中的办法才能被重写
  • 构造方法的访问特点

    • 父类中的构造方法不会被子类继承
    • 子类中所有的构造方法默认先访问父类中的无参构造, 再执行自己
    • 子类构造方法的第一行语句默认都是: super(), 不写也存在, 且必须在第一行
    • 如果想调用父类的有参构造, 必须手写super进行调用
  • this, super使用总结

    image-20250118233715165

    • 在构造方法中调用本类的其他构造方法, 虚拟机不会再添加super(), 因为调用的其他构造方法中也默认有super(), 且this()和super()一样只能在第一行

      image-20250118233722842

多态

  • 同类型的对象, 表现出的不同形态

  • 表现形式: 父类类型 对象名称 = 子类对象;

  • 前提

    • 有继承关系
    • 有父类引用指向子类对象
    • 有方法重写
  • 好处

    • 使用父类型作为参数, 可以接受所有子类对象, 体现多态的扩展性与便利性
  • 调用成员的特点

    • 变量调用: 编译看左边, 运行也看左边

      • javac 编译代码的时候会看左边的父类中有没有这个变量, 如果有, 编译成功, 如果没有则编译失败
      • java 运行代码的时候, 实际获取的就是左边父类中成员变量的值
    • 方法调用: 编译看左边, 运行看右边

      • javac 编译代码的时候会看左边的父类中有没有这个方法, 如果有, 编译成功, 如果没有则编译失败
      • java 运行代码的时候, 实际获取的就是子类中的方法
    • 内存图解

      image-20250118233729555

  • 优势

    • 在多态形式下, 右边对象可以实现解耦合, 便于扩展和维护
    • 定义方法的时候, 使用父类型作为参数, 可以接收所有子类对象, 体现多态的便利性和扩展性
  • 弊端

    • 不能使用子类的特有功能

    • 解决方法: 强制类型转换

      image-20250118233737111

  • 包就是文件夹, 用来管理各种不同功能的 Java 类, 方便后期代码维护

  • 命名规则: 公司域名反写 + 包的作用, 需要全部英文小写, 见名知意 (例如: com.itheima.domain)

  • 使用其他类的规则: 使用其他包的类时, 需要使用全类名 (包名.类名) 或导包 (import)

    • 使用同一个包中的类时, 不需要导包
    • 使用 java.lang 包中的类时, 不需要导包
    • 其他情况都需要导包
    • 如果同时使用两个包中的同名类, 需要使用全类名

final

  • 修饰方法: 表明方法是最终方法, 不能被重写

  • 修饰类: 表明该类是最终类, 不能被继承

  • 修饰变量: 叫做常量, 只能被赋值一次

  • 命名规则

    • 单个单词: 全部大写
    • 多个单词: 全部大写, 单词之间用下划线隔开
  • 修饰基本数据类型: 记录的值不能发生改变

  • 修饰引用数据类型: 记录的地址值不能发生改变, 内部的属性值还是可以改变

权限修饰符

image-20250118233748669

代码块

  • 局部代码块 (用于节约内存, 现在已经淘汰了)

  • 构造代码块 (渐渐被淘汰了)

    • 写在成员位置的代码块
    • 可以把多个构造方法中重复的代码抽取出来
    • 执行时机: 在创建对象时先执行构造代码块, 再执行构造方法
  • 静态代码块

    • 格式: static{ ... }
    • 特点: 需要通过static关键字修饰, 随着类的加载而加载, 并且自动触发, 只执行一次
    • 使用场景: 在类加载的时候, 做一些数据初始化的时候使用

抽象类

  • 抽象方法:共性的行为 (方法) 抽取到父类之后, 由于每一个子类执行的内容不一样, 在父类中不能确定具体的方法体, 该方法就可以定义为抽象方法

    • 定义格式: public abstract 返回值类型 方法名(参数列表);
  • 抽象类: 如果一个类中存在抽象方法, 该类就必须声明为抽象类

    • 定义格式: public abstract class 类名{}
    • 强制子类按某种格式重写方法
  • 注意事项

    • 抽象类不能实例化
    • 抽象类中不一定有抽象方法, 有抽象方法的类一定是抽象类
    • 可以有构造方法
    • 抽象类的子类
      • 要么重写抽象类中所有的抽象方法
      • 要么是抽象类

接口

image-20250118233757634

  • 接口是一种规则, 对行为的抽象, 想让哪个类拥有一个行为, 就让这个类实现对用的接口

  • 当一个方法的参数是接口时, 可以传递接口所有实现类的对象, 这种方法称为接口多态

  • 注意事项

    • 接口用关键字 interface 来定义 public interface 接口名{}
    • 接口不能实例化, 但是用接口的实现类可以实例化,将实现类的对象在内存中的地址指向接口,这个接口就可以使用了
      • 接口 实例 = new 实现接口的类();
    • 接口和类之间是实现关系, 通过 implements 关键字表示 public class 类名 implements 接口名{}
      • 可以单实现, 也可以多实现 public class 类名 implements 接口名1, 接口名2{}
      • 实现类还可以在继承一个类的同时实现多个接口 public class 类名 extends 父类 implements 接口名1, 接口名2{}
    • 接口的子类 (实现类)
      • 要么重写接口中所有的抽象方法
      • 要么是抽象类
  • 接口中成员的特点

    • 成员变量
      • 只能是常量
      • 默认修饰符: public static final
    • 构造方法: 没有
    • 成员方法
      • 只能是抽象方法
      • 默认修饰符: public abstract
  • 接口和接口的关系

    • 继承关系, 可以单继承, 也可以多继承
  • JDK8以后接口中新增的方法

    • 允许在接口中定义默认方法, 需要使用关键字 default 修饰

      • 作用: 解决接口升级问题
      • 接口中默认方法的定义格式
        • 格式: public default 返回值类型 方法名(参数列表) {}
        • 范例: public default void show() {}
      • 接口中默认方法的注意事项
        • 默认方法不是抽象方法, 不强制重写, 但如果被重写, 重写时要去掉 default 关键字
        • public可以省略, default不能省略
        • 如果实现了多个接口, 多个接口中存在相同名字的默认方法, 子类就必须对该方法进行重写
    • 允许在接口中定义静态方法, 需要用关键字 static 修饰

      • 接口中静态方法的定义格式

        • 格式: public static 返回值类型 方法名(参数列表) {}
        • 范例: public static void show() {}
      • 接口中默认方法的注意事项

        • 静态方法只能通过接口名调用, 不能通过实现类名或者对象名调用
        • public可以省略, static不能省略
  • JDK9接口中新增的方法

    • 接口中私有方法的定义格式

      • 默认方法服务

        • 格式: private 返回值类型 方法名(参数列表) {}
        • 范例: private void show() {}
      • 静态方法服务

        • 格式: private static 返回值类型 方法名(参数列表) {}

        • 范例: private static void show() {}

  • 适配器设计模式

    • 当一个接口中抽象方法过多, 但只要使用其中一部分的时候, 可以使用适配器设计模式
    • 书写步骤
      • 编写中间类 XXXAdapter, 实现对应的接口
      • 对接口中的抽象方法进行空实现
      • 让真正的实现类继承中间类, 并重写需要用的方法
      • 为了避免其他类创建适配器类的对象, 中间的适配器类用 abstract 进行修饰

内部类

  • 在一个类的里面再定义一个类

  • 内部类表示的事物是外部类的一部分, 单独存在没有意义

  • 内部类可以直接访问外部类的成员, 包括私有

  • 外部类要访问内部类的成员, 必须创建对象

  • 成员内部类

    • 写在成员位置, 属于外部类的成员

    • 成员内部类可以被一些修饰符所修饰, 比如: private, 默认, protected, public, static等

    • 在成员内部类里面, JDK16开始可以定义静态变量

    • 获取成员内部类对象

      • 方式一: 在外部类编写方法, 对外提供内部类对象 (当内部类被private修饰时使用)
      • 方式二: 外部类名.内部类名 对象名 = 外部类对象.内部类对象;
        • 范例: Outer.Inner oi = new Outer().new Inner();
    • 内存图

      image-20250118233810778

  • 静态内部类

    • 静态内部类只能访问外部类中的静态变量和静态方法, 如果想要访问非静态的需要创建对象

    • 静态内部类也是成员内部类中的一种

    • 创建静态内部类对象的格式: 外部类名.内部类名 对象名 = new 外部类名.内部类名();

    • 调用静态内部类中的方法

      • 调用非静态方法的格式: 先创建对象, 用对象调用
      • 调用静态方法的格式: 外部类名.内部类名.方法名()
  • 局部内部类

    • 将内部类定义在方法里面, 类似于方法里面的局部变量
    • 外界是无法直接使用局部内部类, 需要在方法内创建内部类对象并使用
    • 该类可以直接访问外部类的成员, 也可以访问方法内的局部变量
  • 匿名内部类

    • 隐藏名字的内部类, 可以写在成员位置, 也可以写在局部位置

    • 格式: new 类名或接口名() { 重写方法; };

      1
      2
      3
      4
      5
      new Inter() {
      public void show() {
      // 方法体
      }
      };
      • 包含了继承或实现, 方法重写, 创建对象

      • 整体就是一个类的子类对象或者接口的实现类对象

    • 使用场景

      • 当方法的参数是接口或类时, 以接口为例, 可以传递这个接口的实现类对象, 如果实现类只要使用一次, 就可以用匿名内部类简化代码