首页 > 编程语言 >Java 面向对象

Java 面向对象

时间:2025-01-19 19:34:34浏览次数:1  
标签:Java Person 子类 接口 面向对象 Student 方法 public

面向对象

  • 类 (设计图) : 对象共同特征的描述
  • 对象 : 真实存在的具体东西
public class 类名 {
    1. 成员变量
    2. 成员方法
    3. 构造器
    4. 代码块
    5. 内部类
}
  • 用来描述一类事物的类叫 Javabean 类, 类中不写 main 方法
  • 编写 main 方法的类叫 测试类

封装

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

e.g. 人画圆: "人" "调用" "圆的方法<画圆>"

private

  • 修饰成员 (成员变量和成员方法)
  • 被修饰的成员只能在本类中才能访问
public class Person {
    private int age;

    public void setAge(int a) {
        if (a >= 0 && a <= 200) {
            age = a;
        } else {
            System.out.println("illegal");
        }
    }

    public int getAge() {
        return age;
    }
}

this

下面方法中, int aa 的命名没有表示意义

public void setAge(int a) {
    if (a >= 0 && a <= 200) {
        age = a;
    } else {
        System.out.println("illegal");
    }
}

如果改为下面代码, 则会出错

public void setAge(int age) {
    if (age >= 0 && age <= 200) {
        age = age;
    } else {
        System.out.println("illegal");
    }
}

采用 this, 则可以将局部变量传给成员变量

public void setAge(int age) {
    if (age >= 0 && age <= 200) {
        this.age = age;
    } else {
        System.out.println("illegal");
    }
}

构造方法

  • 用于初始化类
  • 如果没写, 虚拟机会自动加一个空参构造, 赋默认值
  • 可以同时有空参构造和有参构造 (一般都要写)
public class Person {
    private int age;
    private String name;

    public Person() {

    }

    public Person(int age, String name) {
        this.age = age;
        this.name = name;
    }

    public void setAge(int age) {
        if (age >= 0 && age <= 200) {
            this.age = age;
        } else {
            System.out.println("illegal");
        }
    }

    public int getAge() {
        return age;
    }

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

    public String getName() {
        return name;
    }
}

Person p1 = new Person();
Person p1 = new Person(18, "xxx");

[[D:/java_code/phone/Phone.java|e.g.Phone]]

内存

  • 栈内存: 运行空间, 各个方法按照运行顺序进栈出栈
  • 堆内存: new 开辟的变量空间
  • 方法区: 方法与类之间以一定结构存储
Person p1;
Person p2 = p1;

// 因为 p1, p2 存的是地址, 因此此时两个引用指向同一个对象
// 修改 p2 则 p1 也被修改

需要注意的是, Java 中对象之间的赋值是引用赋值, 不同于经常使用的 Cpp 中结构体之间的赋值是值传递

注意区别下面两种写法的不同:

  • 错误写法
Person[] people = new Person[10];
Person temp = new Person();

for (int i = 0; i < 10; i++) {
    // 用输入数据更改 temp
    // ...

    people[i] = temp;
}
  • 正确写法
Person[] people = new Person[10];

for (int i = 0; i < 10; i++) {
    Person temp = new Person();

    // 用输入数据更改 temp
    // ...

    people[i] = temp;
}

  • 栈内存中有 p1, 而 p1 存的是地址值, 其为引用数据类型, 就是要引用在别的空间中的数据值(比如堆内存)

  • 基本数据类型则是数据值就存储在自己的空间里

  • this 的内存原理: 本质是代表方法调用者的地址值

p1.setName("xxx")

// p1 即为方法调用者
  • 成员变量和局部变量
区别 成员变量 局部变量
类中位置 类中, 方法外 方法内, 方法申明上 (形参)
初始化 有默认初始化值 无默认初始化值
内存位置 堆内存 栈内存
生命周期 随对象创建而存在, 对象消失而消失 随方法调用而存在, 方法运行结束而消失
作用域 整个类中 当前方法中

