首页 文章 精选 留言 我的

精选列表

搜索[终端编码],共10014篇文章
优秀的个人博客,低调大师

java安全编码指南之:锁的双重检测

简介 双重检测锁定模式是一种设计模式,我们通过首次检测锁定条件而不是实际获得锁从而减少获取锁的开销。 双重检查锁定模式用法通常用于实现执行延迟初始化的单例工厂模式。延迟初始化推迟了成员字段或成员字段引用的对象的构造,直到实际需要才真正的创建。 但是我们需要非常小心的使用双重检测模式,以避免发送错误。 单例模式的延迟加载 先看一个在单线程正常工作的单例模式: public class Book { private static Book book; public static Book getBook(){ if(book==null){ book = new Book(); } return book; }} 上面的类中定义了一个getBook方法来返回一个新的book对象,返回对象之前,我们先判断了book是否为空,如果不为空的话就new一个book对象。 初看起来,好像没什么问题,我们仔细考虑一下: book=new Book()其实一个复杂的命令,并不是原子性操作。它大概可以分解为1.分配内存,2.实例化对象,3.将对象和内存地址建立关联。 在多线程环境中,因为重排序的影响,我们可能的到意向不到的结果。 最简单的办法就是加上synchronized关键字: public class Book { private static Book book; public synchronized static Book getBook(){ if(book==null){ book = new Book(); } return book; }} double check模式 如果要使用double check模式该怎么做呢? public class BookDLC { private static BookDLC bookDLC; public static BookDLC getBookDLC(){ if(bookDLC == null ){ synchronized (BookDLC.class){ if(bookDLC ==null){ bookDLC=new BookDLC(); } } } return bookDLC; }} 我们先判断bookDLC是否为空,如果为空,说明需要实例化一个新的对象,这时候我们锁住BookDLC.class,然后再进行一次为空判断,如果这次不为空,则进行初始化。 那么上的代码有没有问题呢? 有,bookDLC虽然是一个static变量,但是因为CPU缓存的原因,我们并不能够保证当前线程被赋值之后的bookDLC,立马对其他线程可见。 所以我们需要将bookDLC定义为volatile,如下所示: public class BookDLC { private volatile static BookDLC bookDLC; public static BookDLC getBookDLC(){ if(bookDLC == null ){ synchronized (BookDLC.class){ if(bookDLC ==null){ bookDLC=new BookDLC(); } } } return bookDLC; }} 静态域的实现 public class BookStatic { private static BookStatic bookStatic= new BookStatic(); public static BookStatic getBookStatic(){ return bookStatic; }} JVM在类被加载之后和被线程使用之前,会进行静态初始化,而在这个初始化阶段将会获得一个锁,从而保证在静态初始化阶段内存写入操作将对所有的线程可见。 上面的例子定义了static变量,在静态初始化阶段将会被实例化。这种方式叫做提前初始化。 下面我们再看一个延迟初始化占位类的模式: public class BookStaticLazy { private static class BookStaticHolder{ private static BookStaticLazy bookStatic= new BookStaticLazy(); } public static BookStaticLazy getBookStatic(){ return BookStaticHolder.bookStatic; }} 上面的类中,只有在调用getBookStatic方法的时候才会去初始化类。 ThreadLocal版本 我们知道ThreadLocal就是Thread的本地变量,它实际上是对Thread中的成员变量ThreadLocal.ThreadLocalMap的封装。 所有的ThreadLocal中存放的数据实际上都存储在当前线程的成员变量ThreadLocal.ThreadLocalMap中。 如果使用ThreadLocal,我们可以先判断当前线程的ThreadLocal中有没有,没有的话再去创建。 如下所示: public class BookThreadLocal { private static final ThreadLocal<BookThreadLocal> perThreadInstance = new ThreadLocal<>(); private static BookThreadLocal bookThreadLocal; public static BookThreadLocal getBook(){ if (perThreadInstance.get() == null) { createBook(); } return bookThreadLocal; } private static synchronized void createBook(){ if (bookThreadLocal == null) { bookThreadLocal = new BookThreadLocal(); } perThreadInstance.set(bookThreadLocal); }} 本文分享自微信公众号 - 程序那些事(flydean-tech)。如有侵权,请联系 support@oschina.cn 删除。本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

