7 面向对象进阶篇

static

static修饰成员变量

  1. static 是什么?

    • 叫做静态,可以修饰成员变量、成员方法
  2. satic修饰的成员变量叫什么?怎么使用?特点?

    • 类变量(静态变量)
    • 类名.类变量(推荐)
    • 对象名.类变量(不推荐)
    • 属于类,与类一起加加载一次,在内存中只有一份,会被类的所有对象所共享
  3. 无static修饰的成员变量叫什么?怎么使用?特点?

    • 实例变量(对象的变量)
    • 对象.实例变量
    • 属于对象,每个对象中都有一份

image-20260204154125482

类变量的应用场景(以及与实例变量的区别)

  1. 类变量和实例变量均是成员变量

    • 类变量:数据只需要一份,且需要被共享(访问、修改...)
    • 实例变量:每个对象都有一份,数据各不相同
  2. 类变量的使用特点

    • 当访问自己类中的类变量时,可以省略类名不写
    • 但是在某个类中访问其他类里的类变量时,必须是完整格式(类名.类变量)

static修饰成员方法

  1. static修饰的成员方法叫什么?怎么使用?特点?

    • 类方法(静态方法)
    • 类名.类方法(推荐)
    • 对象名.类方法(不推荐)
    • 属于类,可以直接用类名访问,也可以用对象访问
  2. 无static修饰的成员方法叫什么?怎么使用?特点?

    • 实例方法(对象的方法)
    • 对象.实例方法变量
    • 属于对象,每个对象中都有一份

image-20260204173252980

类方法的应用场景

可以用来设计工具类

  • 什么是工具类?有什么优点?

    • 工具类中的方法都是类方法,每个类方法都是用来完成一个功能
    • 提高了代码的复用性;调用方便,提高了开发效率
  • 为什么工具类要用类方法而不是实例方法?

    • 工具类中的方法仅仅只是为了调用
    • 而实例方法需要创建对象来调用,会浪费内存
  • 工具类定义时有什么要求?

    • 工具类不需要创建对象,建议将工具类的构造器私有化

static的注意事项

  1. 类方法中可以直接访问类的成员,不可以直接访问实例成员
  2. 实例方法中既可以直接访问类成员,也可以直接访问实例成员
  3. 实例方法中可以出现this关键词,类方法中不可以
  4. 成员指变量or方法

static的应用知识

代码块

  • 代码块是类的五大成分之一(成员变量、构造器、方法、代码、内部类)
  1. 静态代码块:

    • 格式:static
    • 特点:类加载时自动执行,由于类只会加载一次,所以静态代码块也只会执行一次
    • 作用:完成类的初始化。例如:对类变量的初始化赋值
  2. 实例代码块:

    • 格式:
    • 特点:每次创建对象时,执行实例代码块,并且在构造器之前执行
    • 作用:完成对象的初始化。例如:对实例变量进行初始化赋值(多数情况下对实例变量进行初始化赋值无意义)

image-20260205000347230

设计模式

  • 什么是设计模式?
    • 设计模式就是具体问题的最优解法

单例设计模式

  • 特点:确保一个类只有一个对象
  • 好处:在某些业务场景下,使用单例模式,可以避免浪费内存
饿汉式单例
  • 特点:在获取对象时,对象已经创建好了
  • 写法:
    1. 私有化类的构造器
    2. 定义一个类变量用与存储对象
    3. 定义一个类方法,用于返回对象

懒汉式单例
  • 特点:拿对象时,才开始创建对象

  • 写法:

    1. 私有化类的构造器

    2. 定义一个类变量用与存储对象

    3. 定义一个类方法,用于返回对象

image-20260205004700444

面向对象三大特征之二:继承

  • 什么是继承?特点?好处?
    • 继承就是用extends关键字,让一个类和另一个类建立起一种父子关系(子类 extends 父类)
    • 子类可以继承(使用)父类的非私有的成员(变量、方法)
    • 减少重复代码的编写

image-20260205012225174

  • 带继承关系的类,java会怎么创建它的对象?该对象又可以直接访问哪些成员?
    • java会用子类和其父类,多张设计图一起创建对象
    • 该对象可以访问,该多张设计图对外暴露的成员

image-20260205011209736

继承相关的注意事项

权限修饰符

  • 权限修饰符是什么?

    • 用来限制类中的成员能够访问的范围
  • 有几种?各自有什么作用?

image-20260205162854144