Practice

[[D:/java_code/game/Person.java|文字版格斗游戏-code]]

[[D:/java_code/student/Student.java|学生管理-code]]

static

public class Student {
    private String name;
    private int age;
    private String gender;
    public static String teacherName;
    // 所有对象共用一个 teacherName
}

static 修饰成员变量, 成员方法 -> 静态变量, 静态方法

  • 静态变量, 推荐类名调用 Student.teacherName = "xxx";

  • 静态方法, 多用于 测试类工具类 中, Javabean类 中很少使用, 推荐类名调用

    • Javabean类: 描述一类事物的类
    • 测试类: 用来检查其他类是否书写正确的类
    • 工具类: 不是用来描述一类事物, 而是用来做一些事情的类
      • 私有化构造方法, 不用创建对象
      • 方法定义为静态, 方便调用
  • 堆内存中有静态存储位置 静态区, 优先于对象的出现

  • 注意

    • 静态方法只能访问静态变量和静态方法
      (因此 测试类 中其他方法需要是静态的才能被 main 方法调用)
    • 静态方法中没有 this
    • 非静态方法可以访问所有

继承

两个类有很多相同的部分, 比如老师和学生不同在于一个学习一个教学, 相同在于都要吃饭睡觉..., 其中相同的部分为人的属性, 因此可以从一个描述人的类 继承 过来

  • extends 关键字
public class Student extends Person {}

// Student 称为子类(派生类), Person 称为父类(基类或超类)

当类和类之间存在相同的内容(属性), 且子类是父类中的一种(考虑实际意义), 考虑使用继承

下面注意区分 继承可以访问

  • 继承关系

    • Java 支持单继承
    • Java 不支持多继承
    • Java 支持多层继承
    • 每一个类都直接或间接的继承于 Object类 (虚拟机自动)
  • 子类只能访问父类中非私有的成员

    • 构造方法都不能继承
    • 成员变量都能继承
      • 不能直接调用私有成员变量
      • 可以通过非私有方法间接获取私有成员变量
    • 私有成员方法不能继承
  • 相同命名的成员访问符合就近原则, 即子类成员覆盖父类成员

  • 重写

    • 当父类的方法不能满足子类现在的需求, 需要进行 方法重写
    • 子类中出现和父类中一模一样的方法申明, 称这个方法是重写的方法
    • 重写注释 @Override 加到方法上方, 程序会检查重写是语法是否正确
    • 重写规则
      • 重写方法的名称和形参必须与父类中的一致
      • 子类重写父类方法时, 子类的访问权限必须 >= 父类
      • 子类重写父类方法时, 子类的返回值类型必须 <= 父类
      • 建议重写的方法尽量和父类一致
      • 只有被继承的方法才能被重写
  • super 代表父类存储空间

    • super() 调用父类的构造方法
    • super.xxx 访问父类的成员变量
    • super.xxx() 调用父类的成员方法
  • 构造方法

    • 构造方法不会被继承, 但子类中所有构造方法先默认访问父类中的无参构造, 再执行自己的
      • 子类初始化时, 可能需要父类的数据, 因此需要调用父类构造方法完成父类数据空间的初始化
      • 子类构造方法第一行默认写 super(), 不写也存在
      • 如果想调用父类的有参构造, 需要手动写

多态

同类型的对象, 表现出的不同形态
比如, 人有老师和学生等不同形态

表现为: 父类类型 对象 = 子类对象;

父类型作为参数, 可以接收所有子类对象

前提

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

[[D:/java_code/person/Test.java|e.g.TestPerson]]

父类类型 对象 = 子类对象;

当我们编写如下代码时,

