- 在VisualStudio中部署GDAL库的C++版本(包括SQLite、PROJ等依赖)
- Android开机流程介绍
- STM32CubeMX教程31USB_DEVICE-HID外设_模拟键盘或鼠标
- 深入浅出Java多线程(五):线程间通信
在开发现代应用程序时,数据验证是确保用户输入的正确性和应用程序数据完整性的关键方面。Spring Boot 提供了强大的数据验证机制,使开发者能够轻松地执行验证操作。本文将深入介绍 Spring Boot 中的 Validation,以及如何在应用程序中正确使用它.
1.用户输入的正确性:数据验证是确保用户输入的正确性的一种重要手段。通过验证用户输入的数据,可以防止无效或错误的数据进入应用程序,提高数据的质量。例如:系统中的备注字段数据库中对应的长度是256,如果用户输入的备注超过这个长度值,那么就会导致mysql报Data too long.
安全性: 数据验证也是保障应用程序安全性的关键因素。通过验证用户输入,可以防范一些潜在的安全威胁,例如 SQL 注入、跨站脚本攻击等.
业务规则的执行: 在应用程序中,通常存在一些业务规则,例如某个字段不能为空、日期范围必须在某个特定范围内等。通过数据验证,可以确保这些业务规则在应用程序中得到正确执行.
日常开发中,有些写项目可能没有采用Spring Validator,采用的是在代码中手动校验数据。但是手动校验数据会带来代码冗余、错误处理的一致性以及业务规则的维护的一些痛点.
public ResponseEntity<String> registerUser(UserRegistrationRequest request) {
if (request == null) {
return ResponseEntity.badRequest().body("Request cannot be null");
}
if (StringUtils.isBlank(request.getUsername())) {
return ResponseEntity.badRequest().body("Username cannot be blank");
}
if (StringUtils.length(request.getPassword()) < 6) {
return ResponseEntity.badRequest().body("Password must be at least 6 characters long");
}
// 处理用户注册逻辑
return ResponseEntity.ok("User registered successfully");
}
缺乏统一的错误处理机制 。
业务规则维护的困难 随着业务规则的增加,手动编写的校验逻辑可能变得庞大且难以维护。修改和扩展校验规则可能需要修改多个地方,增加了维护成本.
缺乏验证组的支持 手动校验通常不支持验证组的概念,难以根据不同场景执行不同的验证规则.
不易于集成前端验证 手动校验不易与前端验证框架集成,导致前后端验证逻辑可能不一致.
通过引入 Spring Validator,我们能够有效解决这些痛点,提高代码的可读性、可维护性,并确保校验逻辑的一致性.
因Springboot的spring-boot-starter-web默认内置了Hibernate-Validator(Spring boot 2.3以前版本),虽然Hibernate-Validator也能做到数据校验,但是考虑到spring-boot-starter-validation 是一个抽象层,使得验证框架的具体实现变得可插拔。这意味着,除了 Hibernate Validator,开发者可以选择其他符合 Bean Validation 规范的实现。所以我们可以手动引入spring-boot-starter-validation实现数据验证.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
spring-boot-starter-validation 不仅支持 JSR-303(Bean Validation 1.0)规范,还提供了对 JSR-380(Bean Validation 2.0)规范的全面支持。这使得开发者可以利用 Bean Validation 2.0 的新特性,更灵活地定义验证规则,包括对集合、嵌套对象的验证等.
通过在实体类的字段上使用标准的 Bean Validation 注解(如 @NotBlank、@Size、@Email 等),我们能够直观地定义数据的验证规则。这些验证规则会在应用程序的不同层次(如控制器层)生效,确保输入数据的正确性.
Spring Boot Validation 提供了一系列注解,用于在实体类中定义验证规则。以下是一些常用的校验相关的注解及其功能以及用法: 1.@NotNull: 校验元素值不能为 null。如果元素为null,则验证失败。通常用于字段级别的验证.
@NotNull(message = "Name cannot be null")
private String name;
2.@NotBlank: 校验字符串元素值不能为 null 或空字符串。必须包含至少一个非空格字符(即执行trim()之后不为'')。如果元素为null或者‘‘,则验证失败。通常用于String类型的字段校验.
@NotBlank(message = "Username cannot be blank")
private String username;
3.NotEmpty: 校验集合元素或数组元素或者字符串是否非空。通常作用于集合字段或数组字段,此时需要集合或者数字的元素个数大于0。也可以作用于字符串,此时校验字符串不能为null或空串(可以是一个空格)。注意与@NotBlank的使用区别.
@NotEmpty(message = "List cannot be empty")
private List<String> items;
4.@Length: 校验字符串元素的长度。作用于字符串。注:Hibernate-Validator中注解,等同于spring-boot-starter-validation中的@Size.
@Length(min = 5, max = 20, message = "Length must be between 5 and 20 characters")
private String username;
5.@Size: 校验集合元素个数或字符串的长度在指定范围内。在集合或字符串字段上添加 @Size 注解.
@Size(min = 1, max = 10, message = "Number of items must be between 1 and 10")
private List<String> items;
@Size(min = 5, max = 20, message = "Length must be between 5 and 20 characters")
private String username;
6.@Min: 校验数字元素的最小值.
@Min(value = 18, message = "Age must be at least 18")
private int age;
7.@Max: 校验数字元素的最大值.
@Max(value = 100, message = "Age must not exceed 100")
private int age;
9.@DecimalMax: 作用于BigDecimal类型字段, 校验字段的最大值,支持比较的值为字符串表示的十进制数。通常搭配它的inclusive()使用,区别边界问题。value 属性表示最大值,inclusive 属性表示是否包含最大值.
@DecimalMax(value = "100.00", inclusive = true, message = "Value must be less than or equal to 100.00")
private BigDecimal amount;
10.@DecimalMin: 作用于BigDecimal类型字段, 校验字段的最小值,支持比较的值为字符串表示的十进制数。通常搭配它的inclusive()使用,区别边界问题。value 属性表示最小值,inclusive 属性表示是否包含最小值.
@DecimalMin(value = "0.00", inclusive = false, message = "Value must be greater than 0.00")
private BigDecimal amount;
11.@Email: 校验字符串元素是否为有效的电子邮件地址。可以通过regexp自定义邮箱匹配正则.
@Email(message = "Invalid email address")
private String email;
12.@Pattern: 根据正则表达式校验字符串元素的格式.
@Pattern(regexp = "[a-zA-Z0-9]+", message = "Only alphanumeric characters are allowed")
private String username;
13.@Digits: 校验数字元素的整数部分和小数部分的位数。作用于BigDecimal,BigInteger,字符串,以及byte, short,int, long以及它们的包装类型.
@Digits(integer = 5, fraction = 2, message = "Number must have up to 5 integer digits and 2 fraction digits")
private BigDecimal amount;
14.@Past: 校验日期或时间元素是否在当前时间之前。即是否是过去时间。作用于Date相关类型的字段.
@Past(message = "Date must be in the past")
private LocalDate startDate;
15.@Future: 校验日期或时间元素是否在当前时间之后。即是否是未来时间。作用于Date相关类型的字段.
@Future(message = "Date must be in the future")
private LocalDate endDate;
注:以上只罗列部分注解以及它们的功能,其余他们的字段属性并没有详细说明,其他注解以及详细的说明需要去看源码.
/**
* @version 1.0
* @description: <p></p >
* @author: 码农Academy
* @create: 2024/1/8 16:46
*/
@Data
public class UserCreateRequestVO {
@NotBlank(message = "请输入用户名")
@Size(max = 128, message = "用户名长度最大为128个字符")
private String userName;
@Email(message = "请填写正确的邮箱地址")
private String email;
@Min(value = 18, message = "用户年龄必须大于18岁")
@Max(value = 60, message = "用户年龄必须小于60岁")
private Integer age;
@NotEmpty(message = "请输入你的兴趣爱好")
@Size(max = 5, message = "兴趣爱好最多可以输入5个")
private List<String> hobbies;
@DecimalMin(value = "50", inclusive = false, message = "体重必须大于50KG")
private BigDecimal weight;
@Validated
@NotNull(message = "请输入地址信息")
private UserAddressRequestVO address;
}
@RestController
@RequestMapping("user")
@Validated
@Slf4j
public class UserController {
/**
* 创建用户
* @param requestVO
* @return
*/
@PostMapping("create")
public ResultResponse<Void> createUser(@Validated @RequestBody UserCreateRequestVO requestVO){
return ResultResponse.success(null);
}
/**
* 校验用户邮箱是否合法
* @param email
* @return
*/
@GetMapping("email")
public ResultResponse<Void> validUserEmail(@Email(message = "邮箱格式不正确") String email){
return ResultResponse.success(null);
}
}
我们需要捕获一下MethodArgumentNotValidException。该部分内容请参考文章:SpringBoot统一异常处理 。
注:单参数校验时我们需要,在方法的类上加上@Validated注解,否则校验不生效.
在UserCreateRequestVO中增加一个address的校验,即需要对嵌套对象进行校验 。
/**
* @version 1.0
* @description: <p></p >
* @author: 码农Academy
* @create: 2024/1/8 19:45
*/
@Data
public class UserAddressRequestVO {
@Size(max = 16, message = "地址信息中国家长度不能超过16个字符")
@NotBlank(message = "地址信息国家不能为空")
private String country;
private String city;
@Size(max = 128, message = "详细地址长度不能超过128个字符")
private String address1;
}
在UserAddressRequestVO中增加address属性 。
@Data
public class UserCreateRequestVO {
@NotNull(message = "请输入地址信息")
private UserAddressRequestVO address;
}
解决办法,要在嵌套对象上使用 @Valid 注解 。
@Data
public class UserCreateRequestVO {
@NotNull(message = "请输入地址信息")
@Valid
private UserAddressRequestVO address;
}
试了一些其他的方式,好像都不行,有知道其他方式的,欢迎评论区留言探讨 。
在项目开发中,我们也可以自定义注解去完成我们的字段校验,比如某些枚举值的传递,需要校验枚举值是否合法。在创建自定义注解之前,我们需要了解一下ConstraintValidator以及实现自定义验证注解的原理 。
ConstraintValidator 是 Java Bean Validation (JSR 380) 规范中用于自定义验证逻辑的接口。它允许你定义针对特定自定义注解的验证规则。它是一个泛型接口,需要提供两个类型参数:
A
:是你的自定义注解的类型。T
:是被验证的元素类型,通常是字段类型。public interface ConstraintValidator<A extends Annotation, T> {
void initialize(A constraintAnnotation);
boolean isValid(T value, ConstraintValidatorContext context);
}
其中:
initialize
方法:在验证器初始化时被调用,可以用于获取约束注解中的配置信息。isValid
方法:执行实际的验证逻辑,返回 true
表示验证通过,false
表示验证失败。以下为枚举校验注解的校验规则实现 。
/**
* @version 1.0
* <p> </p>
* @author: 码农Academy
* @create: 2024/01/09 3:11 下午
*/
public class EnumValidator implements ConstraintValidator<EnumValid, Object> {
private Class clazz;
private String validField;
@Override
public void initialize(EnumValid constraintAnnotation) {
clazz = constraintAnnotation.enumClass();
validField = constraintAnnotation.field();
}
@SneakyThrows
@Override
public boolean isValid(Object object, ConstraintValidatorContext constraintValidatorContext) {
if (object == null || "".equals(object)){
return true;
}
if (!clazz.isEnum()){
return false;
}
Class<Enum> enumClass = (Class<Enum>)clazz;
//获取所有枚举实例
Enum[] enumConstants = enumClass.getEnumConstants();
// 需要比对的字段
Field field = enumClass.getDeclaredField(validField);
field.setAccessible(true);
for(Enum constant : enumConstants){
// 取值final修饰
Object validValue = field.get(constant);
if (validValue == null){
Method method = enumClass.getMethod(validField);
validValue = method.invoke(constant);
}
if(validValue instanceof Number) {
validValue = ((Number)validValue).intValue();
object = ((Number) object).intValue();
}
if (Objects.equals(validValue,object)){
return true;
}
}
return false;
}
}
在 Java Bean Validation 中,约束注解(Constraint Annotation)是通过元注解 @Constraint 来定义的。这个注解包含了以下关键元素:
validatedBy
: 指定用于执行验证的 ConstraintValidator
实现类。以校验枚举值的合法行为例,我们创建一个EnumValid约束注解 。
@Constraint(validatedBy = {EnumValidator.class})
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
public @interface EnumValid {
/**
* 不合法时 抛出异常信息
*/
String message() default "值不合法";
/**
* 校验的枚举类
* @return
*/
Class enumClass() default Enum.class;
/**
* 对应枚举类中需要比对的字段
* @return
*/
String field() default "code";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
在大多数情况下,不需要手动注册 ConstraintValidator。当你使用 @Constraint(validatedBy = EnumValidator.class) 注解时,Java Bean Validation 的实现框架会自动发现并注册相应的验证器。但在一些特殊情况下,你可能需要将验证器注册为 Spring 组件或手动配置。比如 。
需要使用 Spring 管理的组件: 如果你的验证器需要依赖于 Spring 管理的组件(例如,使用 @Autowired 注解注入其他 bean),那么你可能需要将验证器注册为 Spring bean。这确保了验证器能够正确地使用 Spring 的依赖注入机制.
需要通过属性文件进行配置: 如果你的验证器需要配置属性,而这些属性需要从 Spring 的 application.properties 或 application.yml 文件中获取,那么将验证器注册为 Spring bean 可以更容易地实现这一点.
需要在验证器中使用 Spring AOP: 如果你希望在验证逻辑中使用 Spring AOP 切面,以便添加额外的逻辑或跟踪行为,那么将验证器注册为 Spring bean 可以让你更容易集成这些方面。 这种方式可以运用到一些业务校验中,比如账户注册时用户名称不能重复。定义一个校验用户唯一的注解@UniqueUser.
@Constraint(validatedBy = {UniqueUserValidator.class})
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
public @interface UniqueUser {
/**
* 不合法时 抛出异常信息
*/
String message() default "值不合法";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
然后定义一个业务的Validator 。
@Slf4j
@Component
public class UniqueUserValidator implements ConstraintValidator<UniqueUser, UserCreateRequestVO> {
private UserRepository userRepository;
@Override
public boolean isValid(UserCreateRequestVO value, ConstraintValidatorContext context) {
final String userName = value.getUserName();
final UserDO userDO = userRepository.selectUserByName(userName);
final String userId = value.getUserId();
if (StringUtils.isBlank(userId)){
return userDO == null;
}
return userDO == null || Objects.equals(userDO.getUserId(), userId);
}
@Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
在创建用户的接口中使用@UniqueUser.
@RestController
@RequestMapping("user")
@Validated
@Slf4j
public class UserController {
/**
* 创建用户
* @param requestVO
* @return
*/
@PostMapping("create")
public ResultResponse<Void> createUser(@Validated @UniqueUser(message = "用户名称已存在") @RequestBody UserCreateRequestVO requestVO){
return ResultResponse.success(null);
}
}
模拟当用户名存在时,校验不通过 。
此时会抛出javax.validation.ConstraintViolationException。异常统一处理请参考:SpringBoot统一异常处理 。
我们创建一个性别的枚举类:
/**
* @version 1.0
* @description: <p></p >
* @author: 码农Academy
* @create: 2024/1/9 16:07
*/
@AllArgsConstructor
public enum SexEnum {
MAN(1, "男"),
WOMAN(2,"女");
public final Integer code;
public final String desc;
}
然后我们在入参中增加sex字段,并使用@EmunValid注解 。
@Data
public class UserCreateRequestVO {
@NotBlank(message = "请输入用户名")
@Size(max = 128, message = "用户名长度最大为128个字符")
private String userName;
@Email(message = "请填写正确的邮箱地址")
private String email;
@Min(value = 18, message = "用户年龄必须大于18岁")
@Max(value = 60, message = "用户年龄必须小于60岁")
private Integer age;
@NotNull(message = "请输入性别")
@EnumValid(enumClass = SexEnum.class, message = "输入性别不合法")
private Integer sex;
@NotEmpty(message = "请输入你的兴趣爱好")
@Size(max = 5, message = "兴趣爱好最多可以输入5个")
private List<String> hobbies;
@DecimalMin(value = "50", inclusive = false, message = "体重必须大于50KG")
private BigDecimal weight;
}
测试结果如下:
在一个应用中,同一个实体类可能会被用于不同的场景,比如用户创建、用户更新、用户删除等。每个场景对于字段的要求可能不同,有些字段在某个场景下需要验证,而在另一个场景下不需要。不同的业务操作可能对同一实体的验证有不同的需求。例如,在用户创建时可能强调用户名和密码的合法性,而在用户更新时可能更关心其他信息的完整性.
开发中我们针对这种情况,在不知道分组校验的知识时,通常采取的都是对应不同的场景或者业务创建不同的入参实体,比如创建用户UserCreateRequestVO,更新用户UserUpdateRequestVO,删除用户UserDeleteRuquestVO,在不同的实体中根据业务场景设置不同的校验规则。这样做虽然也可以,但是会造成类的膨胀,业务的重复实现.
而实际上用分组校验可以让你根据场景以及业务的差异性,有选择地执行特定组的验证规则.
我们定义两个分组接口CreateUserGroup(用户创建组),UpdateUserGroup(用户更新组),分别继承javax.validation.groups.Default,标识不同的业务场景.
public interface CreateUserGroup extends Default {
}
public interface UpdateUserGroup extends Default {
}
在 Bean Validation 中,分组校验是通过在验证注解上指定 groups 属性来实现的。这个属性允许你为验证规则分配一个或多个验证组。我们设定用户创建时不传递用户ID,其余的参数必传,用户更新接口必须传递用户ID,可以不传递用户名,其他参数必须传递.
@Data
public class UserCreateRequestVO {
@NotBlank(message = "请选择用户", groups = UpdateUserGroup.class)
private String userId;
@NotBlank(message = "请输入用户名", groups = CreateUserGroup.class)
@Size(max = 128, message = "用户名长度最大为128个字符")
private String userName;
@Email(message = "请填写正确的邮箱地址")
private String email;
@Min(value = 18, message = "用户年龄必须大于18岁")
@Max(value = 60, message = "用户年龄必须小于60岁")
private Integer age;
@NotNull(message = "请输入性别")
@EnumValid(enumClass = SexEnum.class, message = "输入性别不合法")
private Integer sex;
@NotEmpty(message = "请输入你的兴趣爱好")
@Size(max = 5, message = "兴趣爱好最多可以输入5个")
private List<String> hobbies;
@DecimalMin(value = "50", inclusive = false, message = "体重必须大于50KG")
private BigDecimal weight;
}
指定了分组的校验规则,分别在对应的分组校验中生效,没有指定分组使用默认分组Default,即对所有的校验都生效.
使用 @Validated 注解,并指定要执行的验证组.
@RestController
@RequestMapping("user")
@Validated
@Slf4j
public class UserController {
/**
* 创建用户
* @param requestVO
* @return
*/
@PostMapping("create")
public ResultResponse<Void> createUser(@Validated(value = CreateUserGroup.class) @RequestBody UserCreateRequestVO requestVO){
return ResultResponse.success(null);
}
/**
* 更新用户
* @param requestVO
* @return
*/
@PostMapping("update")
public ResultResponse<Void> updateUser(@Validated(value = UpdateUserGroup.class) @RequestBody UserCreateRequestVO requestVO){
return ResultResponse.success(null);
}
}
我们指定create接口指定CreateUserGroup分组,update接口指定UpdateUserGroup 。
测试接口如下:
创建用户create接口 因为userId可以不传递,接口可以校验通过 。
更新用户update接口 因为必须传递userId, 我们不传时校验不通过,提示选择用户 。
传递userId,不传递userName时,校验通过 。
由上述测试结果中,可以看出接口抛出的一场结果并不是很友好,我们需要统一的处理一下异常以及返回结果,给予用户友好提示。具体实现,在这里不再赘述,可以移步:SpringBoot统一异常处理 。
Spring Boot Validation通过简化验证流程、集成Bean Validation规范、支持分组验证以及提供友好的错误处理,为Java应用开发者提供了强大而灵活的数据验证机制。最佳实践包括在控制器层使用@Validated注解、合理利用各种验证注解、使用自定义验证注解解决特定业务需求,确保代码清晰简洁、符合规范,并提高系统的可维护性和用户体验.
本文已收录于我的个人博客:码农Academy,专注分享Java技术干货,包括Java基础、Spring Boot、Spring Cloud、Mysql、Redis、Elasticsearch、中间件、架构设计、面试题、程序员攻略等 。
最后此篇关于新来的一个同事,把SpringBoot参数校验玩的那叫一个优雅的文章就讲到这里了,如果你想了解更多关于新来的一个同事,把SpringBoot参数校验玩的那叫一个优雅的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
简而言之:我想从可变参数模板参数中提取各种选项,但不仅通过标签而且通过那些参数的索引,这些参数是未知的 标签。我喜欢 boost 中的方法(例如 heap 或 lockfree 策略),但想让它与 S
我可以对单元格中的 excel IF 语句提供一些帮助吗? 它在做什么? 对“BaselineAmount”进行了哪些评估? =IF(BaselineAmount, (Variance/Baselin
我正在使用以下方法: public async Task Save(Foo foo,out int param) { ....... MySqlParameter prmparamID
我正在使用 CodeGear RAD Studio IDE。 为了使用命令行参数测试我的应用程序,我多次使用了“运行 -> 参数”菜单中的“参数”字段。 但是每次我给它提供一个新值时,它都无法从“下拉
我已经为信用卡类编写了一些代码,粘贴在下面。我有一个接受上述变量的构造函数,并且正在研究一些方法将这些变量格式化为字符串,以便最终输出将类似于 号码:1234 5678 9012 3456 截止日期:
MySql IN 参数 - 在存储过程中使用时,VarChar IN 参数 val 是否需要单引号? 我已经像平常一样创建了经典 ASP 代码,但我没有更新该列。 我需要引用 VarChar 参数吗?
给出了下面的开始,但似乎不知道如何完成它。本质上,如果我调用 myTest([one, Two, Three], 2); 它应该返回元素 third。必须使用for循环来找到我的解决方案。 funct
将 1113355579999 作为参数传递时,该值在函数内部变为 959050335。 调用(main.c): printf("%d\n", FindCommonDigit(111335557999
这个问题在这里已经有了答案: Is Java "pass-by-reference" or "pass-by-value"? (92 个回答) 关闭9年前。 public class StackOve
我真的很困惑,当像 1 == scanf("%lg", &entry) 交换为 scanf("%lg", &entry) == 1 没有区别。我的实验书上说的是前者,而我觉得后者是可以理解的。 1 =
我正在尝试使用调用 SetupDiGetDeviceRegistryProperty 的函数使用德尔福 7。该调用来自示例函数 SetupEnumAvailableComPorts .它看起来像这样:
我需要在现有项目上实现一些事件的显示。我无法更改数据库结构。 在我的 Controller 中,我(从 ajax 请求)传递了一个时间戳,并且我需要显示之前的 8 个事件。因此,如果时间戳是(转换后)
rails 新手。按照多态关联的教程,我遇到了这个以在create 和destroy 中设置@client。 @client = Client.find(params[:client_id] || p
通过将 VM 参数设置为 -Xmx1024m,我能够通过 Eclipse 运行 Java 程序-Xms256M。现在我想通过 Windows 中的 .bat 文件运行相同的 Java 程序 (jar)
我有一个 Delphi DLL,它在被 Delphi 应用程序调用时工作并导出声明为的方法: Procedure ProduceOutput(request,inputs:widestring; va
浏览完文档和示例后,我还没有弄清楚 schema.yaml 文件中的参数到底用在哪里。 在此处使用 AWS 代码示例:https://github.com/aws-samples/aws-proton
程序参数: procedure get_user_profile ( i_attuid in ras_user.attuid%type, i_data_group in data_g
我有一个字符串作为参数传递给我的存储过程。 dim AgentString as String = " 'test1', 'test2', 'test3' " 我想在 IN 中使用该参数声明。 AND
这个问题已经有答案了: When should I use "this" in a class? (17 个回答) 已关闭 6 年前。 我运行了一些java代码,我看到了一些我不太明白的东西。为什么下
我输入 scroll(0,10,200,10);但是当它运行时,它会传递字符串“xxpos”或“yypos”,我确实在没有撇号的情况下尝试过,但它就是行不通。 scroll = function(xp
我是一名优秀的程序员,十分优秀!