单继承

  • Java是单继承的。Java中的类不支持多继承,但是支持多层继承
    • (单继承:一个儿子只有一个亲爸爸;多继承:一个儿子有多个亲爸爸;多态继承:儿子有爸爸,爸爸也有爸爸。)

Object类

  • object类是java所有类的祖宗类。
    • (定义任何一个类时,如果没有指定继承某个类,则该类默认继承object类)

方法重写

  • 什么是方法重写?
    • 子类可以重写一个方法名称、参数列表和父类一样的方法,去覆盖原来父类中的同名方法。
    • Java有就近原则,当方法重写后,Java会访问离自己更近的方法。

方法重写的注意事项

  1. 方法重写时应标注@Override注解,提升代码可读性。
  2. 子类重写父类方法时,访问权限必须>=父类该方法的权限(public>protected>缺省)
  3. 重写方法的返回值类型范围应>=原方法的返回值类型范围
  4. 私有方法(private)、静态方法(static)不能被重写

应用场景

  • 子类重写Object类中的tostring()方法,以便返回对象内容。

方法重写前(返回对象地址)

image-20260205172801337

方法重写后(返回对象内容)

image-20260205172918500

子类访问其他成员的特点

就近原则

image-20260205174505659

子类访问成员方法与访问成员变量无异(就近原则)

子类构造器

  • 特点?

    • 子类的全部构造器,必须先调用父类的构造器,再执行自己的构造器。
    • 子类构造器第一行会默认生成super()代码,来调用父类无参构造器。
    • 如果父类只有有参构造器,则需要手写super(数据)调用父类的有参构造器,否则报错。
  • 应用场景?

    • image-20260206000412782
  • 补充:

    • 任意类的构造器中,可以通过this(...)去调用该类的其他构造器。

image-20260206001114534

  • this () 和super () 都必须出现在构造器的第一行且不能同时出现在同一个构造器中!

面向对象三大特征之三:多态

  • 什么是多态?

    • 多态是在继承/实现情况下的一种现象,表现为:对象多态、行为多态。
  • 多态的具体代码体现

    • People p1 = new Student();
      p1.run();
      
      People p2 = new Teather();
      p2.run();
      
  • 多态的前提

    • 有继承/实现关系;存在父类引用子类对象;存在方法重写。
  • 多态的注意事项

    • 多态是对象、行为的多态。Java中的属性(成员变量)不谈多态。

image-20260206133351155

  • 多态邪修

    • 编译看左边,运行看右边!
  • 多态的好处?

    • 多态形式下,右边对象是解耦合的,更便于扩展和维护。
    • 使用父类类型的变量作为方法的形参时,可以接收一切子类对象。
  • 多态的缺点:

    • 多态下不能直接调用子类的独有方法。

image-20260206134958940**** image-20260207000514205

类型转换

  • 自动类型转换

    • 父类 变量名 = new 子类();
  • 强制类型转换

    • 子类 变量名 = (子类) 父类变量;

强制转换的注意事项

  • 存在继承/实现关系就可以在编译阶段进行强制类型转换,编译阶段不会报错。
  • 运行时,如果发现对象的真实类型与强转后的类型不同,就会报错(ClassCastException)

image-20260207004140735****

  • 强制类型转换前,使用instanceof关键字,判断当前对象的真实类型,再进行强转。
    • p instanceof Student

image-20260207005109098****

image-20260207005137438****

final

  • 可以修饰(类、方法、变量)
    • 修饰类:该类被称为最终类,特点是不能被继承。
    • 修饰方法:该方法被称为最终方法,特点是不能被重写。
    • 修饰变量:加上static修饰的话就被称为常量,特点是只能被初始化赋值一次。
      • final修饰变量的注意事项
        • final修饰基本类型的变量,变量存储的数据不能被改变。
        • final修饰引用类型的变量,变量存储的地址不能被改变,但地址所指向的对象内容是可以被改变的。
          • 两种类型(基本类型和引用类型)的内容,请查看1.语法篇中的数据类型小结

常量

  • 使用static final修饰的成员变量即为常量。
  • 作用:通常用于记录系统的配置信息。
  • 命名规范见1.语法篇中的变量小结

常量的优点

  • 代码可读性好,可维护性更高。
  • 性能更高(宏替换)

