白菜不是菜

Welcome to my blog !


Java里常见的几个语法小坑

Published at May 5, 2018 ·  2 min read

很久没更新博客了,想到几个小坑,虽然没啥技术含量,但或许有人不知道呢。 1.删除sublist的元素导致原对象元素被删除 看下面这段代码 List<Integer> students=new ArrayList<Integer>(); for (int i = 0; i <5 ; i++) { students.add(i); } List<Integer> subList=new ArrayList<Integer>(); subList=students.subList(0,5); subList.remove(0); subList.remove(1); for (int i = 0; i <5 ; i++) { System.out.println(i+"="+students.get(i)); } students是个list,然后我们新建立了一个subList对象,这个对象截取了students的一部分,我们删除了subList对象里的一些元素,看下运行结果。 0=1 1=3 2=4 Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 3, Size: 3 at java.util.ArrayList.rangeCheck(ArrayList.java:657) at java.util.ArrayList.get(ArrayList.java:433) at bai.ListDo.main(ListDo.java:17) 难道说,删除subList对象里的元素也会导致students里的元素被删除?我明明是新建了一个对象啊。然而,事实确实是这样的。 我们要理解一个事情,使用new新建一个对象,只是开辟了一块空间,用来存放这个对象的地址指针,但是这个新建的对象地址,指向的却是原有对象,也就是说,使用subList这个方法的时候,并没有从students里把内容拷贝了一份,仅仅是纪录了一个指针的移动,这样从某种角度来说,是提高了性能节省内存的做法。 看一下subList这个方法的JavaDoc我们就更清楚了。 Returns a view of the portion of this list between the specified * <tt>fromIndex</tt>, inclusive, and <tt>toIndex</tt>, exclusive....

折腾阿里云OSS的API

Published at January 25, 2018 ·  2 min read

这两天想给博客做个插件,利用阿里云的OSS来存储文件.但阿里的文档和代码都烂的超乎想象,要么代码老旧不堪,要么跟小脚老太一样引入一坨依赖,想必这块是外包团队做的吧,或者阿里非核心业务员的技术水平也就这样吧.  所以想绕开阿里云官方提供的代码自己整一套OSS的API,先跑一个上传文件的demo,能在客户端跑通后再用代码去实现.最简单的方法就是用REST client来模拟.折腾了一下,还挺费劲,记录下折腾过程  先来试试上传文件,选择PUT方法,要请求的URL为http://baicaidoc.oss-cn-shenzhen.aliyuncs.com/image/small/mm1.jpg ,添加以下header,header头需要包含哪些内容可以看这里 Authorization:OSS LTAIxkX6Qj2OuMZ6:tLZ7nYYP/hkCJbG/6gkOJ7Mi4E= Date:Thu, 25 Jan 2018 15:20:39 GMT Content-Disposition:attachment;filename=ivy.jpg Host:baicaidoc.oss-cn-shenzhen.aliyuncs.com Content-Encoding:utf-8 然后在body里添加file body. 至于header头怎么写和Authorization字段计算的方法,文档里说的比较清晰了https://help.aliyun.com/document_detail/31951.html. 尤其需要注意的是Date必须是GMT格式,这个对Java来说也好办,不过要注意时区的问题,GMT时间比东八区慢了8个小时.还有Host需要带上bucket,这在早期是不需要的(早期带上反而会报错SignatureDoesNotMatch) 另外就是这个Authorization字段的签名需要注意,base64需要处理byte[]数组,而不是字符串.所以用网上的在线验证工具是验证不了的. Java版的签名代码如下: import bai.tool.Base64; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; /** * Hello world! * */ public class App { public static byte[] hamcsha1(byte[] data, byte[] key) { try { SecretKeySpec signingKey = new SecretKeySpec(key, "HmacSHA1"); Mac mac = Mac.getInstance("HmacSHA1"); mac.init(signingKey); return mac.doFinal(data); } catch (NoSuchAlgorithmException e) { e....

