JavaSE4️⃣OOP - 基本概念

OOP

Java 是一门面向对象的编程语言。

从 POP 和 OOP 的区别,理解 OOP。

  1. 面向过程:POP(Procedure Oriented Programming)

    1. 侧重点:需要做什么。
    2. 实现:把问题模型分解成一个个执行步骤
  2. 面向对象:OOP(Object Oriented Programming)

    1. 侧重点:涉及什么对象,对象有什么属性和行为。
    2. 实现:定义对象,把现实世界映射到计算机模型

面向对象是一种更优秀的程序设计方法,基本思想是使用类、对象、继承、封装、消息等基本概念进行程序设计。

更符合现实世界,将事物抽象为系统中的类,作为系统的一部分,也可以保持事物之间的关系。

1、类 & 对象

1.1、语法

类(class)抽象模板,定义了对象的元数据(属性和方法)。

本身是一种(引用)数据类型

  1. 定义类:使用关键字 class 定义,指定类名。

    class 类名 {
        
    }
    
  2. 注意:一个 Java 源文件可以定义多个类,但最多定义一个 public 类

    1. 若定义了 public 类,源文件名需要与 public 类名一致。
    2. 若无定义 public 类,源文件名任意。
  3. 示例

    class Person {
        public String name;
        public int age;
    }
    

实例(instance):类的具体实现,是程序的基本运行单位

对象实例的概念通用。

  1. 对象实例化:使用关键字 new 创建实例,定义相应类型的变量并指向该实例。

    类名 实例名 = new 类名();
    
  2. 注意

    1. 若无特殊处理,对象实例化后的属性均为默认值
    2. 一个类可以创建多个实例,不同实例拥有独立的内存空间
  3. 示例

    Person p1 = new Person();
    Person p2 = new Person();
    
              ┌─────────┐
    p1 ──────>│  Person │
              ├─────────┤
              │name = ""│
              │age = 0  │
              └─────────┘
              ┌─────────┐
    p2 ──────>│  Person │
              ├─────────┤
              │name = ""│
              │age = 0  │
              └─────────┘
    

1.2、说明

1.2.1、命名规范

Java 中涉及命名的地方,需要遵循规范。

  1. 硬性要求:以英文字母开头,字母、数字、下划线组合。
  2. 规范:多个英文字母采用驼峰命名法

1.2.2、注释

注释(comment):标注代码,提高代码可读性。

  1. 单行注释

    // 打印输出
    System.out.println("Hello World!");
    
  2. 多行注释

    public void doSomething() {
        /*
    		以下代码用于xxxxx功能。
    		1. 
    		2. 
    		3. 
    	*/
    }
    
  3. 文档注释:用于类和方法的定义处,可用于自动生成文档(JavaDoc)。

    /**
     * 文档注释
     *
     * @author Jaywee
     * @date 2022/12/12
     */
    public class Hello {
    }
    

2、属性

属性/字段(field):描述类的特征

在类内部定义,一个类可以定义多个属性。

  1. 定义属性:通常定义在类内部的最上方。

    class xxx {
    
        权限修饰符 数据类型 属性名;
    
    }
    
  2. 访问:可在类内部或外部访问。

    1. 内部this 指向当前对象实例

      1. 若无命名冲突可省略 this.
      2. 若局部变量与属性同名,需要添加 this. 区分。
    2. 外部:需要拥有相应访问权限,取决于属性的权限修饰符。

      // 内部
      this.属性名;
      
      // 外部
      实例名.属性名;
      
  3. 类型

    1. 成员变量:在类内部定义的属性。
    2. 局部变量:在方法内部定义的属性(含方法参数)。
  4. 示例:由 public 修饰的属性,外部可直接访问。

    // 写:赋值
    p1.name = "Secret";
    p1.age = 18;
    
    // 读
    System.out.println(p1.name + ":" + p1.age);
    

3、方法

方法(method):一组执行语句,描述类的行为

在类内部定义,一个类可以定义多个方法。

3.1、语法

  1. 定义方法

    class xxx {
    
        权限修饰符 返回值类型 方法名(参数列表) {
            // 执行语句
            // return 返回值;
        }
    
    }
    
  2. 访问:可在类内部或外部访问。

    1. 内部this 表示当前对象实例。

    2. 外部:访问时需要拥有相应权限,取决于方法或属性的权限修饰符。

      // 内部
      this.方法名(参数);
      
      // 外部
      实例名.属性名;
      实例名.方法名(参数);
      
  3. 示例:属性由 private 修饰,定义方法暴露给外部使用。

    class Person {
        public static void main(String[] args) {
            Person p = new Person();
    
            p.setName("Mike");
            p.setAge(200); // 抛异常,超出了0-100范围
            p.setAge(10);
        }
    
    
        private String name;
        private int age;
    
        public String getName() {
            return this.name;
        }
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return this.age;
        }
    
        public void setAge(int age) {
            // 合法性检查
            if (age < 0 || age > 100) {
                throw new IllegalArgumentException("invalid age value");
            }
            this.age = age;
        }
    }
    

3.2、方法返回

方法内遇到 return 关键字结束并返回。

  1. 返回值:根据方法是否需要返回值,分为两种情况。
    1. :方法内最后一行使用 return,返回相应类型的变量。
    2. :即返回值类型为 void,此时可省略 return 语句。
  2. 结束方法:无论是否需要返回值,通常可结合 if,在特定条件下 return; 提前结束方法。

3.3、方法参数

3.3.1、参数列表

参数列表可以包含任意个参数(包括 0),