抽象类

  • 抽象类是什么?

    • 用abstract修饰的类叫抽象类
  • 抽象方法是什么?

    • 用abstract修饰,且只有方法签名,没有方法体。
  • 抽象类的注意事项

    • 抽象类可以不写抽象方法,但有抽象方法的类一定是抽象类。
    • 类有的成员(成员变量,方法,构造器)抽象类都具备。
    • 抽象类不能创建对象,仅作为一种特殊的父类,让子类继承并实现。
    • 一个类继承抽象类,必须重写完抽象类的全部抽象方法,否则这个类也必须定义为抽象类。
  • 好处

    • 父类知道每个子类都要做某个行为,但每个子类要做的情况不一样,父类就定义成抽象方法,交给子类去重写实现。
      • 抽出这样的抽象类,就是为了更好的支持多态。(抽象类更像是一个强制性的模板,子类必须要完成的模板。)

抽象类的应用场景

模板方法设计模式

(建议使用final关键字修饰模板方法,以防止子类重写父类的模板方法)

  • 定义一个抽象类
  • 在该抽象类中定义两个方法
    • 模板方法:即为子类中出现的相同代码
    • 抽象方法:即为子类中出现的必须要完成的,但每个子类要完成的内容不同的方法。
image-20260207170215541

多态补充

image-20260207170434774