Person p = new Student();
  • 调用成员变量 编译看左边, 运行也看左边

    • 编译看 Person, 如果 Person 中没有这个成员变量, 编译失败
    • 运行也看 Person, 调用 Person 中的成员变量, 比如 sout(p.name) 会是 而不是 学生
  • 调用成员方法 编译看左边, 运行则看右边

    • 编译看 Person, 如果 Person 中没有这个成员方法, 编译失败
    • 运行则看 Student, 调用 Student 中的成员方法, 比如 p.show() 会是 我是学生 而不是 我是人
  • 理解:

    • 成员变量
      • 用什么类去申明对象, 对象引用指向这个类在堆内存中对应的空间
      • Person 申明, 则找 name 时先在 Person 对应的空间里找, 找不到则编译错误
      • 如果用 Student 申明, 则找 name 时先在 Student 对应的空间里找, 找不到再往父类的空间找
    • 成员方法
      • Person 申明, 则找 show() 时先在 Person 对应的空间里找, 找不到则编译错误
      • 而在方法区, 子类中重写的方法, 覆盖了父类的方法
      • 因此运行时, 找 show() 这个重写的方法时, 是找子类的成员方法

优势与弊端

  • 优势

    • 多态形式下, 右边对象可以实现解耦合, 便于扩展和维护

      Person p = new Student();
      p.show();
      p.work();
      ...
      

      改为

      Person p = new Teacher();
      p.show();
      p.work();
      ...
      

      使用多态, 无需修改太多的后续代码

    • 定义方法时, 使用父类型作为参数, 可以接收所有子类对象, 体现多态的扩展性和便利

  • 弊端
    如果使用多态 Person p = new Student();, 则不能调用子类的特有方法 (非重写的方法) p.study(), 因为在父类中没有这个方法, 会编译失败

    • 强转为子类后可以调用子类的特有方法 Student p2 = (Student)p;
    • 可以用 instanceof 关键字判断真实对象类型
      if (p instanceof Student) {
          Student p2 = (Student)p;
          p2.study();
      } else if (p instanceof Teacher) {
          Teacher p2 = (Teacher)p;
          p2.teach();
      } else {
          sout("wrong");
      }
      
      或可以写为
      if (p instanceof Student p2) {
          p2.study();
      } else if (p instanceof Teacher p2) {
          p2.teach();
      } else {
          sout("wrong");
      }
      

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

包名规则: 公司域名反写 + 包的作用, 需要全部英文小写, 见名知意
比如 com.heima.domain

全类名

package com.heima.domain;

public class Student {

}

全类名com.heima.domain.Student

使用其他类时, 需要使用全类名

com.heima.domain.Student s = new com.heima.domain.Student();

如果 import com.heima.domain.Student;
则可以 Student s = new Student();

导包规则

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

final

修饰作用

  • 修饰方法: 表明方法是最终方法, 不能被重写
  • 修饰类: 表明类是最终类, 不能被继承
  • 修饰变量: 表示为常量, 只能被赋值一次
    • 修饰基本类型: 变量存储的 数据值 不能改变
    • 修饰引用类型: 变量存储的 地址值 不能改变, 对象内部的可以改变

常量

  • 命名规范

    • 单个单词: 全部大写
    • 多个单词: 全部大写, 中间用下划线
  • 用处

实际开发中, 常量一般作为系统的配置信息, 方便维护, 提高可读性

public class StudentSystem {

    private static final String ADD_STUDENT = "1";
    private static final String DEL_STUDENT = "2";
    private static final String EXIT = "3";
    ...

    public static void startStudentSystem() {
        ArrayList<Student> list = new ArrayList<>();
        loop:
        while(true) {
            sout("xxx");
            ...

            Scanner sc = new Scanner(System.in);
            String choose = sc.next();

            switch(choose) {
                case ADD_STUDENT -> addStudent(list);
                case DEL_STUDENT -> delStudent(list);
                case EXIT -> {
                    sout("退出");
                    // break loop; // 跳出 loop 标记的循环
                    System.exit(0); // 停止虚拟机运行
                }
            }
        }
    }
}

权限修饰符

private < 空着(缺省/默认) < protected < public

private

只能在同一个类中使用

缺省

