返回顶部
首页 > 资讯 > 精选 >如何写Java代码
  • 654
分享到

如何写Java代码

2023-06-16 17:06:32 654人浏览 薄情痞子
摘要

这篇文章主要讲解了“如何写Java代码”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何写Java代码”吧!技术点本文不是一个吹嘘的文章,不会讲很多高深的架构,相反,会讲解很多基础的问题和写

这篇文章主要讲解了“如何写Java代码”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何写Java代码”吧!

技术点

本文不是一个吹嘘的文章,不会讲很多高深的架构,相反,会讲解很多基础的问题和写法问题,如果读者自认为基础问题和写法问题都是不是问题,那请忽略这篇文章,节省出时间去做一些有意义的事情。

开发工具

不知道有多少”老”程序员还在使用eclipse,这些程序员们要不就是因循守旧,要不就是根本就不知道其他好的开发工具的存在,eclipse吃内存卡顿的现象以及各种偶然莫名异常的出现,都告知我们是时候寻找新的开发工具了。

更换IDE

根本就不想多解释要换什么样的IDE,如果你想成为一个优秀的java程序员,请更换intellij idea. 使用idea的好处,请搜索谷歌。

别告诉我快捷键不好用

更换IDE不在我本文的重点内容中,所以不下想用太多的篇幅去写为什么更换IDE,请谷歌。

在这里,我只能告诉你,更换IDE只为了更好、更快的写好java代码。原因略。

别告诉我快捷键不好用,请尝试新事物。

bean

bean使我们使用最多的模型之一,我将以大篇幅去讲解bean,希望读者好好体会。

domain包名

根据很多java程序员的”经验”来看,一个数据库表则对应着一个domain对象,所以很多程序员在写代码时,包名则使用:com.xxx.domain  ,这样写好像已经成为了行业的一种约束,数据库映射对象就应该是domain。但是你错了,domain是一个领域对象,往往我们再做传统java软件web开发中,这些domain都是贫血模型,是没有行为的,或是没有足够的领域模型的行为的,所以,以这个理论来讲,这些domain都应该是一个普通的entity对象,并非领域对象,所以请把包名改为:com.xxx.entity。

如果你还不理解我说的话,请看一下Vaughn Vernon出的一本叫做《IMPLEMENTING DOMAIN-DRIVEN  DESIGN》(实现领域驱动设计)这本书,书中讲解了贫血模型与领域模型的区别,相信你会受益匪浅。

DTO

数据传输我们应该使用DTO对象作为传输对象,这是我们所约定的,因为很长时间我一直都在做移动端api设计的工作,有很多人告诉我,他们认为只有给手机端传输数据的时候(input  or  output),这些对象成为DTO对象。请注意!这种理解是错误的,只要是用于网络传输的对象,我们都认为他们可以当做是DTO对象,比如电商平台中,用户进行下单,下单后的数据,订单会发到OMS  或者 ERP系统,这些对接的返回值以及入参也叫DTO对象。

我们约定某对象如果是DTO对象,就将名称改为XXDTO,比如订单下发OMS:OMSOrderInputDTO。

DTO转化

正如我们所知,DTO为系统与外界交互的模型对象,那么肯定会有一个步骤是将DTO对象转化为BO对象或者是普通的entity对象,让service层去处理。

场景

比如添加会员操作,由于用于演示,我只考虑用户的一些简单数据,当后台管理员点击添加用户时,只需要传过来用户的姓名和年龄就可以了,后端接受到数据后,将添加创建时间和更新时间和默认密码三个字段,然后保存数据库。

@RequestMapping("/v1/api/user") @RestController public class UserApi {      @Autowired     private UserService userService;      @PostMapping     public User addUser(UserInputDTO userInputDTO){         User user = new User();         user.setUsername(userInputDTO.getUsername());         user.setAge(userInputDTO.getAge());          return userService.addUser(user);     } }

我们只关注一下上述代码中的转化代码,其他内容请忽略:

User user = new User(); user.setUsername(userInputDTO.getUsername()); user.setAge(userInputDTO.getAge());

请使用工具

上边的代码,从逻辑上讲,是没有问题的,只是这种写法让我很厌烦,例子中只有两个字段,如果有20个字段,我们要如何做呢?  一个一个进行set数据吗?当然,如果你这么做了,肯定不会有什么问题,但是,这肯定不是一个最优的做法。

网上有很多工具,支持浅拷贝或深拷贝的Utils.  举个例子,我们可以使用org.springframework.beans.BeanUtils#copyProperties对代码进行重构和优化:

