首页 文章 精选 留言 我的

精选列表

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

在vscode中go编码发生的问题整理

引言 使用VsCode进行Go程序开发,我们肯定会碰到一些问题,这些问题有些是IDE的配置问题,有些是下载包的版本不一致问题,本文主要针对在开发过程中碰到的问题做一个简单的回顾和整理。 前期准备,必看 在进行问题纠错前,先确保自己正确下载了golang的官方工具集go-tool,如果不确定,就跟着我的步骤操作一遍,可能操作后,你的问题就解决了。 1、配置golang的源。 go env -w GOPROXY=https://goproxy.cn,direct 然后我们进行golang工具的默认工具服务下载: 2、Windows平台按下Ctrl+Shift+P,Mac平台按Command+Shift+P,这个时候VS Code界面会弹出一个输入框 3、我们在这个输入框中输入>go:install,下面会自动搜索相关命令,我们选择Go:Install/Update Tools这个命令,按下图选中并会回车执行该命令 在弹出的窗口选中所有,并点击“确定”按钮,进行安装。 等待安装完成,因为国内网络的特殊性,所以一定要谁知好go的代理源,不然总是下载失败。 执行这一步之后,重启Vscode,如果这个时候能够解决你的问题,那就不需要再往下看了。 具体问题解决方案 如果以上步骤不能解决你的问题,那就可以对应自己的问题来进行操作设置了。 一、VSCode中F12无法跳转 通用设置 点击左下角点击齿轮,选择设置界面(也可以直接快捷键CTRL+, 点击用户-扩展-go。进行如下设置 1、DocsTool修改成godoc 2、Use Language Server 去掉勾选 3、重启VsCode go mod模式 如果进行上述操作后还是不行,并且你是使用的go mod 模式,那还有一种比较特殊的方式可以解决这个问题。 执行命令go mod vendor导入依赖,这一步会将依赖转移至该项目根目录下的 vendor(自动新建) 文件夹下 此时F12就可以进行代码跳转了。 二、代码自动带出功能失效 这个问题一般都是因为 go mod模式的切换导致的。如果你的项目是使用的Gopath模式(如果有同学对Gopath和Gomod这俩种模式不了解的,可以看看我的文章如何看待go moudle并应用于工作),那么你需要设置 go env -w GO111MODULE=off 这时候自动带出功能就可能正确使用了 如果你使用的是go mod模式,设置 go env -w GO111MODULE=on 自动带出功能也可以恢复正常。 文章参考 go module 基本使用 vscode go 未找到任何定义 关于我 我的博客|文章首发 最后 本文到此结束,希望对你有帮助 😃 如果还有什么疑问或者建议,可以多多交流,原创文章,文笔有限,才疏学浅,文中若有不正之处,万望告知。 更多精彩技术文章汇总在我的 公众号【程序员工具集]】,持续更新,欢迎关注订阅收藏。

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

字符编码,不走索引的元凶之一