相较前者, 还可以在同一个包的其他类中使用

protected

相较前者, 还可以在不同包下的子类中使用

public

相较前者, 还可以在不同包下的无关类中使用

  • 实际开发中, 一般只有 private 和 public
    • 成员变量私有
    • 方法公开
    • 如果方法中的代码是抽取其他方法中共性代码, 一般也私有
      (理解为类中不同方法有相同的部分, 比如一些 clear 操作)
      (将相同部分提取出来作为一个函数, 方便内部方法调用, 但不用于外界调用)

代码块

局部代码块

public static void main(String[] args) {
    // 局部代码块
    {
        int a = 10;
        sout(a);
    }

    sout(a); // 错误
}

构造代码块

public class Student {
    private String name;
    private int age;

    // 构造代码块
    {
        sout("开始创建对象了");
    }

    // 创建对象时, 会先执行构造代码块, 再执行构造方法

    public Student() {
        // sout("开始创建对象了");
    }

    public Student(String name, int age) {
        // sout("开始创建对象了");
        this.name = name;
        this.age = age;
    }
}

静态代码块

随着类的加载而加载, 自动触发, 只执行一次 (第一次使用这个类时), 可以用于一些数据的初始化

public class Student {
    private String name;
    private int age;

    // 静态代码块
    static {
        sout("xxx");
    }
}

抽象类

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

public abstract 返回值类型 方法名(参数); // 无方法体, 直接 ';'

抽象类: 存在抽象方法的类

public abstract class 类名 {}

[[D:/java_code/person/Person.java|e.g.Person]]

注意

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

意义

强制子类必须按照同一种格式重写共性的方法, 统一申明格式, 便于维护和使用

接口

一种规则, 对行为的抽象

public interface 接口名 {}

接口内方法: public abstract 返回值类型 方法名(参数);

注意

  • 接口不能实例化
  • 接口和类之间是实现关系, 通过 implements 关键字表示
    public class 类名 implements 接口名 {}
    public class 类名 implements 接口名1, 接口名2, ... {}
    public class 类名 extends 父类 implements 接口名1, 接口名2, ... {}
  • 接口的子类 (实现类)
    • 要么重写接口中的所有抽象方法
    • 要么是抽象类

意义

统一一种行为的重写格式

Practice

[[D:/java_code/animal/Animal.java|e.g.Animal抽象类与Swim接口]]

接口中成员的特点

  • 成员变量: 只能是常量, 默认用 public static final 修饰
  • 构造方法: 无
  • 成员方法: 只能是抽象方法, 默认用 public abstract 修饰
    • JDK7以前: 接口中只能定义抽象方法
    • JDK8: 接口中可以定义有方法体的方法
    • JDK9: 接口中可以定义私有方法

接口与类的关系

实现关系, 可以多实现

当一个类实现的多个接口中有相同申明的函数时, 不会报错, 且只用重写一次

接口与接口的关系

继承关系, 可以单继承, 也可以多继承

如果实现类实现了最下面的子接口, 那么需要重写所有的抽象方法

JDK8以后

默认方法

为解决接口升级的问题, 避免"一旦在接口添加抽象方法, 所有实现类都需要立即修改"的问题, 允许在接口中定义默认方法, 需要用 default 修饰

public default 返回值类型 方法名(参数) {}

  • 默认方法不是抽象方法, 不强制被重写; 重写时要去掉 default
  • public 可以省略, default 不能省略
  • 如果实现了多个接口, 多个接口中存在同名的默认方法, 实现类必须对该方法进行重写, 否则调用不知道调用哪个

静态方法

允许在接口中定义静态方法, 需要用 static 修饰

public static 返回值类型 方法名(参数) {}

  • 静态方法不需要重写
  • 静态方法只能通过接口名调用, 不能通过实现类或者对象名调用
  • public 可以省略, static 不能省略

JDK9以后

私有方法

私有方法只为接口内方法服务, 不为外界调用

