java优化代码常见套路

白色玫瑰 程序猿

时间: 2023-05-22 阅读: 1 字数:18918

{}
程序员的痛点(烂代码)\n每次做完项目之后,自己想重新回顾一下以前写的代码,整理出一些东西,却发现如同看天书一般,头晕眼花,完全感觉不像自己的写的代码,辣眼睛\n\n所以为了爱护本人的眼睛,所以觉得很有必要整理一下一些优化代码的套路…\n首先说一个最重要的优化原则:代码优化是你觉得你代码很繁琐、阅读性很差的时候一定要马上优化,立刻马上,不管你现在有多忙,每天优化才叫重构,每年优化那叫重写\n这个原则为什么重...

目录

目录

<a href="#_1">程序员的痛点(烂代码)</a>    <a href="#_10">该如何优化代码</a>    <a href="#md5_184">前台后台两次md5加盐加密</a>    <a href="#JSR303_244">JSR303和全局异常处理</a>    <a href="#RediskeyRedisService_399">Redis通用的key生成策略和通用的RedisService方法</a>    <a href="#_617">程序猿的必读书籍</a>      

程序员的痛点(烂代码)

每次做完项目之后,自己想重新回顾一下以前写的代码,整理出一些东西,却发现如同看天书一般,头晕眼花,完全感觉不像自己的写的代码,辣眼睛,犹如下图 在这里插入图片描述 所以为了爱护本人的眼睛,所以觉得很有必要整理一下一些优化代码的套路…

首先说一个最重要的优化原则:代码优化是你觉得你代码很繁琐、阅读性很差的时候一定要马上优化,立刻马上,不管你现在有多忙,每天优化才叫重构,每年优化那叫重写

这个原则为什么重要?因为很多程序员会在写代码的时候说「先不优化了,等不忙的时候再优化」,然后……就没有然后了,我也是这样,所以就导致了大量捞比代码的产生

该如何优化代码

1、逻辑复杂的业务代码一定要有注释(可能你写的是爽了,后面维护你代码的人可能会想往你头上暴扣)

2、首先是变量名、方法名这些,命名一定要规范,千万别出现aa、bb这种命名,然后我们可以对我们的一些状态变量进行集中管理 这个什么意思呢,比如我们在项目中一个订单的状态,0代码已下单、1代表已付款、2代表交易中等等…这一大堆的状态代表数据。 可能前期我们写的时候印象很深刻,万一后期你要改动,又或者需求有变动?你确定你的一堆状态数字还记得吗

所以我们在项目开始初期就可以写一个工具类,来专门管理我们状态结果 比如

package com.javaxl.miaosha_02.result;

public class CodeMsg {
    
    private int code;
    private String msg;
    
    //通用的错误码
    public static CodeMsg SUCCESS = new CodeMsg(0, "success");
    public static CodeMsg SERVER_ERROR = new CodeMsg(500100, "服务端异常");
    public static CodeMsg BIND_ERROR = new CodeMsg(500101, "参数校验异常:%s");
    //登录模块 5002XX
    public static CodeMsg SESSION_ERROR = new CodeMsg(500210, "Session不存在或者已经失效");
    public static CodeMsg PASSWORD_EMPTY = new CodeMsg(500211, "登录密码不能为空");
    public static CodeMsg MOBILE_EMPTY = new CodeMsg(500212, "手机号不能为空");
    public static CodeMsg MOBILE_ERROR = new CodeMsg(500213, "手机号格式错误");
    public static CodeMsg MOBILE_NOT_EXIST = new CodeMsg(500214, "手机号不存在");
    public static CodeMsg PASSWORD_ERROR = new CodeMsg(500215, "密码错误");
    
    //订单模块 5004XX
    public static CodeMsg ORDER_NOT_EXIST = new CodeMsg(500400, "订单不存在");
    
    //秒杀模块 5005XX
    public static CodeMsg MIAO_SHA_OVER = new CodeMsg(500500, "商品已经秒杀完毕");
    public static CodeMsg REPEATE_MIAOSHA = new CodeMsg(500501, "不能重复秒杀");
    
    
    private CodeMsg( ) {
    }
            
    private CodeMsg( int code,String msg ) {
        this.code = code;
        this.msg = msg;
    }
    
