精华内容
下载资源
问答
  • java优化java优化

    2008-08-27 21:23:12
    java优化java优化java优化java优化java优化java优化java优化java优化java优化java优化java优化java优化java优化java优化java优化java优化java优化java优化
  • java优化编程 java优化编程 java优化编程 java优化编程 java优化编程 java优化编程 java优化编程 java优化编程 java优化编程 java优化编程
  • JAVA优化编程电子书

    2008-10-24 11:16:18
    JAVA优化编程,JAVA优化编程,JAVA优化编程,JAVA优化编程,JAVA优化编程,JAVA优化编程
  • Java优化编程(第2版)林胜利、王坤茹 pdf 扫描版
  • java优化代码常见套路

    万次阅读 多人点赞 2019-12-28 11:32:03
    程序员的痛点(烂代码) ...首先说一个最重要的优化原则:代码优化是你觉得你代码很繁琐、阅读性很差的时候一定要马上优化,立刻马上,不管你现在有多忙,每天优化才叫重构,每年优化那叫重写 这个原则为什么重...

    程序员的痛点(烂代码)

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

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

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

    该如何优化代码

    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++进阶宝典》
    《Swift入门与实践》

    第二阶段:

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

    第三阶段:

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

    第四阶段:

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

    展开全文
  • java 优化过多的 if else 策略模式

    千次阅读 2019-04-15 10:49:11
    如果不进行优化代码将是这样的: //优化前 @Test public void testNoStrategy(String dsType) { if (dsType.equals("faceRecognition")) { FaceRecognition faceRecognition = new FaceRecognition(); face...

    在编码中经常会遇到过多的 if else ,就会显得代码很臃肿,分支比较多,这种情况肯定是不建议用if-else的,维护起来不是很方便,而且容易出现 bug,下面看下如何解决满屏的 if else;
    我项目中的场景: 我们有一个需求是实名认证,但是实名认证有很多种方式,例如: 人脸识别,实名认证四要素(姓名,身份证,手机号,银行卡,主要针对企业),实名认证三要素(姓名,身份证,手机号), 实名认证二要素(姓名,身份证号)等等…这个是由客户自己选择;如果不进行优化代码将是这样的:

    //优化前
        @Test
        public void testNoStrategy(String dsType) {
            if (dsType.equals("faceRecognition")) {
                FaceRecognition faceRecognition = new FaceRecognition();
                faceRecognition.connect(null);
            } else if (dsType.equals("threeElements")) {
                ThreeElements threeElements = new ThreeElements();
                threeElements.connect(null);
            } else if (dsType.equals("twoElements")) {
                TwoElements twoElements = new TwoElements();
                twoElements.connect(null);
            } else {
                FourElements fourElements = new FourElements();
                fourElements.connect(null);
            }
        }
    

    很臃肿,很难看…

    下面就用反射和策略模式解决一下这种应用场景.

    直接看代码:

    这是我的项目目录结构;下面我们看看每个类中都写了什么?

    DataSourceType
    首先定义了一个注解类DataSourceType:

    Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface DataSourceType {
        //策略名
        String value() default "";
    }
    

    DataSourceStrategy
    声明一个接口

    public interface DataSourceStrategy {
    
        //每个策略的逻辑实现
        Map<String, Object> connect(Map<String, String> params);
    }
    

    下面我们看看几个策略类:

    FaceRecognition
    人脸识别功能,在这里写你的业务逻辑,如果的业务逻辑牵扯到事务的话,建议还是在 service 层写;
    同时这里实现了上面咱们定义的一个接口DataSourceStrategy;
    注意: @DataSourceType("faceRecognition"),这里注意一下,后面我们会用到.

    @Component
    @DataSourceType("faceRecognition")
    public class FaceRecognition implements DataSourceStrategy {
        @Override
        public Map<String, Object> connect(Map<String, String> params) {
            //do something....
    
            //返回结果
            JSONObject json = new JSONObject();
            json.put("type", "人脸识别");
            json.put("status", "success");
            return json;
        }
    }
    
    

    FourElements
    实名认证四要素;和上面的人脸识别是一样的道理;三要素和二要素就不一一列举了…

    @Component
    @DataSourceType("fourElements")
    public class FourElements implements DataSourceStrategy {
        @Override
        public Map<String, Object> connect(Map<String, String> params) {
            //do something....
            //返回结果
            JSONObject json = new JSONObject();
            json.put("type", "四要素");
            json.put("status", "success");
            return json;
        }
    }
    

    下面我们看下这个工具类做了什么?
    CacheCollection

    public class CacheCollection {
    
        private static Map<String, Class> ALL_DATA_SOURCE;
    
        static {
            ALL_DATA_SOURCE = new HashMap<>(100);
        }
    
        /**
         * 根据策略名获取 Class
         * @param dsType
         * @return
         */
        public static Class getDS(String dsType) {
            return ALL_DATA_SOURCE.get(dsType);
        }
        /**
         * 策略名为 key,Class 为 value
         */
        public static void putClass(String dsType,Class clazz){
            ALL_DATA_SOURCE.put(dsType,clazz);
        }
    }
    

    声明一个Map容器,很简单就是策略名@DataSourceType("faceRecognition")这个注解中 value值faceRecognition为 key,这个类的Class为value;

    InitDataSource

    @Component
    public class InitDataSource {
    
        @Value("${haoxy.package}")
        private String packageVo;
    
        @PostConstruct
        public void init() {
            initAllDataSourceType(packageVo);
        }
    
        private void initAllDataSourceType(String packageVo) {
            URL url = this.getClass().getClassLoader().getResource(packageVo.replace(".", "/"));
            File dir = new File(url.getFile());
            for (File file : dir.listFiles()) {
                if (file.isDirectory()) {
                    continue;
                } else {
                    String className = packageVo + "." + file.getName().replace(".class", "");
                    try {
                        Class<?> clazz = Class.forName(className);// class cn.haoxy.ref.inter.impl.faceRecognition
                        //判断这个类上是否存在指定的注解
                        if (clazz.isAnnotationPresent(DataSourceType.class)) {
                            //如果存在,得到此注解的value值
                            DataSourceType dataSourceType = clazz.getAnnotation(DataSourceType.class);
                            //放入容器
                            CacheCollection.putClass(dataSourceType.value(), clazz);
                        }
                    } catch (ClassNotFoundException e) {
                        e.printStackTrace();
                        continue;
                    }
                }
            }
        }
    }
    
    

    项目在初始化的时候会执行init()方法,这里用到了反射,利用反射检测策略类上是否含有@DataSourceType这个注解,如果存在这个注解我们就拿到这个注解上的值,注解上的值为 key,Class为 value 存入Map容器中,其中packageVo是我在配置文件(application.yml)中配置的值,也就是策略类所在包的位置;

    application.yml

    server:
      port: 9999
    haoxy.package: cn.haoxy.strategy.operation
    

    DataSourceService

    @Component
    public class DataSourceService {
    
        @Autowired
        private DataSourceContextAware dataSourceContextAware;
    
        public Map<String, Object> connect(String dsType, Map<String, String> map) {
            DataSourceStrategy dataSourceChild = dataSourceContextAware.getStrategyInstance(dsType);
            return dataSourceChild.connect(map);
        }
    }
    

    这个类也就是下面测试类要调用的;这里先贴出来; 其中dataSourceContextAware.getStrategyInstance(dsType)这个方法就是我们去Map容器中去取值,dsType就是下面测试类要传入的值;Map<String, String> map这个 map是我们需要的其他参数,下面我们贴出这个方法具体的代码;

    DataSourceContextAware

    @Component
    public class DataSourceContextAware implements ApplicationContextAware {
    
        private ApplicationContext applicationContext;
    
        public DataSourceStrategy getStrategyInstance(String dsType) {
            Class clazz = CacheCollection.getDS(dsType);
            DataSourceStrategy dataSourceStrategy = (DataSourceStrategy) applicationContext.getBean(clazz);
            return dataSourceStrategy;
        }
    
    
        @Override
        public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
            this.applicationContext = applicationContext;
        }
    }
    

    这里我们获取到DataSourceStrategy返回回去,同时调用dataSourceChild.connect(map);至此到这里就结束了,下面我们就贴下测试类;

    	//优化后
        @Test
        public void testOperation(){
            Map<String, Object> resp = dataSourceService.connect("fourElements", null);
            System.out.println(resp);
        }
    

    这里我们传入的是fourElements实名认证四要素;返回结果:
    {"type":"四要素","status":"success"}

    如果后期我们需要其他的实名认证我们只需要增加策略类即可,这里我一个 if else 都没有用,其实现方式就是策略模式结合反射;

    展开全文
  • java优化占用内存的方法(一)

    万次阅读 2015-09-02 19:26:56
    原文出自【雪的痕迹】 原文地址:...其实从理论上来讲java做的系统并不比其他语言开发出来的系统更占用内存,那么为什么却有这么N多理由来证明它确实占内存呢
    原文出自【雪的痕迹】
    
    原文地址: http://www.java3z.com/cwbwebhome/article/article8/852.html

    java做的系统给人的印象是什么?占内存!说道这句话就会有N多人站出来为java辩护,并举出一堆的性能测试报告来证明这一点。其实从理论上来讲java做的系统并不比其他语言开发出来的系统更占用内存,那么为什么却有这么N多理由来证明它确实占内存呢?两个字,陋习。

    (1)别用new Boolean()
    在很多场景中Boolean类型是必须的,比如JDBC中boolean类型的set与get都是通过Boolean封装传递的,大部分ORM也是用Boolean来封装boolean类型的,比如:

    ps.setBoolean("isClosed",new  Boolean(true));
    ps.setBoolean("isClosed",new  Boolean(isClosed));
    ps.setBoolean("isClosed",new Boolean(i==3));

    通常这些系统中构造的Boolean实例的个数是相当多的,所以系统中充满了大量Boolean实例小对象,这是相当消耗内存的。Boolean类实际上只要两个实例就够了,一个true的实例,一个false的实例。

    Boolean类提供两了个静态变量:
    public static final Boolean TRUE = new Boolean(true);
    public static final Boolean FALSE = new Boolean(false);

    需要的时候只要取这两个变量就可以了,
    比如:
    ps.setBoolean("isClosed",Boolean.TRUE);
    那么象2、3句那样要根据一个boolean变量来创建一个Boolean怎么办呢?可以使用Boolean提供的静态方法: 
    Boolean.valueOf()

    比如:
    ps.setBoolean("isClosed",Boolean.valueOf(isClosed));
    ps.setBoolean("isClosed",Boolean.valueOf(i==3));

    因为valueOf的内部实现是:return (b ? TRUE : FALSE);
    所以可以节省大量内存。相信如果Java规范直接把Boolean的构造函数规定成private,就再也不会出现这种情况了。

    (2)别用new Integer
    和Boolean类似,java开发中使用Integer封装int的场合也非常多,并且通常用int表示的数值通常都非常小。SUN 
    SDK中对Integer的实例化进行了优化,Integer类缓存了-128到127这256个状态的Integer,如果使用Integer.valueOf(int 
    i),传入的int范围正好在此内,就返回静态实例。这样如果我们使用Integer.valueOf代替new 
    Integer的话也将大大降低内存的占用。如果您的系统要在不同的SDK(比如IBM 
    SDK)中使用的话,那么可以自己做了工具类封装一下,比如IntegerUtils.valueOf(),这样就可以在任何SDK中都可以使用这种特性。

    (3)用StringBuffer代替字符串相加
    这个我就不多讲了,因为已经被人讲过N次了。我只想将一个不是笑话的笑话,我在看国内某“著名”java开发的WEB系统的源码中,竟然发现其中大量的使用字符串相加,一个拼装SQL语句的方法中竟然最多构造了将近100个string实例。无语中!

    (4)过滥使用哈希表
    有一定开发经验的开发人员经常会使用hash表(hash表在JDK中的一个实现就是HashMap)来缓存一些数据,从而提高系统的运行速度。比如使用HashMap缓存一些物料信息、人员信息等基础资料,这在提高系统速度的同时也加大了系统的内存占用,特别是当缓存的资料比较多的时候。其实我们可以使用操作系统中的缓存的概念来解决这个问题,也就是给被缓存的分配一个一定大小的缓存容器,按照一定的算法淘汰不需要继续缓存的对象,这样一方面会因为进行了对象缓存而提高了系统的运行效率,同时由于缓存容器不是无限制扩大,从而也减少了系统的内存占用。现在有很多开源的缓存实现项目,比如ehcache、oscache等,这些项目都实现了FIFO、MRU等常见的缓存算法。

    (5)避免过深的类层次结构和过深的方法调用
    因为这两者都是非常占用内存的(特别是方法调用更是堆栈空间的消耗大户)。

    (6)变量只有在用到它的时候才定义和实例化。

    (7)尽量避免使用static变量
    类内私有常量可以用final来代替。 

    java内存管理的思想(主要来源于thinking in java)

    Java内存管理特点
    Java一个最大的优点就是取消了指针,由垃圾收集器来自动管理内存的回收。程序员不需要通过调用函数来释放内存。


    1、Java的内存管理就是对象的分配和释放问题。

    在Java中,程序员需要通过关键字new为每个对象申请内存空间 
    (基本类型除外),所有的对象都在堆 
    (Heap)中分配空间。
    对象的释放是由GC决定和执行的。
    在Java中,内存的分配是由程序完成的,而内存的释放是由GC完成的,这种收支两条线的方法简化了程序员的工作。但也加重了JVM的工作。这也是Java程序运行速度较慢的原因之一。

    GC释放空间方法:
    监控每一个对象的运行状态,包括对象的申请、引用、被引用、赋值等。当该对象不再被引用时,释放对象。


    2、内存管理结构
    Java使用有向图的方式进行内存管理,对于程序的每一个时刻,我们都有一个有向图表示JVM的内存分配情况。

    将对象考虑为有向图的顶点,将引用关系考虑为图的有向边,有向边从引用者指向被引对象。另外,每个线程对象可以作为一个图的起始顶点,例如大多程序从main进程开始执行,那么该图就是以main进程顶点开始的一棵根树。在这个有向图中,根顶点可达的对象都是有效对象,GC将不回收这些对象。如果某个对象 
    (连通子图)与这个根顶点不可达(注意,该图为有向图),那么我们认为这个(这些)对象不再被引用,可以被GC回收。 

    3、使用有向图方式管理内存的优缺点
    Java使用有向图的方式进行内存管理,可以消除引用循环的问题,例如有三个对象,相互引用,只要它们和根进程不可达的,那么GC也是可以回收它们的。
    这种方式的优点是管理内存的精度很高,但是效率较低。


    ++:
    另外一种常用的内存管理技术是使用计数器,例如COM模型采用计数器方式管理构件,它与有向图相比,精度行低(很难处理循环引用的问题),但执行效率很高。


    ★ Java的内存泄露
    Java虽然由GC来回收内存,但也是存在泄露问题的,只是比C++小一点。


    1、与C++的比较

    c++所有对象的分配和回收都需要由用户来管理。即需要管理点,也需要管理边。若存在不可达的点,无法回收分配给那个点的内存,导致内存泄露。存在无用的对象引用,自然也会导致内存泄露。
    Java由GC来管理内存回收,GC将回收不可达的对象占用的内存空间。所以,Java需要考虑的内存泄露问题主要是那些被引用但无用的对象——即指要管理边就可以。被引用但无用的对象,程序引用了该对象,但后续不会再使用它。它占用的内存空间就浪费了。
    如果存在对象的引用,这个对象就被定义为“活动的”,同时不会被释放。


    2、Java内存泄露处理

    处理Java的内存泄露问题:确认该对象不再会被使用。
    典型的做法——
    把对象数据成员设为null
    从集合中移除该对象
    注意,当局部变量不需要时,不需明显的设为null,因为一个方法执行完毕时,这些引用会自动被清理。


    例子:

    List myList=new ArrayList();
    for (int i=1;i<100; i++) {
        Object o=new 
        Object();
        myList.add(o);
        o=null;
    }
    //此时,所有的Object对象都没有被释放,因为变量myList引用这些对象。

    当myList后来不再用到,将之设为null,释放所有它引用的对象。之后GC便会回收这些对象占用的内存。


    ★ 对GC操作
    对GC的操作并不一定能达到管理内存的效果。

    GC对于程序员来说基本是透明的,不可见的。我们只有几个函数可以访问GC,例如运行GC的函数System.gc(),System.。
    但是根据Java语言规范定义, 
    System.gc()函数不保证JVM的垃圾收集器一定会执行。因为,不同的JVM实现者可能使用不同的算法管理GC。通常,GC的线程的优先级别较低。

    JVM调用GC的策略有很多种,有的是内存使用到达一定程度时,GC才开始工作,也有定时执行的,有的是平缓执行GC,有的是中断式执行GC。但通常来说,我们不需要关心这些。除非在一些特定的场合,GC的执行影响应用程序的性能,例如对于基于Web的实时系统,如网络游戏等,用户不希望GC突然中断应用程序执行而进行垃圾回收,那么我们需要调整GC的参数,让GC能够通过平缓的方式释放内存,例如将垃圾回收分解为一系列的小步骤执行,Sun提供的HotSpot 
    JVM就支持这一特性。


    ★ 内存泄露检测
    市场上已有几种专业检查Java内存泄漏的工具,它们的基本工作原理大同小异,都是通过监测Java程序运行时,所有对象的申请、释放等动作,将内存管理的所有信息进行统计、分析、可视化。开发人员将根据这些信息判断程序是否有内存泄漏问题。这些工具包括Optimizeit 
    Profiler,JProbe Profiler,JinSight , Rational 公司的Purify等。

    在运行过程中,我们可以随时观察内存的使用情况,通过这种方式,我们可以很快找到那些长期不被释放,并且不再使用的对象。我们通过检查这些对象的生存周期,确认其是否为内存泄露。


    ★ 软引用
    特点:只有当内存不够的时候才回收这类内存,同时又保证在Java抛出OutOfMemory异常之前,被设置为null。
    保证最大限度的使用内存而不引起OutOfMemory异常。
    在某些时候对软引用的使用会降低应用的运行效率与性能,例如:应用软引用的对象的初始化过程较为耗时,或者对象的状态在程序的运行过程中发生了变化,都会给重新创建对象与初始化对象带来不同程度的麻烦。


    用途:

    可以用于实现一些常用资源的缓存,实现Cache的功能
    处理一些占用内存大而且声明周期较长,但使用并不频繁的对象时应尽量应用该技术


    ★ java程序设计中有关内存管理的经验

    1.最基本的建议是尽早释放无用对象的引用。如:...
    A a = new A();
    //应用a对象
    a = null; //当使用对象a之后主动将其设置为空
    ….
    注:如果a 是方法的返回值,不要做这样的处理,否则你从该方法中得到的返回值永远为空,而且这种错误不易被发现、排除
     
    2.尽量少用finalize函数。它会加大GC的工作量。
    3.如果需要使用经常用到的图片,可以使用soft应用类型。它尽可能把图片保存在内存中
    4.注意集合数据类型,包括数组、树、图、链表等数据结构,这些数据结构对GC来说,回收更为复杂。
    5.尽量避免在类的默认构造器中创建、初始化大量的对象,防止在调用其自类的构造器时造成不必要的内存资源浪费
    6.尽量避免强制系统做垃圾内8.尽量做远程方法调用类应用开发时使用瞬间值变量,除非远程调用端需要获取该瞬间值变量的值。
    9.尽量在合适的场景下使用对象池技术以提高系统性能。存的回收,增长系统做垃圾回收的最终时间
    7.尽量避免显式申请数组空间

    展开全文

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 832,522
精华内容 333,008
关键字:

java优化

java 订阅