普通私有方法, 为默认方法服务:
private 返回值类型 方法名(参数) {}

静态私有方法, 为静态方法服务:
private static 返回值类型 方法名(参数) {}
(因为静态方法只能访问静态变量和静态方法)

接口的应用

  • 接口代表规则, 是行为的抽象; 类想要哪种行为, 让这个类实现对应的接口
  • 当一个方法的参数是接口时, 可以传递接口所有实现类的对象, 即 接口多态
    接口类型 j = new 实现类对象();

适配器设计模式

设计模式: 设计的套路

适配器设计模式: 解决接口与接口实现类之间的矛盾问题

  • 接口 A 中有很多抽象方法, 但 A 的某个实现类 B 可能只想要重写其中一个方法
  • 在接口 A 和接口实现类 B 中, 加一个 适配器 Adapter C (是一个接口, 实现了 A), 对接口 A 中所有抽象方法进行空实现, 让 B 实现 C 而不再是 A, 且只重写想要的那个方法
  • 适配器 一般 public abstract class 适配器名 implemants 接口名 {}, 使用 abstract 不能实例化, 因为 适配器 实例化显然没有意义

内部类

public class Car {
    String carName;
    int carAge;
    String carColor;

    // 内部类
    class Engine {
        String engineName;
        int engineAge;
    }
}

内部类表示的事物是外部类的一部分, 单独出现没有意义

内部类可以直接访问外部类的成员, 包括私有; 外部类访问内部类成员必须创建对象

成员内部类

public class Car {
    String carName;
    int carAge;
    String carColor;

    // 成员内部类
    class Engine {
        String engineName;
        int engineAge;
    }
}
  • 写在成员位置, 属于外部类的成员

  • 与成员一样, 可以被修饰符修饰, 如 private, 默认, protected, public, static

  • JDK16之前, 成员内部类里面不能定义静态变量; 之后可以

创建对象

  1. 在外部其他类直接创建对象, 需要内部类相应的权限
Outer.Inner oi = new Outer().new Inner();
  1. 外部类编写方法, 对外提供内部类对象
public class Outer {
    
    private class Inner {

    }
    
    public Inner getInstance() {
        return new Inner();
    }
}

注意在外部其他类, 不能

Outer o = new Outer();
Outer.Inner i = o.getInstance();

只能

Outer o = new Outer();
Object i = o.getInstance(); // 或者直接使用 o.getInstance()

访问成员

public class Outer {
    private int a = 10;

    class Inner {
        private int a = 20;

        public void show() {
            int a = 30;

            sout(a); // 30
            sout(this.a); // 20
            sout(Outer.this.a); // 10
        }
    }
}

内部类对象在堆内存对应的空间中, 有一个隐藏的 Outer.this 记录外部类对象的地址值

静态内部类

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

创建对象: Outer.Inner oi = new Outer.Inner();
调用静态内部类的静态方法: Outer.Inner.静态方法名();

局部内部类

内部类定义在方法里面, 类似于方法里的局部变量

匿名内部类

隐层了名字的内部类

new 抽象类名或者接口名() {
    重写方法;
}; // 注意 ';'

理解: 大括号即其内部实现看成一个没有名字的类, new 说明这实际上是一个对象, 抽象类名或者接口说明是继承关系还是实现关系

用法:

public static void main(String[] args) {
    method(
        new Animal() {
            @Override
            public void eat(){
                sout("狗吃骨头");
            }
        }

        // 等于传递了一个 Animal 的子类 Dog, 无需创建一个 Dog 对象
        // 多态
    );
}

public static void method(Animal a) {
    a.eat();
}
Swim s = new Swim() {
    @Override
    public void swim() {
        sout("重写游泳方法");
    }
};

// 接口多态
// 左边是接口, 右边是接口的实现类的对象, 符合编译看左边, 运行看右边
new Swim() {
    @Override
    public void swim() {
        sout("重写游泳方法");
    }
}.swim();

// 等同于 "对象.方法();"

使用场景:

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

