编辑
面向对象基础
面向对象介绍
编辑
自定义对象
Java中自定义对象的必要性
就像我们之前用的Scanner 和Random 都是java里面已经写好的对象,直接拿来用就好了,不用再自己写一大串代码来实现键盘录入和随机数的需求,但是有些需求是java中没有定义和写好的,,但实际开发中常遇到需要重复实现的特定功能。这个时候对象就派上用场了,我们可以把这些需求自定义对象,自定义对象可以封装这些功能,提升代码复用性和可维护性。
自定义对象的优势
封装重复逻辑 将频繁使用的代码逻辑(如数据验证、特定计算)封装为独立对象,避免代码冗余。
简化调用 通过定义清晰的方法名和属性,调用时只需关注输入输出,隐藏内部实现细节。
统一管理 修改逻辑时只需调整对象内部代码,所有调用点自动同步更新,减少维护成本。
实现自定义对象的步骤
定义类结构 使用class关键字声明类,明确其职责边界。例如处理日期格式的工具类:
arduino
public class DateFormatter {
private static final String DEFAULT_PATTERN = "yyyy-MM-dd";
}
添加必要方法 根据需求设计公有方法。如实现日期字符串解析:
arduino
public LocalDate parse(String dateStr) throws DateTimeParseException {
return LocalDate.parse(dateStr, DateTimeFormatter.ofPattern(DEFAULT_PATTERN));
}
考虑扩展性 通过构造函数或方法参数支持自定义配置。例如允许指定格式模式:
typescript
public String format(LocalDate date, String pattern) {
return date.format(DateTimeFormatter.ofPattern(pattern));
}
实际应用示例
场景需求 多个模块需要生成指定长度的随机字符串。
自定义实现 创建RandomStringGenerator类:
arduino
public class RandomStringGenerator {
private final String characterPool;
public RandomStringGenerator(String characterPool) {
this.characterPool = characterPool;
}
public String generate(int length) {
StringBuilder sb = new StringBuilder();
Random random = new Random();
for (int i = 0; i < length; i++) {
sb.append(characterPool.charAt(random.nextInt(characterPool.length())));
}
return sb.toString();
}
}
调用方式
ini
RandomStringGenerator generator = new RandomStringGenerator("ABCDEF123");
String randomCode = generator.generate(8); // 输出类似 "A1BF3E2D"
设计注意事项
单一职责原则 每个对象应只负责一个明确的功能领域,避免创建"万能工具类"。
适当访问控制 使用private保护内部状态,通过公有方法提供可控的访问途径。
文档注释 使用JavaDoc说明类用途和方法参数,增强代码可读性:
markdown
/**
* 生成指定字符集范围内的随机字符串
* @param characterPool 允许使用的字符集合
*/
通过合理设计自定义对象,可以有效组织代码结构,减少重复劳动,提升开发效率。
定义成一个对象,这样就可以反复使用了
变量与局部变量的区别
变量是程序中用于存储数据的标识符,可以在程序的多个部分访问和修改。局部变量是特定于某个函数或代码块的变量,只能在其定义的范围内使用。
变量的特点
变量具有全局或局部的生命周期,具体取决于其定义位置。全局变量在整个程序运行期间都存在,可以在任何函数或代码块中访问。局部变量仅在定义它们的函数或代码块执行期间存在。
局部变量的特点
局部变量的作用域仅限于定义它们的函数或代码块。一旦函数或代码块执行完毕,局部变量就会被销毁。局部变量有助于避免命名冲突和提高代码的模块化程度。
变量的作用域
全局变量的作用域涵盖整个程序,可以在任何地方访问。局部变量的作用域仅限于定义它们的函数或代码块。作用域规则决定了变量的可见性和生命周期。
局部变量的优势
使用局部变量可以减少内存占用,因为它们在函数执行完毕后会被释放。局部变量还能提高代码的可读性和可维护性,因为它们的生命周期和影响范围有限。
变量的内存管理
全局变量在程序启动时分配内存,直到程序结束才释放。局部变量在函数或代码块执行时分配内存,执行完毕后立即释放。合理使用局部变量有助于优化内存使用效率。
局部变量的使用场景
局部变量适用于临时存储数据或在函数内部进行计算。它们可以防止不同函数之间的数据干扰,确保每个函数的操作独立且安全。
变量的命名规范
无论是全局变量还是局部变量,都应遵循一致的命名规范。局部变量的命名应反映其用途和上下文,避免过于通用的名称。良好的命名习惯有助于代码的理解和维护。
构造方法
构造方法的概念
构造方法是一种特殊的方法,用于在创建对象时初始化对象的状态。它在对象实例化时自动调用,通常用于设置成员变量的初始值或执行必要的初始化操作。
构造方法的特点
- 方法名必须与类名完全相同。
- 没有返回类型(包括
void)。 - 支持重载,即一个类可以有多个参数不同的构造方法。
- 若未显式定义构造方法,编译器会提供一个默认的无参构造方法;若已定义,则不再提供默认构造方法。
构造方法的语法
csharp
public class ClassName {
// 无参构造方法
public ClassName() {
// 初始化代码
}
// 带参构造方法
public ClassName(type param1, type param2) {
// 使用参数初始化成员变量
}
}
构造方法的使用示例
csharp
public class Person {
private String name;
private int age;
// 无参构造方法
public Person() {
name = "Unknown";
age = 0;
}
// 带参构造方法
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void display() {
System.out.println("Name: " + name + ", Age: " + age);
}
}
// 使用构造方法创建对象
Person person1 = new Person(); // 调用无参构造方法
Person person2 = new Person("Alice", 25); // 调用带参构造方法
构造方法的注意事项
- 若类中定义了带参构造方法,通常需要显式定义无参构造方法,否则无法直接通过
new ClassName()实例化对象。 - 构造方法可以通过
this()调用同类中的其他构造方法,但必须作为方法的首条语句。 - 构造方法不能被继承,子类需通过
super()调用父类的构造方法。
面向对象高级
面向对象的三大特性:封装,继承,多态
封装(合理隐藏,合理暴露)
封装是将数据(属性)和操作数据的方法(行为)捆绑在一起,形成一个独立的单元(类)。通过访问修饰符(如 private、protected、public)控制外部对内部细节的访问,隐藏实现细节,仅暴露必要接口。
-
优点:提高代码安全性,减少耦合,便于维护。
-
示例:
csharpclass BankAccount { private double balance; // 私有属性,外部无法直接访问 public void deposit(double amount) { // 公开方法 if (amount > 0) balance += amount; } public double getBalance() { return balance; } }
继承
继承允许子类复用父类的属性和方法,并可通过扩展或重写实现功能增强。体现"is-a"关系(如 Dog 继承 Animal)。
在继承体系中,子类可以继承到父类的方法 但有时子类并不想原封不动地继承父类的方法,而是想作一定的修改 这就需要采用方法的重写,方法重写又称方法覆盖 子类重写父类方法,需要保证方法声明完全一致(方法名,参数,返回值类型需要保持一致)
-
优点:代码复用,层次化设计。
-
类型:单继承(Java、C#)、多继承(C++通过接口或虚基类实现)。
-
示例:
scalaclass Animal { void eat() { System.out.println("Eating..."); } } class Dog extends Animal { void bark() { System.out.println("Barking..."); } }
多态
多态指同一操作作用于不同对象时产生不同行为,通常通过方法重写(子类覆盖父类方法)或接口实现。分为编译时多态(方法重载)和运行时多态(方法重写)。
-
实现方式:继承 + 方法重写、接口。
-
示例:
typescriptclass Shape { void draw() { System.out.println("Drawing shape"); } } class Circle extends Shape { @Override void draw() { System.out.println("Drawing circle"); } // 运行时多态 } public class Main { public static void main(String[] args) { Shape s = new Circle(); // 父类引用指向子类对象 s.draw(); // 输出 "Drawing circle" } }
三者的关系
- 封装是基础,确保对象内部稳定性。
- 继承建立层次结构,实现代码复用。
- 多态基于继承,提升系统灵活性和可扩展性。
通过结合三大特性,可构建高内聚、低耦合的面向对象系统。
接口
接口的定义与作用
接口(Interface)在编程中是一种抽象类型,用于定义一组方法或属性的规范,而不包含具体实现。它充当不同组件之间的契约,确保实现类必须遵循接口定义的行为。
- 标准化交互:接口规定了类或模块必须实现的方法,确保代码一致性。
- 解耦设计:通过接口隔离实现细节,降低模块间的直接依赖。
- 多态支持:不同类实现同一接口后,可通过接口类型统一调用。
接口的常见应用场景
- API设计:远程服务通过接口定义可调用的方法(如RESTful API)。
- 插件系统:通过接口允许第三方扩展功能(如IDE插件)。
- 测试模拟:用接口的模拟实现替代真实依赖,方便单元测试。
接口与抽象类的区别
| 特性 | 接口 | 抽象类 |
|---|---|---|
| 实现方式 | 纯抽象(无具体方法实现) | 可包含抽象和具体方法 |
| 多继承 | 支持多接口继承 | 仅支持单继承 |
| 状态 | 不能包含字段(仅属性) | 可包含字段和属性 |
代码示例
Java 接口定义
csharp
public interface Drawable {
void draw(); // 抽象方法
default void resize() { // 默认方法(Java 8+)
System.out.println("Resizing...");
}
}
接口设计的最佳实践
- 单一职责 :每个接口应聚焦单一功能(如
IEnumerable仅用于迭代)。 - 明确命名 :使用
I前缀(如C#)或-able后缀(如Java)增强可读性。 - 避免过度使用:优先用接口解耦复杂系统,简单场景可直接用具体类。
通过合理使用接口,能显著提升代码的可维护性和扩展性。
final关键字的基本概念
在编程中,final是一个常见的关键字,其具体含义和用法因语言而异。以下是几种主流语言中final的典型用法和区别。
Java中的final关键字
在Java中,final可以修饰变量、方法和类,具有不同的语义:
修饰变量
-
基本类型变量:值不可更改(常量)。
inifinal int MAX_VALUE = 100; // MAX_VALUE = 200; // 编译错误 -
引用类型变量:引用不可更改,但对象内部状态可能可变。
inifinal List<String> list = new ArrayList<>(); list.add("item"); // 允许 // list = new ArrayList<>(); // 编译错误
修饰方法
-
方法不可被子类重写。
scalaclass Parent { final void display() { System.out.println("Parent"); } } class Child extends Parent { // void display() { } // 编译错误 }
修饰类
-
类不可被继承。
kotlinfinal class ImmutableClass { } // class SubClass extends ImmutableClass { } // 编译错误
使用场景与最佳实践
- 常量定义 :用
final声明不可变配置值或全局常量。 - 设计不可变类 :通过
final类和方法确保关键逻辑不被修改。 - 线程安全 :
final变量在多线程中无需额外同步(Java中保证可见性)。