PHP的mb_check_encoding函数的存在是鸡肋吗

Published at January 12, 2018 ·  1 min read

前不久,有人问到我一个问题,就是使用mb_check_encoding来侦测一段字符的编码,预期是GBK编码,但是PHP给出来UTF-8编码的错误判断。那么,mb_check_encoding的正确姿势是什么呢? 我们来看一段代码, <?php $utf8Str = '别abc扯淡'; var_dump(mb_check_encoding($utf8Str, 'UTF-8')); //输出true var_dump(mb_check_encoding($utf8Str, 'gbk')); //输出true  这段代码的输出是啥呢?按理,我们的PHP文件保存为什么编码,那它输出的就应该是啥编码,然而以上输出的都是true。再换个例子,这样呢? <?php $utf8Str = '别abc扯淡啊'; var_dump(mb_check_encoding($utf8Str, 'UTF-8')); var_dump(mb_check_encoding($utf8Str, 'gbk'));  后面多加了一个汉字,这次PHP做出了正确的判断,给出了是UTF-8的判断。那么mb_check_encoding到底有没有用?是这个函数有bug还是我自己不懂姿势?  难道是,只要汉字是3的整数倍就会判断失灵?试验后确实是的,当然这只是表面现象,但无疑说明这个函数是不可靠的。为什么呢?其实原理说起来也不难理解,计算机并不懂什么叫乱码。一段文字,解释成UTF8或GBK其实都是可以的,我们用肉眼看到有了乱码,根据我们的经验,觉得解释成这种编码是错误的,而解释成另外一种编码才算正确。可是计算机不懂啊,你觉得有个字符很奇怪,你不认识所以认定是乱码,可计算机认识啊,它不觉得奇怪。除非字节数解释成另外一种编码,会多出一个字节,并且ASCII码也不是常见范围,计算机才能大胆判定解释成这种编码不对。所以这样去检测编码是无法完全可靠地.  那既然mb_check_encoding这个函数不可靠,那么用正则可靠么?或许吧。 但是我们更应该关注的是PHP为什么会有这么一个功能?为什么其他语言没有这个方法,或者根本不会遇到这个问题?  问题还是出在PHP本身。因为客户端可能会有多种编码输入,PHP为了解决这个问题就引入这么一个贴心的函数给使用者。可是PHP不应该是遇到问题就去动歪脑筋解决问题啊,而且规范问题。为什么其他语言不需要在SDK里引入这个方法呢?或者说是PHP程序员的使用姿势不正确?  最后,其实PHP给出这个函数也不算错,但是一定要参照其他语言里的惯行做法,在文档里说清楚,这个函数的判断的是一种“可信度”,而不是给出一个非此即彼的“权威”结果。但是遗憾的是,这个函数的文档里没说很好的说清楚,而是这么写的, > “Checks if the specified byte stream is valid for the specified encoding. It is useful to prevent so-called “Invalid Encoding Attack Returns TRUE on success or FALSE on failure.”  其实加上这样一句话“This function only give the confidence level of the result”就好了,也就不会平白引起那么多的疑虑。...

FireJava输出Java服务器端调试日志到控制台

Published at January 10, 2018 ·  1 min read