接口

  • 什么是接口?

    • Java提供了一个关键字interface,用该关键字可以定义一个特殊的结构:接口

    • public interface 接口名{
          //成员变量(默认常量)
          //成员方法(默认抽象方法)
      }
      
  • 注意:接口不能创建对象;接口是用来实现(implements)的,实现接口的类称为实现类。

    • 修饰符 class 实现类 implements 接口1,接口2,...{
      }
      
  • 一个类可以实现多个接口(接口可以理解为干爹,实现(implements)不同于继承(extends),前者可以有多个,而后者只能有一个)

    • 实现类实现多个接口时,必须成写完全部接口的全部抽象方法,否则实现类要定义为抽象类。
  • 好处?

    • 弥补了类单继承的不足,一个类可以同时实现多个接口(可以和继承一起使用)

      • 修饰符 class 实现类 extends 父类 implements 接口1, 接口2, ...{
        
    • 让程序可以面向接口编程,更灵活的切换各种业务。

接口案例

主程序

package F_interface;

import java.util.ArrayList;

public class Test {
    public static void main(String[] args) {
        ArrayList<Student> students = new ArrayList<>();
        students.add(new Student("张三","男",98));
        students.add(new Student("李四","男",97));
        students.add(new Student("小红","女",100));
        students.add(new Student("小粉","女",99));

        StudentManage studentManage = new StudentManage();
        studentManage.PrintInfo(students);
        studentManage.PrintScore(students);
    }
}

数据处理

package F_interface;

import java.util.ArrayList;

public class StudentManage {
    public Temp temp = new StudentOperatelmpl02(); //可以灵活切换实现类01和实现类02

    public void PrintInfo(ArrayList<Student> arrayList){
        temp.PrintInfo(arrayList);
    }

    public void PrintScore(ArrayList<Student> arrayList){
        temp.PrintScore(arrayList);
    }
}

对象表格

package F_interface;

public class Student {
    private String name;
    private String sex;
    private double score;

    public Student() {
    }

    public Student(String name, String sex, double score) {
        this.name = name;
        this.sex = sex;
        this.score = score;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public double getScore() {
        return score;
    }

    public void setScore(double score) {
        this.score = score;
    }
}

接口

package F_interface;

import java.util.ArrayList;

public interface Temp {
    void PrintInfo(ArrayList<Student> arrayList);
    void PrintScore(ArrayList<Student> arrayList);
}

实现类01

package F_interface;

import java.util.ArrayList;

public class StudentOperatelmpl01 implements Temp {
    @Override
    public void PrintInfo(ArrayList<Student> arrayList) {
        System.out.println("----------学生信息----------");
        for (int i = 0; i < arrayList.size(); i++) {
            Student s = arrayList.get(i);
            System.out.println("姓名:" + s.getName() + "、性别:" + s.getSex() + "、成绩:" + s.getScore());
        }
    }

    @Override
    public void PrintScore(ArrayList<Student> arrayList) {
        double allScore = 0;
        System.out.println("----------班级平均分----------");
        for (int i = 0; i < arrayList.size(); i++) {
            Student s = arrayList.get(i);
            allScore += s.getScore();
        }
        System.out.println("平均分:" + allScore/arrayList.size());
    }
}

实现类02

package F_interface;

import java.util.ArrayList;

public class StudentOperatelmpl02 implements Temp{
    @Override
    public void PrintInfo(ArrayList<Student> arrayList) {
        int count01 = 0;
        int count02 = 0;
        System.out.println("----------学生信息----------");
        for (int i = 0; i < arrayList.size(); i++) {
            Student s = arrayList.get(i);
            if (s.getSex().equals("男")) count01++;
            if (s.getSex().equals("女")) count02++;
            System.out.println("姓名:" + s.getName() + "、性别:" + s.getSex() + "、成绩:" + s.getScore());
        }
        System.out.println("男:" + count01);
        System.out.println("女:" + count01);
        System.out.println("总人数:" + arrayList.size());
    }

    @Override
    public void PrintScore(ArrayList<Student> arrayList) {
        double allScore = 0;
        double AllScore = 0;
        double MaxScore = 0;
        double MinScore = 100;
        System.out.println("----------班级平均分(掐尖去尾)----------");
        for (int i = 0; i < arrayList.size(); i++) {
            Student s = arrayList.get(i);
            if (s.getScore() > MaxScore) MaxScore = s.getScore();
            if (s.getScore() < MinScore) MinScore = s.getScore();
            allScore += s.getScore();
        }
        //System.out.println(MaxScore);
        //System.out.println(MinScore);
        AllScore = allScore - MaxScore - MinScore;
        //System.out.println(AllScore);
        System.out.println("平均分:" + AllScore/(arrayList.size() - 2));
    }
}

JDK8开始,接口中新增了一些方法

  • 默认方法
  • 静态方法
  • 私有方法
package F_interface;
    //除私有方法外,其他两种方法默认用public修饰
public interface Demo01 {
    //使用(public)defualt修饰  使用实现类对象调用 Demo01 demo01 = new 实现类; demo01.test1;
    default void test1(){
        System.out.println("默认方法");
        test3(); // ***
    }
    //使用(public)static修饰 使用当前接口调用 Demo01.test2;
    static void test2(){
        System.out.println("静态方法");
    }
    //使用private修饰 JDK9新增 只能在接口内部被调用 既只能在默认方法或者静态方法中被使用 见***
    private void test3(){
        System.out.println("私有方法");
    }
}

用途?

  • 增强了接口的能力,更便于项目的扩展与维护。

接口多继承

  • 一个接口可以同时继承多个其他接口

注意事项

  1. 接口多继承时,如果继承的多接口中存在同方法名不同方法签名的方法时,将不再支持接口多继承
interface A{
    void test1();
}
interface B{
    String test1();
}
interface C extends A, B{...} //报错
  1. 类实现多接口时,如果实现的多接口中存在同方法名不同方法签名的方法时,将不再支持类的多实现
interface A{
    void test1();
}
interface B{
    String test1();
}
class D implements A, B{...} //报错
  1. 子类继承父类的同时又实现接口时,如果存在同名方法(方法名与方法签名均相同)(父类方法与接口默认方法),则先调用父类方法
public class Demo02 {
    public static void main(String[] args) {
        Son son = new Son();
        son.test(); //"父类test"
    }
}
interface E{
    default void test(){
        System.out.println("接口test");
    }
}
class Fa {
    public void test(){
        System.out.println("父类test");
    }
}
class Son extends Fa implements E{}
  1. 类实现多接口时,如果多接口中存在同名(方法名与方法签名均相同)的默认方法,可以不冲突,该类重写该方法即可
interface F{
    default void test3() {...}
}
interface G{
    default void test3() {...}
}
class H implements F, G{
    @Override
    public void test3() {...} //重写test3方法即可
}

内部类

  • 类的五大成分之一(成员变量、方法、构造器、内部类、代码块)
  • 如果一个类定义在另一个类的内部,该类就称为内部类

内部类的四种形式

  1. 成员内部类(了解)
public class Outer{
    // 成员内部类
    public class Innter{
    }
}
  • 是什么?怎么创建对象?特点?

    • 就是类中的普通成员
    • 外部类名.内部类名 对象名 = new 外部类(...). new 内部类(...);
    • 普通类有的成分,它都有
  • 成员内部类中访问外部类成员的特点?

    • 可以直接访问外部类的实例成员、静态成员
    • 可以拿到当前外部类的对象,格式:外部类名.thius.成员
  1. 静态内部类(了解)
public class Outer{
    // 静态内部类
    public static class Innter{
    }
}
  • 是什么?怎么创建对象?特点?

    • 有static修饰的内部类,属于外部类自己持有
    • 外部类名.内部类名 对象名 = new 外部类.内部类(...);
    • 普通类有的成分,它都有
  • 静态内部类中访问外部类成员的特点?

    • 可以直接访问外部类的静态成员,不可以直接访问外部类的实例成员
  1. 局部内部类(了解)
  • 定义在方法中、代码块中、构造器等执行体中
    • 鸡肋语法,知道有这个东西就好
  1. 匿名内部类(重点)
new 类或接口(参数值...){
    类体(一般是方法重写);
};
  • 一种特殊的局部内部类;匿名:指不需要为这个类声明名字

  • 特点:

    • 匿名内部类本质是一个子类,并会立即创建出一个子类对象
  • 作用:

    • 更方便的创建一个子类对象
  • 应用场景:

    • 通常作为一个参数传输给方法。(主要目的是为了简化代码)
public class Test {
    public static void main(String[] args) {
        PrintString p = new  PrintString(){
            @Override
            public void Print() {
                System.out.println("输出");
            }
        };
        go(p);
        //通常作为一个参数传输给方法
        go(new PrintString(){
            @Override
            public void Print() {
                System.out.println("输出");
            }
        });
    }
    
    public static void go(PrintString printString){
        printString.Print();
    }
}

interface PrintString{
    void Print();
}

枚举

  • 枚举是一种特殊的类

  • 枚举类的格式

    • 修饰符 enum 枚举类名{

      名称1,名称2,...;

      其他成员(类的五大成分:成员变量,成员方法,构造器,代码块,内部类)

      }

  • 特点:

    • 枚举类中的第一行,只能写一些合法的标识符(名称),多个名称用逗号隔开

    • 这些名称,本质上是常量,每个常量都会记住枚举类的一个对象

    • 枚举类的构造器是私有的,所以,枚举类对外不能创建对象

    • 枚举类都是最终类,不能被继承

    • 枚举类中,从第二行开始,可以定义其他各种成员

    • 编译器为枚举类新增了几个方法,并且枚举类都是继承:java.lang.Enum类的,从enum类也会继承到一些方法

    • public enum A {
          X, Y, Z;
      
          String name = "我是枚举类";
      }
      
    • image-20260211140818634

  • 枚举的应用场景

    • 用来表示一组信息,作为参数进行传递(可以对传递参数进行约束,避免用户传递了错误参数)

泛型

  • 定义类、接口、方法时、同时声明了一个或多个类型变量(如:),称为泛型类、泛型接口、泛型方法、它们统称为泛型

泛型类

  • 格式:
    • 修饰符 class 类名<类型变量,类型变量,...>
image-20260211175800351

image-20260211175826272

image-20260211175846785

泛型接口

  • 格式:

    • 修饰符 interface 接口名<变量类型,变量类型,. . .>
  • 泛型接口也可以继承

  • 泛型接口的两种使用方式

    • 实现类给出具体类型
    • 实现类延续泛型,创建实现类对象时再确定类型

image-20260212012455031

image-20260212012523135

泛型方法

  • 格式:
    • 修饰符 <类型变量,类型变量,. . .> 返回值类型 方法名(形参列表)
package F_generics;

import java.util.ArrayList;

public class Test02 {
//两种品牌的车要集中进行检查
    public static void main(String[] args) {
        ArrayList<Car> cars = new ArrayList<>();
        cars.add(new BMW());
        cars.add(new BENZ());
        check01(cars);
        
        ArrayList<BMW> bmws = new ArrayList<>();
        bmws.add(new BMW());
        check01(bmws);
        
        ArrayList<Dog> dogs = new ArrayList<>();
        dogs.add(new Dog());
        check01(dogs);//报错
    }   
    
    public static<T extends Car> void check01(ArrayList<T> arrayList){
        
    }
    public static void check02(ArrayList<? extends Car> arrayList){
        
    }
}
  • 特点:

    • 泛型方法看的是你传递的变量是什么类型,类型变量就变成什么类型,比如你传递 “字符串” ,该方法就会把类型变量变成String

      同理,当你传递的是 123 ,该方法就会把类型变量变成Integer

通配符

  • “?”,可以在“使用泛型”的时候代表一切类型;E T K V是在定义泛型的时候使用的

泛型的上下限

  • 上限:“ ?extends Car ” 指 “ ?”只能接收Car或其子类的变量
  • 下限:“ ? super Car ” 指 “ ? ”只能接收Car或其父类的变量

泛型的擦除和注意事项

  • 泛型是工作在编译阶段的,一旦程序编译成class文件,class文件中就不存在泛型了
  • 泛型不支持基本数据类型(八大基本类型),只能支持对象类型(引用类型)
    • 八大基本类型都有其包装类型(引用类型)< 见语法篇中类型小结 >

posted @ 2026-02-12 11:18  Shadow001  阅读(2)  评论(0)    收藏  举报