Java 对象与类——从 C++ 到 Java

news2025/3/10 2:38:43

文章目录

  • 面向对象程序设计概述
  • 使用预定义类
  • 用户自定义类
  • 静态字段与静态方法
  • 方法参数
  • 对象构造
  • JAR 文件
  • 文档注释
  • 类设计技巧

面向对象程序设计概述

面向对象程序设计(OOP)在 20 世纪 70 年代出现,是当今主流编程范型,Java 是面向对象语言,需熟悉 OOP 才能用好 Java。OOP 中,每个对象包含公开功能和隐藏实现部分,对象来源有标准库和自定义两种。

传统面向过程程序设计通过一系列过程求解问题,先确定过程再考虑数据存储;OOP 则将数据放在首位,再考虑操作数据的结构,对于大规模问题,OOP 更便于理解和维护。

image.png

  • 类是构造对象的模板或蓝图,由类构造(construct)对象的过程称为创建类的实例(instance)。
  • 封装是处理对象的重要概念,即将数据和操作数据的方法封装在类中,使对象使用者隐藏具体实现方式,提高代码的重用性和可靠性。Java 中所有类都源自Object类,可通过扩展其他类构建新类。

对象

  • 想要使用 OOP,一定清楚对象三个主要特性:
    • 对象的行为(behaviour):可以对对象完成那些操作,或者可以对对象应用那些方法?

    • 对象的状态(state):当调用那些方法时,对象会如何响应?

    • 对象的标识(identity):如何区分具有相同行为与状态的不同对象?

识别类

  • 在面向对象程序设计中,可从识别类开始编程,识别类的简单经验是分析问题中的名词(对应类)和动词(对应方法) 。

类之间的关系

  • 在类之间,最常见关系有
    • 依赖(“uses-a”):表示一个类使用另一个类的对象。
    • 聚合(“has-a”): 表示一个类使用另一个类的对象。
    • 继承(“is-a”):表示特殊类与一般类的关系。

使用预定义类

在 Java 中,不是所有类都有典型的面向对象特征,如 Math 类,只需知道其方法名和参数即可使用,体现了封装特性。下面将会介绍 Date 类,从中可以看出如何构造对象,以及如何调用类的方法。

对象与对象变量

  • Date 类:可描述时间点,但用其描述时间存在不同地区日期表示差异等问题。可通过构造器创建对象,调用toString方法获取日期字符串描述。

  • 使用预定义类时,需先构造对象,通过构造器(构造函数)完成,如 Date 类的构造器为 Date ()。可使用new Date()创建 Date 对象,还能对新创建对象调用方法,如System.out.println(new Date())

  • 常将构造的对象存于变量中,如Date birthday = new Date(); 。对象变量和对象不同,未初始化的对象变量不能调用对象方法,必须先让其引用一个对象。Java 中对象变量的值是对对象的引用,还可设置变量为null表示不引用任何对象。

// 引用同一个对象的对象变量
Date birthday = new Date();
deadline = birthday;

image.png

C++注释:很多人误以为 Java 中的对象变量等同于 C++ 中的引用,但实际上 C++ 没有null引用,且引用不能赋值。Java 的对象变量更类似于 C++ 的对象指针。**例如,Java 中的Date birthday; ,实际等同于 C++ 中的Date* birthday; ,并且 C++ 中Date* 指针需使用new调用才会初始化,这一点和 Java 创建对象语法类似,如Date* birthday = new Date(); 。**如果将一个变量复制到另一个变量,在 Java 和 C++ 中两个变量都指向同一个对象,Java 中的null引用对应 C++ 中的NULL指针。

Java 对象都存储在堆中,当一个对象包含另一个对象变量时,存储的是指向另一个堆对象的指针。在 C++ 中,指针使用容易出错,可能出现内存管理问题;而 Java 中不存在这些问题,未初始化的指针在运行时系统会产生运行时错误,而非随机结果,且 Java 有垃圾回收器处理内存管理相关事宜。

C++ 通过支持复制构造器和赋值运算符实现对象的自动复制,比如链表复制后是新链表,内容和原链表相同但链接独立;在 Java 中,若要获得对象完整副本,必须使用clone方法 。

Java 类库中的 LocalDate 类

  • LocalDate 类用于处理日期,其时间原点是 1970 年 1 月 1 日 00:00:00 UTC。创建 LocalDate 对象不建议用构造器,而应使用静态工厂方法,如LocalDate.now()获取当前日期,LocalDate.of(1999, 12, 31)创建指定日期的对象 。