针对最新火狐浏览器50+以上版本的firebug协议,类似FirePHP,但是FirePHP已经很久不更新,并且对最新的浏览器也已失效。 > 这个在Firebug之上运行的扩展,结合一个服务器端的库,就可以让你的PHP代码向浏览器发送调试信息,该信息以HTTP响应头(HTTP headers)的方式编码。经过设置,你可以像在Firebug控制台调试JavaScript代码一样得到PHP脚本的警告和错误提示。下面我们来看看具体步骤。 直接上代码 import com.alibaba.fastjson.JSON; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.Objects; /** * @version V1.0 * @Description:直接输出服务器端调试日志到控制台,简易版本。 * @date 2017/6/13 16:51 */ public class DebugTool { public final String VERSION = "2.0.j1"; public final String HEADER_NAME = "X-ChromeLogger-Data"; protected Map<String, Object> console = new HashMap<>(); private String response=""; public DebugTool() { console.put("version", VERSION); console.put("columns", new String[]{"log", "backtrace", "type"}); console.put("rows", new ArrayList<Objects>()); console.put("request_uri", this.getClass().getName()); } public DebugTool(Class cls) { this(); console....

Vala使用体验

Published at January 8, 2018 ·  2 min read

前段时间用vala开发了一个很小的程序,体验了一把vala的使用,网上关于vala的文章比较少,所以写一篇博客,如果你有相同的使用经验可以交流下.  根据百度百科的解释,vala是一种新的、为GNOME开发者提供的具有现代化编程语言功能的一种编程语言。Vala是一种和C#极度类似的语言。  众所周知,C是一门古老而落后的语言,虽然由于历史原因,大量的操作系统底层仍然在使用C(毕竟最早写底层的那批人早就退休甚至不在人世了,谁又愿意没事找事去重构呢.不过,还别说,Google就在干这种事,开发全新的操作系统),但并不适用于大型项目和协作开发.尽管Linux的作者Linus极度憎恨C++这样的面向对象的语言,并拒绝C++在Linux内核的使用.但是,Linux的其它开发者也心知肚明,C并不是一切,并且在Linux的各个方面大量使用C++和面向对象的开发模式.知名的KDE桌面就是基于QT来构建的,而QT是对C++的一个扩展,Linux上的大部分可用的应用都是基于QT来构建的,而另一个桌面环境Gnome则使用了GTK绑定,同时也大量使用了面向对象的特性和组件,比如Gobject,Vala.Vala的一个重要使用场景就是Gnome环境的GUI开发.  Vala语言的主要特点:支持lambda表达式;支持对象反射与内省;使用引用计数进行内存管理,计数嵌入在对象内;使用Glib和Gobject的主循环、事件回调系统。 安装  在Ubuntu/Debian下安装很简单,使用命令sudo apt-get install valac,测试valac编译器的版本号,可以输入valac –version命令。 我现在使用的是0.36版本,最新版本应该是0.40 Beta. HelloWorld程序 class Demo.HelloWorld : GLib.Object { public static int main(string[] args) { stdout.printf("Hello, World\n"); return 0; } } 其实在Vala里,类并不是必须的.类名和文件名并不需要一致,并且一个类里允许多个类. 编译运行 编译这个程序使用命令valac hello.vala,编译成功之后生成hello这个可执行程序,运行这个程序,输入结果为: Hello, World Vala一个比较有趣的地方就是可以直接从Vala源码编译成C源码,比如上面的代码可以使用如下的命令编译成C源码 valac -C ./hello.vala 生成的C源码如下: /* hello.c generated by valac 0.36.5, the Vala compiler * generated from hello.vala, do not modify */ #include <glib.h> #include <glib-object.h> #include <stdlib....

TOTP算法Java版本

Published at January 8, 2018 ·  6 min read

TOTP 概念 TOTP - Time-based One-time Password Algorithm is an extension of the HMAC-based One Time Password algorithm HOTP to support a time based moving factor. TOTP(基于时间的一次性密码算法)是支持时间作为动态因素基于HMAC一次性密码算法的扩展。它是OTP算法的一种 算法如下: TOTP = Truncate(HMAC-SHA-1(K, (T - T0) / X)) K 共享密钥 T 时间 T0 开始计数的时间步长 X 时间步长 代码实现 最简实现需要如下两个类 1.Base32.java public class Base32 { private static final char[] ALPHABET = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '2', '3', '4', '5', '6', '7' }; private static final byte[] DECODE_TABLE; static { DECODE_TABLE = new byte[128]; for (int i = 0; i < DECODE_TABLE....