首页 文章 精选 留言 我的

精选列表

搜索[异步扫描],共10006篇文章
优秀的个人博客,低调大师

添加PMD插件扫描潜在的bug

上一节使用checkstyle来规范你的项目主要解决了代码编码规范问题,比如缩进换行等。这次继续代码健康工具类PMD。 什么是PMD PMD真的不像checkstyle这样的东西所见即所得啊,去官网找了半天也没有找到解释。官网都直接说是PMD。 We've been trying to find the meaning of the letters PMD - because frankly, we don't really know. We just think the letters sound good together. 简单来说,PMD是一个代号,是一个静态代码检测工具。它可以用来检查 潜在的bug:空的try/catch/finally/switch语句 未使用的代码:未使用的局部变量、参数、私有方法等 可选的代码:String/StringBuffer的滥用 复杂的表达式:不必须的if语句、可以使用while循环完成的for循环 重复的代码:拷贝/粘贴代码意味着拷贝/粘贴bugs 总之,这是一个辅助我们检测潜在bug的工具,大大减少了人工审查成本,提高编码效率。 在gradle中使用 gradle还是一贯的简单,新建pmd.gradle /** * The PMD Plugin * * Gradle plugin that performs quality checks on your project’s Java source files using PMD * and generates reports from these checks. * * Tasks: * Run PMD against {rootDir}/src/main/java: ./gradlew pmdMain * Run PMD against {rootDir}/src/test/java: ./gradlew pmdTest * * Reports: * PMD reports can be found in {project.buildDir}/build/reports/pmd * * Configuration: * PMD is very configurable. The configuration file is located at {rootDir}/config/pmd/pmd-ruleset.xml * * Additional Documentation: * https://docs.gradle.org/current/userguide/pmd_plugin.html */ apply plugin: 'pmd' pmd { // The version of the code quality tool to be used. // The most recent version of PMD can be found at https://pmd.github.io toolVersion = "5.8.1" // The source sets to be analyzed as part of the check and build tasks. // Use 'sourceSets = []' to remove PMD from the check and build tasks. sourceSets = [project.sourceSets.main] // The directory where reports will be generated. reportsDir = file("$project.buildDir/reports/pmd") // Whether to allow the build to continue if there are warnings. ignoreFailures = false // Whether or not rule violations are to be displayed on the console. consoleOutput = true // The custom rule set files to be used. ruleSetConfig = resources.text.fromFile("$rootProject.projectDir/config/pmd/pmd-ruleset.xml") } 添加我们的pmd-ruleset.xml配置文件, 这个ruleset有很多种,我们可以先把所有的加上,然后在开发中调整,直到找到最合适的配置方案。因为全部的规则太多,会导致你花费大量的时间解决PMD问题。 ruleset内容可以在https://maven.apache.org/plugins/maven-pmd-plugin/examples/usingRuleSets.html 这里找到 然后在build.gradle中添加 apply from: 'pmd.gradle' 执行 ./gradlew check 或者 ./gradlew build 报告位置: build/reports/pmd/main.html 在maven中使用 maven需要把ruleset放到resources下读取,如果是单moudle项目,直接就可以。如果是多模块项目,需要额外做一些工作。 我们来新建一个项目来单独存储配置文件,build-tools. 在resources下放置ruleset。名字叫做pmd-ruleset.xml, 内容见https://maven.apache.org/plugins/maven-pmd-plugin/examples/usingRuleSets.html 然后maven install把这个子模块给安装到本地仓库。 接着修改parent pom <build> <pluginManagement> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-pmd-plugin</artifactId> <version>3.10.0</version> <dependencies> <dependency> <groupId>com.shuwei</groupId> <artifactId>build-tools</artifactId> <version>0.0.1</version> </dependency> </dependencies> <configuration> <sourceEncoding>utf-8</sourceEncoding> <minimumTokens>100</minimumTokens> <targetJdk>${java.version}</targetJdk> <excludes> <exclude>**/message/*.java</exclude> <exclude>**/generated/*.java</exclude> </excludes> <excludeRoots> <excludeRoot>target/generated-sources</excludeRoot> </excludeRoots> <rulesets> <ruleset>pmd-ruleset.xml</ruleset> </rulesets> <printFailingErrors>true</printFailingErrors> </configuration> <executions> <execution> <id>install</id> <phase>install</phase> <goals> <goal>pmd</goal> </goals> </execution> </executions> </plugin> </plugins> </pluginManagement> <!--所有子模块都要执行的plugin--> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-pmd-plugin</artifactId> </plugin> </plugins> </build> <reporting> <!--所有子模块都要执行的报告--> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-pmd-plugin</artifactId> </plugin> </plugins> </reporting> 和上一节checkstyle不同的时候,这里的plugin下新增了dependency节点。这个节点负责加载一些资源过来,比如我们的配置文件ruleset。所以,这个dependency要先于项目存在,所以才要先单独install一下。当然,也可以把这个项目放到maven私服上,这样更简单了。 依旧把pmd的运行绑定到install命令上,这样,我们运行maven install的时候就可以进行pmd检查了。 检查报告在 j-context/target/site/pmd.html 也可以单独运行pmd mvn pmd:pmd idea插件 搜索安装idea pmd插件,导入我们的ruleset, 然后在项目上右键,run pmd即可。 Ruleset default内容 可以在maven官网看到: https://maven.apache.org/plugins/maven-pmd-plugin/examples/usingRuleSets.html PMD 包含 16 个规则集,涵盖了 Java 的各种常见问题,其中一些规则要比其他规则更有争议: 基本(rulesets/basic.xml)—— 规则的一个基本合集,可能大多数开发人员都不认同它: catch 块不该为空,无论何时重写 equals(),都要重写 hashCode(),等等。 命名(rulesets/naming.xml)—— 对标准 Java 命令规范的测试:变量名称不应太短;方法名称不应过长;类名称应当以小写字母开头;方法和字段名应当以小写字母开头,等等。 未使用的代码(rulesets/unusedcode.xml)—— 查找从未使用的私有字段和本地变量、执行不到的语句、从未调用的私有方法,等等。 设计(rulesets/design.xml)—— 检查各种设计良好的原则,例如: switch 语句应当有 default 块,应当避免深度嵌套的 if 块,不应当给参数重新赋值,不应该对 double 值进行相等比较。 导入语句(rulesets/imports.xml)—— 检查 import 语句的问题,比如同一个类被导入两次或者被导入 java.lang 的类中。 JUnit 测试(rulesets/junit.xml)—— 查找测试用例和测试方法的特定问题,例如方法名称的正确拼写,以及 suite() 方法是不是 static 和 public。 字符串(rulesets/string.xml)—— 找出处理字符串时遇到的常见问题,例如重复的字符串标量,调用 String 构造函数,对 String 变量调用 toString() 方法。 括号(rulesets/braces.xml)—— 检查 for、 if、 while 和 else 语句是否使用了括号。 代码尺寸(rulesets/codesize.xml)—— 测试过长的方法、有太多方法的类以及重构方面的类似问题。 Javabean(rulesets/javabeans.xml)—— 查看 JavaBean 组件是否违反 JavaBean 编码规范,比如没有序列化的 bean 类。 终结函数(finalizer)—— 因为在 Java 语言中, finalize() 方法不是那么普遍(我上次编写这个代码也经是好多年前的事了),所以它们的使用规则虽然很详细,但是人们对它们相对不是很熟悉。这类检查查找 finalize() 方法的各种问题,例如空的终结函数,调用其他方法的 finalize() 方法,对 finalize() 的显式调用,等等。 克隆(rulesets/clone.xml)—— 用于 clone() 方法的新规则。凡是重写 clone() 方法的类都必须实现 Cloneable, clone() 方法应该调用 super.clone(),而 clone() 方法应该声明抛出 CloneNotSupportedException 异常,即使实际上没有抛出异常,也要如此。 耦合(rulesets/coupling.xml)—— 查找类之间过度耦合的迹象,比如导入内容太多;在超类型或接口就已经够用的时候使用子类的类型;类中的字段、变量和返回类型过多等。 严格的异常(rulesets/strictexception.xml)—— 针对异常的测试:不应该声明该方法而抛出 java.lang.Exception 异常,不应当将异常用于流控制,不应该捕获 Throwable,等等。 有争议的(rulesets/controversial.xml)—— PMD 的有些规则是有能力的 Java 程序员可以接受的。但还是有一些争议。这个规则集包含一些更有问题的检验,其中包括把 null 赋值给变量、方法中有多个返回点,以及从 sun 包导入等。 日志(rulesets/logging-java.xml)—— 查找 java.util.logging.Logger 的不当使用,包括非终状态(nonfinal)、非静态的记录器,以及在一个类中有多个记录器。 参考 PMD官网 Maven插件 Gradle插件 用 PMD 铲除 bug 关注我的公众号 唯有不断学习方能改变! -- Ryan Miao

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

异步社区本周预售新书

《算法详解(卷1)——算法基础》 Tim Roughgarden著 算法详解系列图书共有4卷,本书是第一卷——基础算法。本书共有6章,主要介绍了4个主题,它们分别是渐进性分析和大O表示法、分冶算法和主方法、随机化算法以及排序和选择。附录A和附录B简单介绍了数据归纳法和离散概率的相关知识。本书的每一章均有小测验、章末习题和编程题,这为读者的自我检查以及进一步学习提供了较多的便利。 《PHP和MySQL Web开发学习指南》 [澳] 汤姆·巴特勒(Tom Butler)凯文·雅克(Kevin Yank)著 全书共包括12章。第1章介绍了PHP和MySQL的安装;第2章和第3章,分别简单介绍了MySQL和PHP;第4章将创建一些Web页面;第5章介绍了关系数据库理论;第6章介绍了PHP编程基础;第7章扩展了第6章的主题:第8章讨论正则表达式的应用;第9章探讨了cookie和会话;第10章介绍了MySQL的管理技术;第11章探讨了高级SQL查询技巧;第12章介绍了二进制数据的应用和处理。 《Wireshark网络分析实战(第2版)》 [印度]甘德拉·库马尔·纳纳(Nagendra Kumar Nainar)著 Wireshark是最流行的一款网络嗅探软件。本书以示例方式详细讲解了如何使用Wireshark进行网络分析。通过本书的学习,读者可以掌握如何安装、配置Wireshark,如何使用Wireshark捕获数据,如何来对捕获的数据进行分析,以解决常见的网络问题等。 《量化交易学习指南——基于R语言》 [印度]帕勒姆·吉特(Param Jeet) 普拉桑特·瓦次(Prashant Vats)著 本书包括9章内容,分别从R编程基础、统计建模、计量经济学、小波分析、时序分析、算法交易、机器学习在交易中的应用、风险管理、优化、衍生品定价等内容。几乎每一个小标题都是一个热点,非常值得相关领域的人员学习阅读。 《全程软件测试(第3版)》 朱少民著 本书系统地总结了过去十年中软件测试发生的变化,浓缩了作者许多宝贵的软件测试经验。本书首先介绍对于软件测试的不同看法,全程软件测试的思想,软件测试的基础设施与TA框架、团队能力建设;然后逐步深入到测试的计划、设计、执行、持续反馈和改进;接着,讨论全程测试的思想,包括全程静态测试、全程性能测试、全程安全性、全程建模、全程可视化。本书最后展望了软件测试的未来。 本书适合软件测试人员阅读,也可作为相关专业人士的参考指南。 《RISC-V架构与嵌入式开发快速入门》 胡振波著 本书是一本介绍RISC-V架构嵌入式开发的入门书籍,以通俗的语言系统介绍了嵌入式开发的基础知识和RISC-V架构的内容,力求帮助读者快速掌握RISC-V架构的嵌入式开发技巧。本书共分为两部分。第一部分为第1~14章,基本涵盖了使用RISC-V架构进行嵌入式开发所需的所有关键知识。第二部分为附录部分,详细介绍了RISC-V指令集架构,辅以作者加入的背景知识解读和注解,以便于读者理解。

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

Android 异步消息分发机制

Android的消息机制–主要是指 Handler 的运行机制 和 MessageQueue 、 looper 的工作过程。 MessageQueueMessageQueue 消息队列,用来存储消息,虽然称为消息队列,但是它的存储结构是采用 单链表的数据结构来存储消息队列的。 包含两个操作插入(enqueueMessage) 和 读取(next) enqueueMessage :往消息链表 中插入一条信息 next:从消息列表中读取一天消息,并将其从消息队列中移除,next方法是一个无限循环的方法,如果消息列表里没有消息,那么会一直阻塞在这里。 LooperLooper 消息循环,用来处理消息。 Looper 会不停的从 MessageQueue 中查看是否有新的消息,如果有新消息就会立即处理Looper.java private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); } 在 Looper 的构造方法会创建一个MessageQueue,消息队列,然后将当前线程的对象保存起来。 public static @Nullable Looper myLooper() { return sThreadLocal.get(); } private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); } 问题 为什么Looper.prepare() 不能被调用两次?答:因为每次调用 Looper.prepare() 的时候的都会判断 ThreadLocal是否已经存储当前的 Looper 对象,如果已经存在就会抛出异常。 /** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the loop. */ public static void loop() { final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } final MessageQueue queue = me.mQueue; // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } // This must be in a local variable, in case a UI event sets the logger final Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } final long traceTag = me.mTraceTag; if (traceTag != 0 && Trace.isTagEnabled(traceTag)) { Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); } try { msg.target.dispatchMessage(msg); } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what); } msg.recycleUnchecked(); } } loop 方法就是一个死循环,跳出死循环的方法,是 MessageQueue的 Next 方法为空,即queue.next() 为空。Looper 调用 quit 方法或者 quitSafely 方法,通知消息队列退出,会使 MessageQueue 的 next 方法 返回为空。 loop 调用 MessageQueue 的 next 方法获取新的消息,如果 没有消息,next 方法会一直阻塞在那里,导致 loop 方法也会一直阻塞在那里。 如果 next 方法返回了新的消息,Looper 就会处理新的消息,调用 msg.target.dispatchMessage(msg),msg.target 就是发送这条消息的 Handler 对象(Handler 中有讲解)。这样就把代码逻辑切换到指定线程去执行了。 3. ThreadLocal线程内部的数据存储类, 通过它可以在指定的线程存储数据,而且存储后,只能在指定线程获取存储的数据,其他线程无法获取。 应用:当某些数据是以线程为作用域,并且不同线程具有不同数据 存在Looper中,并不是线程,作用是在每个线程中存储数据 问题:Handler内部是如何获取当前线程的 Looper 的?Handler.java public Handler(Callback callback, boolean async) { if (FIND_POTENTIAL_LEAKS) { final Class<? extends Handler> klass = getClass(); if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) == 0) { Log.w(TAG, "The following Handler class should be static or leaks might occur: " + klass.getCanonicalName()); } } mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; } 答:Handler 通过 ThreadLocal 获取每个线程的 Looper. ThreadLocal 可以在不同线程中互不干扰的存储并提供数据。 在调用 Looper.prepare() 的时候 “sThreadLocal.set(new Looper(quitAllowed));” - 创建了一个 Looper 实例并将一个Looper的实例放入了ThreadLocal 存储。然后在 Handler 中 调用“mLooper = Looper.myLooper();”-从 sThreadLocal.get() 获取到 Looper 对象。 问题:主线程中为什么可以默认使用Handler?答: 线程默认是没有Looper 的 ,如果要使用 Handler ,就必须为线程创建 Looper. 但是主线程,也就是 UI 线程,它就是 ActivityThread, ActivityThread 被创建的时候默认会初始化 Looper,当前UI线程调用了Looper.prepare()和Looper.loop()方法. Handler主要工作:发送和接收消息; public final boolean sendMessage(Message msg) { return sendMessageDelayed(msg, 0); } public final boolean sendMessageDelayed(Message msg, long delayMillis) { if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); } public boolean sendMessageAtTime(Message msg, long uptimeMillis) { MessageQueue queue = mQueue; if (queue == null) { RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue"); Log.w("Looper", e.getMessage(), e); return false; } return enqueueMessage(queue, msg, uptimeMillis); } private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); } 由上面可以看出 Handler 发送消息的过程就是向消息链表中插入了一条消息,MessageQueue 的 next 方法会返回这条消息给 Looper,Looper 收到消息,通过msg.target.dispatchMessage(msg) 交给 Handler 处理。 上面 enqueueMessage 方法首先 “msg.target = this;” 会把 this 赋值给 msg.target ,也就是说 把Handler 赋值给 msg的 target 属性。 public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } } 首先会检查 Message的 callback 是否为 null,不为空就处理通过 handleCallback 来处理消息。 callback 是一个 Runnable 接口,就是 Handler 的 post 方法传递的 Runnable 参数 private static void handleCallback(Message message) { message.callback.run(); } 举个栗子 第一种handler 的使用方式,post方法 Handler mHandler = new handler(); mHandler.post(new Runnable() { @Override public void run() { Log.e("TAG", Thread.currentThread().getName()); mTxt.setText("yoxi"); } }); 其次 查看 mCallback 是否为空,mCallback 是个接口 public interface Callback { public boolean handleMessage(Message msg); } Callback 提供另一种使用 Handler 的方式,不想派生Handler的子类,可以通过 Callback 来实现。 举个栗子 第二种使用 Handler 的方式 private Handler mHandler = new Handler() { public void handleMessage(android.os.Message msg) { switch (msg.what) { case value: break; default: break; } }; }; 最后,调用 Handler的 handleMessage 方法来处理消息。 原文发布时间为:2018-08-25本文来自云栖社区合作伙伴“Android开发中文站”,了解相关信息可以关注“Android开发中文站”。

资源下载

更多资源
腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Rocky Linux

Rocky Linux

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

WebStorm

WebStorm

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

用户登录
用户注册