更改器方法与访问器方法

  • 可通过getYeargetMonthValuegetDayOfMonth等方法获取年、月、日信息;plusDays方法用于根据当前对象生成新的日期对象,且原对象不受影响。
  • 只访问对象且修改对象的方法修改器方法(mutator method);只访问对象不修改对象的方法称为访问器方法(accessor method)。

C++注释: C++ 中,带const后缀就是访问器方法,没有则是更改器方法;Java 中这两种方法在语法上无明显区别。

用户自定义类

Java 中类的最简定义形式,包含字段(field)、构造器(constructor)和方法(method)。

employee 类

  • 给出一个用于工资管理系统的Employee类代码示例,包含私有实例字段name(姓名)、salary(工资)、hireDay(雇佣日期);构造器用于初始化这些字段;
  • 还有getNamegetSalarygetHireDay等公共方法用于获取对应字段值,以及raiseSalary方法用于给员工涨薪。
import java.time.*;

public class EmployeeTest {
    public static void main() {
        // fill the staff array with three Employee objects
        Employee[] staff = new Employee[3];

        staff[0] = new Employee("Carl Cracker", 75000, 1987, 12, 15);
        staff[1] = new Employee("Harry Hacker", 50000, 1989, 10, 1);
        staff[2] = new Employee("Tony Tester", 40000, 1990, 3, 15);

        // raise everyone's salary by 5%
        for (Employee e : staff)
            e.raiseSalary(5);

        // print out information about all Employee objects
        for (Employee e : staff)
            System.out.println("name=" + e.getName() + ", salary=" + e.getSalary() + ", hireDay=" + e.getHireDay());
    }
}

class Employee {
    private String name;
    private double salary;
    private LocalDate hireDay;

    public Employee(String n, double s, int year, int month, int day) {
        name = n;
        salary = s;
        hireDay = LocalDate.of(year, month, day);
    }

    public String getName() {
        return name;
    }

    public double getSalary() {
        return salary;
    }

    public LocalDate getHireDay() {
        return hireDay;
    }

    public void raiseSalary(double byPercent) {
        double raise = salary * byPercent / 100;
        salary += raise;
    }
}

多个源文件的使用

  • 一个源文件可以包含两个类,如果每个类存放在单独源文件中,如Employee类存于Employee.javaEmployeeTest类存于EmployeeTest.java
  • 有两种编译源程序的方法:
    • 使用通配符编译所有匹配源文件javac Employee*.java
    • 直接使用命令javac EmployeeTest.java,若EmployeeTest.java使用了Employee类,编译器会自动查找编译Employee.java

剖析 Employee 类

  • Employee类的所有方法都标记为public,意味着任何类的方法都可调用这些方法。
  • 类中有 3 个私有实例字段namesalaryhireDay,用private修饰确保只有Employee类自身的方法能访问,将实例字段标记为private对数据封装和安全性的重要性,不建议用public标记实例字段,否则会破坏封装性。
  • 指出name字段是String类对象,hireDay字段是LocalDate类对象,说明类包含的实例字段通常属于某个类类型 。

从构造器开始

  • Employee类的构造器public Employee(String n, double s, int year, int month, int day)为例,类的构造器与类同名,在创建类的对象时会运行,用于将实例字段初始化为特定状态。如new Employee("James Bond", 100000, 1958, 1, 1)会初始化相应的namesalaryhireDay字段。

  • 构造器总是结合new运算符调用,不能对已存在对象调用构造器重新设置实例字段;每个类可以有一个以上构造器,参数可以有 0 个、1 个或多个,且构造器没有返回值。

警告:不要在构造器中定义与实例字段同名的局部变量,否则会遮蔽实例字段。

C++注释:Java 构造器工作方式与 C++ 类似,但 Java 对象在堆中构造,且必须结合new操作符。

用 var 声明局部变量

  • 在 Java 10 中,若能从变量初始值推导出类型,可用var关键字声明局部变量,如var harry = new Employee("Harry Hacker", 50000, 1989, 10, 1); ,这样可避免重复写类型名。
  • var只能用于方法中的局部变量,参数和字段的类型仍需声明。

C++注释: C++11 引入了auto特性,编译器可根据初始化值自动推导变量类型。它能简化复杂类型声明,增强模板编程灵活性,使用时变量必须初始化。