标签:Java,Person,子类,接口,面向对象,Student,方法,public
From: https://www.cnblogs.com/wxgmjfhy/p/18679814

相关文章

  • Java 基础 API
    APIAPI:应用程序编程接口,即已经写好的东西,可以直接使用String字符串的内容是不会更改的Stringname="abc";name="def";//name="def"是创建了一个新的字符串,然后把引用赋给了name构建方法Strings="abc";//直接赋值Strings=newString();/......
  • 【详解】ElasticSearchJava操作ES实例
    目录ElasticSearchJava操作ES实例简介环境准备1.安装Elasticsearch2.添加依赖连接Elasticsearch1.创建客户端2.关闭客户端基本操作1.创建索引2.插入数据3.查询数据环境准备示例代码代码说明运行代码1.添加依赖2.创建客户端3.索引文档4.查询......
  • 【详解】JavaSpringMVC+MyBitis+多数据源切换
    目录JavaSpringMVC+MyBatis+多数据源切换1.环境准备2.添加依赖3.配置多数据源4.创建数据源配置类5.动态数据源切换5.1动态数据源类5.2数据源上下文持有者5.3切面管理数据源选择5.4自定义注解6.使用示例6.1UserMapper6.2OrderMapper6.3Service......
  • Java锁 死锁及排查 JVM 工具 jconsole 工具 排查死锁
    目录概述死锁案例(面试)如何排查死锁使用JVM工具排查死锁使用jconsole工具排查死锁细节概述死锁是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力于涉那它们都将无法推进下去,如果系统资源充足,进程的资源请求都能够得到满......
  • Java的数据类型
    4.数据类型4.1java中的数据类型分为两大类:基本数据类型和引用类型。基本数据类型:数值型byte[1],short[2],int[4],long[8]浮点型float[4],double[8]字符型char[2]存放单个字符布尔型boolean[1]存放true,false引用类型:类(class)接口(interface)数组([])4.2整数类型4......
  • Java中的运算符
    5.1运算符的介绍运算符是一种特殊的符号,用以表示数据的运算、赋值和比较等5.2算术运算符5.2.1介绍算术运算符是对数值类型的变量进行运算的。5.2.2算术运算符分类运算符运算范例结果+正号+55-负号b=6;-b-6+加10+10......
  • [2866]基于JAVA的装饰工程进度智慧管理系统的设计与实现
    毕业设计(论文)开题报告表姓名学院专业班级题目基于JAVA的装饰工程进度智慧管理系统的设计与实现指导老师(一)选题的背景和意义选题背景与意义随着科技的快速发展和信息化时代的到来,装饰工程行业面临着日益激烈的竞争压力。为了提高效率、降低成本、保证工程质量,需要一种......
  • [2844]基于JAVA的虚拟电厂智慧管理系统的设计与实现
    毕业设计(论文)开题报告表姓名学院专业班级题目基于JAVA的虚拟电厂智慧管理系统的设计与实现指导老师(一)选题的背景和意义在当今社会,随着新能源技术的发展和电力市场化改革的深入,虚拟电厂(VirtualPowerPlant,VPP)作为一种新型的能源管理模式,正逐渐受到广泛关注。虚拟电厂......
  • Java初学者笔记-06、Stream流
    什么是Stream流JDK8开始新增的一套API,可以用于链式操作集合或者数组的数据。优势:Stream流大量的结合了Lambda的语法风格来编程,功能强大,性能高效,代码简洁,可读性好。list.stream().filter(s->s.startswith("张")).filter(s->s.Length()==3).collect(Collectors.toList());......
  • 基础 Java
    基础Java跨平台通过虚拟机JVM实现并不是直接运行在操作系统中,而是运行在虚拟机中针对不同操作系统,安装不同的虚拟机JDK和JREJDK:Java开发工具包(包括JVM虚拟机,核心类库,开放工具)JRE:Java运行环境(JDK去掉部分工具,保留运行工具)关键......