记录一些编程学习中遇到的各种奇怪问题
1 CRLF & LF
操作系统 | CRLF | LF |
---|---|---|
Windows | 正确换行 | 可能显示为单个空格 |
类 Unix | 两个字符(CR + LF) | 正确换行 |
2 Linux
2.1 Env
系统级别环境变量文件:
这些文件定义的环境变量对所有用户生效,通常由系统管理员管理:
/etc/environment
:主要用于设置系统范围的环境变量,如PATH
。/etc/profile
:在用户登录时读取,用于设置所有用户的环境变量。/etc/profile.d/
:该目录下的脚本文件在用户登录时会被/etc/profile
调用,用于设置特定应用程序或服务的环境变量。/etc/bash.bashrc
:在每次打开新的 Bash shell 时读取,用于设置所有用户的 Bash 特定的环境变量。
用户级别环境变量文件:
这些文件定义的环境变量仅对特定用户生效:
~/.bash_profile
或~/.profile
:在用户登录时读取,用于设置用户特定的环境变量。~/.bashrc
:在每次打开新的 Bash shell 时读取,用于设置用户特定的 Bash 环境变量。
注意:
~
表示用户的主目录。- 在某些系统中,可能同时存在
~/.bash_profile
和~/.profile
,也可能只存在其中一个。 ~/.profile
通常被认为是更通用的配置文件,而~/.bash_profile
则更特定于 Bash shell。- 具体的环境变量设置可能因 Linux 发行版而异。
- 修改后,需要重新登录或者执行
source ~/.bashrc
使修改生效。
2.1.1 PATH
$PATH
决定了PATH
的搜索路径- 如
export PATH=$JAVA_HOME/bin:$PATH
,优先搜索添加的$JAVA_HOME/bin
然后搜索已有的PATH
,反之亦然
- 如
3 JAVA
3.1 Printf
在Java中,
%n
和\n
都可以用于创建新的一行。然而,它们之间存在一些重要的区别。\n
是一个通用的换行符,它在所有平台上都表示新的一行。然而,不同的操作系统可能使用不同的字符或字符序列来表示行结束。例如,Unix系统使用\n
,而Windows系统使用\r\n
。相比之下,
%n
是一个平台特定的行分隔符。在printf
函数中使用时,Java运行时会将其替换为当前平台的行分隔符。这意味着,如果你的代码在不同的平台上运行,%n
可能会产生不同的结果。因此,如果你希望你的代码在所有平台上都能产生相同的输出,你应该使用
\n
。但是,如果你希望你的代码能够适应不同的平台,并使用各自的行分隔符,你应该使用%n
。
3.2 访问修饰符
在Java中,有四种访问修饰符,它们分别是:
public
:公共的,表示该方法或属性可以在任何地方被访问,包括不同的包和类。protected
:受保护的,表示该方法或属性可以在同一个包内的任何类中被访问,也可以在子类中被访问,即使子类在不同的包中。private
:私有的,表示该方法或属性只能在其所在的类中被访问。default(无修饰符):当一个方法或属性没有明确指定访问修饰符时,它的访问级别是"包私有"(
package-private
),也就是说,它可以在同一个包内的任何类中被访问,但不能被其他包的类访问。
3.3 entity
& DTO
在软件开发中,entity
(实体)和DTO
(数据传输对象)分别有不同的用途和使用场景。确定使用哪一个取决于具体的需求和应用场景。下面将详细介绍entity
和DTO
的区别、适用场景以及如何在不同情况下做出选择。
3.3.1 Entity
3.3.1.1 定义
entity
通常代表数据库中的表或集合中的对象。它们与数据库中的数据直接映射,通常包含业务逻辑和数据持久化的相关信息。
3.3.1.2 特点
- 与数据库紧密耦合:
entity
类通常与数据库表结构一一对应。 - 包含业务逻辑:可以包含业务逻辑方法。
- 数据持久化:使用ORM框架(如Hibernate、JPA)进行数据持久化。
3.3.1.3 适用场景
- 数据持久化:在需要与数据库交互的场景中,使用
entity
类。 - 业务逻辑处理:在需要在对象上执行业务逻辑的场景中使用。
- 直接映射数据库表:当需要直接操作数据库表数据时。
3.3.2 DTO
3.3.2.1 定义
DTO
(Data Transfer Object)是用于在不同层之间传输数据的对象。通常用于传输数据而不包含业务逻辑。
3.3.2.2 特点
- 与数据库解耦:DTO与数据库表结构无关,只用于数据传输。
- 无业务逻辑:通常不包含业务逻辑,仅用于封装数据。
- 轻量级:相对于
entity
,DTO通常更加轻量,仅包含需要传输的数据。
3.3.2.3 适用场景
- 数据传输:在不同层(如服务层和控制层)之间传递数据时使用。
- API接口:在设计API接口时,使用DTO传输数据以保证接口的稳定性和数据格式的控制。
- 安全性:隐藏内部数据结构,仅暴露需要传输的字段,提升安全性。
3.3.3 选择指南
持久化和业务逻辑:
- 如果需要与数据库直接交互,并且需要在对象上处理业务逻辑,选择
entity
。 - 例如:保存用户数据到数据库,或者处理订单业务逻辑。
- 如果需要与数据库直接交互,并且需要在对象上处理业务逻辑,选择
数据传输:
- 如果需要在不同层之间传输数据,选择
DTO
。 - 例如:前端和后端之间的数据交换,或微服务之间的数据传递。
- 如果需要在不同层之间传输数据,选择
性能和安全性考虑:
- 使用DTO可以减少数据传输量,提高传输效率。
- 使用DTO可以隐藏不必要的字段,增强数据安全性。
3.3.4 实践示例
3.3.4.1 Entity示例
@Entity
public class User {
@Id
private Long id;
private String username;
private String password;
// getters and setters
}
3.3.4.2 DTO示例
public class UserDTO {
private Long id;
private String username;
// getters and setters
}
3.3.4.3 使用示例
// Service layer - using entity
public User getUser(Long id) {
return userRepository.findById(id);
}
// Controller layer - using DTO
public UserDTO getUserDTO(Long id) {
User user = userService.getUser(id);
UserDTO userDTO = new UserDTO();
userDTO.setId(user.getId());
userDTO.setUsername(user.getUsername());
return userDTO;
}
3.4 Lombok
Lombok 是一个 Java 库,它可以帮助开发者减少 Java 类中样板代码的编写。Lombok 使用注解的方式来自动生成 getter/setter、构造函数、equals、hashCode、toString 等常用方法。本文档将介绍 Lombok 的常用注解及其使用方法。
3.4.1 引入 Lombok
在使用 Lombok 之前,需要先在项目中引入 Lombok 库。
3.4.1.1 Maven
在 pom.xml
文件中添加以下依赖:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
<scope>provided</scope>
</dependency>
3.4.1.2 Gradle
在 build.gradle
文件中添加以下依赖:
dependencies {
compileOnly 'org.projectlombok:lombok:1.18.24'
annotationProcessor 'org.projectlombok:lombok:1.18.24'
}
3.4.2 常用注解
3.4.2.1 @Getter
和 `@Setter
@Getter
和
@Setter` 注解可以自动生成 getter 和 setter 方法。
import lombok.Getter;
import lombok.Setter;
public class User {
@Getter @Setter
private String name;
@Getter @Setter
private int age;
}
3.4.2.2 @ToString
@ToString
注解可以自动生成 toString
方法。
import lombok.ToString;
@ToString
public class User {
private String name;
private int age;
}
3.4.2.3 `@EqualsAndHashCode
@EqualsAndHashCode
注解可以自动生成
equals和
hashCode` 方法。
import lombok.EqualsAndHashCode;
@EqualsAndHashCode
public class User {
private String name;
private int age;
}
3.4.2.4 @NoArgsConstructor
, @AllArgsConstructor
, @RequiredArgsConstructor
@NoArgsConstructor
注解生成无参构造函数。@AllArgsConstructor
注解生成包含所有字段的构造函数。@RequiredArgsConstructor
注解生成包含final
字段的构造函数(@Autowired
隐式注入)。
import lombok.NoArgsConstructor;
import lombok.AllArgsConstructor;
import lombok.RequiredArgsConstructor;
@NoArgsConstructor
@AllArgsConstructor
@RequiredArgsConstructor
public class User {
private String name;
private int age;
}
3.4.2.5 @Data
@Data
注解是一个综合注解,相当于同时使用 @Getter
、@Setter
、@ToString
、@EqualsAndHashCode
和 @RequiredArgsConstructor
。
import lombok.Data;
@Data
public class User {
private String name;
private int age;
}
3.4.2.6 @Builder
@Builder
注解可以使用构建者模式来创建对象。
import lombok.Builder;
@Builder
public class User {
private String name;
private int age;
}
// 使用示例
User user = User.builder()
.name("John")
.age(30)
.build();
3.4.2.7 @Value
@Value
注解可以将一个类标记为不可变类,相当于同时使用 @Getter
、@AllArgsConstructor
、@EqualsAndHashCode
、@ToString
和 @FieldDefaults(makeFinal = true, level = AccessLevel.PRIVATE)
。
import lombok.Value;
@Value
public class User {
String name;
int age;
}
3.4.3 高级用法
3.4.3.1 @Slf4j
@Slf4j
注解可以自动生成 SLF4J
日志记录器。
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class User {
public void doSomething() {
log.info("Doing something…");
}
}
3.4.3.2 @SneakyThrows
@SneakyThrows
注解可以在方法中自动处理受检异常,而无需显式捕获或声明抛出。
import lombok.SneakyThrows;
public class User {
@SneakyThrows
public void doSomething() {
throw new Exception("Checked Exception");
}
}
3.5 ThreadLocal
3.6 0.1+0.2!=0.3
在Java中,0.1 + 0.2 != 0.3
的原因是浮点数表示的精度问题。浮点数在计算机中是以二进制形式存储的,而某些十进制小数(如0.1和0.2)无法精确地转换为二进制表示。这会导致计算结果出现微小的误差。
以下是一个简要的Java示例,说明为什么 0.1 + 0.2
不等于 0.3
:
public class FloatingPointPrecision {
public static void main(String[] args) {
double a = 0.1;
double b = 0.2;
double sum = a + b;
System.out.println("0.1 + 0.2 = " + sum);
System.out.println("0.1 + 0.2 == 0.3: " + (sum == 0.3));
}
}
3.6.1 解释
- 定义变量:定义
a
为0.1
,b
为0.2
。 - 计算和:计算
a + b
并将结果存储在sum
中。 - 打印结果:打印
0.1 + 0.2
的结果。 - 比较结果:比较
sum
和0.3
是否相等,并打印比较结果。
3.6.2 输出
0.1 + 0.2 = 0.30000000000000004
0.1 + 0.2 == 0.3: false
3.6.3 结论
由于浮点数精度问题,0.1 + 0.2
的结果是 0.30000000000000004
,而不是精确的 0.3
,因此 0.1 + 0.2 != 0.3
。
4 Spring
4.1 Swagger 2
- 在使用
Springboot
2.7.18 +Swagger
2.9.2 时- JPA: 一切正常
- Mybatis-Plus: 启动时报错
Failed to start bean 'documentationPluginsBootstrapper'
- 已知方案
- application. properties 中添加
spring.mvc.pathmatch.matching-strategy=ant_path_matcher
以适配swagger
2 (defaultpath_pattern_parser
)path_pattern_parser
: 这是一个新的路径匹配策略,它支持更复杂的路径模式,如{foo:.*}
,但不支持 Ant 风格的路径模式
- application. properties 中添加
- 实际方案
- 更换
SpringBoot
版本到 2.5.6
- 更换
- 已知方案
4.2 IoC (Inversion of Control,控制反转)
4.2.1 IoC 概述
IoC 是 Spring 框架的核心原则之一,它是一种设计思想,将对象的创建、管理和依赖关系的维护交给 Spring 容器来完成,而不是由开发者直接控制。
4.2.2 IoC 的优势
- 降低耦合度:对象之间的依赖关系由容器管理,减少了对象之间的直接依赖。
- 提高灵活性:对象的创建和装配方式更加灵活,可以通过配置文件或注解进行配置。
- 简化开发:开发者只需关注业务逻辑,无需关心对象的创建和依赖注入的细节。
- 便于测试:容器可以方便地替换对象的实现,便于进行单元测试和集成测试。
4.2.3 IoC 容器
Spring 提供了两种 IoC 容器的实现:
- BeanFactory:基础 IoC 容器,提供基本的依赖注入功能。
- ApplicationContext:BeanFactory 的子接口,提供了更丰富的功能,如国际化、事件传播等。
4.2.4 依赖注入(Dependency Injection)
在软件工程中,依赖注入(Dependency Injection,DI)是一种设计模式,用于实现控制反转(Inversion of Control,IoC)原则。它的核心思想是将一个对象所需的依赖关系从外部注入,而不是在对象内部自行创建。这种方式能够降低组件之间的耦合度,提高代码的可维护性和可测试性。
在 Java 中,依赖注入可以通过构造函数、Setter 方法或接口实现。然而,手动管理依赖关系可能会变得复杂且容易出错。Spring 框架通过 IoC 容器和注解(如 @Autowired
和 @Resource
)简化了依赖注入的实现,使得开发者可以更轻松地管理和配置对象之间的依赖关系。
4.2.4.1 @Autowired
和 @Resource
Spring 提供了 @Autowired
和 @Resource
两种注解来实现依赖注入。
`@Inject`
@Inject
同样可以用于依赖注入。来自Java EE的JSR-330规范,它可以用于构造器、字段和方法,按类型进行自动装配。如果有多个匹配的bean,它会抛出异常。
4.2.4.1.1 @Autowired
- 来源:
@Autowired
是 Spring 自带的注解。 - 装配方式: 默认按照类型(byType)进行自动装配。如果容器中存在多个相同类型的 Bean,可以通过
@Qualifier
注解指定 Bean 的名称(byName)进行装配。 - 特性:
- 支持构造函数、字段、Setter 方法注入。
- 可以配合
@Qualifier
注解实现按名称注入。 required
属性默认为true
,表示注入的 Bean 必须存在,否则抛出异常。可以设置为false
,表示找不到 Bean 时不报错。
代码示例:
@Service
public class UserService {
// 字段注入
@Autowired
private UserRepository userRepository;
// 构造函数注入
@Autowired
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
// Setter 方法注入
@Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
// 按名称注入
@Autowired
@Qualifier("userRepositoryImpl")
private UserRepository userRepository;
}
4.2.4.1.2 @Resource
- 来源:
@Resource
是 JSR-250 规范定义的注解。 - 装配方式: 默认按照名称(byName)进行自动装配。如果找不到名称匹配的 Bean,则按照类型(byType)进行装配。
- 特性:
- 支持字段和 Setter 方法注入。
- 可以通过
name
属性指定 Bean 的名称进行注入。 type
属性指定 Bean 的类型。
代码示例:
@Service("userService")
public class UserService {
// 字段注入
@Resource
private UserRepository userRepository;
// Setter 方法注入
@Resource(name = "userRepositoryImpl")
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
总结:
@Autowired
和@Resource
都可以用于字段和 Setter 方法注入。@Autowired
默认按类型装配,@Resource
默认按名称装配。@Autowired
可以配合@Qualifier
注解实现按名称注入。@Autowired
的required
属性可以控制注入 Bean 是否必须存在。@Resource
可以通过name
和type
属性指定注入的 Bean。
最佳实践:
- 建议优先使用
@Autowired
,因为它更符合 Spring 的理念。 - 如果需要按名称注入,可以使用
@Autowired
配合@Qualifier
。 - 如果需要更灵活的注入方式,可以使用
@Resource
。 - 尽量避免在同一个类中同时使用
@Autowired
和@Resource
,以保持代码风格一致。
4.2.5 Bean 的作用域
Spring 中的 Bean 可以配置不同的作用域,常用的作用域有:
- singleton:单例模式,整个容器中只有一个实例。
- prototype:原型模式,每次请求都会创建一个新的实例。
- request:Web 环境下,每个 HTTP 请求创建一个实例。
- session:Web 环境下,每个 HTTP 会话创建一个实例。
- application:Web 环境下,整个 Web 应用只有一个实例。
4.3 AOP (Aspect Oriented Programming,面向切面编程)
4.3.1 AOP 概述
AOP 是 Spring 框架的另一个核心原则,它是一种编程范式,允许开发者将横切关注点(如日志、事务、安全等)从业务逻辑中分离出来,独立进行模块化处理。
4.3.2 AOP 的优势
- 提高模块化:将横切关注点与业务逻辑分离,提高代码的模块化程度。
- 增强可维护性:横切关注点的修改不会影响业务逻辑,提高代码的可维护性。
- 提高可重用性:横切关注点可以被多个模块复用,提高代码的复用性。
4.3.3 AOP 的核心概念
- 切面(Aspect):横切关注点的模块化封装,包含切点和通知。
- 切点(Pointcut):定义在哪些方法上应用通知。
- 通知(Advice):在切点处执行的操作,包括前置通知、后置通知、环绕通知等。
- 连接点(Joinpoint):程序执行过程中可以应用通知的点。
- 目标对象(Target Object):被通知的对象。
- 代理对象(Proxy Object):AOP 框架创建的对象,用于拦截目标对象的方法调用并应用通知。
4.3.4 Spring AOP 的实现方式
Spring AOP 支持两种实现方式:
- JDK 动态代理:基于接口的动态代理。
- CGLIB 动态代理:基于类的动态代理。
4.4 Bean 的生命周期
Spring 容器中的 Bean 具有一个完整的生命周期,包括以下阶段:
- 实例化:通过构造函数或工厂方法创建 Bean 实例。
- 属性赋值:为 Bean 实例的属性赋值。
- 初始化:调用 Bean 实例的初始化方法(如
@PostConstruct
注解的方法)。 - 使用:Bean 实例可以被应用程序使用。
- 销毁:调用 Bean 实例的销毁方法(如
@PreDestroy
注解的方法)。
4.5 循环依赖
循环依赖是指两个或多个 Bean 之间相互依赖的情况。Spring 容器可以解决构造器注入的循环依赖,但不能解决Setter 注入的循环依赖。
4.5.1 解决构造器注入的循环依赖
Spring 容器通过三级缓存来解决构造器注入的循环依赖。这三级缓存分别是:
- SingletonObjects:缓存已经创建完成的单例 Bean。
- EarlySingletonObjects:缓存提前曝光的单例 Bean,这些 Bean 已经实例化,但还未进行属性填充和初始化。
- SingletonFactories:缓存用于创建 Bean 的工厂。
解决过程如下:
- 当 Spring 容器创建一个 Bean A 时,首先会尝试从
SingletonObjects
中获取。如果获取不到,则进入下一步。 - Spring 容器会尝试从
EarlySingletonObjects
中获取。如果获取不到,则进入下一步。 - Spring 容器会尝试从
SingletonFactories
中获取对应的 Bean 工厂,并通过工厂创建 Bean A 的实例。 - 将 Bean A 的实例放入
EarlySingletonObjects
中,并移除SingletonFactories
中对应的工厂。 - Spring 容器对 Bean A 进行属性填充和初始化。
- 将 Bean A 的实例从
EarlySingletonObjects
中移除,放入SingletonObjects
中。
通过这种方式,Spring 容器可以在 Bean A 创建完成之前,将其提前曝光给其他需要依赖它的 Bean,从而解决了构造器注入的循环依赖问题。
4.5.2 无法解决 Setter 注入的循环依赖
Setter 注入的循环依赖会导致 Bean 实例的属性值不完整,从而引发运行时错误。例如,Bean A 依赖 Bean B,Bean B 又依赖 Bean A,如果使用 Setter 注入,则在 Bean A 初始化时,Bean B 还没有完成初始化,导致 Bean A 中注入的 Bean B 属性值为 null
,从而引发 NullPointerException
。
解决 Setter 注入循环依赖的方法:
- 使用
@Lazy
注解: 在依赖注入的字段或方法上添加@Lazy
注解,可以延迟 Bean 的初始化,直到第一次使用时才进行初始化,从而避免循环依赖问题。 - 调整依赖关系: 重新设计 Bean 之间的依赖关系,避免出现循环依赖。
- 使用接口: 将 Bean 之间的依赖关系改为接口依赖,可以降低耦合度,避免循环依赖。
4.6 Spring 中的设计模式
工厂模式 (Factory Pattern):Spring 使用工厂模式来创建 Bean 对象。
BeanFactory
和ApplicationContext
是 Spring 中的两个核心工厂接口,它们负责 Bean 的实例化、配置和管理。单例模式 (Singleton Pattern):Spring 中的 Bean 默认是单例的。单例模式确保一个类只有一个实例,并提供全局访问点。这在 Spring 中可以节省资源,提高性能。
代理模式 (Proxy Pattern):Spring AOP 的实现依赖于代理模式。Spring 使用 JDK 动态代理或 CGLIB 动态代理来创建代理对象,从而在不修改原始类的情况下,实现对方法调用的拦截和增强。
模板方法模式 (Template Method Pattern):Spring 中的
JdbcTemplate
、RestTemplate
等模板类使用了模板方法模式。模板方法模式定义了一个算法的骨架,将一些步骤延迟到子类中实现。这使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。观察者模式 (Observer Pattern):Spring 的事件驱动模型基于观察者模式。当一个事件发生时,所有注册的监听器都会收到通知并作出相应的响应。
适配器模式 (Adapter Pattern):Spring AOP 中的
AdvisorAdapter
使用了适配器模式。适配器模式将一个类的接口转换成客户端所期望的另一个接口,从而使原本不兼容的类能够一起工作。策略模式 (Strategy Pattern):Spring 中的
PlatformTransactionManager
使用了策略模式。策略模式定义了一组算法,将每个算法封装起来,并使它们可以互换。这使得算法的变化独立于使用算法的客户端。装饰器模式 (Decorator Pattern):Spring 中的
BeanWrapper
使用了装饰器模式。装饰器模式动态地给一个对象添加一些额外的职责,而无需修改其结构。
5 JUnit
JUnit 是一个开源的 Java 单元测试框架,用于编写和运行可重复的自动化测试。它是测试驱动开发 (TDD) 的关键组成部分,也是 xUnit 架构家族的一员。
核心特点:
- 注解: 提供了一组注解来标识测试方法 (如
@Test
)、测试类 (如@Suite
)、测试生命周期方法 (如@BeforeEach
) 等。 - 断言: 提供了丰富的断言方法 (如
assertTrue
、assertEquals
、assertNull
) 来验证测试结果是否符合预期。 - 测试运行器: 提供了测试运行器来执行测试并生成测试报告。
5.1 断言方法
Assert.isTrue
和 assertTrue
都是用于单元测试的断言方法,用于验证某个条件是否为真。
5.1.1 Assert.isTrue
- 属于 JUnit 框架中的断言方法。
- 接收一个布尔值参数,如果参数为
true
,则测试通过;如果参数为false
,则测试失败并抛出AssertionError
异常。 - 可以添加一个可选的字符串参数作为失败时的错误消息。
示例:
Assert.isTrue(1 + 1 == 2, "1 + 1 should equal 2"); // 测试通过
Assert.isTrue(1 + 1 == 3, "1 + 1 should not equal 3"); // 测试失败,抛出 AssertionError
5.1.2 assertTrue
- 属于 JUnit 框架和 TestNG 框架中的断言方法。
- 在 JUnit 中,
assertTrue
是Assert.isTrue
的别名,功能完全相同。 - 在 TestNG 中,
assertTrue
具有类似的功能,但可能存在细微的差异。
示例(JUnit):
assertTrue(1 + 1 == 2); // 测试通过
assertTrue(1 + 1 == 3); // 测试失败,抛出 AssertionError
总结:
- 在 JUnit 中,
Assert.isTrue
和assertTrue
可以互换使用。 - 在 TestNG 中,
assertTrue
具有类似的功能,但可能存在细微的差异。 - 这两种方法都用于验证某个条件是否为真,在单元测试中非常有用。
6 English Grammer
6.1 E.G. & I.E.
- E.G. 和 I.E. 是两个常用的拉丁文缩写,常用于英语写作中。
- E.G. 是 exempli gratia 的缩写,意思是“例如”或“举例来说”。用于列举说明前面的陈述。
- I.E. 是 id est 的缩写,意思是“也就是说”或“换句话说”。用于解释或澄清前面的陈述。
- 大小写:
- 正式写作中,E.G. 和 I.E. 应全部大写。
- 非正式写作中,小写也常见。
- 用法:
- E.G. 和 I.E. 之前应加逗号。
- E.G. 和 I.E. 应斜体表示。
- E.G. 和 I.E. 不应与句子中的其他单词连写。
- 示例:
- 我最喜欢的动物是狗,E.G. 金毛犬、拉布拉多犬和哈士奇。
- 这个城市有很多著名的景点,I.E. 它是一个旅游胜地。
- 注意:
- E.G. 和 I.E. 的意思相似,但用法有所区别。
- 在非正式写作中,可以使用“for example”、“for instance”、“that is”、“in other words”等词语代替 E.G. 和 I.E.。
8 面向对象 (Object-Oriented)
面向对象编程(OOP)是一种编程范式,它通过"对象"这一抽象概念来组织代码,旨在提高代码的可维护性、可重用性和可扩展性。在面向对象编程中,有三个重要概念:面向对象分析(OOA)、面向对象设计(OOD)和面向对象编程(OOP)。以下是它们的区别和联系:
面向对象分析(OOA):
- 定义:OOA是指在软件开发的分析阶段,通过面向对象的方法对系统需求进行建模和分析。这一阶段的主要目标是理解并捕捉系统的功能需求和业务需求。
- 过程:识别系统中的对象、类及其关系,定义对象的属性和行为,创建初步的模型和用例图。
- 输出:通常包括需求文档、用例图、类图等模型,帮助开发团队和利益相关者理解系统的需求和结构。
面向对象设计(OOD):
- 定义:OOD是指在软件开发的设计阶段,通过面向对象的方法将分析阶段得到的模型转换为具体的设计模型,详细描述系统的结构和行为。
- 过程:设计类的具体实现,定义类的接口和交互,确定系统的模块划分和层次结构,考虑设计模式和架构风格。
- 输出:通常包括详细的类图、顺序图、协作图等,提供了系统的设计蓝图,指导后续的实现阶段。
面向对象编程(OOP):
- 定义:OOP是指在软件开发的实现阶段,通过面向对象的编程语言和技术,将设计阶段的模型和蓝图转换为可执行的代码。
- 过程:编写类和对象的代码,实现属性和方法,处理对象之间的交互和继承关系,使用适当的设计模式和编程实践。
- 输出:最终的可执行代码,以及相关的测试和文档,形成完整的软件产品。
总结来说,OOA侧重于需求分析和建模,OOD侧重于系统的结构和设计,OOP则侧重于具体的实现和编码。这三者相互衔接,共同构成了完整的面向对象软件开发过程。
9 ASCII
常用的ASCII字符包括控制字符和可打印字符。以下是一些常用的ASCII字符及其对应的十进制和十六进制值:
9.1 控制字符
- NUL (Null): 十进制 0, 十六进制 0x00
- BEL (Bell): 十进制 7, 十六进制 0x07
- BS (Backspace): 十进制 8, 十六进制 0x08
- TAB (Horizontal Tab): 十进制 9, 十六进制 0x09
- LF (Line Feed, 换行): 十进制 10, 十六进制 0x0A
- CR (Carriage Return, 回车): 十进制 13, 十六进制 0x0D
- ESC (Escape): 十进制 27, 十六进制 0x1B
9.2 可打印字符
- 空格 (Space): 十进制 32, 十六进制 0x20
- ! (感叹号): 十进制 33, 十六进制 0x21
- " (双引号): 十进制 34, 十六进制 0x22
- # (井号): 十进制 35, 十六进制 0x23
- $ (美元符号): 十进制 36, 十六进制 0x24
- % (百分号): 十进制 37, 十六进制 0x25
- & (和号): 十进制 38, 十六进制 0x26
- ' (单引号): 十进制 39, 十六进制 0x27
- ( (左括号): 十进制 40, 十六进制 0x28
- ) (右括号): 十进制 41, 十六进制 0x29
- * (星号): 十进制 42, 十六进制 0x2A
- + (加号): 十进制 43, 十六进制 0x2B
- , (逗号): 十进制 44, 十六进制 0x2C
- - (减号): 十进制 45, 十六进制 0x2D
- . (句号): 十进制 46, 十六进制 0x2E
- / (斜杠): 十进制 47, 十六进制 0x2F
9.3 数字
- 0: 十进制 48, 十六进制 0x30
- 1: 十进制 49, 十六进制 0x31
- 2: 十进制 50, 十六进制 0x32
- 3: 十进制 51, 十六进制 0x33
- 4: 十进制 52, 十六进制 0x34
- 5: 十进制 53, 十六进制 0x35
- 6: 十进制 54, 十六进制 0x36
- 7: 十进制 55, 十六进制 0x37
- 8: 十进制 56, 十六进制 0x38
- 9: 十进制 57, 十六进制 0x39
9.4 大写字母
- A: 十进制 65, 十六进制 0x41
- B: 十进制 66, 十六进制 0x42
- C: 十进制 67, 十六进制 0x43
- D: 十进制 68, 十六进制 0x44
- E: 十进制 69, 十六进制 0x45
- F: 十进制 70, 十六进制 0x46
- G: 十进制 71, 十六进制 0x47
- H: 十进制 72, 十六进制 0x48
- I: 十进制 73, 十六进制 0x49
- J: 十进制 74, 十六进制 0x4A
- K: 十进制 75, 十六进制 0x4B
- L: 十进制 76, 十六进制 0x4C
- M: 十进制 77, 十六进制 0x4D
- N: 十进制 78, 十六进制 0x4E
- O: 十进制 79, 十六进制 0x4F
- P: 十进制 80, 十六进制 0x50
- Q: 十进制 81, 十六进制 0x51
- R: 十进制 82, 十六进制 0x52
- S: 十进制 83, 十六进制 0x53
- T: 十进制 84, 十六进制 0x54
- U: 十进制 85, 十六进制 0x55
- V: 十进制 86, 十六进制 0x56
- W: 十进制 87, 十六进制 0x57
- X: 十进制 88, 十六进制 0x58
- Y: 十进制 89, 十六进制 0x59
- Z: 十进制 90, 十六进制 0x5A
9.5 小写字母
- a: 十进制 97, 十六进制 0x61
- b: 十进制 98, 十六进制 0x62
- c: 十进制 99, 十六进制 0x63
- d: 十进制 100, 十六进制 0x64
- e: 十进制 101, 十六进制 0x65
- f: 十进制 102, 十六进制 0x66
- g: 十进制 103, 十六进制 0x67
- h: 十进制 104, 十六进制 0x68
- i: 十进制 105, 十六进制 0x69
- j: 十进制 106, 十六进制 0x6A
- k: 十进制 107, 十六进制 0x6B
- l: 十进制 108, 十六进制 0x6C
- m: 十进制 109, 十六进制 0x6D
- n: 十进制 110, 十六进制 0x6E
- o: 十进制 111, 十六进制 0x6F
- p: 十进制 112, 十六进制 0x70
- q: 十进制 113, 十六进制 0x71
- r: 十进制 114, 十六进制 0x72
- s: 十进制 115, 十六进制 0x73
- t: 十进制 116, 十六进制 0x74
- u: 十进制 117, 十六进制 0x75
- v: 十进制 118, 十六进制 0x76
- w: 十进制 119, 十六进制 0x77
- x: 十进制 120, 十六进制 0x78
- y: 十进制 121, 十六进制 0x79
- z: 十进制 122, 十六进制 0x7A
这些字符是ASCII编码中最常用的字符,广泛应用于文本处理和数据传输。