引言 2021年春节后的某个忙(mo)碌(yu)的下午,旁边的刘哥(老江湖,从业5年+)突然发出了一声叹息:“哎,mysql 出bug了,有索引不走”。 作为一个热心的人,我立即说到:“是不是,对索引字段做了函数操作”。 刘哥沉思了2秒,略有玩味的小眼神看了看我,慢慢说道:“温兄,常规的情况,对索引字段做函数操作,或者 字符串与数字比较造成的隐式转换,这次的SQL都不涉及”。 我一听顿时来了兴趣,略带兴奋的跟刘哥说:“刘哥,您发SQL,我也研究下”。 刘哥略有无奈的看了看我后,依旧把SQL发给了我: SELECT*FROMoc_orderoojoinorders_detailodONoo.order_id=od.order_id 不过须臾(近20分钟),我便查明了原因,装作大师的模样,一字一句的说到:”这是,字符集隐式转换问题” 刘哥听后,百度了下,直拍大腿,“对对,怎么把这个给忘了”。 定位问题 1.首先执行explain,查看执行计划 explain SELECT * FROM oc_order oo join orders_detail od ON oo.order_id =od.order_id 确实被驱动表orders_detail 没走索引 第一个表就是驱动表,后边的都是被驱动表,会从驱动板取出数据作为参数,到被驱动表查询匹配的记录。 --- 非专业术语,温安适 20210223 2. 查看被驱动表的索引 SHOW INDEX FROM test.orders_detail; 3.查看字段字符集 SELECT COLUMN_NAME,character_set_name,collation_name FROM INformation_schema.`COLUMNS` WHERE TABLE_NAME='oc_order' AND COLUMN_NAME='order_id'; SELECT COLUMN_NAME,character_set_name,collation_name FROM INformation_schema.`COLUMNS` WHERE TABLE_NAME='orders_detail'AND COLUMN_NAME='order_id'; Mysql字符集说明 utf8m4是utf8超集,utf8,与utf8mb4会比较,utf8会转换为utf8mb4. 验证 调整SQL语句,将oc_orders的order_id强制转换为utf8。 explain SELECT * FROM oc_order oo join orders_detail od ON convert(oo.order_id USING UTF8) =od.order_id 查看执行计划,确实走了索引 解决方式 调整SQL语句 SELECT * FROM oc_order oo join orders_detail od ON convert(oo.order_id USING UTF8) =od.order_id 调整字符集一致,建议 总结 可能不走索引的3种情况 对索引字段做函数操作 隐式类型转换,字符串与数字比较,字符串会转换为数字 隐式字符集转换,utf8m4是utf8超集,utf8,与utf8mb4会比较,utf8会转换为utf8mb4.

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

java安全编码指南之:Thread API调用规则