优秀的个人博客,低调大师

java安全编码指南之:表达式规则

简介 在java编写过程中,我们会使用到各种各样的表达式,在使用表达式的过程中,有哪些安全问题需要我们注意的呢?一起来看看吧。 注意表达式的返回值 我们在使用JDK库的时候,一定要注意认真的读一下JDK中方法的含义和它的返回值。 有些返回值可能表示这个操作是否成功,有的返回值可能是方法操作的结果。我们看两个常见的例子: public void deleteFileWrong(){ File file= new File("/tmp/www.flydean.com.txt"); file.delete(); System.out.println("File delete success!"); } public void deleteFileRight(){ File file= new File("/tmp/www.flydean.com.txt"); if(file.delete()){ System.out.println("File delete success!"); } } 先看一个文件删除的例子,delete方法是有返回值的,所以我们在调用delete方法之后,一定要判断一下返回值,看是否删除成功。 再看一个常见的String中字符替换的例子: public void stringReplaceWrong(){ String url="www.flydean.com"; url.replace("www","WWW"); System.out.println("replaced url..."+url); } public void stringReplaceRight(){ String url="www.flydean.com"; url=url.replace("www","WWW"); System.out.println("replaced url..."+url); } 我们要记住,String是不可变的,所以它的replace方法,会返回一个替换过后的String,但是原String是不变的,所以我们需要将返回值重新赋值。 注意避免NullPointerException NullPointerException应该是最最常见的运行时异常了。怎么避免这个异常呢? 我们要做的就是在调用object的方法时候,一定要判断这个object是不是为空。 在JDK8之后,我们引入了Stream操作: public void streamWrong(Collection<Object> collection){ collection.stream().filter(obj->obj.equals("www.flydean.com")); } Stream操作的过程中,我们需要注意stream中的元素是否为空。 有时候,我们可能觉得已经判断是为空了,但是条件判断不准确,导致未知的异常,看下面这个例子: public int countWrong(Collection<Object> collection, Object object){ int count=0; if(collection ==null){ return count; } for(Object element: collection){ if((element ==null && object== null) || element.equals(object)){ count++; } } return count; } 这个例子是用来查找collection中到底有多少元素和object相同,如果两者都为空,也记为相同。 但是上面的例子有一个漏洞,它没有考虑element ==null 而 object !=null的情况,所以会导致NullPointerException的生成。 我们需要这样修改: public int countRight(Collection<Object> collection, Object object){ int count=0; if(collection ==null){ return count; } for(Object element: collection){ if((element ==null && object== null) || (element !=null && element.equals(object))){ count++; } } return count; } 数组相等的判断 如果我们需要比较两个数组是否相等,其实我们想比较的是两个数组中的元素是否相等。 我们知道数组是一个特殊的Object,那么数组对象也有一个equals方法,考虑下面的例子: public boolean compareWrong(){ int[] array1 = new int[10]; int[] array2 = new int[10]; return array1.equals(array2); } 返回的结果是false,因为数组直接使用了Object中定义的equals方法,我们看下该方法的定义: public boolean equals(Object obj) { return (this == obj); } 可以看到,该方法比较的是两个地址是否相等。所以我们的到了false结果。 其实,我们可以使用Arrays.equals工具类中的方法来进行两个数组的比较: public boolean compareRight(){ int[] array1 = new int[10]; int[] array2 = new int[10]; return Arrays.equals(array1, array2); } 基础类型的封装类间的比较 在java中,我们知道有一些基础类型像boolean, byte,char, short, int他们会有相对应的封装类型:Boolean,Byte,Character,Short,Integer等。 我们可以直接将基础类型的值赋值给封装类型,封装类型会自行进行转换。 考虑下面的例子: Boolean boolA=true; Boolean boolB=true; System.out.println(boolA==boolB); 结果是多少呢? 答案是true。为什么两个不同对象的比较会是true呢? 在回答这个问题之前,我们看一下字符串的比较: String stringA="www.flydean.com"; String stringB="www.flydean.com"; System.out.println(stringA==stringB); 这个我们大家应该都知道,因为String有一个字符串常量池,直接从字符串常量构建的String对象,其实是同一个对象。 同样的对于Boolean和Byte来说,如果直接从基础类值构建的话,也是同一个对象。 而对于Character来说,如果值的范围在\u0000 to \u007f,则属于同一个对象,如果超出了这个范围,则是不同的对象。 对于Integer和Short来说,如果值的范围在-128 and 127,则属于同一个对象,如果超出了这个范围,则是不同的对象。 再考虑下面的例子: Boolean boolA=true; Boolean boolC=new Boolean(true); System.out.println(boolA==boolC); 输出的结果是false,因为boolC使用了new关键字,构建了一个新的对象。 集合中类型不匹配 现在java集合可以通过指定类型,从而只存储特定类型的对象。考虑下面的一个例子: public void typeMismatch(){ HashSet<Short> shortSet= new HashSet<>(); for(int i=0;i<10;i++){ shortSet.add((short)i); shortSet.remove(i); } System.out.println(shortSet.size()); } 上面代码我们定义了一个Short的集合,然后将0-9添加进去,接着我们又调用了remove方法把i从集合删除。 但是最后输出结果是10,表明我们并没有删除成功。为什么呢? 看下HashSet的remove方法: public boolean remove(Object o) { return map.remove(o)==PRESENT; } remove方法的参数是Object,我们传入的i是int类型的,跟short不匹配,所以导致删除失败。 我们需要这样修改: public void typeMatch(){ HashSet<Short> shortSet= new HashSet<>(); for(int i=0;i<10;i++){ shortSet.add((short)i); shortSet.remove((short)i); } System.out.println(shortSet.size()); } Asset的副作用 我们会使用Asset语句在代码中做调试使用,在使用的过程中需要注意Asset语句不要对系统的业务逻辑产生副作用,也就是说即使Asset语句不运行,也不会修改代码的业务逻辑。 看下面的例子: public void assetWrong(ArrayList<Integer> list){ assert list.remove(0)>0; } 上的代码我们从list中删除第一个元素,并判断删除的元素是否大于0. 上面的代码如果assert语句不执行的话,会导致业务逻辑也不执行,所以需要修改成下面这样: public void assetRight(ArrayList<Integer> list){ int result=list.remove(0); assert result>0; } 本文的例子: learn-java-base-9-to-20/tree/master/security 本文已收录于 http://www.flydean.com/java-security-code-line-expresion/ 最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现! 欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!