使用 null 引用

  • 对象变量可包含对象引用或特殊值null,表示不引用任何对象。
  • null值应用方法会产生NullPointerException异常,类似 “索引越界” 异常,严重时会终止程序。

隐式参数与显式参数

  • Employee类的raiseSalary方法为例,调用中的参数分为隐式参数和显式参数,隐式参数是出现在方法名前的对象(即方法调用的目标对象),显式参数是位于方法名后面括号中的值。例如number007.raiseSalary(5)number007是隐式参数,5是显式参数。

  • 在方法中,this关键字指示隐式参数。可以通过this明确区分实例字段和局部变量。

public void raiseSalary(double byPercent) { 
    double raise = this.salary * byPercent / 100; 
    this.salary += raise; 
}`

C++注释:Java 方法都在类内部定义,而 C++ 在类外定义普通方法,在类内定义自动成为内联方法,Java 中方法是否内联由虚拟机决定 。

封装的优点

  • Employee类的getNamegetSalarygetHireDay方法为例,这些是典型的访问器方法(也称为字段访问器),用于返回实例字段值。
  • 封装优点
    • 将实例字段标记为私有,通过公共访问器和更改器方法操作,可以保护数据不被随意破坏。
    • 可以改变类的内部实现,而不影响其他代码。
    • 更改器方法可以进行错误检查。

警告:不要编写返回可变对象引用的访问器方法,否则可能破坏封装性。例如原Employee类的getHireDay方法返回Date对象引用存在问题,因为Date类有更改器方法,会导致对象状态被意外改变。

正确的做法是对可变对象进行克隆(clone)后返回,如return (Date) hireDay.clone();

基于类的访问权限

  • Employee类的equals方法为例,在 Java 中,一个类的方法不仅可以访问调用该方法的对象的私有数据,还能访问所属类的所有对象的私有数据。

C++注释:C++也有同样的原则。方法可以访问所属类任何对象的私有特性(feature),而不仅于隐式参数。

私有方法

  • 在 Java 中,将方法的访问修饰符从public改为private即可实现私有方法。

  • 私有方法的实现方式改变时,类设计者无需保证其可用性;若数据表示改变,私有方法可能受影响,但这并不重要,因为私有方法不会在别处被使用,可根据需要删除。而公共方法不能随意删除,因为可能有其他代码依赖 。

  • 实现类时,为保证数据安全,应将数据字段设为私有。多数方法设计为公共的,但在特殊情况下,可将一些仅用于辅助计算、与当前实现关系紧密或需特定调用协议及顺序的方法设为私有方法,这些方法不应成为公共接口的一部分。

final 实例字段

  • 可以将实例字段定义为final,这种字段必须在构造对象时初始化,且之后不能再修改。
  • final修饰符对基本类型或不可变类的字段很有用,如String类是不可变的。
  • 对于可变类的字段使用final修饰需谨慎,例如private final StringBuilder evaluations;final只是保证evaluations变量的引用不会指向其他StringBuilder对象,但对象本身的内容是可以更改的 。

静态字段与静态方法

静态字段

  • static修饰的字段为静态字段,每个类只有一个静态字段,而非静态的实例字段每个对象都有自己的副本。
classe Employee {
    private static int nextId = 1;
    private int id;
    
    public void setId() {
        id = nextId;
        nextId++;
    }
}

静态常量

  • 静态变量使用较少,但静态常量很常用。如Math类中的PI是静态常量,可通过Math.PI访问。System.out也是静态常量,它被声明为final,不能再重新赋值 。

  • System类中有setOut方法可修改System.out,这是因为该方法是原生方法,可绕过 Java 的访问控制机制,在自行编写程序时不应模仿 。

静态方法

  • 静态方法是不在对象上执行的方法,如Math类的pow方法,没有隐式参数。
  • 静态方法适用于两种情况:一是方法不需要访问对象状态,所有参数通过显式参数提供;二是方法只需要访问类的静态字段 。

C++注释:Java 中静态字段与静态方法的功能和 C++ 相同,但语法有差异。在 C++ 中,要使用::操作符访问作用域之外静态字段和静态方法。

工厂方法

  • 静态方法的一种常见用途是作为工厂方法来构造对象,像LocalDate类的nowof方法。
  • NumberFormat类不使用构造器而采用工厂方法:
    • 构造器名字必须与类名相同,无法通过不同名字分别得到货币实例和百分比实例;
    • 使用构造器不能改变所构造对象的类型,而工厂方法可返回NumberFormat子类
NumberFormat currencyFormatter = NumberFormat.getCurrencyInstance();
NumberFormat percentFormatter = NumberFormat.gNumberFormat.etPercentInstance();

main 方法

  • 静态方法是可以在不创建对象的情况下调用的方法,如调用Math.pow无需构造Math类对象,main方法也是静态方法 。

  • main方法在启动程序时执行,此时还没有对象,它的作用是执行并构造程序所需的对象 。

  • 每个类可以有一个main方法,这常用于类的单元测试。

方法参数

public static void tripleSalary(double x) {
    x = 3 * x;
}

tripleValue方法试图将参数值增至 3 倍,但调用后原始变量值不变。因为按值调用时,方法中的参数是原始值的副本,对副本的修改不会影响原始变量,方法结束后副本不再使用 。

public static void swap(Employee x, Employee y) {
    Employee tmp = x;
    x = y;
    y = tmp;
}

编写swap方法试图交换两个Employee对象,但实际无法实现。因为方法中的参数是对象引用的副本,交换的是副本,原始变量引用的对象并未改变,方法结束后副本被丢弃,原始变量不受影响 。

image.png

  • Java中对方法参数能做什么和不能做什么:

    • 方法不能修改基本数据类型的参数。

    • 方法可以改变对象参数的状态。

    • 方法不能让一个对象参数引用一个新的对象 。

C++注释:C++中有按值调用和按引用调用。引用参数标有&符号。例如,可以轻松地实现void tripleValue(double&x)方法或void swap(Employee&x,Employee&y)方法来修改它
们的引用参数。

对象构造

重载

  • 一些类有多个构造器,这种现象称为重载。指定初始字符串来构造对象。多个方法名字相同但参数不同就出现了重载 。
var messages = new StringBuilder();
var todoList = new StringBuilder("To do:\n");

默认字段初始化

  • 在构造器中若未显式给字段设置初始值,字段会自动赋默认值,数值为 0、布尔值为false、对象引用为null
  • 很多类包含无参数的构造器,用于创建对象时将对象状态设为适当默认值。

警告:若编写类时未写构造器,系统会提供一个无参数构造器,将实例字段设为默认值。但如果类中提供了至少一个构造器而没有无参数构造器,那么构造对象时不提供参数会产生错误。

只有当类没有其他构造器时,才会有默认的无参数构造器,若想让类使用者能通过无参数方式构造对象,就必须自行提供无参数构造器 。

显式字段初始化

  • 通过重载类的构造器,可以采用多种形式设置类实例字段的初始状态。
  • 初始值也可以是方法调用的结果。
class Employee {
    private static nextId;
    private int id = assginId();
    
    private static int assginId() {
        int r = nextId;
        nextId++;
        return r;
    }
}

C++注释:从 C++11 开始也支持直接在类的定义中初始化实例字段,也可以通过初始化列表语法来调用字段构造器, Java 无需这种方式 。

参数名

  • 编写小型构造器时,常用单个字母作参数名,如public Employee(String n, double s) ,但这样不易理解参数含义。
  • 部分程序员会在参数前加前缀 “a”,如public Employee(String aName, double aSalary) ,使参数含义更清晰。
  • 由于参数变量会遮蔽同名实例字段,可利用this关键字访问实例字段。
public Employee(String name, double salary) {
    this.name = name;
    this.salary = salary;
}

C++注释:在C++中,经常用下划线或某个固定的字母(一般选用m或x)作为实例字段的前缀。例如,salary字段可能被命名为_salarymSalaryxSalary。Java程序员通常不这样做。

调用另一个构造器

  • this关键字除指示隐式参数外,若构造器的第一个语句形如this(...),则表示调用同一个类的另一个构造器。
public Employee(double s) {
    // call Employee(String, double)
    this("Employee #" + nextId, s);
    nextId++;
}

C++注释:从 C++11 标准开始支持构造函数调用其他构造函数,ClassName::ClassName(parameters) : ClassName(otherParameters) { // ... };

初始化块

  • 除了在构造器中赋值和声明时赋值,Java 还有初始化块机制。类声明中的初始化块,在构造对象时会执行。

  • 若构造器首行调用其他构造器,按参数执行被调用的构造器;若没有,先将数据字段初始化为默认值,再按声明顺序执行字段初始化和初始化块,最后执行构造器主体。

对象析构与 finalize 方法

  • C++ 有显式析构器用于对象不再使用时执行清理,回收存储空间,而 Java 因自动垃圾回收,不支持显式析构器。
  • Java 对象若使用文件等内存外资源,资源不再需要时回收很重要。用完需立即关闭的资源,可提供close方法清理。

警告:明确不要用finalize方法清理,它原在垃圾回收前调用,但调用时间不确定且已被弃用。

包名

  • Java 允许用包将类组织在一起,方便管理代码并与其他代码库分开。使用包可确保类名唯一性,包名由因特网域名逆序加工程名构成,如com.horstmann.corejava

类的导入

  • 一个类可使用所属包及其他包中的公共类。访问方式有两种,一是使用完全限定名,如java.time.LocalDate today = java.time.LocalDate.now(); ;二是使用import语句,可导入特定类或整个包,import语句应位于源文件顶部。

C++注释import#include二者不同,#include用于加载外部特性,C++ 编译器无法查看其他文件内部,而 Java 编译器可以;Java 显式给出类名可避免使用import,C++ 无法避免使用#includeimport语句使引用类更简洁;Java 的packageimport类似于 C++ 的命名空间(namespace)和using指令 。

静态导入

  • 有一种import语句可用于导入静态方法和静态字段,而非仅导入类。例如,通过import static java.lang.System.*;,可在使用System类的静态方法和字段时省略类名前缀,如out.println("Goodbye, World!");exit(0);
  • 也可以导入特定的方法或字段,如import static java.lang.System.out;。虽然这种写法简洁,但可能影响代码清晰度 。

在包中增加类

  • 若要将类放入包中,需在源文件开头添加包名,如package com.horstmann.corejava; 。若未放置package语句,类属于无名包。

  • 源文件应放在与包名匹配的子目录中,编译器会将类文件也放在相同目录结构下。例如,com.horstmann.corejava包中的源文件应放在com\horstmann\corejava(Windows 系统)目录中 。

image.png

包访问

  • Java 中用publicprivate修饰符控制访问权限,public部分可被任意类使用,private部分只能由定义类使用。若未指定,相关部分(类、方法或变量)可被同一包中的方法访问。

类路径

  • 类存储在文件系统目录或 JAR(Java 归档)文件中,类路径需与包名匹配。JAR 文件用 ZIP 格式,能节省空间和提升性能。为使类可被多个程序共享,需将类文件或 JAR 文件放置在合适目录,并设置类路径(所有包含类文件路径的集合)。

设置类路径

  • 最好使用-classpath(或-cp、Java 9 中的--class-path)选项指定类路径,也可通过设置CLASSPATH环境变量设置。

警告:将CLASSPATH设为固定值或完全绕开类路径设置都可能引发问题,Java 9 中还可从模块路径加载类 。

JAR 文件

JAR(Java 归档)文件用于在打包应用程序时,将大量类文件和其他资源整合为单个文件,采用 ZIP 压缩格式,可包含图像、声音等文件 。

创建 JAR 文件

  • 使用jar工具(位于jdk/bin目录下)创建 JAR 文件,常用命令格式为jar cvf jarFileName file1 file2...

清单文件

  • 每个 JAR 文件包含一个清单文件(manifest),位于META-INF子目录中,用于描述归档文件特性。简单的清单文件定义Manifest-Version,复杂的清单文件包含主节和多个子节,指定文件、包或 URL 等属性。创建和更新清单文件有相应的jar命令操作方式 。

可执行 JAR 文件

  • 通过jar命令的e选项或在清单文件中指定Main-Class来指定程序入口点,从而创建可执行 JAR 文件。用户可通过java -jar命令或在特定操作系统下双击图标启动程序。

多版本 JAR 文件

  • Java 9 引入多版本 JAR 文件,可包含面向不同 Java 版本的类文件,以解决兼容性问题 。

关于命令行选项的说明

  • JDK 命令行选项传统格式是单个短横线加字母,jar命令例外。从 Java 9 开始,转向新格式,多字母选项前加两个横线,常用选项有单字母快捷方式。

文档注释

JDK 中的javadoc工具能根据源文件生成 HTML 文档。在源代码中添加以/**开始的注释,可方便生成具有专业水平的 文档,且能保证代码和注释的一致性 。

注释的插入

  • javadoc从模块、包、公共类与接口、公共和受保护的字段、公共和受保护的构造器及方法中抽取信息。
  • 注释以/**开始,*/结束,包含标记和自由格式文本,可使用 HTML 修饰符,还可在注释中添加指向其他文件的链接 。

类注释

/**
	A <code>Card</code> object represents a playing card, 	such
	as "Queen of Hearts".A card has a suit (Diamond, Heart,
	Spade or club) and a value (1 Ace,2...10,11 Jack,
	12 Queen,13 King)
*/
public class Card {
    // ...
}

方法注释

  • 置于所描述方法之前,除通用标记外,还可用@param标记方法参数说明,@return标记返回值说明,@throws标记可能抛出的异常说明 。
/**
	Raises the salary of an employee.
	@param byPercent the percentage by which to raise the salary (e.g.,10 means 10%)
	@return the amount of the raise
*/
public double raiseSalary(double byPercent) {
    double raise = salary * byPercent / 100;
    salary += raise;
    return raise;
}

字段注释

  • 主要为公共字段(通常是静态常量)建立文档 。
/**
	The“Hearts"card suit
*/
public static final int HEARTS = 1;

通用注释

  • @since标记用于建立 “since” 条目,描述引入特性的版本;
  • @author标记生成 “author” 条目记录作者,可多个;
  • @version标记产生 “version” 条目描述当前版本 。
  • @see@link用于添加超链接,链接到javadoc文档相关部分或外部文档。

包注释

  • 类、方法和变量注释可直接写在 Java 源文件中,而包注释需在每个包目录中添加单独文件。
  • 可以提供名为package-info.java的 Java 文件,包含以/***/界定的Javadoc注释及一个package语句;也可提供名为package.html的 HTML 文件,抽取<body>...</body>之间的文本作为包注释 。

注释抽取

  • 若希望生成的 HTML 文档放在docDirectory目录下,先切换到源文件目录。
  • 对于包,运行javac -d docDirectory nameOfPackage(多个包时列出所有包名);
  • 对于无名包中的文件,运行javac -d docDirectory *.java

类设计技巧

  1. 保证数据私有:要确保类的数据字段为私有,避免破坏封装性。即便需要访问或修改数据,也应通过访问器或更改器方法,这样能防止数据表示形式变化影响类的使用者,且便于检测错误。
  2. 显式初始化数据:Java 会自动初始化对象的实例字段,但不初始化局部变量。设计类时,不应依赖系统默认值,而应显式初始化所有数据,可提供默认值或在构造器中设置 。
  3. 减少基本类型使用:尽量用其他类替换多个相关的基本类型,使类更易理解和修改。如用Address类替换Customer类中与地址相关的多个基本类型字段,便于处理地址相关的变化 。
  4. 合理设置访问器和更改器:并非所有字段都需要单独的访问器和更改器方法,应根据实际需求决定,例如员工的雇佣日期可能无需更改器方法 。
  5. 避免职责过多的类:若一个类包含多个独立概念,应将其分解为更简单的类,但也要避免过度分解。以CardDeck类为例,将其拆分为表示牌堆和一张牌的两个类,使每个类职责更清晰 。
  6. 类名和方法名体现职责:类名和方法名应能准确反映其功能和含义,遵循标准惯例,如访问器方法以get开头,更改器方法以set开头 。
  7. 优先使用不可变类:像LocalDatejava.time包中的很多类是不可变的,不可变类能避免多线程环境下的并发修改问题,保证线程安全。对于表示值的类,如字符串、时间点等,设计成不可变类更为合适 。
    例字段,但不初始化局部变量。设计类时,不应依赖系统默认值,而应显式初始化所有数据,可提供默认值或在构造器中设置 。
  8. 减少基本类型使用:尽量用其他类替换多个相关的基本类型,使类更易理解和修改。如用Address类替换Customer类中与地址相关的多个基本类型字段,便于处理地址相关的变化 。
  9. 合理设置访问器和更改器:并非所有字段都需要单独的访问器和更改器方法,应根据实际需求决定,例如员工的雇佣日期可能无需更改器方法 。
  10. 避免职责过多的类:若一个类包含多个独立概念,应将其分解为更简单的类,但也要避免过度分解。以CardDeck类为例,将其拆分为表示牌堆和一张牌的两个类,使每个类职责更清晰 。
  11. 类名和方法名体现职责:类名和方法名应能准确反映其功能和含义,遵循标准惯例,如访问器方法以get开头,更改器方法以set开头 。
  12. 优先使用不可变类:像LocalDatejava.time包中的很多类是不可变的,不可变类能避免多线程环境下的并发修改问题,保证线程安全。对于表示值的类,如字符串、时间点等,设计成不可变类更为合适 。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2312451.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

一篇文章讲解清楚ARM9芯片启动流程

SAM9X60 ARM9 boot启动流程关键词介绍&#xff1a; 第一级bootloader - 也叫boot ROM&#xff0c;是集成在MPU内部的ROM里面 它的主要功能是执行对MPU的基本初始化和配置&#xff0c;查找并将第二级bootloader从外部NVM中读取出来并放到MPU内部的SRAM. 可以让MPU强制停留在第一…

【2025】Electron + React 架构筑基——从零到一的跨平台开发

引言 源代码仓库&#xff1a; Github仓库【electron_git】 你是否厌倦了在命令行中反复输入git status&#xff0c;却依然无法直观看到文件变化&#xff1f; 是否羡慕VS Code的丝滑Git集成&#xff0c;却苦恼于无法定制自己的专属工具&#xff1f; 本专栏将为你打开一扇新的…

Vue3实战学习(IDEA中打开、启动与搭建Vue3工程极简脚手架教程(2025超详细教程)、Windows系统命令行启动Vue3工程)(2)

目录 一、命令行中重新启动已搭建好的Vue3工程。(快速上手) &#xff08;0&#xff09;Windows环境下使用命令行从零到一手动搭建Vue3工程教程。 &#xff08;1&#xff09;首先找到已建Vue3工程的目录。 &#xff08;2&#xff09;无需再下载依赖包&#xff0c;直接执行npm ru…

【ArcGIS】地理坐标系

文章目录 一、坐标系理论体系深度解析1.1 地球形态的数学表达演进史1.1.1 地球曲率的认知变化1.1.2 参考椭球体参数对比表 1.2 地理坐标系的三维密码1.2.1 经纬度的本质1.2.2 大地基准面&#xff08;Datum&#xff09;的奥秘 1.3 投影坐标系&#xff1a;平面世界的诞生1.3.1 投…

Oxidized收集H3C交换机网络配置报错,not matching configured prompt (?-mix:^(<CD>)$)

背景&#xff1a;问题如上标题&#xff0c;H3C所有交换机配置的model都是comware 解决方案&#xff1a; 1、找到compare.rb [rootoxidized model]# pwd /usr/local/lib/ruby/gems/3.1.0/gems/oxidized-0.29.1/lib/oxidized/model [rootoxidized model]# ll comware.rb -rw-r--…

【ubuntu20】--- 搭建 gerrit 最新最详细

在编程的艺术世界里&#xff0c;代码和灵感需要寻找到最佳的交融点&#xff0c;才能打造出令人为之惊叹的作品。而在这座秋知叶i博客的殿堂里&#xff0c;我们将共同追寻这种完美结合&#xff0c;为未来的世界留下属于我们的独特印记。 【ubuntu20】--- 搭建 gerrit 最新最详细…

Python----数据分析(Matplotlib五:pyplot的其他函数,Figure的其他函数, GridSpec)

一、pyplot的其他函数 1.1、xlabel 在matplotlib中&#xff0c; plt.xlabel() 函数用于为当前活动的坐标轴&#xff08;Axes&#xff09;设置x轴的 标签。当你想要标识x轴代表的数据或单位时&#xff0c;这个函数非常有用。 plt.xlabel(xlabel text) 1.2、ylabel 在matplotl…

C语言——链表

大神文献&#xff1a;https://blog.csdn.net/weixin_73588765/article/details/128356985 目录 一、链表概念 1. 什么是链表&#xff1f; 1.1 链表的构成 2. 链表和数组的区别 数组的特点&#xff1a; 链表的特点&#xff1a; 二者对比&#xff1a; 二…

使用免费IP数据库离线查询IP归属地

一、准备工作 1.下载免费IP数据库 首先&#xff0c;访问 MaxMind官网&#xff08;https://www.maxmind.com/en/home&#xff09;如果你还没有MaxMind账号&#xff0c;可以通过此链接地址&#xff08;https://www.maxmind.com/en/geolite2/signup&#xff09;进行账号注册&…

MySQL(单表)知识点

文章目录 1.数据库的概念2.下载并配置MySQL2.1初始化MySQL的数据2.2注册MYSQL服务2.3启动MYSQL服务2.4修改账户默认密码2.5登录MYSQL2.6卸载MYSQL 3.MYSQL数据模型3.1连接数据库 4.SQL简介4.1SQL的通用语法4.2SQL语句的分类4.3DDL语句4.3.1数据库4.3.2表(创建,查询,修改,删除)4…

1.15-16-17-18迭代器与生成器,函数,数据结构,模块

目录 15&#xff0c;Python3 迭代器与生成器15-1 迭代器15-1-1 基础知识15-1-2 迭代器与for循环工作原理 15-2 生成器&#xff08;本质就是迭代器&#xff09;15-2-1 yield 表达式15-2-2 三元表达式15-2-3 列表生成式15-2-4 其他生成器&#xff08;——没有元祖生成式——&…

Java 大视界 -- Java 大数据在智能家居能源管理与节能优化中的应用(120)

&#x1f496;亲爱的朋友们&#xff0c;热烈欢迎来到 青云交的博客&#xff01;能与诸位在此相逢&#xff0c;我倍感荣幸。在这飞速更迭的时代&#xff0c;我们都渴望一方心灵净土&#xff0c;而 我的博客 正是这样温暖的所在。这里为你呈上趣味与实用兼具的知识&#xff0c;也…

第七课:Python反爬攻防战:Headers/IP代理与验证码

在爬虫开发过程中&#xff0c;反爬虫机制成为了我们必须面对的挑战。本文将深入探讨Python爬虫中常见的反爬机制&#xff0c;并详细解析如何通过随机User-Agent生成、代理IP池搭建以及验证码识别来应对这些反爬策略。文章将包含完整的示例代码&#xff0c;帮助读者更好地理解和…

MySql的安装及数据库的基本操作命令

1.MySQL的安装 1.1进入MySQL官方网站 1.2点击下载 1.3下拉选择MySQL社区版 1.4选择你需要下载的版本及其安装的系统和下载方式 直接安装以及压缩包 建议选择8.4.4LST LST表明此版本为长期支持版 新手建议选择红框勾选的安装方式 1.5 安装包下载完毕之后点击安装 2.数据库…

VsCode导入时选择相对路径

自动导入时总是以db://开头了&#xff0c;而我们通常需要的是相对路径&#xff0c;对VsCode进行如下设置&#xff1a; 打开 VSCode 设置&#xff1a; 使用快捷键 Ctrl ,&#xff08;Windows/Linux&#xff09;或 Cmd ,&#xff08;Mac&#xff09;。 或者在菜单栏中选择 …

计算机视觉|3D卷积网络VoxelNet:点云检测的革新力量

一、引言 在科技快速发展的背景下&#xff0c;3D 目标检测技术在自动驾驶和机器人领域中具有重要作用。 在自动驾驶领域&#xff0c;车辆需实时、准确感知周围环境中的目标物体&#xff0c;如行人、车辆、交通标志和障碍物等。只有精确检测这些目标的位置、姿态和类别&#x…

AI数字人源码开发---SaaS化源码部署+PC+小程序一体化

#数字人#数字人分身#123数字人#数字人分身源码部署搭建 AI数字人源码开发步骤 确定功能需求&#xff1a;首先确定需要实现的功能和特性&#xff0c;包括语音识别、自然语言处理、人脸识别等功能。这些功能将构成AI数字人的核心功能。 开发AI数字人源码&#xff1a;使用合适的…

Linux网络相关内容与端口

网络相关命令 ping命令测试连接状态 wget命令&#xff1a;非交互式文件下载器&#xff0c;可以在命令行内下载网络文件 使用ctrlc可以中止下载 curl命令&#xff1a;可以发送http网络请求&#xff0c;用于文件下载、获取信息等 其实和浏览器打开网站一样&#xff0c;cu…

【招聘精英】

我们公司是一个位于石家庄的一个科技型新型技术公司。主要做人力资源、用工、科技等方面。 有意向回石家庄的或者已经在石家庄的技术大咖、软件大牛、产品大佬、UI大神可以来了解一下。 现在招聘 高级前端开发 高级java开发 其他岗位也可以联系。 有意向的朋友可以私信我。 -…

ESP8266 NodeMCU 与 Atmega16 微控制器连接以发送电子邮件

NodeMCU ESP8266 AVR 微控制器 ATmega16 的接口 Atmega16 是一款低成本的 8 位微控制器,比以前版本的微控制器具有更多的 GPIO。它具有所有常用的通信协议,如 UART、USART、SPI 和 I2C。由于其广泛的社区支持和简单性,它在机器人、汽车和自动化行业有广泛的应用。 Atmega1…