简介 java中多线程的开发中少不了使用Thread,我们在使用Thread中提供的API过程中,应该注意些什么规则呢? 一起来看一看吧。 start一个Thread Thread中有两个方法,一个是start方法,一个是run方法,两个都可以调用,那么两个有什么区别呢? 先看一下start方法: public synchronized void start() { if (threadStatus != 0) throw new IllegalThreadStateException(); group.add(this); boolean started = false; try { start0(); started = true; } finally { try { if (!started) { group.threadStartFailed(this); } } catch (Throwable ignore) { } } } private native void start0(); start()是一个synchronized的方法,通过它会去调用native的start0方法,而最终将会调用Thread的run()方法。 我们知道,创建一个Thread有两种方式,一种是传入一个Runnable,一个是继承Thread,并重写run()方法。 如果我们直接调用Thread的run()方法会发生什么事情呢? 先看一下run方法的定义: public void run() { if (target != null) { target.run(); } } 默认情况下, 这个target就是一个Runnable对象,如果Thread是通过Runnable来构建的话,调用Thread.run()会在当前线程中运行run方法中的内容。 如果Thread是以其形式构建,并且没有重新run()方法,那么直接调用Thread.run()将什么都不会做。 public void wrongStart(){ Runnable runnable= ()-> System.out.println("in thread running!"); Thread thread= new Thread(runnable); thread.run(); } public void correctStart(){ Runnable runnable= ()-> System.out.println("in thread running!"); Thread thread= new Thread(runnable); thread.start(); } 所以,上面两种调用方式,只有第二种是正确的。 不要使用ThreadGroup Thread中有个字段类型是java.lang.ThreadGroup,这个主要是用来给Thread进行分组,我们看下Thread的这个构造函数: public Thread(ThreadGroup group, Runnable target) { this(group, target, "Thread-" + nextThreadNum(), 0); } 上面的构造函数可以在传入runnable的同时传递一个ThreadGroup对Thread进行分组。 如果没有指定ThreadGroup,那么将会为其分配一个默认的default group。 ThreadGroup是做什么的呢?ThreadGroup是java 1.0引入的方法,主要是一次性的对一组thread进行操作。我们可以调用ThreadGroup.interrupt()来一次性的对整个Group的Thread进行interrupts操作。 虽然ThreadGroup提供了很多有用的方法,但是其中很多方法都被废弃了,比如:allowThreadSuspension(), resume(), stop(), 和 suspend(),并且ThreadGroup中还有很多方法是非线程安全的: ThreadGroup.activeCount() 这个方法主要是用来统计一个ThreadGroup中活动的线程个数,这个方法会统计还未启动的线程,同时也会受系统线程的影响,所以是不准确的。 ThreadGroup.enumerate() 这个方法是将ThreadGroup和子group的线程拷贝到一个数组中,但是如果数组太小了,多余的线程是会被自动忽略的。 ThreadGroup本身有一个 stop() 方法用来停止所有的线程,但是stop是不安全的,已经被废弃了。 那么我们该怎么去安全的停止很多个线程呢? 使用executor.shutdown()就可以了。 不要使用stop()方法 刚刚讲了ThreadGroup中不要调用stop()方法,因为stop是不安全的。 调用stop方法会立马释放线程持有的所有的锁,并且会抛出ThreadDeath异常。 因为会释放所有的锁,所以可能会造成受这些锁保护的对象的状态发生不一致的情况。 替代的方法有两种,一种是使用volatile flag变量,来控制线程的循环执行: private volatile boolean done = false; public void shutDown(){ this.done= true; } public void stopWithFlag(){ Runnable runnable= ()->{ while(!done){ System.out.println("in Runnable"); } }; Thread thread= new Thread(runnable); thread.start(); shutDown(); } 另外一种方法就是调用interrupt(), 这里我们要注意interrupt()的使用要点: 如果当前线程实例在调用Object类的wait(),wait(long)或wait(long,int)方法或join(),join(long),join(long,int)方法,或者在该实例中调用了Thread.sleep(long)或Thread.sleep(long,int)方法,并且正在阻塞状态中时,则其中断状态将被清除,并将收到InterruptedException。 如果此线程在InterruptibleChannel上的I/O操作中处于被阻塞状态,则该channel将被关闭,该线程的中断状态将被设置为true,并且该线程将收到java.nio.channels.ClosedByInterruptException异常。 如果此线程在java.nio.channels.Selector中处于被被阻塞状态,则将设置该线程的中断状态为true,并且它将立即从select操作中返回。 如果上面的情况都不成立,则设置中断状态为true。 先看下面的例子: public static void main(String[] args) { Runnable runnable= ()->{ while (!Thread.interrupted()) { System.out.println("in thread"); } }; Thread thread= new Thread(runnable); thread.start(); Thread.sleep(5000); thread.interrupt(); } 我们在while循环中调用了Thread.interrupted()方法用来判断线程是否被设置了中断位,然后在main方法中调用了thread.interrupt()来设置中断,最终可以正确的停止Thread。 注意,这里运行的Thread并没有被阻塞,所以并不满足我们上面提到的第一个条件。 下面我们再看一个例子: public static void main(String[] args) { Runnable runnable= ()->{ while (!Thread.interrupted()) { System.out.println("in thread"); try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } } }; Thread thread= new Thread(runnable); thread.start(); thread.interrupt(); } 这个例子和上面的例子不同之处就是在于,Thread中调用了sleep方法,导致Thread被阻塞了,最终满足了第一个条件,从而不会设置终端位,只会抛出InterruptedException,所以这个例子中线程是不会被停止的,大家一定要注意。 wait 和 await 需要放在循环中调用 为什么要放在循环中呢?因为我们希望wait不是被错误的被唤醒,所以我们需要在wait被唤醒之后,重新检测一遍条件。 错误的调用是放在if语句中: synchronized (object) { if (<condition does not hold>) { object.wait(); } // Proceed when condition holds } 正确的方法是放在while循环中: synchronized (object) { while (<condition does not hold>) { object.wait(); } // Proceed when condition holds } 本文的代码: learn-java-base-9-to-20/tree/master/security 本文已收录于 http://www.flydean.com/java-security-code-line-thread/ 最通俗的解读,最深刻的干货,最简洁的教程,众多你不知道的小技巧等你来发现! 欢迎关注我的公众号:「程序那些事」,懂技术,更懂你!

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

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

资源下载

更多资源
Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

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部分的功能。

用户登录
用户注册