当前位置: 首页 > article >正文

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

文章目录

  • 面向对象程序设计概述
  • 使用预定义类
  • 用户自定义类
  • 静态字段与静态方法
  • 方法参数
  • 对象构造
  • 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 objectsEmployee[] 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 objectsfor (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, 	suchas "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包中的很多类是不可变的,不可变类能避免多线程环境下的并发修改问题,保证线程安全。对于表示值的类,如字符串、时间点等,设计成不可变类更为合适 。

相关文章:

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

文章目录 面向对象程序设计概述使用预定义类用户自定义类静态字段与静态方法方法参数对象构造包JAR 文件文档注释类设计技巧 面向对象程序设计概述 面向对象程序设计&#xff08;OOP&#xff09;在 20 世纪 70 年代出现&#xff0c;是当今主流编程范型&#xff0c;Java 是面向…...

java2025年常见设计模式面试题

1. 请解释建造者模式&#xff08;Builder Pattern&#xff09;及其应用场景。 答案&#xff1a; 建造者模式用于创建一个复杂的对象&#xff0c;同时允许用户只通过指定复杂对象的类型和内容就能构建它们&#xff0c;隐藏了复杂的构建逻辑。 示例&#xff1a; public class C…...

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

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

setlocale()的参数,“zh_CN.UTF-8“, “chs“, “chinese-simplified“的差异。

在 C/C 中&#xff0c;setlocale() 函数的参数 zh_CN.UTF-8、chs 和 chinese-simplified 均用于设置中文简体环境&#xff0c;但它们的语义、平台支持和编码行为存在显著差异&#xff1a; ​1. zh_CN.UTF-8&#xff08;推荐&#xff09;​ ​含义&#xff1a; zh_CN: 中文&…...

Python项目-基于Django的在线教育平台开发

1. 项目概述 在线教育平台已成为现代教育的重要组成部分&#xff0c;特别是在后疫情时代&#xff0c;远程学习的需求显著增加。本文将详细介绍如何使用Python的Django框架开发一个功能完善的在线教育平台&#xff0c;包括系统设计、核心功能实现以及部署上线等关键环节。 本项…...

【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 投…...

Redis- 切片集群

切片集群 切片集群什么是Redis Cluster吗&#xff1f;为什么需要切片集群&#xff1f;Redis Cluster的数据分片机制是怎样的&#xff1f;哈希槽的算法是什么基本算法流程 待填坑 切片集群 什么是Redis Cluster吗&#xff1f;为什么需要切片集群&#xff1f; Redis Cluster是R…...

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--…...

力扣146 - LRU缓存

视频讲解 哈希 双向链表 为什么要用双向链表&#xff1f; 快速删除节点&#xff08;O(1&#xff09;&#xff09; 如果是单链表的话&#xff0c;删除一个节点时&#xff0c;需要从头遍历&#xff0c;找到前驱节点&#xff0c;才能修改 prev->next&#xff0c;导致 O(n)…...

单例模式:确保一个类只有一个实例

目录 引言 1. 单例模式的核心思想 2. 单例模式的实现方式 2.1 饿汉式单例 2.2 懒汉式单例 2.3 线程安全的懒汉式单例 2.4 双重检查锁定&#xff08;Double-Checked Locking&#xff09; 2.5 静态内部类实现单例 2.6 枚举实现单例 3. 单例模式的使用场景 4. 单例模式…...

doris: SQL Server

Doris JDBC Catalog 支持通过标准 JDBC 接口连接 SQL Server 数据库。本文档介绍如何配置 SQL Server 数据库连接。 使用须知​ 要连接到 SQL Server 数据库&#xff0c;您需要 SQL Server 2012 或更高版本&#xff0c;或 Azure SQL 数据库。 SQL Server 数据库的 JDBC 驱动…...

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

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

RtlLookupAtomInAtomTable函数分析之RtlpAtomMapAtomToHandleEntry函数的作用是验证其正确性

第一部分&#xff1a; NTSTATUS RtlLookupAtomInAtomTable( IN PVOID AtomTableHandle, IN PWSTR AtomName, OUT PRTL_ATOM Atom OPTIONAL ) { NTSTATUS Status; PRTL_ATOM_TABLE p (PRTL_ATOM_TABLE)AtomTableHandle; PRTL_ATOM_TABLE_ENTRY a; …...

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;——没有元祖生成式——&…...

window下的docker内使用gpu

Windows 上使用 Docker GPU需要进行一系列的配置和步骤。这是因为 Docker 在 Windows 上的运行环境与 Linux 有所不同,需要借助 WSL 2(Windows Subsystem for Linux 2)和 NVIDIA Container Toolkit 来实现 GPU 的支持。以下是详细的流程: 一、环境准备 1.系统要求 Window…...

CVE-2025-0392:JeeWMS graphReportController.do接口SQL注入漏洞复现

文章目录 CVE-2025-0392:JeeWMS graphReportController.do接口SQL注入漏洞复现0x01 前言0x02 漏洞描述0x03 影响版本0x04 漏洞环境0x05 漏洞复现1.构造POC2.复现CVE-2025-0392:JeeWMS graphReportController.do接口SQL注入漏洞复现 0x01 前言 免责声明:请勿利用文章内的相…...

DR和BDR的选举规则

在 OSPF&#xff08;开放最短路径优先&#xff09;协议中&#xff0c;DR&#xff08;Designated Router&#xff0c;指定路由器&#xff09; 和 BDR&#xff08;Backup Designated Router&#xff0c;备份指定路由器&#xff09; 的选举是为了在广播型网络&#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…...

创新监管,保障生产安全

在现代工业生产中&#xff0c;电气焊作业是不可或缺的一环&#xff0c;但同时也伴随着一定的安全风险。为了提高焊接作业的安全性&#xff0c;迪格特电子科技有限公司开发了电气焊安全作业管理平台&#xff0c;该平台通过智能化监管系统&#xff0c;实现了对焊机联网的全面监管…...

深入解析 C# 中的泛型:概念、用法与最佳实践

C# 中的 泛型&#xff08;Generics&#xff09; 是一种强大的编程特性&#xff0c;允许开发者在不预先指定具体数据类型的情况下编写代码。通过泛型&#xff0c;C# 能够让我们编写更灵活、可重用、类型安全且性能优良的代码。泛型广泛应用于类、方法、接口、委托、集合等多个方…...