@PostMapping public User addUser(UserInputDTO userInputDTO){     User user = new User();     BeanUtils.copyProperties(userInputDTO,user);      return userService.addUser(user); }

BeanUtils.copyProperties是一个浅拷贝方法,复制属性时,我们只需要把DTO对象和要转化的对象两个的属性值设置为一样的名称,并且保证一样的类型就可以了。如果你在做DTO转化的时候一直使用set进行属性赋值,那么请尝试这种方式简化代码,让代码更加清晰!

转化的语义

上边的转化过程,读者看后肯定觉得优雅很多,但是我们再写java代码时,更多的需要考虑语义的操作,再看上边的代码:

User user = new User(); BeanUtils.copyProperties(userInputDTO,user);

虽然这段代码很好的简化和优化了代码,但是他的语义是有问题的,我们需要提现一个转化过程才好,所以代码改成如下:

@PostMapping  public User addUser(UserInputDTO userInputDTO){          User user = convertFor(userInputDTO);           return userService.addUser(user);  }   private User convertFor(UserInputDTO userInputDTO){           User user = new User();          BeanUtils.copyProperties(userInputDTO,user);          return user;  }

这是一个更好的语义写法,虽然他麻烦了些,但是可读性大大增加了,在写代码时,我们应该尽量把语义层次差不多的放到一个方法中,比如:

User user = convertFor(userInputDTO); return userService.addUser(user);

这两段代码都没有暴露实现,都是在讲如何在同一个方法中,做一组相同层次的语义操作,而不是暴露具体的实现。

如上所述,是一种重构方式,读者可以参考Martin Fowler的《Refactoring Imporving the Design of  Existing Code》(重构 改善既有代码的设计) 这本书中的Extract Method重构方式。

抽象接口定义

当实际工作中,完成了几个api的DTO转化时,我们会发现,这样的操作有很多很多,那么应该定义好一个接口,让所有这样的操作都有规则的进行。

如果接口被定义以后,那么convertFor这个方法的语义将产生变化,他将是一个实现类。

看一下抽象后的接口:

public interface DTOConvert<S,T> {     T convert(S s); }

虽然这个接口很简单,但是这里告诉我们一个事情,要去使用泛型,如果你是一个优秀的java程序员,请为你想做的抽象接口,做好泛型吧。

我们再来看接口实现:

public class UserInputDTOConvert implements DTOConvert { @Override public User convert(UserInputDTO userInputDTO) { User user = new User(); BeanUtils.copyProperties(userInputDTO,user); return user; } }

我们这样重构后,我们发现现在的代码是如此的简洁,并且那么的规范:

@RequestMapping("/v1/api/user") @RestController public class UserApi {      @Autowired     private UserService userService;      @PostMapping     public User addUser(UserInputDTO userInputDTO){         User user = new UserInputDTOConvert().convert(userInputDTO);          return userService.addUser(user);     } }

review code

如果你是一个优秀的java程序员,我相信你应该和我一样,已经数次重复review过自己的代码很多次了。

我们再看这个保存用户的例子,你将发现,api中返回值是有些问题的,问题就在于不应该直接返回User实体,因为如果这样的话,就暴露了太多实体相关的信息,这样的返回值是不安全的,所以我们更应该返回一个DTO对象,我们可称它为UserOutputDTO:

@PostMapping public UserOutputDTO addUser(UserInputDTO userInputDTO){         User user = new UserInputDTOConvert().convert(userInputDTO);         User saveUserResult = userService.addUser(user);         UserOutputDTO result = new UserOutDTOConvert().convertToUser(saveUserResult);         return result; }

这样你的api才更健全。

不知道在看完这段代码之后,读者有是否发现还有其他问题的存在,作为一个优秀的java程序员,请看一下这段我们刚刚抽象完的代码:

User user = new UserInputDTOConvert().convert(userInputDTO);

你会发现,new这样一个DTO转化对象是没有必要的,而且每一个转化对象都是由在遇到DTO转化的时候才会出现,那我们应该考虑一下,是否可以将这个类和DTO进行聚合呢,看一下我的聚合结果:

public String getUsername() {         return username;     }      public void setUsername(String username) {         this.username = username;     }      public int getAge() {         return age;     }      public void setAge(int age) {         this.age = age;     }       public User convertToUser(){         UserInputDTOConvert userInputDTOConvert = new UserInputDTOConvert();         User convert = userInputDTOConvert.convert(this);         return convert;     }      private static class UserInputDTOConvert implements DTOConvert<UserInputDTO,User> {         @Override         public User convert(UserInputDTO userInputDTO) {             User user = new User();             BeanUtils.copyProperties(userInputDTO,user);             return user;         }     }  }