优秀的个人博客,低调大师

AndroidStudio笔记(4)编码效率+1 的 File Templates

前言 这一篇是接着 AndroidStudio笔记(3)的,在上一篇中我们使用了 Live Templates的关键字来快速补全和生成代码,那有没有更近一步的骚操作?比如创建文件?答案是肯定的,那就是 File Templates。先看看今天的成果物预览图,如果不感兴趣就可以不看下去。 一键创建 TestViewPagerAdapter 创建TestViewPagerAdapter演示 创建RecyclerViewAdapter 创建RecyclerViewAdapter演示 正文 File Templates 的入口 File Templates 有两个入口。 File -> New -> Edit File Templates Edit File Templates File Templates Settings -> Editor -> File and Code Templates File and Code Templates 简单创建 ViewPagerAdapter File Templates 先来试试水,点击左上角的加号,新建一个模板,名字输入为 : ViewPagerAdapter,将编写好的模板代码放到内容框中: ViewPagerAdapter create 代码模板: package ${PACKAGE_NAME}; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentStatePagerAdapter; import java.util.ArrayList; import java.util.List; /** * Created by ${USER} on ${DATE} */ public class ${NAME} extends FragmentStatePagerAdapter { private List<Fragment> mFragments = new ArrayList<Fragment>(); public ${NAME}(FragmentManager fm, ArrayList fragmentArrayList) { super(fm); this.mFragments = fragmentArrayList; } @Override public Fragment getItem(int position) { return mFragments.get(position); } @Override public int getCount() { return mFragments.size(); } } 从上面的代码可以看出,事实上和我们平常写的代码没有什么区别,唯一不同的就是一些占位符,而AS又为我们内置了一些占位符 占位符 含义 ${PACKAGE_NAME} 包名 ${NAME} 弹出的窗口中输入的文件名称 ${USER} 当前电脑的登陆用户的名称 ${DATE} 当前如期 ${TIME} 当前时间 ${YEAR} 当前年份 ${MONTH} 当前月份 ${MONTH_NAME_SHORT} 月份的前三个单词,在中文中无区别,如:九月 ${MONTH_NAME_FULL} 月份的全称,如:九月 ${DAY} 号数,一个月中的几号 ${DAY_NAME_SHORT} 星期几的前三个单词,在中文中无区别,如:星期四 ${DAY_NAME_FULL} 星期几的全称,如:星期四 ${HOUR} 当前小时 ${MINUTE} 当前分钟 ${PROJECT_NAME} 当前项目名称 来看看我们的成果吧。 创建TestViewPagerAdapter演示 看到这里,可能大家会有一个疑问,既然能通过 {NAME}这样通过我们的输入来替换一些属性呢?这就是我们接下来的内容了,创建一个 RecyclerView 的Adapter。 前面说道,AS为我们提供了一些占位符,其实我们只需要按照格式,定义没有的占位符,那么就会显示相应的输入框了,如下: Des Create Des 进阶创建 RecyclerView.Adapter 还是和前面一样的步骤,创建新的 Code File Templates,复制下面的代码块放入内容区: package ${PACKAGE_NAME}; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import java.util.ArrayList; import java.util.List; /** * Created by ${USER} on ${DATE} */ #parse("File Header.java") public class ${NAME} extends RecyclerView.Adapter<${NAME}.${VIEW_HOLDER}>{ private List<${BEAN_NAME}> mDatas = new ArrayList<>(); @Override public ${VIEW_HOLDER} onCreateViewHolder(ViewGroup parent, int viewType) { return new ${VIEW_HOLDER}(LayoutInflater.from(parent.getContext()). inflate(R.layout.${LAYOUT_NAME}, parent, false)); } @Override public void onBindViewHolder(${NAME}.${VIEW_HOLDER} holder, int position) { } @Override public int getItemCount() { return mDatas.size(); } public void addItems(List<${BEAN_NAME}> datas){ this.mDatas.addAll(datas); notifyDataSetChanged(); } class ${VIEW_HOLDER} extends RecyclerView.ViewHolder { ${VIEW_HOLDER}(View itemView) { super(itemView); } } } 占位符解释 名字 含义 ${NAME} 文件名、类名 ${VIEW_HOLDER} ViewHolder的类名 ${BEAN_NAME} 实体类的名字 ${LAYOUT_NAME} 布局的名称 要使用这个模板,我们需要先创建 bean 和 layout 文件,我为了演示,已经是提前创建好了。 来看看成果吧: 创建RecyclerViewAdapter演示 看看,的确是达到了我们想要的结果,是不是节省了大量的时间?快来试一试吧! 最后 总结与结束 File Templates这个东西并不难,难的是我先要有代码,然后一步一步的改造成File Templates,个人也希望大家都能有自己的File Templates,我们不应该把大多数时间浪费到重复性的工作上。 另外就是File Templates是可以导出,不用怕丢失,所以我们可以放心大胆的收集和编写自己的File Templates,具体导出方法如下: 导出 彩蛋 另外,相信大家也发现了,我们在使用 File Templates 的时候我们需要先创建好相应的东西,如 bean、layout等,有没有一种方式能够让我省去这些创建步骤呢?答案是肯定的,敬请期待我的下一篇文章。 我的博客地址,第一时间会将文章发布在上面。 我的GitHub地址,所有文章中的源码都在这里。 FullScreenDeveloper

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册