    public int getCode() {
        return code;
    }
    public void setCode(int code) {
        this.code = code;
    }
    public String getMsg() {
        return msg;
    }
    public void setMsg(String msg) {
        this.msg = msg;
    }
    
    public CodeMsg fillArgs(Object... args) {
        int code = this.code;
        String message = String.format(this.msg, args);
        return new CodeMsg(code, message);
    }

    @Override
    public String toString() {
        return "CodeMsg [code=" + code + ", msg=" + msg + "]";
    }
}

在实际开发中如果项目比较大,甚至可以分模块来管理,每一个模块都专门写一个工具类来管理你的状态代码

3、尽量避免重复代码 当你发现某些代码重复出现的次数一多,你就应该有想法把它们抽取出来进行优化了

比如我们在做前后端分离项目的时候,后端每一个方法都需要返回固定的Json格式,以前我们是这样干的,我们可能会封装一个JSON格式的工具类JsonData,里面有3个参数,第一个是返回码、第二个是消息提示、第三个是结果集 比如我下面的登录方法

      public JsonData login(Staff staff){
         JsonData jsonData = null;
         Staff login = staffService.login(staff);
         if(login != null){
            //登录成功
            jsonData = new JsonData(1,"欢迎管理员"+staff.getStaffName()+"",login);
         }
         else{
            jsonData = new JsonData(0,"用户名或密码错误",login);
         }
         return jsonData;
      }

然后我们发现我们每次都要重复写我们的状态码、消息提示这些东西,那么我们就可以想办法优化一下了,在固定的地方写好,我们调用就好了,我们用泛型T指定类型,成功就返回成功的类型,失败了返回失败的类型

package com.p2p.p2pstaff.config;

public class Result<T> {
    
    private int code;
    private String msg;
    private T data;
    
    /**
     *  成功时候的调用
     * */
    public static  <T> Result<T> success(T data){
        return new Result<T>(data);
    }
    
    /**
     *  失败时候的调用
     * */
    public static  <T> Result<T> error(CodeMsg codeMsg){
        return new Result<T>(codeMsg);
    }
    
    private Result(T data) {
        this.data = data;
    }
    
    private Result(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }
    
    private Result(CodeMsg codeMsg) {
        if(codeMsg != null) {
            this.code = codeMsg.getCode();
            this.msg = codeMsg.getMsg();
        }
    }
    
    
    public int getCode() {
        return code;
    }
    public void setCode(int code) {
        this.code = code;
    }
    public String getMsg() {
        return msg;
    }
    public void setMsg(String msg) {
        this.msg = msg;
    }
    public T getData() {
        return data;
    }
    public void setData(T data) {
        this.data = data;
    }
}

我们就在全局状态管理类中添加我们的失败状态

    //staff
    public static  CodeMsg STAFF_FAIL = new CodeMsg(0,"登录失败");

然后最终优化后的代码

      public Result<Staff> login(Staff staff){
         Staff login = staffService.login(staff);
         if(login != null){
             //登录成功
            return Result.success(login);
         } else{
            return Result.error(CodeMsg.STAFF_FAIL);
         }
      }

前台后台两次md5加盐加密

后台md5加密相比大家是耳孰能详,我们的shiro等很多权限框架都用到了这一点,而在后台加密依然可能存在密码被截取的可能性。

想象你的密码在被加密前就已经被抓取到了那么加密还有什么用呢?也就是截取我们表单提交的内容,这个是有很多办法能够实现的,比如我们利用抓包工具等等,所以说密码一样存在泄漏的可能。 所以我们就有了在前台就先加密一次然后再提交到后台,这样就算截取到了也是我们加密后的密码了

所以我们需要在登录前进行密码处理

    //获取我们输入的密码
    var inputPass = $("#password").val();
    /* var g_passsword_salt="1a2b3c4d" */
    var salt = g_passsword_salt;
    var str = ""+salt.charAt(0)+salt.charAt(2) + inputPass +salt.charAt(5) + salt.charAt(4);
    var password = md5(str);

前台加密完后进入后台,用我们加密过的密码进行二次加密,我们两次加密的密码都要存入数据库的,不然我们登录是无法验证的