然后api中的转化则由:

User user = new UserInputDTOConvert().convert(userInputDTO); User saveUserResult = userService.addUser(user);

变成了:

User user = userInputDTO.convertToUser(); User saveUserResult = userService.addUser(user);

我们再DTO对象中添加了转化的行为,我相信这样的操作可以让代码的可读性变得更强,并且是符合语义的。

再查工具类

再来看DTO内部转化的代码,它实现了我们自己定义的DTOConvert接口,但是这样真的就没有问题,不需要再思考了吗?

我觉得并不是,对于Convert这种转化语义来讲,很多工具类中都有这样的定义,这中Convert并不是业务级别上的接口定义,它只是用于普通bean之间转化属性值的普通意义上的接口定义,所以我们应该更多的去读其他含有Convert转化语义的代码。

我仔细阅读了一下GUAVA的源码,发现了com.Google.common.base.Convert这样的定义:

public abstract class Converter<A, B> implements Function<A, B> {     protected abstract B doForward(A a);     protected abstract A doBackward(B b);     //其他略 }

从源码可以了解到,GUAVA中的Convert可以完成正向转化和逆向转化,继续修改我们DTO中转化的这段代码:

private static class UserInputDTOConvert implements DTOConvert<UserInputDTO,User> {         @Override         public User convert(UserInputDTO userInputDTO) {                 User user = new User();                 BeanUtils.copyProperties(userInputDTO,user);                 return user;         } }

修改后:

private static class UserInputDTOConvert extends Converter<UserInputDTO, User> {          @Override          protected User doForward(UserInputDTO userInputDTO) {                  User user = new User();                  BeanUtils.copyProperties(userInputDTO,user);                  return user;          }           @Override          protected UserInputDTO doBackward(User user) {                  UserInputDTO userInputDTO = new UserInputDTO();                  BeanUtils.copyProperties(user,userInputDTO);                  return userInputDTO;          }  }

看了这部分代码以后,你可能会问,那逆向转化会有什么用呢?其实我们有很多小的业务需求中,入参和出参是一样的,那么我们变可以轻松的进行转化,我将上边所提到的UserInputDTO和UserOutputDTO都转成UserDTO展示给大家:

DTO:

public class UserDTO {     private String username;     private int age;      public String getUsername() {             return username;     }      public void setUsername(String username) {             this.username = username;     }      public int getAge() {             return age;     }      public void setAge(int age) {             this.age = age;     }       public User convertToUser(){             UserDTOConvert userDTOConvert = new UserDTOConvert();             User convert = userDTOConvert.convert(this);             return convert;     }      public UserDTO convertFor(User user){             UserDTOConvert userDTOConvert = new UserDTOConvert();             UserDTO convert = userDTOConvert.reverse().convert(user);             return convert;     }      private static class UserDTOConvert extends Converter<UserDTO, User> {             @Override             protected User doForward(UserDTO userDTO) {                     User user = new User();                     BeanUtils.copyProperties(userDTO,user);                     return user;             }              @Override             protected UserDTO doBackward(User user) {                     UserDTO userDTO = new UserDTO();                     BeanUtils.copyProperties(user,userDTO);                     return userDTO;             }     }  }

api:

@PostMapping  public UserDTO addUser(UserDTO userDTO){          User user =  userDTO.convertToUser();          User saveResultUser = userService.addUser(user);          UserDTO result = userDTO.convertFor(saveResultUser);          return result;  }

当然,上述只是表明了转化方向的正向或逆向,很多业务需求的出参和入参的DTO对象是不同的,那么你需要更明显的告诉程序:逆向是无法调用的:

private static class UserDTOConvert extends Converter<UserDTO, User> {          @Override          protected User doForward(UserDTO userDTO) {                  User user = new User();                  BeanUtils.copyProperties(userDTO,user);                  return user;          }           @Override          protected UserDTO doBackward(User user) {                  throw new AssertionError("不支持逆向转化方法!");          }  }

看一下doBackward方法,直接抛出了一个断言异常,而不是业务异常,这段代码告诉代码的调用者,这个方法不是准你调用的,如果你调用,我就”断言”你调用错误了。

关于异常处理的更详细介绍,可以参考我之前的文章:如何优雅的设计java异常 ,应该可以帮你更好的理解异常。

bean的验证

如果你认为我上边写的那个添加用户api写的已经非常完美了,那只能说明你还不是一个优秀的程序员。我们应该保证任何数据的入参到方法体内都是合法的。

为什么要验证

很多人会告诉我,如果这些api是提供给前端进行调用的,前端都会进行验证啊,你为什还要验证?

其实答案是这样的,我从不相信任何调用我api或者方法的人,比如前端验证失败了,或者某些人通过一些特殊的渠道(比如Charles进行抓包),直接将数据传入到我的api,那我仍然进行正常的业务逻辑处理,那么就有可能产生脏数据!

“对于脏数据的产生一定是致命”,这句话希望大家牢记在心,再小的脏数据也有可能让你找几个通宵!

jsr 303验证

hibernate提供的jsr 303实现,我觉得目前仍然是很优秀的,具体如何使用,我不想讲,因为谷歌上你可以搜索出很多答案!

再以上班的api实例进行说明,我们现在对DTO数据进行检查:

public class UserDTO {     @NotNull     private String username;     @NotNull     private int age;         //其他代码略 }

api验证:

@PostMapping     public UserDTO addUser(@Valid UserDTO userDTO){             User user =  userDTO.convertToUser();             User saveResultUser = userService.addUser(user);             UserDTO result = userDTO.convertFor(saveResultUser);             return result;     }

我们需要将验证结果传给前端,这种异常应该转化为一个api异常(带有错误码的异常)。

@PostMapping public UserDTO addUser(@Valid UserDTO userDTO, BindingResult bindingResult){      checkDTOParams(bindingResult);       User user =  userDTO.convertToUser();      User saveResultUser = userService.addUser(user);      UserDTO result = userDTO.convertFor(saveResultUser);      return result; } private void checkDTOParams(BindingResult bindingResult){      if(bindingResult.hasErrors()){              //throw new 带验证码的验证错误异常      } }

BindingResult是Spring mvc验证DTO后的一个结果集,可以参考spring 官方文档

检查参数后,可以抛出一个“带验证码的验证错误异常”,具体异常设计可以参考如何优雅的设计java异常

拥抱lombok

上边的DTO代码,已经让我看的很累了,我相信读者也是一样,看到那么多的Getter和Setter方法,太烦躁了,那时候有什么方法可以简化这些呢。

请拥抱lombok,它会帮助我们解决一些让我们很烦躁的问题

去掉Setter和Getter

其实这个标题,我不太想说,因为网上太多,但是因为很多人告诉我,他们根本就不知道lombok的存在,所以为了让读者更好的学习,我愿意写这样一个例子:

@Setter @Getter public class UserDTO {     @NotNull     private String username;     @NotNull     private int age;      public User convertToUser(){         UserDTOConvert userDTOConvert = new UserDTOConvert();         User convert = userDTOConvert.convert(this);         return convert;     }      public UserDTO convertFor(User user){         UserDTOConvert userDTOConvert = new UserDTOConvert();         UserDTO convert = userDTOConvert.reverse().convert(user);         return convert;     }      private static class UserDTOConvert extends Converter<UserDTO, User> {         @Override         protected User doForward(UserDTO userDTO) {             User user = new User();             BeanUtils.copyProperties(userDTO,user);             return user;         }          @Override         protected UserDTO doBackward(User user) {             throw new AssertionError("不支持逆向转化方法!");         }     }  }

看到了吧,烦人的Getter和Setter方法已经去掉了。

但是上边的例子根本不足以体现lombok的强大。我希望写一些网上很难查到,或者很少人进行说明的lombok的使用以及在使用时程序语义上的说明。

比如:@Data,@AllArgsConstructor,@NoArgsConstructor..这些我就不进行一一说明了,请大家自行查询资料.

bean中的链式风格

什么是链式风格?我来举个例子,看下面这个Student的bean:

public class Student {     private String name;     private int age;      public String getName() {         return name;     }      public Student setName(String name) {         this.name = name;         return this;     }      public int getAge() {         return age;     }      public Student setAge(int age) {         return this;     } }

仔细看一下set方法,这样的设置便是chain的style,调用的时候,可以这样使用:

Student student = new Student()         .setAge(24)         .setName("zs");

相信合理使用这样的链式代码,会更多的程序带来很好的可读性,那看一下如果使用lombok进行改善呢,请使用 @Accessors(chain =  true),看如下代码:

@Accessors(chain = true) @Setter @Getter public class Student {     private String name;     private int age; }

这样就完成了一个对于bean来讲很友好的链式操作。

静态构造方法

静态构造方法的语义和简化程度真的高于直接去new一个对象。比如new一个List对象,过去的使用是这样的:

List list = new ArrayList<>();

看一下guava中的创建方式:

List list = Lists.newArrayList();

Lists命名是一种约定(俗话说:约定优于配置),它是指Lists是List这个类的一个工具类,那么使用List的工具类去产生List,这样的语义是不是要比直接new一个子类来的更直接一些呢,答案是肯定的,再比如如果有一个工具类叫做Maps,那你是否想到了创建Map的方法呢:

HashMap<String, String> objectObjectHashMap = Maps.newHashMap();

好了,如果你理解了我说的语义,那么,你已经向成为java程序员更近了一步了。

再回过头来看刚刚的Student,很多时候,我们去写Student这个bean的时候,他会有一些必输字段,比如Student中的name字段,一般处理的方式是将name字段包装成一个构造方法,只有传入name这样的构造方法,才能创建一个Student对象。

接上上边的静态构造方法和必传参数的构造方法,使用lombok将更改成如下写法(@RequiredArgsConstructor 和  @NonNull):

@Accessors(chain = true) @Setter @Getter @RequiredArgsConstructor(staticName = "ofName") public class Student {     @NonNull private String name;     private int age; }

测试代码:

Student student = Student.ofName("zs");

这样构建出的bean语义是否要比直接new一个含参的构造方法(包含 name的构造方法)要好很多。

当然,看过很多源码以后,我想相信将静态构造方法ofName换成of会先的更加简洁:

@Accessors(chain = true) @Setter @Getter @RequiredArgsConstructor(staticName = "of") public class Student {         @NonNull private String name;         private int age; }

测试代码:

Student student = Student.of("zs");

当然他仍然是支持链式调用的:

Student student = Student.of("zs").setAge(24);

这样来写代码,真的很简洁,并且可读性很强。

使用builder

Builder模式我不想再多解释了,读者可以看一下《Head First》(设计模式) 的建造者模式。

今天其实要说的是一种变种的builder模式,那就是构建bean的builder模式,其实主要的思想是带着大家一起看一下lombok给我们带来了什么。

看一下Student这个类的原始builder状态:

public class Student {     private String name;     private int age;      public String getName() {             return name;     }      public void setName(String name) {             this.name = name;     }      public int getAge() {             return age;     }      public void setAge(int age) {             this.age = age;     }      public static Builder builder(){             return new Builder();     }     public static class Builder{             private String name;             private int age;             public Builder name(String name){                     this.name = name;                     return this;             }              public Builder age(int age){                     this.age = age;                     return this;             }              public Student build(){                     Student student = new Student();                     student.setAge(age);                     student.setName(name);                     return student;             }     }  }

调用方式:

Student student = Student.builder().name("zs").age(24).build();

这样的builder代码,让我是在恶心难受,于是我打算用lombok重构这段代码:

@Builder public class Student {     private String name;     private int age; }

调用方式:

Student student = Student.builder().name("zs").age(24).build();

代理模式

正如我们所知的,在程序中调用rest接口是一个常见的行为动作,如果你和我一样使用过spring  的RestTemplate,我相信你会我和一样,对他抛出的非Http状态码异常深恶痛绝。

所以我们考虑将RestTemplate最为底层包装器进行包装器模式的设计:

public abstract class FilterRestTemplate implements RestOperations {         protected volatile RestTemplate restTemplate;          protected FilterRestTemplate(RestTemplate restTemplate){                 this.restTemplate = restTemplate;         }          //实现RestOperations所有的接口 }

然后再由扩展类对FilterRestTemplate进行包装扩展:

public class ExtractRestTemplate extends FilterRestTemplate {     private RestTemplate restTemplate;     public ExtractRestTemplate(RestTemplate restTemplate) {             super(restTemplate);             this.restTemplate = restTemplate;     }      public <T> RestResponseDTO<T> postForEntityWithNoException(String url, Object request, Class<T> responseType, Object... uriVariables)                     throws RestClientException {             RestResponseDTO<T> restResponseDTO = new RestResponseDTO<T>();             ResponseEntity<T> tResponseEntity;             try {                     tResponseEntity = restTemplate.postForEntity(url, request, responseType, uriVariables);                     restResponseDTO.setData(tResponseEntity.getBody());                     restResponseDTO.setMessage(tResponseEntity.getStatusCode().name());                     restResponseDTO.setStatusCode(tResponseEntity.getStatusCodeValue());             }catch (Exception e){                     restResponseDTO.setStatusCode(RestResponseDTO.UNKNOWN_ERROR);                     restResponseDTO.setMessage(e.getMessage());                     restResponseDTO.setData(null);             }             return restResponseDTO;     } }

包装器ExtractRestTemplate很完美的更改了异常抛出的行为,让程序更具有容错性。在这里我们不考虑ExtractRestTemplate完成的功能,让我们把焦点放在FilterRestTemplate上,“实现RestOperations所有的接口”,这个操作绝对不是一时半会可以写完的,当时在重构之前我几乎写了半个小时,如下:

public abstract class FilterRestTemplate implements RestOperations {      protected volatile RestTemplate restTemplate;      protected FilterRestTemplate(RestTemplate restTemplate) {             this.restTemplate = restTemplate;     }      @Override     public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables) throws RestClientException {             return restTemplate.getForObject(url,responseType,uriVariables);     }      @Override     public <T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException {             return restTemplate.getForObject(url,responseType,uriVariables);     }      @Override     public <T> T getForObject(URI url, Class<T> responseType) throws RestClientException {             return restTemplate.getForObject(url,responseType);     }      @Override     public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables) throws RestClientException {             return restTemplate.getForEntity(url,responseType,uriVariables);     }     //其他实现代码略。。。 }

我相信你看了以上代码,你会和我一样觉得恶心反胃,后来我用lombok提供的代理注解优化了我的代码(@Delegate):

@AllArgsConstructor public abstract class FilterRestTemplate implements RestOperations {     @Delegate     protected volatile RestTemplate restTemplate; }

这几行代码完全替代上述那些冗长的代码。

是不是很简洁,做一个拥抱lombok的程序员吧。

重构

需求案例

项目需求

项目开发阶段,有一个关于下单发货的需求:如果今天下午3点前进行下单,那么发货时间是明天,如果今天下午3点后进行下单,那么发货时间是后天,如果被确定的时间是周日,那么在此时间上再加1天为发货时间。

思考与重构

我相信这个需求看似很简单,无论怎么写都可以完成。

很多人可能看到这个需求,就动手开始写Calendar或Date进行计算,从而完成需求。

而我给的建议是,仔细考虑如何写代码,然后再去写,不是说所有的时间操作都用Calendar或Date去解决,一定要看场景。

对于时间的计算我们要考虑joda-time这种类似的成熟时间计算框架来写代码,它会让代码更加简洁和易读。

请读者先考虑这个需求如何用java代码完成,或先写一个你觉得完成这个代码的思路,再来看我下边的代码,这样,你的收获会更多一些:

final DateTime DISTRIBUTION_TIME_SPLIT_TIME = new DateTime().withTime(15,0,0,0); private Date calculateDistributionTimeByOrderCreateTime(Date orderCreateTime){     DateTime orderCreateDateTime = new DateTime(orderCreateTime);     Date tomorrow = orderCreateDateTime.plusDays(1).toDate();     Date theDayAfterTomorrow = orderCreateDateTime.plusDays(2).toDate();     return orderCreateDateTime.isAfter(DISTRIBUTION_TIME_SPLIT_TIME) ? wrapDistributionTime(theDayAfterTomorrow) : wrapDistributionTime(tomorrow); } private Date wrapDistributionTime(Date distributionTime){     DateTime currentDistributionDateTime = new DateTime(distributionTime);     DateTime plusOneDay = currentDistributionDateTime.plusDays(1);     boolean isSunday = (DateTimeConstants.SUNDAY == currentDistributionDateTime.getDayOfWeek());     return isSunday ? plusOneDay.toDate() : currentDistributionDateTime.toDate() ; }

读这段代码的时候,你会发现,我将判断和有可能出现的不同结果都当做一个变量,最终做一个三目运算符的方式进行返回,这样的优雅和可读性显而易见,当然这样的代码不是一蹴而就的,我优化了3遍产生的以上代码。读者可根据自己的代码和我写的代码进行对比。

提高方法

如果你做了3年+的程序员,我相信像如上这样的需求,你很轻松就能完成,但是如果你想做一个会写java的程序员,就好好的思考和重构代码吧。

写代码就如同写字一样,同样的字,大家都会写,但是写出来是否好看就不一定了。如果想把程序写好,就要不断的思考和重构,敢于尝试,敢于创新,不要因循守旧,一定要做一个优秀的java程序员。

提高代码水平最好的方法就是有条理的重构!(注意:是有条理的重构)

设计模式

设计模式就是工具,而不是提现你是否是高水平程序员的一个指标。

我经常会看到某一个程序员兴奋的大喊,哪个程序哪个点我用到了设计模式,写的多么多么优秀,多么多么好。我仔细去翻阅的时候,却发现有很多是过度设计的。

业务驱动技术 or 技术驱动业务

业务驱动技术 or 技术驱动业务 ?  其实这是一个一直在争论的话题,但是很多人不这么认为,我觉得就是大家不愿意承认罢了。我来和大家大概分析一下作为一个java程序员,我们应该如何判断自己所处于的位置.

业务驱动技术:如果你所在的项目是一个收益很小或者甚至没有收益的项目,请不要搞其他创新的东西,不要驱动业务要如何如何做,而是要熟知业务现在的痛点是什么?如何才能帮助业务盈利或者让项目更好,更顺利的进行。

技术驱动业务:如果你所在的项目是一个很牛的项目,比如淘宝这类的项目,我可以在满足业务需求的情况下,和业务沟通,使用什么样的技术能更好的帮助业务创造收益,比如说下单的时候要进队列,可能几分钟之后订单状态才能处理完成,但是会让用户有更流畅的体验,赚取更多的访问流量,那么我相信业务愿意被技术驱动,会同意订单的延迟问题,这样便是技术驱动业务。

我相信大部分人还都处于业务驱动技术的方向吧。

所以你既然不能驱动业务,那就请拥抱业务变化吧。

代码设计

一直在做java后端的项目,经常会有一些变动,我相信大家也都遇到过。

比如当我们写一段代码的时候,我们考虑将需求映射成代码的状态模式,突然有一天,状态模式里边又添加了很多行为变化的东西,这时候你就挠头了,你硬生生的将状态模式中添加过多行为和变化。

慢慢的你会发现这些状态模式,其实更像是一簇算法,应该使用策略模式,这时你应该已经晕头转向了。

说了这么多,我的意思是,只要你觉得合理,就请将状态模式改为策略模式吧,所有的模式并不是凭空想象出来的,都是基于重构。

java编程中没有银弹,请拥抱业务变化,一直思考重构,你就有一个更好的代码设计!

你真的优秀吗?

真不好意思,我取了一个这么无聊的标题。

国外流行一种编程方式,叫做结对编程,我相信国内很多公司都没有这么做,我就不在讲述结对编程带来的好处了,其实就是一边code  review,一边互相提高的一个过程。既然做不到这个,那如何让自己活在自己的世界中不断提高呢?

“平时开发的时候,做出的代码总认为是正确的,而且写法是完美的。”,我相信这是大部分人的心声,还回到刚刚的问题,如何在自己的世界中不断提高呢?

答案就是:

  1. 多看成熟框架的源码

  2. 多回头看自己的代码

  3. 勤于重构

你真的优秀吗? 如果你每周都完成了学习源码,回头看自己代码,然后勤于重构,我认为你就真的很优秀了。

即使也许你只是刚刚入门,但是一直坚持,你就是一个真的会写java代码的程序员了。

技能

UML

不想多讨论UML相关的知识,但是我觉得你如果真的会写java,请先学会表达自己,UML就是你说话的语言,做一名优秀的java程序员,请至少学会这两种UML图:

  1. 类图

  2. 时序图

clean code

我认为保持代码的简洁和可读性是代码的最基本保证,如果有一天为了程序的效率而降低了这两点,我认为是可以谅解的,除此之外,没有任何理由可以让你任意挥霍你的代码。

  1. 读者可以看一下Robert C. Martin出版的《Clean Code》(代码整洁之道) 这本书

  2. 可以参考美团文章聊聊clean code

  3. 也可以看一下阿里的Java编码规范

无论如何,请保持你的代码的整洁。

linux 基础命令

这点其实和会写java没有关系,但是linux很多时候确实承载运行java的容器,请学好linux的基础命令。

感谢各位的阅读,以上就是“如何写Java代码”的内容了,经过本文的学习后,相信大家对如何写Java代码这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是编程网,小编将为大家推送更多相关知识点的文章,欢迎关注!

--结束END--

本文标题: 如何写Java代码

本文链接: https://lsjlt.com/news/284349.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 如何写Java代码
    这篇文章主要讲解了“如何写Java代码”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何写Java代码”吧!技术点本文不是一个吹嘘的文章,不会讲很多高深的架构,相反,会讲解很多基础的问题和写...
    99+
    2023-06-16
  • 如何编写高性能的Java代码
    这篇文章主要介绍了如何编写高性能的Java代码,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。一、并发Unable to create new native thread ……...
    99+
    2023-06-02
  • java选择排序法代码如何写
    以下是使用Java编写选择排序算法的代码: public class SelectionSort { public stat...
    99+
    2023-10-26
    java
  • python如何写代码
    小编给大家分享一下python如何写代码,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!Python常用的编辑器有vim,Eclipse with PyDev,Su...
    99+
    2023-06-14
  • 在java中如何编写规范的代码
    本篇内容介绍了“在java中如何编写规范的代码”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!一、MyBatis 不要为了多个查询条件而写 1...
    99+
    2023-06-16
  • java代码如何编写到数据库里
    将Java代码编写到数据库里,通常有以下几个步骤:1. 连接数据库:使用Java提供的JDBC(Java Database Conn...
    99+
    2024-02-29
    java 数据库
  • 如何编写JavaScript代码
    这篇文章主要介绍如何编写JavaScript代码,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!如果你对于代码,除了关注是否能准确的执行业务逻辑,还关心代码本身是怎么写的,是否易读,那...
    99+
    2024-04-02
  • 如何编写代码Review
    这篇文章主要介绍“如何编写代码Review”,在日常操作中,相信很多人在如何编写代码Review问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”如何编写代码Review”的疑惑...
    99+
    2024-04-02
  • java启动监听mq消息代码如何写
    在Java中启动监听MQ消息的代码可以使用JMS(Java Message Service)的API来实现。下面是一个简单的示例代码...
    99+
    2023-10-27
    java mq
  • java下载文件到本地代码如何写
    以下是一个简单的Java代码示例,用于从指定的URL下载文件到本地: import java.io.FileOutputStream...
    99+
    2024-04-02
  • Java NIO代码怎么写
    这篇文章主要讲解了“Java NIO代码怎么写”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java NIO代码怎么写”吧!Java代码:import java.io.IOExce...
    99+
    2023-06-17
  • Java堆代码怎么写
    这篇文章主要介绍了Java堆代码怎么写的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Java堆代码怎么写文章都会有所收获,下面我们一起来看看吧。1、堆的定义①、它是完全二叉树,除了树的最后一层节点不需要是满的,...
    99+
    2023-06-28
  • 如何写出好的代码
    本篇内容介绍了“如何写出好的代码”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!写好注释写代码这件事情,大概...
    99+
    2024-04-02
  • HTML按钮代码如何写
    这篇文章主要介绍了HTML按钮代码如何写的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇HTML按钮代码如何写文章都会有所收获,下面我们一起来看看吧。HTML<button>标签标签定义及使用说明&l...
    99+
    2023-06-27
  • 如何把代码写到gitee
    在这个日渐数字化、信息化的时代,程序员们经常需要将自己写的代码共享、存档、备份,而gitee就是一个很好的选择。gitee提供了免费的代码托管服务,以及基于git的代码版本控制系统。下面将介绍如何把代码写到gitee,并分享一些使用gite...
    99+
    2023-10-22
  • 如何编写ipclear.vbs源代码
    本篇内容介绍了“如何编写ipclear.vbs源代码”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!复制代码 代码如下:Option ...
    99+
    2023-06-08
  • java验证码代码怎么写
    以下是一个简单的Java验证码生成代码示例:```javaimport java.util.Random;public class ...
    99+
    2023-08-09
    java
  • 如何在Windows上使用Java IDE编写NumPy代码?
    NumPy是Python中非常重要的第三方库之一,它提供了高性能的多维数组以及相关的计算功能。然而,Python并不是唯一的编程语言可以使用NumPy,事实上,在Java中也可以使用NumPy。本篇文章将向您介绍如何在Windows上使用...
    99+
    2023-09-06
    ide windows numpy
  • java排序代码怎么写
    有很多种排序算法可以在Java中实现,以下是一些常见的排序算法的示例代码:1. 冒泡排序(Bubble Sort)```javapu...
    99+
    2023-08-17
    java
  • 如何优化CSS代码写法
    今天就跟大家聊聊有关如何优化CSS代码写法,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。众所周知,SEO优化很重要的一点就是布局,而DIV+CSS布...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作