java技巧性技能点
哈哈哈,我胡汉三...不对本博主Fn又回来了,从事开发也几年了,最近看了点东西,闲着一起聊一聊呗,如有误,望指正。
自定义缓存
在现在的项目开发过程中,缓存是必不可少的一部分,无论是redis还是memcached,这些都是一些缓存需要的中间件,通常使用的mybatis,其中也有缓存的参与,因此这一部分的东西掌握是必不可少的,而项目是分布式部署的,那么可以使用JDK本身来自定义一个缓存容器,无论是LRU,FIFO,还是自定义二级缓存,都需要借助容器类来进行实现的,下面是一个可作为本地二级缓存使用的一个类,可自行扩展设计。
package com.fn.test.cache;
import lombok.Data;
import lombok.experimental.Accessors;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
/**
* @Class : CacheManager
* @Description :单个服务器内保存在JDK中,可做为二级缓存使用
* @Author | @Date : Fn-f | 2019-12-01 19:22:01
*/
@Service("cacheManager")
public class CacheManager {
// 日志信息,真实项目中可使用LoggerFactory
private static final Logger LOGGER = Logger.getLogger("CacheManager");
// cacheMap,作为缓存的容器
private Map<String, CacheItem> cache = new HashMap<String, CacheItem>();
/**
* 添加值至本地缓存中
* @param key
* @param val
*/
public void set (String key, Object val) {
set(key, val, null);
}
/**
* 添加值至本地缓存中,含过期时间(默认秒)
* @param key
* @param val
* @param expired
*/
public void set (String key, Object val, Long expired) {
LOGGER.info("cache set -> param: {" + key + ", " + val + ", " + expired + "}");
cache.put(key, new CacheItem().setObj(val).setTime(System.currentTimeMillis()).setExpired(expired));
}
/**
* 获取key对应的值
* @param key
* @return
*/
public Object get (String key) {
LOGGER.info("cache get -> param: {" + key + "}");
CacheItem item = cache.get(key);
if (item != null && (
item.getExpired() == null
||
System.currentTimeMillis() < (item.getExpired() + item.getTime() * 1000L))) {
return item.getObj();
}
cache.remove(key);
return null;
}
/**
* 删除缓存中的key
* @param key
*/
public void del (String key) {
LOGGER.info("cache del -> param: {" + key + "}");
if (cache.get(key) != null) {
cache.remove(key);
}
}
@Data
@Accessors(chain = true)
private class CacheItem {
// 当前存储对象
private Object obj;
// 当前存储时间
private Long time;
// 过期时间段
private Long expired;
}
}
函数式接口
JDK 8 发布已经好几年了,当前JDK12已经出来了,而对于java开发者来说,已经进入吃力学习,不断追赶,但是对于公司来说,大部分公司还是使用的是JDK 8 为主的,当然也有使用JDK 11 及以上版本的,毕竟JDK版本越高,其中无论是开发,还是优化都有一些调整,而目前使用的JDK 8 中的函数式编程,前面有一章节介绍了 Stream 流,这章也可看看,试用一下呗,自我感觉还是可以的,毕竟代码越少,程序越简洁,执行效率可能会更高(这一点先不用着急否认呐,毕竟荣誉代码与简洁代码的执行之间,编译还是优化都有很大的差别的,可以仔细研究呐),可以看看<Java 8 实战>这本书呐
package com.fn.test.util;
import org.springframework.util.CollectionUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
/**
* @Class : StreamUtil
* @Description :JDK 8 lambda使用,可自行扩展
* @Author | @Date : Fn-f | 2019-12-01 19:22:01
*/
public class StreamUtil {
/**
* 满足某种函数返回指定数据集合信息,去重
* @param list
* @param fn
* @param <T>
* @param <R>
* @return
*/
public static <T, R> List<R> ids (List<T> list, Function<T, R> fn) {
return !CollectionUtils.isEmpty(list) ? new ArrayList(list.stream().map(fn).collect(Collectors.toSet())) : Collections.EMPTY_LIST;
}
/**
* 满足某种函数返回K-V信息,groupingBy返回信息为Map<T, List>,可能并非目前所需
* @param list
* @param fn
* @param <T>
* @param <R>
* @return
*/
public static <T, R> Map<T, R> map (List<R> list, Function<R, T> fn) {
return !CollectionUtils.isEmpty(list) ? new ArrayList(list.stream().collect(Collectors.toMap(fn, i -> i)) : Collections.EMPTY_MAP;
}
/**
* 可以指定其他类似
*/
//public static test () { ...
}
位运算(基础)
对于本博主这个非科班出身的程序员来说,这个熟悉,但是开发中使用还是没有太多机会的,当然也做过一些研究,什么补位,反码,补码等等吧,而现在看到其一个具体的应用,自我感觉记录下来,有遇到同样问题的开发人员可以借鉴参考呐
开发过程中,有可能会给某个东西贴标签,又或给某个人以某种身份,当有多中标签或身份时,难道要进行信息值累加吗? 这个时候用位运算是最好不过的。
package com.fn.test.demo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import lombok.experimental.Accessors;
/**
* @Class : ItemInfo
* @Description :商品信息:食物的味觉信息
* @Author | @Date : Fn-f | 2019-12-01 19:22:01
*/
@Data
@Accessors(chain = true)
public class ItemInfo {
private String name;
private String price;
private int tasteval;
private Taste teste;
/**
* 味道信息
*/
@Data
static class Taste {
private boolean sweet;
private boolean acid;
private boolean bitter;
private boolean spicy;
private boolean fragrant;
private boolean smelly;
// 与运算,复boolean制
public Taste (int tasteval) {
this.sweet = (tasteval & TasteEnum.SWEET.getCode()) > 0;
this.acid = (tasteval & TasteEnum.ACID.getCode()) > 0;
this.bitter = (tasteval & TasteEnum.BITTER.getCode()) > 0;
this.spicy = (tasteval & TasteEnum.SMELLY.getCode()) > 0;
this.fragrant = (tasteval & TasteEnum.FRAGRANT.getCode()) > 0;
this.smelly = (tasteval & TasteEnum.SMELLY.getCode()) > 0;
}
}
/**
* 味道枚举信息
*/
@Getter
@AllArgsConstructor
enum TasteEnum {
SWEET ( 1 << 0, "甜"),
ACID ( 1 << 1, "酸"),
BITTER ( 1 << 2, "苦"),
SPICY ( 1 << 3, "辣"),
SALTY ( 1 << 4, "咸"),
FRAGRANT ( 1 << 5, "香"),
SMELLY ( 1 << 6, "臭"),
;
private int code;
private String desc;
}
/**
* 测试
*/
public static void main(String[] args) {
ItemInfo apple = new ItemInfo();
apple.setName("苹果");
apple.setPrice("19.9元");
// 苹果味道:甜酸香
apple.setTasteval(
TasteEnum.SWEET.getCode()
+ TasteEnum.ACID.getCode()
+ TasteEnum.FRAGRANT.getCode());
// 解析味道信息,进行展示等
apple.setTeste(new Taste(apple.getTasteval()));
System.out.println(apple);
// ItemInfo(
// name=苹果, price=19.9元, tasteval=35,
// teste=ItemInfo.Taste(
// sweet=true, acid=true, bitter=false,
// spicy=false, fragrant=true, smelly=false)
// )
}
}
随机数
随机数处理,可以与时间戳一起,增大随机性
import java.util.Random; /** * @Class : Random * @Description :随机数 * @Author | @Date : Fn-f | 2019-12-01 19:10:01 */ public class R { private static final String BASE_NUM = "0123456789"; private static final String BASE_STR = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz"; /** * 获取指定大小的随机串(配合时间点来处理) * * @param base 指定字符源 * @param size 随机串大小 * @param zwst zero with start tag(从零开始标志) * @return String */ public static String random(String base, int size, boolean zwst) { if (size <= 0) { return ""; } StringBuilder s = new StringBuilder(); Random r = new Random(); for (int i = 0; i < size; i++) { char c; do { c = base.charAt(r.nextInt(base.length())); // 首位避免为零判断逻辑 } while (zwst && i == 0 && ((int) '0' == (int) c)); s.append(c); } return s.toString(); } public static void main(String[] args) { System.out.println(random(BASE_NUM, 9, false)); // 189640865 System.out.println(random(BASE_STR, 9, false)); // SvITxDRc3 } }
多线程使用(异步处理)
多线程的用法,有可能需要花费新的章节进行阐述,毕竟体系是十分胖庞大的,可以在项目中使用线程进行异步处理,可以使用线程池或相关的类来进行
比如使用 ThreadPoolManager()、Runnable()、Callable()、CompletableFuture.runAsync()、CompletableFuture.whenComplete()、等等处理形式,这一小节后续放置独立的一篇进行阐述。
枚举的别样用法
枚举中除了日常使用方式之外,其实质上可以当做一个类来进行使用,内部也可以指定方法,相应的属性处理对应的业务逻辑即可,这个也是一个优秀的操作。
视频剪辑工具FFMPEG
当有视频、音频、图片需要处理时,该工具能够帮助你得到自己想要的数据。尝试之前,必须要先安装该工具哟
工具类Hutool
简介:https://gitee.com/loolly/hutool/
Hutool简介:
Hutool是一个小而全的Java工具类库,通过静态方法封装,降低相关API的学习成本,提高工作效率,使Java拥有函数式语言般的优雅,让Java语言也可以“甜甜的”。
Hutool中的工具方法来自于每个用户的精雕细琢,它涵盖了Java开发底层代码中的方方面面,它既是大型项目开发中解决小问题的利器,也是小型项目中的效率担当;
Hutool是项目中“util”包友好的替代,它节省了开发人员对项目中公用类和公用工具方法的封装时间,使开发专注于业务,同时可以最大限度的避免封装不完善带来的bug。
Hutool名称的由来
Hutool = Hu + tool,是原公司项目底层代码剥离后的开源库,“Hu”是公司名称的表示,tool表示工具。Hutool谐音“糊涂”,一方面简洁易懂,一方面寓意“难得糊涂”。
Hutool如何改变我们的coding方式
Hutool的目标是使用一个工具方法代替一段复杂代码,从而最大限度的避免“复制粘贴”代码的问题,彻底改变我们写代码的方式。
以计算MD5为例:
【以前】打开搜索引擎 -> 搜“Java MD5加密” -> 打开某篇博客-> 复制粘贴 -> 改改好用
【现在】引入Hutool -> SecureUtil.md5()
Hutool的存在就是为了减少代码搜索成本,避免网络上参差不齐的代码出现导致的bug。
......
本文章后续不间断增新,望指正。
(愿你的每一行代码,都有让世界进步的力量 ------ fn)
浙公网安备 33010602011771号