后台取出我们需要认证的盐,然后用我们的shiro去认证密码,这个工具类就和我们以前shiro使用的验证的是一样的

   /**
    * 进行密码验证
    *
    * @param credentials      未加密的密码
    * @param salt            盐
    * @param encryptCredentials 加密后的密码
    * @return
    */
   public static boolean checkCredentials(String credentials, String salt, String encryptCredentials) {
      return encryptCredentials.equals(createCredentials(credentials, salt));
   }
public String login(HttpServletResponse response, LoginVo loginVo) {
        if(loginVo == null) {
            throw new GlobalException(CodeMsg.SERVER_ERROR);
        }
        String mobile = loginVo.getMobile();
        String formPass = loginVo.getPassword();
        //判断手机号是否存在
        MiaoshaUser user = getById(Long.parseLong(mobile));
        if(user == null) {
            throw new GlobalException(CodeMsg.MOBILE_NOT_EXIST);
        }
        //验证密码
        String dbPass = user.getPassword();
        String saltDB = user.getSalt();
        if(!PasswordHelper.checkCredentials(formPass, saltDB, dbPass)) {
            throw new GlobalException(CodeMsg.PASSWORD_ERROR);
        }
        //生成cookie
        String token     = UUIDUtil.uuid();
        addCookie(response, token, user);
        return token;
    }

就这样两次加密就完成了

JSR303和全局异常处理

全局异常处理 如果系统发生了异常,不做统一异常处理,前端会给用户展示一大片看不懂的文字。做统一异常处理后当异常发生后可以给用户一个温馨的提示,不至于使用户满头雾水,所以一方面是为了更好的用户体验 如果不统一全局异常,服务端和前端在遇到异常的时候处理起来杂乱无章非常费力。所以另一方面是为了制定规范提高工作效率

我们这里也就是通过写一个全局异常处理类,来处理我们的运行异常,并给与相对应的提示,而不是返回500错误i西南西

package com.javaxl.miaosha_02.exception;

import java.util.List;

import javax.servlet.http.HttpServletRequest;

import com.javaxl.miaosha_02.result.CodeMsg;
import com.javaxl.miaosha_02.result.Result;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;


@ControllerAdvice
@ResponseBody
public class GlobalExceptionHandler {
    @ExceptionHandler(value=Exception.class)
    public Result<String> exceptionHandler(HttpServletRequest request, Exception e){
        e.printStackTrace();
        if(e instanceof GlobalException) {
            GlobalException ex = (GlobalException)e;
            return Result.error(ex.getCm());
        } else if(e instanceof BindException) {
            BindException ex = (BindException)e;
            List<ObjectError> errors = ex.getAllErrors();
            ObjectError error = errors.get(0);
            String msg = error.getDefaultMessage();
            return Result.error(CodeMsg.BIND_ERROR.fillArgs(msg));
        }else {
            return Result.error(CodeMsg.SERVER_ERROR);
        }
    }
}

我们的信息提示类,也就是前面接收的封装全局信息的类,去继承我们的全局异常处理类,来返回错误提示信息

package com.javaxl.miaosha_02.exception;


import com.javaxl.miaosha_02.result.CodeMsg;

public class GlobalException extends RuntimeException{

    private static final long serialVersionUID = 1L;
    
    private CodeMsg cm;
    
    public GlobalException(CodeMsg cm) {
        super(cm.toString());
        this.cm = cm;
    }

    public CodeMsg getCm() {
        return cm;
    }

}

JSR-303 是 JAVA EE 6 中的一项子规范,叫做 Bean Validation,官方参考实现是Hibernate Validator。

此实现与 Hibernate ORM 没有任何关系。 JSR 303 用于对 Java Bean 中的字段的值进行验证。 Spring MVC 3.x 之中也大力支持 JSR-303,可以在控制器中对表单提交的数据方便地验证。

我们大部分前台项目都是做了JS正则判断的代码,那么如果别人知道了你的请求地址,它是不是就能跳过你的js验证,直接去访问你的数据库的某一个方法呢?这当然是可以的,所以我们就需要用JSR303来处理这种请求

比如我们在注册的时候信息必须满足格式才能插入数据库,而果然跳过js验证,那么数据库就会多很多垃圾数据,这样肯定是不行的,所以我们就加了验证在后台

   public Result<String> doLogin(HttpServletResponse response, @Valid LoginVo loginVo) {
       log.info(loginVo.toString());
       //登录
       String token = userService.login(response, loginVo);
       return Result.success(token);
   }