调用方法时需要按顺序传递相应类型的参数。

  1. 参数类型

    形参 实参
    含义 方法定义的参数列表 调用方法时传入的参数
    声明位置 方法签名 方法调用前的任意位置
    内存单位 方法被调用时分配,调用结束后释放 变量声明时分配,与是否调用方法无关
    作用域 方法内部 取决于参数定义的位置
  2. 可变参数(Java 5):使用 类型... 定义,效果等同于 类型[],但有所区别。

    1. 可变参数:参数为 null 时,接收的实际值是一个空数组

    2. 数组:参数为 null 时,接收的实际值是 null,容易导致 NPE

      class Demo {
          public static void main(String[] args) {
              Demo demo = new Demo();
              // 结果:arr1 == null,arr2 == []
              demo.doSomething(null, null);
          }
      
      
          public void doSomething(int[] arr1, int... arr2) {
              // ...
          }
      }
      

3.3.2、参数绑定

Java 中只有值传递,没有引用传递。

  • 基本类型:传递参数值的副本值,二者互不影响(二者指原参数和副本)。
  • 引用类型:传递参数值的堆地址副本,二者指向同一个对象实例。

① JVM 相关知识

  1. 栈帧:每个方法对应栈内存的一个独立栈帧,方法中定义的局部变量均存放在栈帧中。
    1. 基本类型:通常直接存放变量值,也有例外。
    2. 引用类型:存放变量在堆内存中实例的地址。
  2. 方法之间传递参数时,传递的是各自栈帧中存储的参数值副本

② 值传递 & 引用传递

  1. 值传递副本拷贝

    1. 方法调用时,实参复制一份副本,传递到方法的形参(此过程即参数绑定)。

    2. 在方法内对参数的任何操作,都是对栈帧中参数副本的操作,不影响原参数的值。

      class Demo {
          public static void main(String[] args) {
              int n = 10;
              Person p = new Person();
              // 调用方法
              method1(n, p);
          }
          
          public void method(int num, Person person) {
              // 不影响原实参,如下图所示
              num = 0;
              person = new Person();
          }
      }
      

      image

  2. 引用传递直接指向

    1. 方法调用时,将实参的地址直接传递给方法的形参(而非副本)。
    2. 个人理解
      1. 值传递:传递地址副本,两个方法各自拥有一个变量,二者指向堆内存的同一地址。
      2. 引用传递:共享同一个变量。

3.4、方法重载

重载(overload):在一个类中允许定义多个同名方法,要求具有不同参数类型或个数。

  1. 使用场景:功能类似,但参数有所不同。
  2. 调用重载方法时,Java 编译器检查调用方法的参数类型和个数,自动选择恰当的方法。

示例String 类提供了多个 indexOf() 重载方法。

查找字符串中特定字符的索引。

  • int indexOf(int ch):根据字符 Unicode 码查找。
  • int indexOf(String str):根据字符串查找。
  • int indexOf(int ch, int fromIndex):根据字符 Unicode 码查找,指定起始搜索位置。
  • int indexOf(String str, int fromIndex):根据字符串查找,指定起始搜索位置。

3.5、构造方法

构造方法(Construction method):也称构造器。

创建一个对象时,自动调用相应参数的构造方法完成实例化。

  1. 作用:完成对象初始化。
  2. 特点
    1. 构造方法与类同名,没有返回值,可以重载
    2. 任何类都有构造方法,无论是否有显式定义。
    3. 构造方法之间可以相互调用,通过 this(参数列表);

3.5.1、默认构造方法

默认构造方法没有参数、没有执行语句

  1. 无显式定义:编译器会自动生成一个默认构造方法。

    // 编译前
    class Person {
    }
    
    // 编译后等价于
    class Person {
        // 自动生成
        public Person() {
        }
    }
    
  2. 显式定义

    1. 若显式定义了构造方法,编译器不会生成默认构造方法。

      class Person {
          private String name;
          private int age;
          
          public Person(String name, int age) {
              this.name = name;
              this.age = age;
          }
      }
      
    2. 如需同时使用无参和有参构造方法,需要同时定义。

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

3.5.2、变量默认值

Java 中任何变量使用前必须先设置初值

① 初始化类型

  1. 无显式赋值:编译器会为其进行隐性赋值,为相应数据类型的默认值。

    class Person {
        private String name;	// null
        private int age;		// 0
    }
    
  2. 声明时赋值

    class Person {
        private String name = "default";
        private int age = 18;
    }
    
  3. 构造器赋值

    class Person {
        private String name;
        private int age;
    
        public Person() {
            name = "default";
            age = 18;
        }
    }
    

② 初始化顺序

赋值顺序:Java 按以下顺序进行初始化,后赋值的会覆盖先赋值的。

  1. 默认值:为属性赋相应类型的默认值。
  2. 初始值:赋值为声明时的初始值。
  3. 构造方法:根据构造方法的代码,对属性进行赋值。

示例:实例化 Person,思考最终结果是什么。

  1. 默认值:name 为 null,age 为 0。

  2. 初始值:name 为 Jerry,age 为 10。

  3. 构造方法:name 为 Tom,age 为 20(最终结果)。

    class Person {
        public static void main(String[] args) {
            Person p = new Person("Tom", 20);
        }
        
        private String name = "Jerry";
        private int age = 10;
    
        public Person(String name, String age) {
            this.name = name;
            this.age = age;
        }
    }
    
posted @ 2023-02-02 18:20  Jaywee  阅读(40)  评论(0编辑  收藏  举报

👇