首页
统计
留言板
直播
更多
壁纸
推荐
Git仓库
Search
1
IntelliJ IDEA激活 - 至2022年10月 (持续更新)
645 阅读
2
GitLab企业版搭建(附生成证书)
626 阅读
3
淘宝 京东秒杀脚本
422 阅读
4
Groovy模板引擎 API 构建动态脚本
324 阅读
5
欢迎使用 Typecho
294 阅读
杂货间
开发
Java
JavaScript
Android
JQuery
MySQL
PHP
Groovy
Git
运维
CentOS
Red Heat
Ubuntu
Debian
运行环境
登录
/
注册
Search
标签搜索
开发
Java
Android
MySQL8
CentOS
CentOS8
Linux
Git
Swing
JavaScript
JQuery
MySQL
临时手机号
IDEA
Steam
YouTube
订阅
激活码
GitLab
nginx
Dotdotmaples
累计撰写
30
篇文章
累计收到
7
条评论
首页
栏目
杂货间
开发
Java
JavaScript
Android
JQuery
MySQL
PHP
Groovy
Git
运维
CentOS
Red Heat
Ubuntu
Debian
运行环境
页面
统计
留言板
直播
壁纸
推荐
Git仓库
搜索到
11
篇与
Java
的结果
Java 工具类
将一个类中的值通过setter和getter传递给另一个类型public static <T> T entityToDto(Object entity, Class<T> dtoType) { T instance; try { instance = dtoType.getDeclaredConstructor().newInstance(); } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) { throw new RuntimeException(e); } // 反射entity 获取getter Class<?> entryType = entity.getClass(); Arrays.stream(dtoType.getMethods()).filter( method -> method.getName().startsWith("set") ).forEach(method -> { String methodName = method.getName().replace("set", "get"); try { Object value = entryType.getMethod(methodName).invoke(entity); method.invoke(instance, value); } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException ignore) {} }); return instance; }
2022年05月12日
105 阅读
0 评论
1 点赞
2020-05-28
使用Youtube官方API订阅功能的实现
在开篇之前,请允许我引用并重新组织一位博主的言语:若想实现对一个YouTube频道的订阅功能,目前所知有两种方法。 1. 采用直接而笨重的轮询机制 - 即每隔一段时间去请求频道下的视频接口,根据返回的视频名称判断是否是新视频。这样做的缺点显而易见,一是除非轮询时间间隔特别短,否则基本没法保证时效性。二是频繁的访问查询接口会浪费掉大量的api 配额,因此这不是一种优雅的解决方案。 2. 使用官方的发布订阅系统 - 一种基于Webhooks实现的订阅推送(对于Webhooks机制不清楚的同学可以了解后再去尝试),可以实现几乎实时的更新推送部分内容引用自以下博客,本篇博客也就是对以下文章中第三点(订阅功能实现)的补充说明https://blog.csdn.net/zzz_zjz/article/details/105006921 详细资料参考官方的这篇文档 https://developers.google.com/youtube/v3/guides/push_notifications 其主要流程就是,在下面这个网址中添加订阅频道和回调地址 https://pubsubhubbub.appspot.com/subscribe这样你就会在你的服务器上接收到这样的更新信息<feed xmlns:yt="http://www.youtube.com/xml/schemas/2015" xmlns="http://www.w3.org/2005/Atom"> <link rel="hub" href="https://pubsubhubbub.appspot.com"/> <link rel="self" href="https://www.youtube.com/xml/feeds/videos.xml?channel_id=CHANNEL_ID"/> <title>YouTube video feed</title> <updated>2015-04-01T19:05:24.552394234+00:00</updated> <entry> <id>yt:video:VIDEO_ID</id> <yt:videoId>VIDEO_ID</yt:videoId> <yt:channelId>CHANNEL_ID</yt:channelId> <title>Video title</title> <link rel="alternate" href="http://www.youtube.com/watch?v=VIDEO_ID"/> <author> <name>Channel title</name> <uri>http://www.youtube.com/channel/CHANNEL_ID</uri> </author> <published>2015-03-06T21:40:57+00:00</published> <updated>2015-03-09T19:05:24.552394234+00:00</updated> </entry> </feed>除以上资料外并没有更多像具体怎么实现的信息了,我想看到这像我一样的Java新手来言简直就是一头雾水。 为此我在此贴上我为时两个星期的测试代码(还是因为公司事务较忙,没空去查看日志所致) 以下代码只做测试、研究使用,生产环境勿用import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import java.io.BufferedReader; import java.io.IOException; import java.util.Enumeration; import java.util.Map; /** * @author 枫铃也 * @time 2020-05-14 14:59 * @description YouTube订阅服务接口 https://pubsubhubbub.appspot.com/subscribe */ @RestController @RequestMapping("/system/youtube") @Api(tags = "YouTubePubSubHubbubController") public class YouTubePubSubHubbubController { private static final Logger LOGGER = LoggerFactory.getLogger(YouTubePubSubHubbubController.class); @RequestMapping(value = "subscribe", method = {RequestMethod.GET, RequestMethod.POST}) @ApiOperation(value = "YouTube订阅认证接口", notes = "YouTube订阅认证接口") public String subscribe(HttpServletRequest request, @RequestHeader Map<String, String> headers) { LOGGER.info("----------------打印请求全部消息开始----------------"); Enumeration<String> headerNames = request.getHeaderNames(); while (headerNames.hasMoreElements()) { String name = headerNames.nextElement(); LOGGER.info("{} - {}", name, request.getHeader(name)); } LOGGER.info("----------------打印请求全部消息结束----------------"); LOGGER.info("-----------------打印请求头消息开始-----------------"); headers.forEach((String key, String value) -> LOGGER.info(String.format("Header '%s' = %s", key, value))); LOGGER.info("-----------------打印请求头消息结束-----------------"); Map<String, String[]> parameterMap = request.getParameterMap(); LOGGER.info("------------------打印全部参数开始------------------"); String result = ""; if (parameterMap.isEmpty()) { LOGGER.info("请求参数为空"); }else { parameterMap.forEach((String key, String[] value) -> { for (String s : value) { LOGGER.info("Param '{}' = {}", key, s); } }); result = parameterMap.get("hub.challenge")[0]; } LOGGER.info("------------------打印全部参数结束------------------"); LOGGER.info("-----------------打印Reader数据开始----------------"); StringBuilder buffer = new StringBuilder(); try (BufferedReader reader = request.getReader()){ String line; while ((line = reader.readLine()) != null) { buffer.append(line); } } catch (IOException e) { LOGGER.error(e.getMessage(), e); } LOGGER.info("reader -> {}", buffer); LOGGER.info("-----------------打印Reader数据结束----------------"); return result; } } 请大家忽略以上代码中的日志和API文档生成的辅助类,并附上实践 具体 完装的日志输出09:39:30.330 [http-nio-4930-exec-7] INFO o.f.m.i.y.c.s.YouTubePubSubHubbubController - ----------------打印请求全部消息开始---------------- 09:39:30.330 [http-nio-4930-exec-7] INFO o.f.m.i.y.c.s.YouTubePubSubHubbubController - host - 这个我不能给你们看,嘿嘿嘿 09:39:30.330 [http-nio-4930-exec-7] INFO o.f.m.i.y.c.s.YouTubePubSubHubbubController - link - <https://www.youtube.com/xml/feeds/videos.xml?channel_id=UCXQexglLCaJyTImYLmSO9Ng>; rel=self, <http://pubsubhubbub.appspot.com/>; rel=hub 09:39:30.330 [http-nio-4930-exec-7] INFO o.f.m.i.y.c.s.YouTubePubSubHubbubController - content-type - application/atom+xml 09:39:30.330 [http-nio-4930-exec-7] INFO o.f.m.i.y.c.s.YouTubePubSubHubbubController - cache-control - no-cache,max-age=0 09:39:30.330 [http-nio-4930-exec-7] INFO o.f.m.i.y.c.s.YouTubePubSubHubbubController - pragma - no-cache 09:39:30.330 [http-nio-4930-exec-7] INFO o.f.m.i.y.c.s.YouTubePubSubHubbubController - content-length - 943 09:39:30.330 [http-nio-4930-exec-7] INFO o.f.m.i.y.c.s.YouTubePubSubHubbubController - connection - keep-alive 09:39:30.330 [http-nio-4930-exec-7] INFO o.f.m.i.y.c.s.YouTubePubSubHubbubController - accept - */* 09:39:30.330 [http-nio-4930-exec-7] INFO o.f.m.i.y.c.s.YouTubePubSubHubbubController - from - googlebot(at)googlebot.com 09:39:30.330 [http-nio-4930-exec-7] INFO o.f.m.i.y.c.s.YouTubePubSubHubbubController - user-agent - FeedFetcher-Google; (+http://www.google.com/feedfetcher.html) 09:39:30.330 [http-nio-4930-exec-7] INFO o.f.m.i.y.c.s.YouTubePubSubHubbubController - accept-encoding - gzip,deflate,br 09:39:30.330 [http-nio-4930-exec-7] INFO o.f.m.i.y.c.s.YouTubePubSubHubbubController - ----------------打印请求全部消息结束---------------- 09:39:30.330 [http-nio-4930-exec-7] INFO o.f.m.i.y.c.s.YouTubePubSubHubbubController - -----------------打印请求头消息开始----------------- 09:39:30.331 [http-nio-4930-exec-7] INFO o.f.m.i.y.c.s.YouTubePubSubHubbubController - Header 'host' = 这个我不能给你们看,嘿嘿嘿 09:39:30.331 [http-nio-4930-exec-7] INFO o.f.m.i.y.c.s.YouTubePubSubHubbubController - Header 'link' = <https://www.youtube.com/xml/feeds/videos.xml?channel_id=UCXQexglLCaJyTImYLmSO9Ng>; rel=self, <http://pubsubhubbub.appspot.com/>; rel=hub 09:39:30.331 [http-nio-4930-exec-7] INFO o.f.m.i.y.c.s.YouTubePubSubHubbubController - Header 'content-type' = application/atom+xml 09:39:30.331 [http-nio-4930-exec-7] INFO o.f.m.i.y.c.s.YouTubePubSubHubbubController - Header 'cache-control' = no-cache,max-age=0 09:39:30.331 [http-nio-4930-exec-7] INFO o.f.m.i.y.c.s.YouTubePubSubHubbubController - Header 'pragma' = no-cache 09:39:30.331 [http-nio-4930-exec-7] INFO o.f.m.i.y.c.s.YouTubePubSubHubbubController - Header 'content-length' = 943 09:39:30.331 [http-nio-4930-exec-7] INFO o.f.m.i.y.c.s.YouTubePubSubHubbubController - Header 'connection' = keep-alive 09:39:30.331 [http-nio-4930-exec-7] INFO o.f.m.i.y.c.s.YouTubePubSubHubbubController - Header 'accept' = */* 09:39:30.331 [http-nio-4930-exec-7] INFO o.f.m.i.y.c.s.YouTubePubSubHubbubController - Header 'from' = googlebot(at)googlebot.com 09:39:30.331 [http-nio-4930-exec-7] INFO o.f.m.i.y.c.s.YouTubePubSubHubbubController - Header 'user-agent' = FeedFetcher-Google; (+http://www.google.com/feedfetcher.html) 09:39:30.331 [http-nio-4930-exec-7] INFO o.f.m.i.y.c.s.YouTubePubSubHubbubController - Header 'accept-encoding' = gzip,deflate,br 09:39:30.331 [http-nio-4930-exec-7] INFO o.f.m.i.y.c.s.YouTubePubSubHubbubController - -----------------打印请求头消息结束----------------- 09:39:30.331 [http-nio-4930-exec-7] INFO o.f.m.i.y.c.s.YouTubePubSubHubbubController - ------------------打印全部参数开始------------------ 09:39:30.331 [http-nio-4930-exec-7] INFO o.f.m.i.y.c.s.YouTubePubSubHubbubController - 请求参数为空 09:39:30.331 [http-nio-4930-exec-7] INFO o.f.m.i.y.c.s.YouTubePubSubHubbubController - ------------------打印全部参数结束------------------ 09:39:30.332 [http-nio-4930-exec-7] INFO o.f.m.i.y.c.s.YouTubePubSubHubbubController - -----------------打印Reader数据开始---------------- 09:39:30.332 [http-nio-4930-exec-7] INFO o.f.m.i.y.c.s.YouTubePubSubHubbubController - reader -> <?xml version='1.0' encoding='UTF-8'?><feed xmlns:yt="http://www.youtube.com/xml/schemas/2015" xmlns="http://www.w3.org/2005/Atom"><link rel="hub" href="https://pubsubhubbub.appspot.com"/><link rel="self" href="https://www.youtube.com/xml/feeds/videos.xml?channel_id=UCXQexglLCaJyTImYLmSO9Ng"/><title>YouTube video feed</title><updated>2020-05-28T01:39:27.887188153+00:00</updated><entry> <id>yt:video:GRrUhlU34GM</id> <yt:videoId>GRrUhlU34GM</yt:videoId> <yt:channelId>UCXQexglLCaJyTImYLmSO9Ng</yt:channelId> <title>Dota2 - Team Nigma vs. Gambit - Game 3 - ESL One Birmingham 2020 - Group B - EU/CIS</title> <link rel="alternate" href="https://www.youtube.com/watch?v=GRrUhlU34GM"/> <author> <name>ESL Archives</name> <uri>https://www.youtube.com/channel/UCXQexglLCaJyTImYLmSO9Ng</uri> </author> <published>2020-05-28T01:36:33+00:00</published> <updated>2020-05-28T01:39:27.887188153+00:00</updated> </entry></feed> 09:39:30.332 [http-nio-4930-exec-7] INFO o.f.m.i.y.c.s.YouTubePubSubHubbubController - -----------------打印Reader数据结束----------------一行字数太多,若换行显示或不方便查看大家则可以复制到Notpad++或其他编辑器缩放显示 通过以上日志输出可以看出此订阅方式的参数并非是传统的from表单形式。到此,具体的代码实现也就没什么可写的了。
2020年05月28日
271 阅读
4 评论
1 点赞
2020-04-07
Steam第三方登陆
来源申明:https://weibo.com/p/1001603922680121320645生成跳转到Steam登录页面的源码开始//Steam使用openid登录的网址 final static String STEAM_LOGIN = "https://steamcommunity.com/openid/login"; public static String getUrl(String returnTo) throws UnsupportedEncodingException { Map<String, String> params = new HashMap<String, String>(); params = new HashMap<String, String>(); params.put("openid.ns", "//specs.openid.net/auth/2.0"); params.put("openid.mode", "checkid_setup"); //登陆成功后要返回url,值得一说的是这个url是可以携带参数的 params.put("openid.return_to", returnTo); //realm的中文解释是领域与范围,我想大概就是你网站域名,授权用户登录你域名下的应用,我这里默认就用传过来的returnTo了 params.put("openid.realm", returnTo); params.put("openid.identity", "//specs.openid.net/auth/2.0/identifier_select"); params.put("openid.claimed_id", "//specs.openid.net/auth/2.0/identifier_select"); return STEAM_LOGIN + "?" + SteamLoginUtil.getUrlParamsByMap(params); } /** * 将url中传递参数转化为map 其中值进行encode * * @param param * aa=11&bb=22&cc=33 * @return * @throws UnsupportedEncodingException */ public static Map<String, String> getUrlParams(String param) throws UnsupportedEncodingException { Map<String, String> map = new HashMap<String, String>(0); if (StringUtils.isBlank(param)) { return map; } String[] params = param.split("&"); for (int i = 0; i < params.length; i++) { String[] p = params[ i ].split("="); if (p.length == 2) { map.put(p[0], URLDecoder.decode(p[ 1 ],"UTF-8")); } } return map; }生成跳转到Steam登录页面的源码结束OK,用上述的代码,传入一个returnTo的参数,就生成一串URL。浏览器打开这个URL就能看见类似如下的画面 表明Steam可以授权给你登录,只要用户登录成功,steam就会跳转到你设置的returnTo地址,并且携带一大波参数。 虽然返回给你了这些参数,用户还不能立即得到登录授权,为什么呢(如果我现在直接给你授权,我返回给你的参数都是明文的,那岂不是任何一个人都能伪造出这么一串参数来登录你的系统,你咋能放心呢)。其实这个时候openid的机制已经记住了这个用户要登录你的系统,但是你的系统还要确认一下,跟openID说这个用户确实是从我这里发出来的登录请求,我请求你允许他登录我的系统。 怎样“请求你~允许他~登录我的系统”呢? 这里用了HttpClient去请求openid的提供者。这个过程用户是看不到的,透明的。 就相当于你的网站和openid提供者说了句悄悄话。说的就是“请求你~允许他~登录我的系统”。而openid的提供者一查,这用户刚才亮出了身份,确实跟我过说要登录你的系统,OK给你俩授权。你俩自己聊去吧! 过程是这样,代码在这里。HttpClient授权验证代码/** * 将steam返回的request参数再次提交steam进行授权,检查是否成功登录,返回steamid算成功,返回空字符串为不成功 * * @param request * @return boolean * @throws ClientProtocolException * @throws IOException */ public static String validate(Map<String,String> request) throws ClientProtocolException, IOException{ //openid.signed这里面的参数用是“,”号隔开的,是提示你返回了哪些参数 Object signed = request.get("openid.signed"); //如果没有openid.signed,那肯定这个请求是不正确的直接跳出即可 if(signed ==null || "".equals(signed)){ return ""; } //此处开始构造HttpClient对象,配置参数,设置访问方法,获取返回值等,进行一次完整访问 HttpClient httpclient = HttpClients.createDefault(); HttpPost httppost = new HttpPost(STEAM_LOGIN+"?"+SteamLoginUtil.getUrlParamsByMap(request)); List<NameValuePair> nvps = new ArrayList<NameValuePair>(); String[] signeds = signed.toString().split(","); for(int i=0;i<signeds.length;i++){ String val = request.get("openid."+signeds[ i ]); nvps.add(new BasicNameValuePair("openid."+signeds[ i ], val==null?"":val)); } nvps.add(new BasicNameValuePair("openid.mode", "check_authentication")); httppost.setEntity(new UrlEncodedFormEntity(nvps)); HttpResponse response = httpclient.execute(httppost); HttpEntity entity = response.getEntity(); if (entity == null) { return ""; } InputStream instreams = entity.getContent(); String result = SteamLoginUtil.convertStreamToString(instreams); //System.out.println("Do something"); System.out.println(result); // Do not need the rest httppost.abort(); //此处是为了将steamid截取出来 String steamid = ""; steamid = request.get("openid.claimed_id"); steamid = steamid.replace("//steamcommunity.com/openid/id/",""); //虽然steamid能从上一次请求参数中截取出来,我们还是要判断HttpClient返回来的消息是否授权了,判断方式是看字符串中是否含有“is_valid:true”,有就是授权成功了,如果没有,就是“is_valid:false”。 if(!result.contains("is_valid:true")){ return ""; } return steamid; } /** * 将输入流读取为一串字符串 * @param is * @return */ public static String convertStreamToString(InputStream is) { BufferedReader reader = new BufferedReader(new InputStreamReader(is)); StringBuilder sb = new StringBuilder(); String line = null; try { while ((line = reader.readLine()) != null) { sb.append(line + "\n"); } } catch (IOException e) { e.printStackTrace(); } finally { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } return sb.toString(); }HttpClient授权验证代码结束验证成功消息如下图源码总结如下import java.util.HashMap; import java.util.Iterator; import java.util.Map.Entry; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; public class StringUtils { public static String parseWhereString (HashMap<String, Object> params,String alias){ StringBuffer buffer = new StringBuffer(); if(params == null || params.isEmpty()){ return ""; }else{ buffer.append(" where "); Set<Entry<String, Object>> set = params.entrySet(); Iterator<Entry<String, Object>> iterator = set.iterator(); Entry<String, Object> entry = null; while(iterator.hasNext()){ buffer.append(" and "); entry = iterator.next(); buffer.append(alias); if (!alias.equals("")) { buffer.append("."); } buffer.append(entry.getKey()); buffer.append("="); buffer.append(" ? "); entry = null; } iterator = null; String temp = buffer.toString(); int location = temp.indexOf("and"); buffer = new StringBuffer(); buffer.append(temp.substring(0, location)); buffer.append(temp.substring(location+4)); temp = null; return buffer.toString(); } } /** * 去掉开头结尾制定字符串 * @param stream * @param trimstr * @return */ public static String sideTrim(String stream, String trimstr) { // null或者空字符串的时候不处理 if (stream == null || stream.length() == 0 || trimstr == null || trimstr.length() == 0) { return stream; } // 结束位置 int epos = 0; // 正规表达式 String regpattern = "[" + trimstr + "]*+"; Pattern pattern = Pattern.compile(regpattern, Pattern.CASE_INSENSITIVE); // 去掉结尾的指定字符 StringBuffer buffer = new StringBuffer(stream).reverse(); Matcher matcher = pattern.matcher(buffer); if (matcher.lookingAt()) { epos = matcher.end(); stream = new StringBuffer(buffer.substring(epos)).reverse().toString(); } // 去掉开头的指定字符 matcher = pattern.matcher(stream); if (matcher.lookingAt()) { epos = matcher.end(); stream = stream.substring(epos); } // 返回处理后的字符串 return stream; } /** * <p>Checks if a String is whitespace, empty ("") or null.</p> * * <pre> * StringUtils.isBlank(null) = true * StringUtils.isBlank("") = true * StringUtils.isBlank(" ") = true * StringUtils.isBlank("bob") = false * StringUtils.isBlank(" bob ") = false * </pre> * * @param str the String to check, may be null * @return <code>true</code> if the String is null, empty or whitespace * @since 2.0 */ public static boolean isBlank(String str) { int strLen; if (str == null || (strLen = str.length()) == 0) { return true; } for (int i = 0; i < strLen; i++) { if ((Character.isWhitespace(str.charAt(i)) == false)) { return false; } } return true; } /** * <p>Gets the substring before the last occurrence of a separator. * The separator is not returned.</p> * * <p>A <code>null</code> string input will return <code>null</code>. * An empty ("") string input will return the empty string. * An empty or <code>null</code> separator will return the input string.</p> * * <pre> * StringUtils.substringBeforeLast(null, *) = null * StringUtils.substringBeforeLast("", *) = "" * StringUtils.substringBeforeLast("abcba", "b") = "abc" * StringUtils.substringBeforeLast("abc", "c") = "ab" * StringUtils.substringBeforeLast("a", "a") = "" * StringUtils.substringBeforeLast("a", "z") = "a" * StringUtils.substringBeforeLast("a", null) = "a" * StringUtils.substringBeforeLast("a", "") = "a" * </pre> * * @param str the String to get a substring from, may be null * @param separator the String to search for, may be null * @return the substring before the last occurrence of the separator, * <code>null</code> if null String input * @since 2.0 */ public static String substringBeforeLast(String str, String separator) { if (isEmpty(str) || isEmpty(separator)) { return str; } int pos = str.lastIndexOf(separator); if (pos == -1) { return str; } return str.substring(0, pos); } // Empty checks //----------------------------------------------------------------------- /** * <p>Checks if a String is empty ("") or null.</p> * * <pre> * StringUtils.isEmpty(null) = true * StringUtils.isEmpty("") = true * StringUtils.isEmpty(" ") = false * StringUtils.isEmpty("bob") = false * StringUtils.isEmpty(" bob ") = false * </pre> * * <p>NOTE: This method changed in Lang version 2.0. * It no longer trims the String. * That functionality is available in isBlank().</p> * * @param str the String to check, may be null * @return <code>true</code> if the String is empty or null */ public static boolean isEmpty(String str) { return str == null || str.length() == 0; } }import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.message.BasicNameValuePair; public class SteamLoginUtil { final static String STEAM_LOGIN = "https://steamcommunity.com/openid/login"; /** * 接入steam登录url * * @param returnTo * @param request * @return * @throws UnsupportedEncodingException */ public static String getUrl(String returnTo) throws UnsupportedEncodingException { Map<String, String> params = new HashMap<String, String>(); params = new HashMap<String, String>(); // String loginTicket = request.getAttribute("loginTicket")==null ? "" : // "?lt="+ request.getAttribute("loginTicket").toString(); // params.put("lt", request.getAttribute("loginTicket")==null // ?"":request.getAttribute("loginTicket").toString()); params.put("openid.ns", "http://specs.openid.net/auth/2.0"); params.put("openid.mode", "checkid_setup"); params.put("openid.return_to", returnTo); params.put("openid.realm", returnTo); params.put("openid.identity", "http://specs.openid.net/auth/2.0/identifier_select"); params.put("openid.claimed_id", "http://specs.openid.net/auth/2.0/identifier_select"); return STEAM_LOGIN + "?" + SteamLoginUtil.getUrlParamsByMap(params); } /** * 将数据提交steam进行验证,是否成功登录 * * @param request * @return boolean * @throws ClientProtocolException * @throws IOException */ public static String validate(Map<String, String> request){ RequestConfig defaultRequestConfig = RequestConfig.custom() .setSocketTimeout(5000) .setConnectTimeout(5000) .setConnectionRequestTimeout(5000) .build(); CloseableHttpClient httpclient = null; HttpPost httppost = null; try { Object signed = request.get("openid.signed"); if (signed == null || "".equals(signed)) { return ""; } httpclient = HttpClients.createDefault(); httppost = new HttpPost(STEAM_LOGIN + "?" + SteamLoginUtil.getUrlParamsByMap(request)); httppost.setConfig(defaultRequestConfig); List<NameValuePair> nvps = new ArrayList<NameValuePair>(); String[] signeds = signed.toString().split(","); for (int i = 0; i < signeds.length; i++) { String val = request.get("openid." + signeds[i]); nvps.add(new BasicNameValuePair("openid." + signeds[i], val == null ? "" : val)); } nvps.add(new BasicNameValuePair("openid.mode", "check_authentication")); httppost.setEntity(new UrlEncodedFormEntity(nvps)); HttpResponse response = httpclient.execute(httppost); HttpEntity entity = response.getEntity(); if (entity == null) { return ""; } InputStream instreams = entity.getContent(); String result = SteamLoginUtil.convertStreamToString(instreams); // Do not need the rest httppost.abort(); String steamid = ""; steamid = request.get("openid.claimed_id"); steamid = steamid.replace("https://steamcommunity.com/openid/id/", ""); if (!result.contains("is_valid:true")) { return ""; } return steamid; } catch (ClientProtocolException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally{ if(httppost != null){ httppost.releaseConnection(); } if(httpclient != null){ try { httpclient.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } return ""; } /** * 将url中传递参数转化为map 其中值进行encode * * @param param * aa=11&bb=22&cc=33 * @return * @throws UnsupportedEncodingException */ public static Map<String, String> getUrlParams(String param) throws UnsupportedEncodingException { Map<String, String> map = new HashMap<String, String>(0); if (StringUtils.isBlank(param)) { return map; } String[] params = param.split("&"); for (int i = 0; i < params.length; i++) { String[] p = params[i].split("="); if (p.length == 2) { map.put(p[0], URLDecoder.decode(p[1], "UTF-8")); } } return map; } /** * 将map转化为url可携带的参数字符串 * * @param map * @return * @throws UnsupportedEncodingException */ public static String getUrlParamsByMap(Map<String, String> map) throws UnsupportedEncodingException { if (map == null) { return ""; } StringBuffer sb = new StringBuffer(); for (Map.Entry<String, String> entry : map.entrySet()) { // 解码 sb.append(entry.getKey() + "=" + URLEncoder.encode(entry == null ? "" : entry.getValue(), "UTF-8")); sb.append("&"); } String s = sb.toString(); if (s.endsWith("&")) { s = StringUtils.substringBeforeLast(s, "&"); } return s; } public static String convertStreamToString(InputStream is) { BufferedReader reader = new BufferedReader(new InputStreamReader(is)); StringBuilder sb = new StringBuilder(); String line = null; try { while ((line = reader.readLine()) != null) { sb.append(line + "\n"); } } catch (IOException e) { e.printStackTrace(); } finally { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } return sb.toString(); } }
2020年04月07日
209 阅读
0 评论
0 点赞
计算Map初始容量算法
在实际开发中经常需要使用到Map,但给Map一个怎样的初始容量比较合理?众所周知是最接近(要大于)数据长度的2的N次方那么,如何计算?欢迎指出错误/** * 计算Map初始容量 * @author 枫铃也 * @since 2019-10-09 15:51 */ public class CapacityUtil { private static final double MODULE = 2; public static int getCapacityFactor(int size) { int n = size; int power = 0; while (n > 1) { power++; n >>>= 1; } int capacity = (int) Math.pow(MODULE, power); if (capacity < size || capacity % MODULE != 0) { capacity *= MODULE; } return capacity; } }欢迎指出错误
2019年10月11日
116 阅读
0 评论
1 点赞
2019-10-10
Java阿拉伯数字转中文数字
带单位转换的我只将数字范围定在十万以内的正数并且没考虑 0需要向上调整范围的童鞋自己修改代码呦欢迎指出错误先看效果再看代码不多说屁话上代码/** * <p>@description: 阿拉伯数字转中文数字(不带单位) </p> * <p>@method: numberConvertToBaseChinese </p> * <p>@author: 枫铃也 </p> * <p>@since: 2019-10-10 16:03 </p> * * <p>@return java.lang.String </p> */ public static String numberConvertToBaseChinese(int num) { StringBuffer buffer = new StringBuffer(); char[] nums = {'零', '一', '二', '三', '四', '五', '六', '七', '八', '九'}; char[] baseChar = Integer.toString(num).toCharArray(); for (char c : baseChar) { buffer.append(nums[c - 48]); } return buffer.toString(); } /** * <p>@description: 阿拉伯数字转中文数字(带单位) </p> * <p>@method: numberConvertToChinese </p> * <p>@author: 枫铃也 </p> * <p>@since: 2019-10-10 16:03 </p> * * <p>@return java.lang.String </p> */ public static String numberConvertToChinese(int num) { char[] nums = {'零', '一', '二', '三', '四', '五', '六', '七', '八', '九'}; char[] units = {' ', '十', '百', '千', '万'}; char[] baseChar = Integer.toString(num).toCharArray(); char[] numChar = new char[baseChar.length]; for (int i = 0; i < baseChar.length; i++) { numChar[i] = nums[baseChar[i] - 48]; } char[] unit = new char[numChar.length]; unit = Arrays.copyOf(units, unit.length); StringBuffer buffer = new StringBuffer(); for (int i = 0; i < numChar.length; i++) { if (numChar[i] != nums[0]) { buffer.append(numChar[i]).append(unit[unit.length - i - 1]); }else { buffer.append(numChar[i]); } } int index = 0; while ((index = buffer.indexOf(nums[0] + String.valueOf(nums[0]))) > 0) { buffer.replace(index, index + 2, String.valueOf(nums[0])); } return buffer.toString().substring(0, buffer.length() - 1); }神马,没有注释?我故意的(此处拒绝板砖和拖鞋之类的具有任何杀伤性的物品),嘿嘿自己看懂了才算是有收获欢迎指出错误
2019年10月10日
49 阅读
0 评论
0 点赞
日期工具类
import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.TimeZone; /** * @author 枫铃也 * @since 2019-10-09 13:51 */ public class DateUtil { private static final TimeZone TIME_ZONE = TimeZone.getDefault(); private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public static String format(Date date) { return DATE_FORMAT.format(date); } /** * <p>@description: 得到当前日历 </p> * <p>@method: getCalendar </p> * <p>@author: 枫铃也 </p> * <p>@since: 2019-10-09 14:47 </p> * * <p>@return java.util.Calendar </p> */ public static Calendar getCalendar() { return Calendar.getInstance(TIME_ZONE); } /** * <p>@description: 得到当前秒的开始日历 </p> * <p>@method: getSecondStartCalendar </p> * <p>@author: 枫铃也 </p> * <p>@since: 2019-10-09 14:48 </p> * * <p>@return java.util.Calendar </p> */ public static Calendar getSecondStartCalendar() { Calendar calendar = getCalendar(); //将毫秒至0 calendar.set(Calendar.MILLISECOND, 0); return calendar; } /** * <p>@description: 得到上一秒的开始日历 </p> * <p>@method: getLastSecondStartCalendar </p> * <p>@author: 枫铃也 </p> * <p>@since: 2019-10-09 14:48 </p> * * <p>@return java.util.Calendar </p> */ public static Calendar getLastSecondStartCalendar() { Calendar calendar = getSecondStartCalendar(); //将秒为上一秒 calendar.set(Calendar.SECOND, calendar.get(Calendar.SECOND) - 1); return calendar; } /** * <p>@description: 得到下一秒的开始日历 </p> * <p>@method: getLaterSecondStartCalendar </p> * <p>@author: 枫铃也 </p> * <p>@since: 2019-10-09 14:48 </p> * * <p>@return java.util.Calendar </p> */ public static Calendar getLaterSecondStartCalendar() { Calendar calendar = getSecondStartCalendar(); //将秒为下一秒 calendar.set(Calendar.SECOND, calendar.get(Calendar.SECOND) + 1); return calendar; } /** * <p>@description: 得到当前秒的结束日历 </p> * <p>@method: getSecondEndCalendar </p> * <p>@author: 枫铃也 </p> * <p>@since: 2019-10-09 14:48 </p> * * <p>@return java.util.Calendar </p> */ public static Calendar getSecondEndCalendar() { Calendar calendar = getCalendar(); //将毫秒至999 calendar.set(Calendar.MILLISECOND, 999); return calendar; } /** * <p>@description: 得到上一秒的结束日历 </p> * <p>@method: getLastSecondEndCalendar </p> * <p>@author: 枫铃也 </p> * <p>@since: 2019-10-09 14:48 </p> * * <p>@return java.util.Calendar </p> */ public static Calendar getLastSecondEndCalendar() { Calendar calendar = getSecondEndCalendar(); //将秒置上一秒 calendar.set(Calendar.SECOND, calendar.get(Calendar.SECOND) - 1); return calendar; } /** * <p>@description: 得到下一秒的结束日历 </p> * <p>@method: getLaterSecondEndCalendar </p> * <p>@author: 枫铃也 </p> * <p>@since: 2019-10-09 14:48 </p> * * <p>@return java.util.Calendar </p> */ public static Calendar getLaterSecondEndCalendar() { Calendar calendar = getSecondEndCalendar(); //将秒置下一秒 calendar.set(Calendar.SECOND, calendar.get(Calendar.SECOND) + 1); return calendar; } /** * <p>@description: 得到当前分钟的开始日历 </p> * <p>@method: getMinuteStartCalendar </p> * <p>@author: 枫铃也 </p> * <p>@since: 2019-10-09 14:48 </p> * * <p>@return java.util.Calendar </p> */ public static Calendar getMinuteStartCalendar() { Calendar calendar = getSecondStartCalendar(); //将秒至0 calendar.set(Calendar.SECOND, 0); return calendar; } /** * <p>@description: 得到上一分钟的开始日历 </p> * <p>@method: getLastMinuteStartCalendar </p> * <p>@author: 枫铃也 </p> * <p>@since: 2019-10-09 14:48 </p> * * <p>@return java.util.Calendar </p> */ public static Calendar getLastMinuteStartCalendar() { Calendar calendar = getMinuteStartCalendar(); //将分钟置上一分钟 calendar.set(Calendar.MINUTE, calendar.get(Calendar.MINUTE) - 1); return calendar; } /** * <p>@description: 得到下一分钟的开始日历 </p> * <p>@method: getLaterMinuteStartCalendar </p> * <p>@author: 枫铃也 </p> * <p>@since: 2019-10-09 14:48 </p> * * <p>@return java.util.Calendar </p> */ public static Calendar getLaterMinuteStartCalendar() { Calendar calendar = getMinuteStartCalendar(); //将分钟置下一分钟 calendar.set(Calendar.MINUTE, calendar.get(Calendar.MINUTE) + 1); return calendar; } /** * <p>@description: 得到当前分钟的结束日历 </p> * <p>@method: getMinuteEndCalendar </p> * <p>@author: 枫铃也 </p> * <p>@since: 2019-10-09 14:48 </p> * * <p>@return java.util.Calendar </p> */ public static Calendar getMinuteEndCalendar() { Calendar calendar = getSecondEndCalendar(); //将秒至59 calendar.set(Calendar.SECOND, 59); return calendar; } /** * <p>@description: 得到上一分钟的结束日历 </p> * <p>@method: getLastMinuteEndCalendar </p> * <p>@author: 枫铃也 </p> * <p>@since: 2019-10-09 14:48 </p> * * <p>@return java.util.Calendar </p> */ public static Calendar getLastMinuteEndCalendar() { Calendar calendar = getMinuteEndCalendar(); //将分钟为上一分钟 calendar.set(Calendar.MINUTE, calendar.get(Calendar.MINUTE) - 1); return calendar; } /** * <p>@description: 得到下一分钟的结束日历 </p> * <p>@method: getLaterMinuteEndCalendar </p> * <p>@author: 枫铃也 </p> * <p>@since: 2019-10-09 14:48 </p> * * <p>@return java.util.Calendar </p> */ public static Calendar getLaterMinuteEndCalendar() { Calendar calendar = getMinuteEndCalendar(); //将分钟为下一分钟 calendar.set(Calendar.MINUTE, calendar.get(Calendar.MINUTE) + 1); return calendar; } /** * <p>@description: 得到当前小时的开始日历 </p> * <p>@method: getHourStartCalendar </p> * <p>@author: 枫铃也 </p> * <p>@since: 2019-10-09 14:48 </p> * * <p>@return java.util.Calendar </p> */ public static Calendar getHourStartCalendar() { Calendar calendar = getMinuteStartCalendar(); //将分钟置0 calendar.set(Calendar.MINUTE, 0); return calendar; } /** * <p>@description: 得到上一小时的开始日历 </p> * <p>@method: getLastHourStartCalendar </p> * <p>@author: 枫铃也 </p> * <p>@since: 2019-10-09 14:48 </p> * * <p>@return java.util.Calendar </p> */ public static Calendar getLastHourStartCalendar() { Calendar calendar = getHourStartCalendar(); //将小时置上一小时 calendar.set(Calendar.HOUR_OF_DAY, calendar.get(Calendar.HOUR_OF_DAY) - 1); return calendar; } /** * <p>@description: 得到下一小时的开始日历 </p> * <p>@method: getLaterHourStartCalendar </p> * <p>@author: 枫铃也 </p> * <p>@since: 2019-10-09 14:48 </p> * * <p>@return java.util.Calendar </p> */ public static Calendar getLaterHourStartCalendar() { Calendar calendar = getHourStartCalendar(); //将小时置下一小时 calendar.set(Calendar.HOUR_OF_DAY, calendar.get(Calendar.HOUR_OF_DAY) + 1); return calendar; } /** * <p>@description: 得到当前小时的结束日历 </p> * <p>@method: getHourEndCalendar </p> * <p>@author: 枫铃也 </p> * <p>@since: 2019-10-09 14:48 </p> * * <p>@return java.util.Calendar </p> */ public static Calendar getHourEndCalendar() { Calendar calendar = getMinuteEndCalendar(); //将分钟置59 calendar.set(Calendar.MINUTE, 59); return calendar; } /** * <p>@description: 得到上一小时的结束日历 </p> * <p>@method: getLastHourEndCalendar </p> * <p>@author: 枫铃也 </p> * <p>@since: 2019-10-09 14:48 </p> * * <p>@return java.util.Calendar </p> */ public static Calendar getLastHourEndCalendar() { Calendar calendar = getHourEndCalendar(); //将小时置上一小时 calendar.set(Calendar.HOUR_OF_DAY, calendar.get(Calendar.HOUR_OF_DAY) - 1); return calendar; } /** * <p>@description: 得到下一小时的结束日历 </p> * <p>@method: getLaterHourEndCalendar </p> * <p>@author: 枫铃也 </p> * <p>@since: 2019-10-09 14:48 </p> * * <p>@return java.util.Calendar </p> */ public static Calendar getLaterHourEndCalendar() { Calendar calendar = getHourEndCalendar(); //将小时置下一小时 calendar.set(Calendar.HOUR_OF_DAY, calendar.get(Calendar.HOUR_OF_DAY) + 1); return calendar; } /** * <p>@description: 得到当前日的开始日历 </p> * <p>@method: getDayStartCalendar </p> * <p>@author: 枫铃也 </p> * <p>@since: 2019-10-09 14:48 </p> * * <p>@return java.util.Calendar </p> */ public static Calendar getDayStartCalendar() { Calendar calendar = getHourStartCalendar(); //将小时置0 calendar.set(Calendar.HOUR_OF_DAY, 0); return calendar; } /** * <p>@description: 得到前一天的开始日历 </p> * <p>@method: getLastDayStartCalendar </p> * <p>@author: 枫铃也 </p> * <p>@since: 2019-10-09 14:48 </p> * * <p>@return java.util.Calendar </p> */ public static Calendar getLastDayStartCalendar() { Calendar calendar = getHourStartCalendar(); //将日设置为前一天 calendar.set(Calendar.HOUR_OF_DAY, calendar.get(Calendar.HOUR_OF_DAY) - 1); return calendar; } /** * <p>@description: 得到后一天的开始日历 </p> * <p>@method: getLaterDayStartCalendar </p> * <p>@author: 枫铃也 </p> * <p>@since: 2019-10-09 14:48 </p> * * <p>@return java.util.Calendar </p> */ public static Calendar getLaterDayStartCalendar() { Calendar calendar = getHourStartCalendar(); //将日设置为后一天 calendar.set(Calendar.HOUR_OF_DAY, calendar.get(Calendar.HOUR_OF_DAY) + 1); return calendar; } /** * <p>@description: 得到当前日的结束日历 </p> * <p>@method: getDayEndCalendar </p> * <p>@author: 枫铃也 </p> * <p>@since: 2019-10-09 14:48 </p> * * <p>@return java.util.Calendar </p> */ public static Calendar getDayEndCalendar() { Calendar calendar = getHourEndCalendar(); //将小时置23 calendar.set(Calendar.HOUR_OF_DAY, 23); return calendar; } /** * <p>@description: 得到前一日的结束日历 </p> * <p>@method: getLastDayEndCalendar </p> * <p>@author: 枫铃也 </p> * <p>@since: 2019-10-09 14:48 </p> * * <p>@return java.util.Calendar </p> */ public static Calendar getLastDayEndCalendar() { Calendar calendar = getDayEndCalendar(); //将日置前一日 calendar.set(Calendar.HOUR_OF_DAY, calendar.get(Calendar.HOUR_OF_DAY) - 1); return calendar; } /** * <p>@description: 得到后一日的结束日历 </p> * <p>@method: getLaterDayEndCalendar </p> * <p>@author: 枫铃也 </p> * <p>@since: 2019-10-09 14:48 </p> * * <p>@return java.util.Calendar </p> */ public static Calendar getLaterDayEndCalendar() { Calendar calendar = getDayEndCalendar(); //将日置后一日 calendar.set(Calendar.HOUR_OF_DAY, calendar.get(Calendar.HOUR_OF_DAY) + 1); return calendar; } /** * <p>@description: 得到当前月的开始日历 </p> * <p>@method: getMonthStartCalendar </p> * <p>@author: 枫铃也 </p> * <p>@since: 2019-10-09 14:48 </p> * * <p>@return java.util.Calendar </p> */ public static Calendar getMonthStartCalendar() { Calendar calendar = getDayStartCalendar(); //设置为1号 calendar.set(Calendar.DAY_OF_MONTH, 1); return calendar; } /** * <p>@description: 得到上个月的开始日历 </p> * <p>@method: getLastMonthStartCalendar </p> * <p>@author: 枫铃也 </p> * <p>@since: 2019-10-09 14:48 </p> * * <p>@return java.util.Calendar </p> */ public static Calendar getLastMonthStartCalendar() { Calendar calendar = getMonthStartCalendar(); //设置月为上一月 calendar.set(Calendar.MONTH, calendar.get(Calendar.MONTH) - 1); return calendar; } /** * <p>@description: 得到下个月的开始日历 </p> * <p>@method: getLaterMonthStartCalendar </p> * <p>@author: 枫铃也 </p> * <p>@since: 2019-10-09 14:48 </p> * * <p>@return java.util.Calendar </p> */ public static Calendar getLaterMonthStartCalendar() { Calendar calendar = getMonthStartCalendar(); //设置月为下一月 calendar.set(Calendar.MONTH, calendar.get(Calendar.MONTH) + 1); return calendar; } /** * <p>@description: 得到当前月的结束日历 </p> * <p>@method: getMonthEndCalendar </p> * <p>@author: 枫铃也 </p> * <p>@since: 2019-10-09 14:48 </p> * * <p>@return java.util.Calendar </p> */ public static Calendar getMonthEndCalendar() { Calendar calendar = getDayEndCalendar(); //设置为最后一天 calendar.set(Calendar.DAY_OF_MONTH, calendar.getActualMaximum(Calendar.DAY_OF_MONTH)); return calendar; } /** * <p>@description: 得到上一月的结束日历 </p> * <p>@method: getLastMonthEndCalendar </p> * <p>@author: 枫铃也 </p> * <p>@since: 2019-10-09 14:48 </p> * * <p>@return java.util.Calendar </p> */ public static Calendar getLastMonthEndCalendar() { Calendar calendar = getMonthEndCalendar(); //设置月为上一月 calendar.set(Calendar.MONTH, calendar.get(Calendar.MONTH) - 1); return calendar; } /** * <p>@description: 得到下个月的结束日历 </p> * <p>@method: getLaterMonthEndCalendar </p> * <p>@author: 枫铃也 </p> * <p>@since: 2019-10-09 14:48 </p> * * <p>@return java.util.Calendar </p> */ public static Calendar getLaterMonthEndCalendar() { Calendar calendar = getMonthEndCalendar(); //设置月为下个月 calendar.set(Calendar.MONTH, calendar.get(Calendar.MONTH) + 1); return calendar; } /** * <p>@description: 得到当前年的开始日历 </p> * <p>@method: getYearStartCalendar </p> * <p>@author: 枫铃也 </p> * <p>@since: 2019-10-09 14:48 </p> * * <p>@return java.util.Calendar </p> */ public static Calendar getYearStartCalendar() { Calendar calendar = getMonthStartCalendar(); //设置月为1月 calendar.set(Calendar.MONTH, 0); return calendar; } /** * <p>@description: 得到前一年的开始日历 </p> * <p>@method: getLastYearStartCalendar </p> * <p>@author: 枫铃也 </p> * <p>@since: 2019-10-09 14:48 </p> * * <p>@return java.util.Calendar </p> */ public static Calendar getLastYearStartCalendar() { Calendar calendar = getYearStartCalendar(); //设置年份为前一年 calendar.set(Calendar.YEAR, calendar.get(Calendar.YEAR) - 1); return calendar; } /** * <p>@description: 得到后一年的开始日历 </p> * <p>@method: getLaterYearStartCalendar </p> * <p>@author: 枫铃也 </p> * <p>@since: 2019-10-09 14:48 </p> * * <p>@return java.util.Calendar </p> */ public static Calendar getLaterYearStartCalendar() { Calendar calendar = getYearStartCalendar(); //设置年份为后一年 calendar.set(Calendar.YEAR, calendar.get(Calendar.YEAR) + 1); return calendar; } /** * <p>@description: 得到当前年的结束日历 </p> * <p>@method: getYearEndCalendar </p> * <p>@author: 枫铃也 </p> * <p>@since: 2019-10-09 14:48 </p> * * <p>@return java.util.Calendar </p> */ public static Calendar getYearEndCalendar() { Calendar calendar = getMonthEndCalendar(); //设置月为12月 calendar.set(Calendar.MONTH, 11); //设置为31号 calendar.set(Calendar.DAY_OF_MONTH, 31); return calendar; } /** * <p>@description: 得到上一年的结束日历 </p> * <p>@method: getLastYearEndCalendar </p> * <p>@author: 枫铃也 </p> * <p>@since: 2019-10-09 14:48 </p> * * <p>@return java.util.Calendar </p> */ public static Calendar getLastYearEndCalendar() { Calendar calendar = getYearEndCalendar(); //设置年为前一年 calendar.set(Calendar.YEAR, calendar.get(Calendar.YEAR) - 1); return calendar; } /** * <p>@description: 得到后一年的结束日历 </p> * <p>@method: getLaterYearEndCalendar </p> * <p>@author: 枫铃也 </p> * <p>@since: 2019-10-09 14:48 </p> * * <p>@return java.util.Calendar </p> */ public static Calendar getLaterYearEndCalendar() { Calendar calendar = getYearEndCalendar(); //设置年为后一年 calendar.set(Calendar.YEAR, calendar.get(Calendar.YEAR) + 1); return calendar; } public static void main(String[] args) { System.out.println("明年的开始:" + DateUtil.format(getLaterYearStartCalendar().getTime())); System.out.println("明年的结束:" + DateUtil.format(getLaterYearEndCalendar().getTime())); System.out.println("今年的开始:" + DateUtil.format(getYearStartCalendar().getTime())); System.out.println("今年的结束:" + DateUtil.format(getYearEndCalendar().getTime())); System.out.println("去年的开始:" + DateUtil.format(getLastYearStartCalendar().getTime())); System.out.println("去年的结束:" + DateUtil.format(getLastYearEndCalendar().getTime())); } }
2019年10月09日
61 阅读
0 评论
0 点赞
Java 8 的一些集合操作
将一个对象集合中的某一个字段取出来生成一个新的集合List<Store> storeList = storeService.listStore(page, size); List<Integer> collect = storeList.stream().map(s -> s.getStoreId()).collect(Collectors.toList()); //OR List<Integer> collect = storeList.stream().map(Store::getStoreId).collect(Collectors.toList());将List对象集合转化为key为对象ID的MapList<Specification> specifications = specificationService.listSpecification(); Map<Integer, Specification> collect = specifications.stream().collect(Collectors.toMap(Specification::getSpecificationId, Function.identity()));跟据某个属性分组Map<String, List<PersonData>> collect = list.stream().collect(Collectors.groupingBy(PersonData::getType));根据某个属性分组,汇总某个属性Map<String, Integer> collect2 = list.stream().collect(Collectors.groupingBy(PersonData::getType,Collectors.summingInt(PersonData::getAge)));根据某个属性添加条件过滤数据list = list.stream().filter(u -> !u.getType().equals("访客")).collect(Collectors.toList());判断一组对象里面有没有属性值是某个值boolean add = list.stream().anyMatch(m -> "王五".equals(m.getName()));
2019年10月02日
97 阅读
0 评论
0 点赞
日常开发规范
注:基于SonarLint 官方网址: SonarLint特殊记录HashMap的大小应该为目标大小/加载因子且为2^n 向上取整HashMap默认的加载因子是0.75 .它在时间和空间成本上寻求了一种折中。 如果只装载100个元素的HashMap没有特殊的用途,那么为了在时间和空间上达到最佳性能,HashMap的初始容量可以设为100/0.75 = 133.33。为了防止rehash,向上取整,为134。 但是还有另外一个问题,就是hash碰撞的问题。如果我们将HashMap的容量设置为134,那么如何保证其中的哈希碰撞会比较少呢? 除非重写hashcode()方法,否则,似乎没有办法保证。 那么这里不得不提HashMap如何为元素选择下标的方法了。static int indexFor(int h, int length) { return h & (length-1); }其中h为key哈希后得到的值,length为哈希表的长度。 134-1 = 128 + 6 -1; 那么 length-1的二进制值的最后3位为101; 假设这100个装载的元素中他们的key在哈希后有得到两个值(h),他们的二进制值除了低3位之外都相同,而第一个值的低3位为011,第二个值的低3位为001; 这时候进行java的&预算,011 & 101 = 001 ;001 & 101 = 001; 他们的值相等了,那么这个时候就会发生哈希碰撞。 除此之外还有一个更加严重的问题,由于在101中第二位是0,那么,无论我们的key在哈希运算之后得到的值h是什么,那么在&运算之后,得到的结果的倒数第二位均为0; 因此,对于hash表所有下标的二进制的值而言,只要低位第二位的值为1,(例如0010,0011,0111,1111)那么这个下标所代表的桶将一直是空的,因为代码中的&运算的结果永远不会产生低位第二位为1的值。这就大大地浪费了空间,同时还增加了哈希碰撞的概率。这无疑会降低HashMap的效率。 那么如何才能减少这种浪费呢?最佳的方法当然是让length-1的二进制值全部位均为1.那么length的值是多少合适呢? 没错,length=2^n。 只要将hash表的长度设为2的N次方,那么,所有的哈希桶均有被使用的可能。 与134最靠近的2^n无疑是128。 如果只修改HashMap的长度而不修改HashMap的加载因子的话,HashMap会进行rehash操作,这是一个代价很大的操作,所以不可取。 那么应该选择的就应该是256。 而由于空间加大和有效利用哈希桶,这时的哈希碰撞将大大降低,因此HashMap的读取效率会比较高。 所以,最后结论就是:HashMap的大小应该设置为256。 其实在Java中,无论你的HashMap(x)中的x设置为多少,HashMap的大小都是2^n。2^n是大于x的第一个数。因为HashMap的初始化代码中有以下这行代码:int capacity = 1; while (capacity < initialCapacity) capacity <<= 1;但是这就带来了一个问题,如果x=100,那么HashMap的初始大小应该是128.但是100/128=0.78,已经超过默认加载因子的大小了。因此会resize一次,变成256。所以最好的结果还是256。1. 字符串不应该在循环中使用'+'连接原:Strings are immutable objects, so concatenation doesn't simply add the new String to the end of the existing string. Instead, in each loop iteration, the first String is converted to an intermediate object type, the second string is appended, and then the intermediate object is converted back to a String. Further, performance of these intermediate operations degrades as the String gets longer. Therefore, the use of StringBuilder is preferred. 译:Sring是不可变的对象,所以在进行字符拼接的时候不只是将新的字符串添加到现有字符串的末尾。相反,在每个循环迭代中,先将第一个字符串转换为中间对象类型,再添加第二个字符串,然后将中间对象转换回字符串。此外,随着字符串变长,这些中间操作的性能会下降。因此,最好使用StringBuilder。//不规范代码示例 String str = ""; for (int i = 0; i < arrayOfStrings.length ; ++i) { str = str + arrayOfStrings[i]; } //规范代码示例 StringBuilder bld = new StringBuilder(); for (int i = 0; i < arrayOfStrings.length; ++i) { bld.append(arrayOfStrings[i]); } String str = bld.toString();拓展(1)String是Java的基础类,他可以定义少量的字符串(2)StringBuffer是对String的扩充,他可以拼接大量字符串,但是他的效率比较低,因为他是线程安全的(3)StringBuidder是对StringBuffer的拓展,他提高了效率,但是他是线程不安全的2. 不应该在打印输出或者是记录日志的时候进行运算原:Passing message arguments that require further evaluation into a Guava com.google.common.base.Preconditions check can result in a performance penalty. That's because whether or not they're needed, each argument must be resolved before the method is actually called. Similarly, passing concatenated strings into a logging method can also incur a needless performance hit because the concatenation will be performed every time the method is called, whether or not the log level is low enough to show the message. Instead, you should structure your code to pass static or pre-computed values into Preconditions conditions check and logging calls. Specifically, the built-in string formatting should be used instead of string concatenation, and if the message is the result of a method call, then Preconditions should be skipped altoghether, and the relevant exception should be conditionally thrown instead. 译:程序在调用方法之前,无论是否需要它们都将解析每一个参数并将需要进一步计算的消息参数传递到Guava 而com.google.common.base.Preconditions在检查中可能会导致性能损失。 类似的,将连接的字符串传递到日志方法也会导致不必要的性能损失,因为每次调用该方法时都会执行运算或者拼接,无论日志级别是否低到足以显示消息。 相反,应该构造代码,将静态值或预计算值传递到Preconditions条件检查和日志记录调用中。 具体来说,应该使用内置的字符串格式而不是字符串连接,如果消息是方法调用的结果,则应该跳过先决条件altoghether,而应该有条件地抛出相关异常。//不规范代码示例 // 即使日志级别过高,无法显示调试消息,也会执行字符串连接 logger.log(Level.DEBUG, "Something went wrong: " + message); logger.fine("An exception occurred with message: " + message); LOG.error("Unable to open file " + csvPath, e); Preconditions.checkState(a > 0, "Arg must be positive, but got " + a); //即使使用了判断,可当a>0的时候依旧需要进行拼接 Preconditions.checkState(condition, formatMessage()); Preconditions.checkState(condition, "message: %s", formatMessage()); //规范代码示例 logger.log(Level.SEVERE, "Something went wrong: {0} ", message); // 只有在需要时才应用字符串格式 logger.fine("An exception occurred with message: {}", message); // SLF4J, Log4j logger.log(Level.SEVERE, () -> "Something went wrong: " + message); // 从 Java 8 开始,我们可以使用Supplier,因为它是懒加载的 LOG.error("Unable to open file {0}", csvPath, e); if (LOG.isDebugEnabled() { LOG.debug("Unable to open file " + csvPath, e); } Preconditions.checkState(arg > 0, "Arg must be positive, but got %d", a); if (!condition) { throw new IllegalStateException(formatMessage()); // formatMessage()方法只在条件下调用 } if (!condition) { throw new IllegalStateException("message: " + formatMessage()); }拓展(1)supplier也是是用来创建对象的,但是不同于传统的创建对象语法:new。每次调用get()方法时都会调用构造方法创建一个新对象,示例代码:public class TestSupplier { private int age; TestSupplier(){ System.out.println(age); } public static void main(String[] args) { //创建Supplier容器,声明为TestSupplier类型,此时并不会调用对象的构造方法,即不会创建对象 Supplier<TestSupplier> sup= TestSupplier::new; System.out.println("--------"); //调用get()方法,此时会调用对象的构造方法,即获得到真正对象 sup.get(); //每次get都会调用构造方法,即获取的对象不同 sup.get(); } }运行结果:-------- 0 03.应该使用日志记录异常信息原:Throwable.printStackTrace(...) prints a Throwable and its stack trace to some stream. By default that stream System.Err, which could inadvertently expose sensitive information. Loggers should be used instead to print Throwables, as they have many advantages: ● Users are able to easily retrieve the logs. ● The format of log messages is uniform and allow users to browse the logs easily. This rule raises an issue when printStackTrace is used without arguments, i.e. when the stack trace is printed to the default stream. 译:Throwable.printStackTrace(...)将Throwable及其堆栈跟踪信息打印到某些流。 默认情况下,该流System.Err可能会无意间公开敏感信息。 应该使用记录器来打印Throwable,因为它们具有许多优点: ● 用户能够轻松检索日志。 ● 日志消息的格式统一,并且允许用户轻松浏览日志。 当使用不带参数的printStackTrace时(即,将堆栈跟踪打印到默认流时),此规则会引起问题。//不规范代码示例 try { /* ... */ } catch(Exception e) { e.printStackTrace(); } //规范代码示例 try { /* ... */ } catch(Exception e) { LOGGER.log("context", e); }
2019年09月27日
81 阅读
0 评论
0 点赞
1
2