我在外面登录的方法中加了自定义注解,验证格式是否正确,只有通过了验证才能访问方法,否则就进入异常处理 在这里插入图片描述 自定义注解的代码

package com.javaxl.miaosha_02.validator;

import static java.lang.annotation.ElementType.ANNOTATION_TYPE;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import javax.validation.Constraint;
import javax.validation.Payload;

@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = {IsMobileValidator.class })
public @interface  IsMobile {
    
    boolean required() default true;
    
    String message() default "手机号码格式错误";

    Class<?>[] groups() default { };

    Class<? extends Payload>[] payload() default { };
}

验证格式是否符合我们的要求

package com.javaxl.miaosha_02.validator;
import  javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

import com.javaxl.miaosha_02.util.ValidatorUtil;
import org.apache.commons.lang3.StringUtils;


public class IsMobileValidator implements ConstraintValidator<IsMobile, String> {

    private boolean required = false;
    public void initialize(IsMobile constraintAnnotation) {
        required = constraintAnnotation.required();
    }
    public boolean isValid(String value, ConstraintValidatorContext context) {
        if(required) {
            return ValidatorUtil.isMobile(value);
        }else {
            if(StringUtils.isEmpty(value)) {
   return true;
            }else {
   return ValidatorUtil.isMobile(value);
            }
        }
    }
}

所以只要加了这个注解的就都会先进入验证才能访问数据库 所以我们最后直接通过错误格式并访问不了,而是进了我们的错误处理页面 在这里插入图片描述

Redis通用的key生成策略和通用的RedisService方法

通用key生成是个什么概念呢,也就相当于分组了,我们在项目中需要用到redis的地方肯定不止一个模块,肯定很多模块都需要用到redis,所以我们在存储的时候生成一个文件夹,然后每一个key的名字我们以固定的格式给它拼接上,就如下图效果 在这里插入图片描述 BasePrefix 我们通过放射获取类名,然后拼接上我们的prefix

package com.javaxl.miaosha_02.redis;

public abstract class BasePrefix implements KeyPrefix{
    
    private int expireSeconds;
    
    private String prefix;
    
    public BasePrefix(String prefix) {//0代表永不过期
        this(0, prefix);
    }
    
    public BasePrefix( int expireSeconds, String prefix) {
        this.expireSeconds = expireSeconds;
        this.prefix = prefix;
    }
    
    public int expireSeconds() {//默认0代表永不过期
        return expireSeconds;
    }

    public String getPrefix() {
        String className = getClass().getSimpleName();
        return className+":" + prefix;
    }
}

MiaoshaUserKey生成策略 这也就是生成我们的prefix和规定我们的过期时间的类 最终我们生成看到了就是我们的ClassName+prefix所生成的key

package com.javaxl.miaosha_02.redis;

public class MiaoshaUserKey extends BasePrefix{

    public static final int TOKEN_EXPIRE = 3600*24 * 2;
    private MiaoshaUserKey(int expireSeconds, String prefix) {
        super(expireSeconds, prefix);
    }
    public static MiaoshaUserKey token = new MiaoshaUserKey(TOKEN_EXPIRE, "tk");
    public static MiaoshaUserKey getById = new MiaoshaUserKey(0, "id");
}

通用的Redis操作类 高并发redis做缓存是很通用的手段 当数据量过大时操作redis就有可能出现重复的现象

然后我们通过泛型封装一个通用的存值、取值、自增、自减等操纵的方法,尽量来避免这些问题

package com.javaxl.miaosha_02.redis;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSON;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

@Service
public class RedisService {
    
    @Autowired
    JedisPool jedisPool;
    
    /**
     * 获取当个对象
     * */
    public <T> T get(KeyPrefix prefix, String key,  Class<T> clazz) {
         Jedis jedis = null;
         try {
   jedis =  jedisPool.getResource();
   //生成真正的key
   String realKey  = prefix.getPrefix() + key;
   String  str = jedis.get(realKey);
   T t =  stringToBean(str, clazz);
   return t;
         }finally {
    returnToPool(jedis);
         }
    }
    
    /**
     * 设置对象
     * */
    public <T> boolean set(KeyPrefix prefix, String key,  T value) {
         Jedis jedis = null;
         try {
   jedis =  jedisPool.getResource();
   String str = beanToString(value);
   if(str == null || str.length() <= 0) {
    return false;
   }
            //生成真正的key
   String realKey  = prefix.getPrefix() + key;
   int seconds =  prefix.expireSeconds();
   if(seconds <= 0) {
    jedis.set(realKey, str);
   }else {
    jedis.setex(realKey, seconds, str);
   }
   return true;
         }finally {
    returnToPool(jedis);
         }
    }
    
    /**
     * 判断key是否存在
     * */
    public <T> boolean exists(KeyPrefix prefix, String key) {
         Jedis jedis = null;
         try {
   jedis =  jedisPool.getResource();
            //生成真正的key
   String realKey  = prefix.getPrefix() + key;
            return  jedis.exists(realKey);
         }finally {
    returnToPool(jedis);
         }
    }
    
    /**
     * 删除
     * */
    public boolean delete(KeyPrefix prefix, String key) {
         Jedis jedis = null;
         try {
   jedis =  jedisPool.getResource();
            //生成真正的key
            String realKey  = prefix.getPrefix() + key;
            long ret =  jedis.del(key);
            return ret > 0;
         }finally {
    returnToPool(jedis);
         }
    }
    
    /**
     * 增加值
     * */
    public <T> Long incr(KeyPrefix prefix, String key) {
         Jedis jedis = null;
         try {
   jedis =  jedisPool.getResource();
            //生成真正的key
   String realKey  = prefix.getPrefix() + key;
            return  jedis.incr(realKey);
         }finally {
    returnToPool(jedis);
         }
    }
    
    /**
     * 减少值
     * */
    public <T> Long decr(KeyPrefix prefix, String key) {
         Jedis jedis = null;
         try {
   jedis =  jedisPool.getResource();
            //生成真正的key
   String realKey  = prefix.getPrefix() + key;
            return  jedis.decr(realKey);
         }finally {
    returnToPool(jedis);
         }
    }
    
    private <T> String beanToString(T value) {
        if(value == null) {
            return null;
        }
        Class<?> clazz = value.getClass();
        if(clazz == int.class || clazz == Integer.class) {
   return ""+value;
        }else if(clazz == String.class) {
   return (String)value;
        }else if(clazz == long.class || clazz == Long.class) {
            return ""+value;
        }else {
            return JSON.toJSONString(value);
        }
    }

    @SuppressWarnings("unchecked")
    private <T> T stringToBean(String str, Class<T> clazz) {
        if(str == null || str.length() <= 0 || clazz == null) {
   return null;
        }
        if(clazz == int.class || clazz == Integer.class) {
   return (T)Integer.valueOf(str);
        }else if(clazz == String.class) {
   return (T)str;
        }else if(clazz == long.class || clazz == Long.class) {
            return  (T)Long.valueOf(str);
        }else {
            return JSON.toJavaObject(JSON.parseObject(str), clazz);
        }
    }

    private void returnToPool(Jedis jedis) {
         if(jedis != null) {
   jedis.close();
         }
    }

}

程序猿的必读书籍

第一阶段:

《C语言程序与设计》 《c++进阶宝典》 《Java数据结构和算法》

第二阶段:

《教你怎么不生气》 《老子》 《沉默的愤怒》

第三阶段:

《颈椎病康复指南》 《腰椎间盘突出日常护理》 《强迫症的自我恢复》

第四阶段:

《活着》 在这里插入图片描述 end…

原文地址:https://blog.csdn.net/qq_43517653/article/details/103741824?ops_request_misc=&request_id=&biz_id=&utm_medium=distribute.pc_search_result.none-task-blog-2~all~koosearch~default-10-103741824-null-null.142^v87^koosearch_v1,239^v2^insert_chatgpt&utm_term=java%E4%BC%98%E5%8C%96

本文章网址:https://www.sjxi.cn/detil/35339477bd9b4a16877120fb1ec70990
最新评论
当前未登陆哦
登陆后才可评论哦

湘ICP备2021009447号

×

(穷逼博主)在线接单

QQ: 1164453243

邮箱: abcdsjx@126.com

前端项目代做
前后端分离
Python 爬虫脚本
Java 后台开发
各种脚本编写
服务器搭建
个人博客搭建
Web 应用开发
Chrome 插件编写
Bug 修复