数据专栏

智能大数据搬运工,你想要的我们都有

科技资讯:

科技学院:

科技百科:

科技书籍:

网站大全:

软件大全:

本文内容参考自《PHP安全之道》。
0x00 服务器请求伪造 (SSRF: Server-Side Request Forgery)
SSRF漏洞是一种由攻击者利用某服务器请求来获取内网或外网系统资源,从服务器发起请求的一个安全漏洞。由于它是由server端发起的,所以能够请求到与它相连而与外网隔离的内部系统。一般情况下,SSRF攻击的目标是企业的内网系统。
SSRF行程的原因大多是由于服务器提供了从其他服务器应用中获取数据的功能且没有对目标地址进行过滤和限制,比如从指定url地址中获取网页文本内容、加载指定地址的图片、文档等。
SSRF漏洞流程如下:
攻击者构造请求 ==> server端根据攻击者构造的请求对内网server进行请求 ==> 内网server将请求反馈给该server ==> 该server将获取到的内网资源返回给攻击者。
0x01 SSRF漏洞的危害
SSRF漏洞的危害主要是使服务器资源泄露。很多网站给用户提供了将外部url所指向的图片、文件直接保存到当前系统上的功能。一般来说,如果该url无效系统会忽略或者返回错误。
攻击者可以使用一些不常见但有效的url, 比如: http://127.0.0.1:81/dir/images/ http://127.0.0.1:8080/dir/images/ http://127.0.0.1:8888/dir/images/ http://127.0.0.1:22/dir/images/ http://127.0.0.1:3306/dir/images/
然后根据server端返回的信息来判断端口是否开放。大部分应用不会去判断端口,只要是有效的url就会去请求。而大部分的TCP服务会在建立连接时发送banner信息(欢迎语,在banner信息中可以得到软件开发商、软件名称、版本、服务类型等信息)。banner信息是使用ASICC编码的, 能做作为原始的html数据展示。当然,服务端在处理返回信息的时候一般不会直接展示,但是不同的错误代码,返回信息的长度以及返回时间都可以作为依据来判断远程服务器的端口状态。
以下是一段未经过安全编码的代码,很可能被攻击者利用: if(isset($_GET['url'])){ $url = $_GET['url']; $file_name = '/tmp/'.date('YmdHis').'_'.rand().'.txt'; $ch = curl_init(); $time_out = 5; curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $time_out); $content = curl_exec($ch); $fp_in = fopen($file_name, 'w'); fwrite($fp_in, $content); fclose($fp_in); $fp_out = fopen($file_name, 'r'); $result = fread($fp_out, filesize($file_name)); fclose($fp_out); echo $result; }else{ echo '请输入url'; }
由于不安全编码很容易被攻击者利用对内网web系统进行指纹识别, 识别内网系统所使用的框架、平台、模块。通过对对应用框架的指纹(独特的文件和目录)可以识别出应用的类型甚至版本。
0x02 在PHP中容易引起SSRF的函数
PHP中用来获取远程或本地诶荣的函数, 比如 file_get_contents , fsocketopen , curl 等容易造成漏洞。 $content = file_get_contents($_GET['url']); echo $content;
上面的代码中, 因为未安全的使用file_get_contents, 如果攻击者把'/etc/password' 放在参数url中, 则会直接展示该server的用户列表。
0x03 容易造成SSRF的场景 页面分享 翻译服务 图片加载与下载(包括自动下载外部图片) 图片、文章的收藏功能
0X04 SSRF漏洞的防御
合理控制访问权限,尽可能地控制PHP的访问权限,防止穿透到内网以及访问非授权的资源。
1. 无特殊需求, 不接收用户提交的url, 防止攻击者构建url进行穿透访问
2. 在没有对服务器本身we你按访问需求的情况下,建议开启PHP的 open_basedir 配置, 限制访问的目录. open_basedir = /home/www/sites/
或者在init_set()中设置: ini_set('open_basedir', '/home/www/sites/');
开启open_basedir可以有效的防止 file_get_contents、curl、fsocketopen 等函数对服务器敏感文件的访问。
3.如果必须接受用户传递的url,使用白名单 + 黑名单机制。
比如, 只允许用户请求特定的url,仅允许http、https请求,限制请求的host(屏蔽内网), 限制请求的端口,限制可访问的文件类型(如只允许访问html/图片等静态文件),统一返回错误信息(避免将请求的具体的错误信息返回给用户)。 使用parse_url来获取url的各个部分来进行url的判断, 使用pathinfo来判断文件类型 要屏蔽内网的ip(127., 172., 192., 10.)和localhost作为host的url $arr_scheme_white_list = ['http', 'https']; //protocol限制 $arr_host_white_list = ['www.example.com']; //host白名单限制可访问的外网 $arr_host_black_list = ['127.', '172.', '192.', '10.', 'localhost']; //host黑名单限制访问内网 $arr_port_white_list = ['80', '443']; //端口限制 $arr_type_white_list = ['html', 'gif', 'jpg', 'jpeg', 'png']; //文件格式限制 $url = $_GET['url']; $arr_url_info = parse_url($url); $host = strtolower($arr_url_info['host']) ?? ''; $scheme = strtolower($arr_url_info['scheme'] ?? ''); $port = $arr_url_info['port'] ?? ''; //scheme/protocol限制 if(!in_array($scheme, $arr_scheme_white_list, true)){ die('访问地址错误'); } //host限制 if(!in_array($host, $arr_host_white_list, true)){ die('访问地址错误'); } foreach($arr_host_black_list AS $black_host){ if(strpos($host, $black_host) === 0){ die('访问地址错误!'); } } //端口限制 if(!in_array($port, $arr_port_white_list, true)){ die('访问地址错误'); } //文件类型限制 $type = pathinfo($url, $arr_type_white_list); if(!in_array($type, $arr_type_white_list, true)){ die('访问地址错误'); } //获取文件内容 $file_info = file_get_contents($url); if(empty($file_info)){ die('访问失败'); }else{ echo $file_info; }
软件开发
2020-06-16 16:35:00
自序
这次面试的公司有一点点多,主要是因为毕业后前两份工作找的都很草率,这次换工作就想着,emm,毕业三年了,该找个工作好好沉淀几年了。
先说下这次面试的结果吧: 到 hr 面的:阿里、美团、滴滴、金山云、每日一淘、火币、宜信、旺店通、黄包车、linkedMe
其他:
小米 (四轮技术面,大概4个小时的样子,大数据部门,不知道是不是四面的负责人嫌弃我木有大数据的经验,不过我确实木有哈)
京东 (电话面试一轮+现场两轮,面试完快中午一点了,说是让我先回家,后面让hr 电话联系我 一周后一面的面试官问我还考虑京东不,如果考虑的话,就进行后续。当时已经有了更好的offer,就说不考虑了,希望以后有机会再合作,所以没有后续了)
头条 (二面完gg,我的算法确实菜哈,然后leetcode又只刷过10道题,去面头条,确实有些作死的节奏,实在是对不起帮我内推的石冲大佬)
爱奇艺 (电话面试一轮+现场两轮,到技术终面了,这个怪我,面试官也一直很忙,然后我俩就互相一直改面试时间,最后定的那个面试时间我还迟到了一个小时,还是时间到了才给hr 打电话说一个小时后才能到 虽然我知道这样做非常不好,但是当时情况比较复杂,自己根本忙不过来,一直在面试,也没有办法中途给hr 打电话说一下。一天面两家,两家离的还比较远的小伙伴吸取一下教训。 我本来是想约第二天下午的,hr 就想当天,结果就晚上7:40开始二面了,面到9点,然后木有然后了)
有赞 (电话面试一轮+现场两轮,到技术终面了,面试官“base考虑杭州吗”,我“啊,你们北京不是也需要人吗,最好北京哈,杭州暂时不考虑”,然后木有然后了,哈哈。
后面面阿里的时候我就自己打脸了,面试官“base杭州考虑吗”,我“面过阿里我就去杭州,面不过我就在北京”。爱,就要大胆的说出来。)
这次面试基本都是三~四轮技术面,很多都是每一轮都有至少一道算法题,所以准备换工作的小伙伴,算法可以搞起来了哈,leetcode easy和medium 难度的就ok了,当然如果你也要刷hard 难度的题,是更好的哈。
我作为一名只刷过10道leetcode的渣渣,表示以后要好好刷leetcode了,拯救一下自己的智商。准备面头条的小伙伴,那就medium 和 hard难度的搞起来吧。你们加油,我就不想了。
群里有很多小伙伴怀疑我是985、211或者研究生毕业,都不是的哈,渣本(但是我还是很爱我的母校的),16年毕业,我一个妹子都可以做到的,你们更可以做到,所以相信自己,去努力就好了。
这篇文章主要是记录一下自己的面试经历,分享一些群里小伙伴们都很关注的面试题,然后文章末尾我会推荐一些书,完全免费推荐的哈,我个人感觉不错的,可以提升技术的,当然面试中也会对你有特别大的帮助。 阿里的面试题不会分享哈,这次主要分享tmdj、以及其他公司的一些面试题,把我分享的这些面试题都掌握了,对想去面阿里的小伙伴的帮助也是非常非常大的。
当然,面试题只是起一个查漏补缺的作用,并不是让你直接去整理答案,去背答案的哈。一个合格的面试官,是会针对你的简历去问的,即每个人的面试题都是不一样的。
头条
二轮技术面,17:00~20:25,晚饭时间hr 小姐姐还特贴心的带我体验了一把传说中的头条餐厅,不超过半小时 1.聊项目,画项目架构图,画一个用户从发起请求 到接收到响应 中间经过哪些服务 每个服务做什么事情 的流程图 2.讲项目中的难点、挑战,你是如何解决的 3.redis 中有几种类型 & 各自底层怎么实现的 & 项目中哪个地方用了什么类型,怎么使用的 4.redis如何实现分布式锁,zk如何实现分布式锁,两者的区别。如果service还没执行完,分布式锁在redis中已经过期了,怎么解决这种问题 5.synchronized底层实现,加在方法上和加在同步代码块中编译后的区别、类锁、对象锁 6.锁升级的过程 7.java运行时区域 及 各个区域的作用、对GC的了解、java内存模型 及 为什么要这么设计 8.对索引的理解,组合索引,索引的最佳实践 9.countDownLatch用过没有,在项目中如何使用的,对aqs 的了解 10.写生产者消费者问题,考虑高并发的情况,可以使用Java 类库,白纸写代码 11.如下图所示
12.设计一个发号器,考虑集群和高并发的情况,要求发号器生成的id是递增趋势,通过id可以区分出来是今天生成的id还是昨天生成的id,但是生成的id中不能直接带有日期,要具有一定的混淆功能,白纸写代码 13.一个二位数组,每个元素都可以往上下左右四个方向走,寻找最长递增路径。如下图所示,最长递增路径即红色字体路径。白纸写代码。
美团
电话面试(40分钟)+现场三轮技术面试(3.5小时)+hrbp面试(30分钟) 1.数据库和缓存的一致性问题。先更新数据库,再更新缓存,若更新完数据库了,还没有更新缓存,此时有请求过来了,访问到了缓存中的数据,怎么办? 2.聚簇索引/非聚簇索引,mysql索引底层实现,为什么不用B-tree,为什么不用hash,叶子结点存放的是数据还是指向数据的内存地址,使用索引需要注意的几个地方 3.mysql默认的事务隔离级别,mvcc,rr怎么实现的,rc如何实现的 4.mysql间隙锁有没有了解,死锁有没有了解,写一段会造成死锁的sql语句,死锁发生了如何解决,mysql有没有提供什么机制去解决死锁 5.谈下对GC的了解,何为垃圾,有哪些GC算法,有哪些垃圾回收器,cms和g1的区别,emm,还有一个直击灵魂的问题,看过cms的源码吗,笑cry 6.有没有排查过线上oom的问题,如何排查的 7.有没有使用过jvm自带的工具,如何使用的 8.假设有下图所示的一个full gc 的图,纵向是内存使用情况,横向是时间,你如何排查这个full gc的问题,怎么去解决你说出来的这些问题
9.说说对java中集合类的理解,项目中用过哪些,哪个地方用的,如何使用的 10.对CAS的理解,CAS带来的问题,如何解决这些问题 11.volatile底层、synchronized底层、锁升级的过程、MESI 12.ehcache支持哪些缓存 13.juc有研究没有,讲一讲 14.聊项目,画项目架构图,画一个用户从发起请求 到接收到响应 中间经过哪些服务 每个服务做什么事情 的流程图 15.讲项目中的难点、挑战,如何解决的,项目这一块会问的特别细 16.如何保证RocketMQ 消息的顺序性,如何解决重复消费问题 17.项目中如何保证接口的幂等操作 18.讲一讲对redis 的了解,项目中如何使用的,哪个地方使用的,为什么要使用 19.哨兵机制、redis两种备份方式的区别,项目中用的哪种,为什么 20.讲一讲对分布式锁的了解 21.项目中系统监控怎么做的 22.如何理解Spring中的AOP 和 IOC,以及DI,读过Spring源码没有 23.读过MyBatis源码没有 24.说一个你了解最多的框架,说出你的理解 25.如何理解分布式事务,为什么会出现这个问题,如何去解决,了解哪些分布式事务中间件 26.聊一聊对分库分表的理解 27.hystrix功能 & 在项目中怎么使用的 & hystrix 怎么检测断路器是否要开启/关闭 & hystrix 实现原理,除hystrix之外的其他熔断限流中间件有了解没有,了解多少说多少 28.dubbo有了解没有 29.怎么理解java 中和 mysql 中的乐观锁、悲观锁 30.一致性hash
滴滴
现场三轮技术面试 + 一轮hrbp面(4小时5分钟) > 1.聊项目,画项目架构图,画一个用户从发起请求 到接收到响应 中间经过哪些服务 每个服务做什么事情 的流程图,讲数据库设计 > > 2.处理过线上oom问题没有,如何处理的 > > 3.遇到过线上服务器cpu飙高的情况没有,如何处理的 > > 4.线上有没有遇到其他问题,如何处理的 > > 5.对线程池的理解,项目中哪个地方使用了,如何使用的,用的Excutor框架中的哪个实现类,为什么用这个 > > 6.对CAS的理解,CAS带来的问题,如何解决这些问题 > > 7.volatile底层、synchronized底层、锁升级的过程、MESI > > 8.对mysql索引的理解、对组合索引的理解、索引的最佳实践 > > 9.分布式锁的实现、对比redis分布式锁 & zk分布式锁 > > 10.唯一id如何实现的,snowflake实现原理,snowflake有哪些问题,如何11.避免根据订单号可以推算出今天的订单量 > > 12.如果线上一个功能是用栈结构实现的,使用过程中要注意哪些问题,为什么 > > 13.怎么理解线程安全 > > 14.怎么理解接口幂等,项目中如何保证的接口幂等 > > 15.怎么理解微服务,服务如何划分,可以从哪几个方面去划分,为什么这样划分,微服务带来了哪些好处,哪些坏处,如何看待这个问题 > > 16.如何理解网关,网关带来的好处和坏处,如何解决 > > 17.hystrix功能 & 在项目中怎么使用的 & hystrix 怎么检测断路器是否要开启/关闭 & hystrix 实现原理 > > 18.怎么理解命令模式和观察者模式,手写一个观察者模式或者命令模式的代码,策略模式也行 > > 19.掌握哪些设计模式,常用哪些,项目中如何使用的,为什么用这个,不用那个,手写一个线程安全的单例模式 > > 20.如何设计一个秒杀系统 > > 21.如果我现在就是要实现每秒10w请求,不能熔断限流,如何去设计 > > 22.假设现在双十一零点,大量下单请求,如何对这些订单进行分库分表,为什么 > > 23.服务A调用服务B中一个接口,服务B调用服务C中一个接口,如何实现若服务B响应服务A成功,则服务C一定响应服务B成功,需要考虑系统性能问题 > > 24.递归使用中有什么需要注意的地方,递归写法一般可以用什么去替换 > > 25.有两个表,table a,table b,写sql查询出仅在table a中的数据、仅在table b中的数据、既在table a 又在table b 中的数据 > > 26.spring 源码有了解没有 > > 27.myBatis源码有了解没有 > > 28.mysql事务隔离级别、mvcc
我:既然现在很多业务线都是Go了,有没有考虑把剩余的业务线也转成Go呀?面试官:我认为,语言只是工具,语言不应该是影响开发的一个因素吧。面试官说的很有道理。
京东
电话面试(30分钟)+现场两轮技术面试(1小时40分钟),面完12:50,说让我先回来,后续hr 电话和我联系 一周后一面的面试官问我还考虑京东吗?,回复不考虑了,希望以后有机会再合作 1.一个final修饰的属性,定义的时候没有初始化,在无参构造函数中初始化,可以吗,为什么 2.说说对java中集合类的理解,项目中用过哪些,哪个地方用的,如何使用的,为什么不用其他的集合类 3.hashMap,concurrentHashMap底层实现, 4.list删除是怎么实现的,遍历的时候可以删除吗,为什么 5.redis中有哪些数据结构,了解过其底层怎么实现的吗,和java中相似的数据结构的对比 6.redis是单线程的还是多线程的,为什么这么快 7.redis hash中某个key过大,变为String类型的大key,怎么处理,使用中如何避免出现这种问题 8.设计模式在项目中哪个地方用到了,怎么使用的,能不能画一个你熟悉的设计模式的UML图,手写单例模式,手写静态内部类实现的单例模式 9.讲一讲mysql索引,实际工作中,哪些场景用了b+tree索引,哪些场景用了hash索引 10.explain 可以看到哪些信息,什么信息说明什么,explain的结果列讲一下 11.Spring源码看过没有,会多少讲多少 12.MyBatis源码看过没有,会多少讲多少 13.cas,cas的缺点,如何解决 14.aqs,countDownLatch如何实现 15.线程池如何实现,核心线程数和最大线程数设置成多少,为什么这么设置,项目中哪个地方使用了线程池,使用时需要注意什么 16.mysql事务隔离级别,幻读,脏读,项目中用什么事务隔离级别,为什么 17.volatile底层原理、synchronized实现机制, 18.对XA、TCC的理解,了解哪些分布式事务框架,有什么缺点 19.feign 和 dubbo,了解多少说多少 20.eureka 和 zookeeper,了解多少说多少 21.hystrix 和 sentinel,了解多少说多少 22.Spring cloud alibaba,了解多少说多少 23.对分库分表、读写分离的了解,了解多少说多少 24.画一下java 线程几个状态 及 状态之间互相转换的图 25.聊项目,画项目架构图,画一个用户从发起请求 到接收到响应 中间经过哪些服务 每个服务做什么事情 的流程图,讲数据库设计 具体到部分表中有哪些字段 26.emm 我们部门体量比较大,可能需要加班,到凌晨两三点的那种,也可能通宵,通宵是大促期间,你能接受吗 27.emm 也会加班到十点,这个不是大促期间,但也不是每天,非常态情况,你能接受吗,你在哪里住,过来要多久,有男朋友吗?一起去吃午饭吧,我们这边有员工餐厅,不了不了,我回家吃饭吧
下面是面试tmdj 之外的公司中遇到的一些问题哈,tmdj 中已经被问到的就不再重复写了,只写一下个别公司中我还记得的面试题
others
火币 :四轮技术面试+一轮hr 面试(4小时),后来hr 小姐姐和我说,她们正常是两轮技术面试,因为技术面试完面试官一直没有找到她,然后,emm,就又来了一轮技术面试,又来了一轮技术面试,笑cry 1.kafka 如何保证消息顺序消费、在consumer group 中新增一个consumer 会提高消费消息的速度吗、那如果我想提高消息消费的速度,我要怎么办 2.redis几种数据结构 及 底层,项目中如何使用的redis 3.哨兵机制、选举算法 4.一致性hash 5.redis是单线程的还是多线程的,为什么速度这么快 6.多路复用的几种方式以及区别 7.对线程池的理解,在项目中如何使用的,多个线程之间如何共享数据,多个进程之间如何共享数据 8.hashMap、concurrentHashMap的区别 及 底层实现、hashMap和hashTable 的区别 9.什么是红黑树,什么是b-tree,为什么hashMap中用红黑树不用其他树 10.对mysql 索引的理解,为什么mysql索引中用b+tree,不用b-tree 或者其他树,为什么不用hash 索引 11.数据库和缓存的双写一致性问题
每日一淘 :三轮技术面试+一轮hrbp 面 1.用过哪些Object类的方法,如何使用的 2.java如何实现序列化的,Serialization底层如何实现的 3.countDownLatch如何实现的 4.项目中监控报警机制如何做的,说说你的了解 5.线上服务器cpu飙高,如何处理这个问题 6.服务A调用服务B,用户请求服务A,发现返回较慢,如何定位这个问题 7.TIME_WAIT是什么状态还记得吗,什么情况下网络会出现这个状态
linkedMe :二轮技术面试+一轮hr面试 1.内核态 和 用户态、cas 和 sout 哪个用到了内核态和用户态的切换 2.哪些典型的应用用的是udp 3.线程池有了解吗,项目中如何使用的 4.计算密集型/IO密集型 任务 分别如何设置线程池的核心线程数和最大线程数,为什么这么设置 5.假如我下午5点要和5个人一起开会,但是这5个人现在都出去了,不在公司,但是今天会回来,问,我如何开这场会,用java 并发方面的知识回答
旺店通 :5小时+,中午我还木有吃饭,下午面试时候真是饿的要死,而且下午脑细胞死了好多好多 先机试(50分钟时间,三选二,不联网,明确告知机试不通过没有后续) 一面给面试官讲一下自己机试题的思路,面试官运行看结果,然后问了几个问题(什么是B-tree,什么是B+tree之类的) 笔试(10道选择题+2道数据库+2道算法题,30分钟) 二面给面试官讲自己的机试题的思路,面试官运行看结果,然后给面试官讲笔试题,一道一道讲为什么这么写,过程中面试官可能会改题,然后问你怎么解决修改后的题,然后又问了几个题 三面开始正常面试,但不是看简历问,一部分是简历上的,一部分是看面试官心情 hr面 当场给了offer,但是啊,从他家出来的时候的想法就是,早知道下午这个样子,不如中午吃个午饭,回家好好睡一觉 想去他家的小伙伴就好好写代码吧,多看java 中一些方法的实现,因为机试的题目都要求不能用java 中提供的方法,要自己写,然后还要好好准备算法
算法题 [1,1,2,2,3,4,4,5,5,5] 找出不重复的元素(黄包车) 反转链表,要求时间复杂度O(N),空间复杂度O(1) (火币) 非递归实现斐波那契数列 (爱奇艺) 这一周股市价格为[2,6,1,4,8],求哪一天买入哪一天卖出,可获得最大收益,最大收益为多少 (爱奇 - 艺) 按照箭头方向查找二叉树 (金山云)
表a b c之间用id关联,求阴影部分的数据 (金山云)
一个整形无序数组,里面三个数只和等于一个目标值,求这三个数 (小米) 链表问题 (小米)
扑克牌问题 (小米) 有十张扑克牌,从上面开始抽,抽出一张放桌子上,然后再抽出一张放扑克牌的最下面,这样循环往复的操作,直到手里的牌都没有了。这时,桌子上牌的顺序正好是1 2 3 4 5 6 7 8 9 10。要求写代码求出原顺序 手写大顶堆 (linkedMe) 手写LRU 算法 (火币) 字符串相加 (滴滴) 两个数字类型的字符串,直接转int或者double肯定都放不下,然后求这两个数的和,返回值还是字符串,15分钟时间,要求无bug 寻找目标值位置 (滴滴) 有一个二维数组,数组横向有序,纵向有序,求目标值的位置,10分钟时间 求字符串“efabcbaefehiabcba”中最长的回文数,不去重(美团) 反转int类型的值x,不要借用String,只用int 即可。&& 针对该程序,写出其应有的测试用例 (美团) top K 问题(每日一淘) HR面
真诚待人,以真心换真心,不要弄虚作假,HR 问什么问题,如实回答即可。在回拒offer 时候,也请好好说话。
tips
其实面试过程中,你是可以感受到哪些面试官是真的很欣赏你,哪些只是想找一个可以干活的人的,最后一定要去一个欣赏你的面试官那里,因为待遇真的会不一样 (假装我体验过只是想找我干活的leader哈,很感激以前遇到的每一位leader 都很欣赏我,给我我想要的空间去做自己想做的事情,真的非常感谢你们)
嗯,免费安利环节到了,学不了吃亏学不了上当哈 《深入理解Java虚拟机》 《Java并发编程的艺术》 《Java并发编程实战》 《MySQL技术内幕 InnoDB存储引擎》 《Redis设计与实现》 《JVM G1源码分析和调优》 《重新定义Spring Cloud实战》 《Redis深度历险:核心原理与应用实践》 《Spring技术内幕》《myBatis技术内幕》 《深入拆解Java虚拟机》 等等等等 需要更多大厂面试题的,请关注公众号:程序零世界(C0W1024) 回复”学习“ 即可免费领取
来源:Bella的技术轮子 ,作者:Bella酱
软件开发
2020-06-16 16:16:00
1.并发事务可能带来的问题
脏读:事务A正在修改数据,事务B也来访问数据,此时访问的是修改后的数据,若事务A操作不成功导致回滚,那么事务B查到的就是错误的数据了。
不可重复读:A多次读取同一数据,B在A多次读取的过程中修改了该数据,导致A多次读取到的数据不一致。
幻读:A多次查询同一条件,B在A多次查询的过程中增加了数据,导致A查询到了新增的数据。
2.隔离级别
读取未提交(Read uncommit) :可以读取未提交的数据。可能导致脏读、不可重复读、幻读。
读取已提交(Read commit) :只能读取已提交的数据。可能导致不可重复读、幻读。
**可重复读(repeatable commit):**多次读取的数据是一致的。事务开始另一事务就不能对该数据修改。可能导致幻读。
串行化(Serializable) :事务按顺序执行。安全但花销很大。
MySQL默认隔离级别为repeatable commit。
软件开发
2020-06-16 15:54:00
oracle 数据库时报错 : java.sql.SQLException: sql injection violation, syntax error, expect DIMENSION, actual COMMA : select a.material_type num,d.text material_type, c.equipment_model model ,c.serial_number from mv_spm_spt_material_info a, t_ept_config b, t_ept_base_info c ,t_base_dictionary d where b.material_id = a.id and b.equipment_id = c.base_id and a.material_type = d.value and d.key = 'MATERIAL_TYPE' and a.module_level = 'A' and a.module_type = 'B000' and c.serial_number = '116381' at com.alibaba.druid.wall.WallFilter.check(WallFilter.java:582) at com.alibaba.druid.wall.WallFilter.connection_prepareStatement(WallFilter.java:185) at com.alibaba.druid.filter.FilterChainImpl.connection_prepareStatement(FilterChainImpl.java:446) at com.alibaba.druid.filter.FilterAdapter.connection_prepareStatement(FilterAdapter.java:928) at com.alibaba.druid.filter.FilterEventAdapter.connection_prepareStatement(FilterEventAdapter.java:122) at com.alibaba.druid.filter.FilterChainImpl.connection_prepareStatement(FilterChainImpl.java:446) at com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl.prepareStatement(ConnectionProxyImpl.java:342) at com.alibaba.druid.pool.DruidPooledConnection.prepareStatement(DruidPooledConnection.java:311) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at org.hibernate.jdbc.BorrowedConnectionProxy.invoke(BorrowedConnectionProxy.java:74) at com.sun.proxy.$Proxy32.prepareStatement(Unknown Source) from www.1b23.com 框架
由于配置了druid连接池的 wall 拦截器,SQL防火墙拦截了你的SQL,解决办法: 关闭 druid 的 wall 拦截
软件开发
2020-06-16 15:47:00
事务:对数据增删改查的一个集合(里面包含多个操作)。
原子性:事务是一个原子操作单元,对数据的修改要么全都执行,要么都不执行
一致性:执行事务的前后,数据都必须是一致的
(例如A+B=500,不管AB之间如何转账,钱的总数还是500)
隔离性:同一时间只允许一个事务访问同一数据,不同事务之间不会干扰
持久性:事务完成后,事务对数据库的所以更新将会保留在数据库中,不能回滚
软件开发
2020-06-16 15:37:00
Js--DOM详解
博客说明 文章所涉及的资料来自互联网整理和个人总结,意在于个人学习和经验汇总,如有什么地方侵权,请联系本人删除,谢谢!
概念 Document Object Model 文档对象模型 将标记语言文档的各个组成部分,封装为对象。可以使用这些对象,对标记语言文档进行CRUD的动态操作 W3C DOM 标准被分为 3 个不同的部分: 核心 DOM - 针对任何结构化文档的标准模型 Document:文档对象 Element:元素对象 Attribute:属性对象 Text:文本对象 Comment:注释对象 Node:节点对象,其他5个的父对象 XML DOM - 针对 XML 文档的标准模型 HTML DOM - 针对 HTML 文档的标准模型
核心DOM模型
Document:文档对象 创建(获取):在html dom模型中可以使用window对象来获取 window.document document 方法: 获取Element对象: getElementById() : 根据id属性值获取元素对象。id属性值一般唯一 getElementsByTagName():根据元素名称获取元素对象们。返回值是一个数组 getElementsByClassName():根据Class属性值获取元素对象们。返回值是一个数组 getElementsByName(): 根据name属性值获取元素对象们。返回值是一个数组 创建其他DOM对象: createAttribute(name) createComment() createElement() createTextNode() 属性
Element:元素对象 获取/创建:通过document来获取和创建 方法: removeAttribute():删除属性 setAttribute():设置属性 Node:节点对象,其他5个的父对象 特点:所有dom对象都可以被认为是一个节点 方法: CRUD dom树: appendChild():向节点的子节点列表的结尾添加新的子节点。 removeChild() :删除(并返回)当前节点的指定子节点。 replaceChild():用新节点替换一个子节点。 属性: parentNode 返回节点的父节点。
HTML DOM 标签体的设置和获取:innerHTML 使用html元素对象的属性 控制元素样式 使用元素的style属性来设置 //修改样式方式1 div1.style.border = "1px solid red"; div1.style.width = "200px"; //font-size--> fontSize div1.style.fontSize = "20px"; 提前定义好类选择器的样式,通过元素的className属性来设置其class属性值。
感谢 黑马程序员
以及勤劳的自己
软件开发
2020-06-16 14:12:00
快排 选定一个基准元素 x 分区过程, >= x 的数全放到它的右边, <= x 的数全放到它的左边 交换,如果此时两个指针 i、j 没有相遇过,则 i 一定是遇到了一个 >= x 的数, j 遇到了一个 <= x 的数,此时需要将 a[i]、a[j] 交换 再对左右区间分别重复第二步,直到区间中只有一个数时返回 void quick_sort(int[] a, int l, int r) { if (l >= r) return; int i = l-1, j = r+1, x = a[l + r>>>1]; while (i < j) { while(a[++i] < x); while(a[++j] > x); if (i < j) swap(a, i, j); } quick_sort(a, l, j); quick_sort(a, j+1, r); }
快速选择 :快应用于寻找数组数组中的第 k 小/第 n-k 大的数 int quick_select(int[] a, int l, int r, int k) { if (l >= r) return a[k]; int p = (int)(Math.random() * (r-l+1)) + l, x = a[p], i = l-1, j = r+1; while (i < j) { do i++; while (a[i] < x); do j--; while (a[j] > x); if (i < j) swap(a, i, j); } if (j >= k) return quick_select(a, l, j, k); else return quick_select(a, j+1, r, k); }
归并 取中心点 mid = l + r >>> 1 递归处理左半边 [l, m] 和右半边 [mid+1, r] ,当某一次归并处理完毕后,两个区间内的数分别有序 最后用双指针算法将两个区间合并 void merge_sort(int[] a, int l, int r) { if (l >= r) return; int m = l + r >>> 1; merge_sort(a, l, m); merge_sort(a, m+1,r); int i = l, j = m+1, k = 0, b[] = new int[r-l+1]; while (i <= m && j <= r) { if (a[i] <= a[j]) b[k++] = a[i++]; else b[k++] = a[j++]; } while (i <= m) b[k++] = a[i++]; while (j <= r) b[k++] = a[j++]; for (i = l, j = 0; i <= r; i++, j++) a[i] = b[j]; }
软件开发
2020-06-29 10:18:00
哈喽,大家好,今天为大家分享的是自定义手动分页的函数封装 有小伙伴要问了,laravel框架不是本身就提供了一个分页函数吗?干嘛还要自己在写一个。 因为在做某些需求的时候并不是能够直接进行分页的,而是查出数据之后,还需要做一些逻辑筛选或者合并之类的操作后,才可以进行分页,只有这样才能满足功能需求。
废话不多说,直接看分析: 通过原分页函数进入到了这里,看到其实主要用到的是“LengthAwarePaginator”这个类,所以我将它单独拿出来,重新做了封装,建议将函数放入到公共控制器里,方便随时调用,封装函数如下: // 自定义手动分页函数 public function diypage($request,$list,$prePage=25){ $total =collect($list)->count(); if(isset($request->page)){ $current_page =intval($request->page); $current_page =$current_page<=0?1:$current_page; }else{ $current_page = 1; } $url = $url='http://'.$_SERVER['SERVER_NAME'].$_SERVER["REQUEST_URI"]; if(strpos($url,'&page')) $url=str_replace('&page='.$request->page, '',$url); $item =array_slice(collect($list)->toArray(),($current_page-1)*$prePage,$prePage); return new LengthAwarePaginator($item,$total,$prePage,$current_page,[ 'path'=>$url, 'pageName'=>'page' ]); }
以上就是我封装好的函数,直接调用即可,调用方式如下: $list=$this->diypage(Request $request,$alldata,10);
视图调用如下: {{ $list->links() }}
好了,本次分享结束,大家有学到吗?
软件开发
2020-06-29 10:18:00
union的用法
需求
:联合两个记录表做一个类似时间轴的东西,因为要排序和分类,unionAll $field = [ 't.create_time', 'h.unit', 'h.door', 'h.build_id', ]; $where = [ ['t.creater_id', '=', $admin_id], ['t.company_id', '=', $userInfo['company_id']], ['t.if_delete', '=', 0], ]; $temp_field = $field; $temp_field['content'] = 'message'; $temp_field[] = '0 as policy_id'; $temp_field[] = '1 as source'; $temp_field[] = 'record_id as id'; $res = Db::field($temp_field) ->table(KpurposeRecord::instance()->getTable()) ->alias('t') ->join('k_house h', 'h.house_id=t.house_id and h.if_delete = 0', 'left') ->where($where) ->unionAll(function ($query) use ($admin_id, $field, $where) { $temp_field = $field; $temp_field[] = 'message'; $temp_field[] = 'policy_id'; $temp_field[] = '2 as source'; $temp_field[] = 'reserve_id as id'; $query->field($temp_field) ->table(Kreservation::instance()->getTable()) ->alias('t') ->join('k_house h', 'h.house_id=t.house_id and h.if_delete = 0', 'left') ->where($where); }); $res->order('create_time', 'desc'); if (!empty($page) && !empty($limit)) { $res->page($page, $limit); } $list['customer_development'] = $res->select();
PS
field字段顺序要一致,不然数据会错乱 最后,应尽量避免这种操作,写法麻烦,查询效率还不高
软件开发
2020-06-29 09:50:00
Python用了快两年了吧,其中有些东西一直是稀里糊涂地用,import便是我一直没有明白的东西。曾经有过三次解决它的机会,我都因得过且过、一拖再拖而没能化敌为友。今天下午,它又给了我一次机会,我想我还是从了它的心愿吧。
故事是从这篇台湾同胞的博客《Python的import陷阱》[1](网址见底部)开始的,然后又跳到了Python社区的PEP 328提案[2],再结合过去的经验以及一些测试,我想我大概懂了吧。下面是我的总结,希望内容能够言简意赅、易于理解。
import语句有什么用?import语句用来导入其他python文件(称为模块module),使用该模块里定义的类、方法或者变量,从而达到代码复用的目的。为了方便说明,我们用实例来说明import的用法,读者朋友可以跟着尝试(尝试时建议使用python3,python2和python3在import的表现有差异,之后会提到)。
首先,先建立一个文件夹Tree作为工作目录,并在其内建立两个文件m1.py和m2.py,在m1.py写入代码: import os import m2 m2.printSelf()
在m2.py写入代码: def printSelf(): print('In m2')
打开命令行,进入到Tree目录下,敲下python m1.py运行,发现没有报错,且打印出In m2,说明这样使用import没有问题。
由此我们总结出import语句的第一种用法: import module_name 。
即import后直接接模块名。在这种情况下,Python会在两个地方寻找这个模块,第一是sys.path(通过运行代码import sys; print(sys.path)查看),os这个模块所在的目录就在列表sys.path中,一般安装的Python库的目录都可以在sys.path中找到(前提是要将Python的安装目录添加到电脑的环境变量),所以对于安装好的库,我们直接import即可。第二个地方就是运行文件(这里是m1.py)所在的目录,因为m2.py和运行文件在同一目录下,所以上述写法没有问题。
用上述方法导入原有的sys.path中的库没有问题。但是,最好不要用上述方法导入同目录下的文件!因为这可能会出错。演示这个错误需要用到import语句的第二种写法,所以先来学一学import的第二种写法。在Tree目录下新建一个目录Branch,在Branch中新建文件m3.py,m3.py的内容如下: def printSelf(): print('In m3')
如何在m1中导入m3.py呢,请看更改后的m1.py: from Branch import m3 m3.printSelf()
总结import语句的第二种用法: from package_name import module_name 。
一般把模块组成的集合称为包(package)。与第一种写法类似,Python会在sys.path和运行文件目录这两个地方寻找包,然后导入包中名为module_name的模块。
现在我们来说明为什么不要用import的第一种写法来导入同目录下的文件。在Branch目录下新建m4.py文件,m4.py的内容如下: def printSelf(): print('In m4')
然后我们在m3.py中直接导入m4,m3.py变为: import m4 def printSelf(): print('In m3')
这时候运行m1.py就会报错了,说没法导入m4模块。为什么呢?我们来看一下导入流程:m1使用from Branch import m3导入m3,然后在m3.py中用import m4导入m4。
看出问题了吗?m4.py和m1.py不在同一目录,怎么能直接使用import m4导入m4呢。(读者可以试试直接在Tree目录下新建另一个m4.py文件,你会发现再运行m1.py就不会出错了,只不过导入的是第二个m4.py了)
面对上面的错误,使用python2运行m1.py就不会报错,因为在python2中,上面提到的import的两种写法都属于相对导入,而在python3中,却属于绝对导入。话说到了这里,就要牵扯到import中最关键的部分了——相对导入和绝对导入。
我们还是谈论python3的import用法。上面提到的两种写法属于绝对导入,即用于导入sys.path中的包和运行文件所在目录下的包。对于sys.path中的包,这种写法毫无问题;导入自己写的文件,如果是非运行入口文件(上面的m1.py是运行入口文件,可以使用绝对导入),则需要相对导入。
比如对于非运行入口文件m3.py,其导入m4.py需要使用相对导入: from . import m4 def printSelf(): print('In m3')
这时候再运行m1.py就ok了。列举一下相对导入的写法: from . import module_name。导入和自己同目录下的模块。 from .package_name import module_name。导入和自己同目录的包的模块。 from .. import module_name。导入上级目录的模块。 from ..package_name import module_name。导入位于上级目录下的包的模块。 当然还可以有更多的.,每多一个点就多往上一层目录。
不知道你有没有留神上面的一句话——“上面的m1.py是运行入口文件,可以使用绝对导入”,这句话是没问题的,也和我平时的做法一致。那么,运行入口文件可不可以使用相对导入呢?比如m1.py内容改成: from .Branch import m3 m3.printSelf()
答案是可以,但不能用python m1.py命令,而是需要进入到Tree所在的目录,使用python -m Tree.m1来运行。为什么?关于前者,PEP 328提案中的一段文字好像给出了原因: Relative imports use a module's name attribute to determine that module's position in the package hierarchy. If the module's name does not contain any package information (e.g. it is set to ' main ') then relative imports are resolved as if the module were a top level module, regardless of where the module is actually located on the file system.
我不太懂,但是又有一点明白。我们应该见过下面一段代码: if __name__ == '__main__': main()
意思是如果运行了当前文件,则 __name__ 变量会置为 __main__ ,然后会执行main函数,如果当前文件是被其他文件作为模块导入的话,则 __name__ 为模块名,不等于 __main__ ,就不会执行main函数。
比如对于上述更改后的m1.py,执行python m1.py命令后,会报如下错误: Traceback (most recent call last): File "m1.py", line 1, in from .Branch import m3 ModuleNotFoundError: No module named '_main_.Branch'; '__main__' is not a package
据此我猜测执行python m1.py命令后,当前目录所代表的包'.'变成了 __main__ 。
那为什么python -m Tree.m1就可以呢?那位台湾老师给出了解释:
执行指令中的-m是为了让Python预先import你要的package或module给你,然后再执行script。
即不把m1.py当作运行入口文件,而是也把它当作被导入的模块,这就和非运行入口文件有一样的表现了。
注意,在Tree目录下运行python -m m1是不可以的,会报 ImportError: attempted relative import with no known parent package的错误。因为m1.py中的from .Branch import m3中的. ,解释器并不知道是哪一个package。使用python -m Tree.m1,解释器就知道.对应的是Tree这个package。
那反过来,如果m1.py使用绝对导入(from Branch import m3),能使用python -m m1运行吗?我试了一下,如果当前目录是Tree就可以。如果在其他目录下运行,比如在Tree所在的目录(使用python -m Tree.m1运行),就不可以。这可能还是与绝对导入相关。
(之前看到了一个大型项目,其运行入口文件有一大堆的相对导入,我还傻乎乎地用python直接运行它。之后看到他给的样例运行命令是带了-m参数的。现在才恍然大悟。)
理解import的难点差不多就这样了。下面说一说import的其他简单但实用的用法。 import moudle_name as alias。有些module_name比较长,之后写它时较为麻烦,或者module_name会出现名字冲突,可以用as来给它改名,如import numpy as np。 from module_name import function_name, variable_name, class_name。上面导入的都是整个模块,有时候我们只想使用模块中的某些函数、某些变量、某些类,用这种写法就可以了。使用逗号可以导入模块中的多个元素。 有时候导入的元素很多,可以使用反斜杠来换行,官方推荐使用括号。 from Tkinter import Tk, Frame, Button, Entry, Canvas, Text, \ LEFT, DISABLED, NORMAL, RIDGE, END # 反斜杠换行 from Tkinter import (Tk, Frame, Button, Entry, Canvas, Text, LEFT, DISABLED, NORMAL, RIDGE, END) # 括号换行(推荐)
说到这感觉import的核心已经说完了。再跟着上面的博客说一说使用import可能碰到的问题吧。
问题1描述:ValueError: attempted relative import beyond top-level package。直面问题的第一步是去了解熟悉它,最好是能复现它,让它躺在两跨之间任我们去践踏蹂躏。仍然是上面四个文件,稍作修改,四个文件如下: # m1.py from Branch import m3 m3.printSelf() # m2.py def printSelf(): print('module2') # m3.py from .. import m2 # 复现的关键在这 # print(__name__) def printSelf(): print('In m3') # m4.py def printSelf(): print('In m4')
运行python m1.py,就会出现该问题。问题何在?我猜测,运行m1.py后,m1代表的模块就是顶层模块(参见上面PEP 328的引用),而m3.py中尝试导入的m2模块所在的包(即Tree目录代表的包)比m1的层级更高,所以会报出这样的错误。
怎么解决呢?
将m1.py的所有导入改为相对导入,然后进入m1.py的上层目录,运行python -m Tree.m1即可。
对于使用import出现的其他问题,碰到了再接着更新。
外部资料网址:
[1] https://link.zhihu.com/?target=https%3A//medium.com/pyladies-taiwan/python-%25E7%259A%2584-import-%25E9%2599%25B7%25E9%2598%25B1-3538e74f57e3
[2] https://link.zhihu.com/?target=https%3A//www.python.org/dev/peps/pep-0328/%23id1 本文仅供学习之用,版权归原作者所有,如有侵权请联系删除。
在学习Python的道路上肯定会遇见困难,别慌,我这里有一套学习资料,包含40+本电子书,800+个教学视频,涉及Python基础、爬虫、框架、数据分析、机器学习等,不怕你学不会! https://shimo.im/docs/JWCghr8prjCVCxxK/ 《Python学习资料》
关注公众号【Python圈子】,优质文章每日送达。
软件开发
2020-06-29 09:40:00
上篇文章 和小伙伴们说了 Spring 源码中 XML 文件的解析流程,本来可以继续往下走看加载核心类了,但是松哥还是希望能够慢一点,既然要学就学懂,在 XML 文件解析的过程中还涉及到一些其他的类和概念,因此我就先用几篇文章介绍一下这些涉及到的概念或者类,然后我们再继续往下看。
本文要和大家介绍的是 上篇文章 中涉及到的 EntityResolver 类,看看这个类到底是干嘛用的。
本文是 Spring 源码系列第四篇,阅读前面文章有助于更好理解本文: Spring 源码解读计划 Spring 源码第一篇开整!配置文件是怎么加载的? Spring 源码第二弹!XML 文件解析流程
先来回顾下,在 EntityResolver 这个类在 上篇文章 哪里出现了。
我们在讲到 doLoadDocument 方法时,在该方法中调用 loadDocument 方法时,传递的第二个参数就是一个 EntityResolver 实例,当时我们说这个是用来处理文件的验证方式的,但是到底是怎么处理的,今天我们就来看下。
1.XML 验证模式
要了解 EntityResolver,就得先来看看 XML 文件验证模式。
现在我们大多数情况下可能都是使用 JSON 传递数据,XML 使用较少,可能有的小伙伴对 XML 文件的一些规则还不太熟悉,我这里稍微说一下。
XML 是指可扩展标记语言(eXtensible Markup Language),它是一种标记语言,类似 HTML;XML 标签没有被预定义,需要用户自行定义标签,也就是 XML 文件中的节点都是用户自定义的。XML 文件从设计之初就是为了传输数据,而非显示数据。
一般来说,一个 XML 文件由六个部分组成: 文档生命 元素 属性 注释 CDATA 区 处理指令
虽然说 XML 文件本身是没有预定义 XML 标签,但是当 XML 文件作为框架的配置时,对于 XML 标签还是要有一定的约束,否则每个人都按照自己的喜好定义 XML 标签,框架就没法读取这样的 XML 文件了。
在 XML 技术中,开发者可以通过一个文档来约束一个 XML 的文档中的标签,这个文档称之为约束。遵循 XML 语法的 XML 我们称之为格式良好的 XML,而遵循 XML 约束的 XML 我们称之为有效的 XML。XML 约束文档主要定义了在 XML 中允许出现的元素名称、属性及元素出现的顺序等等。
要想约束 XML 标签,有两种方式: DTD Schema
DTD(Document Type Definition),全称为文档类型定义,一个 DTD 约束文件我们既可以定义在 XML 文件内部,也可以定义一个本地文件,也可以引用一个网络上的公共的 DTD。
XML Schema 也是一种用于定义和描述 XML 文档结构与内容的模式语言,相比于 DTD,Schema 对于名称空间的支持更加友好,同时也支持更多的数据类型,而且它的约束能力也比较强大,另外还有非常重要的一点是,Schema 文档本身也是 XML 文档,而不是像 DTD 一样使用自成一体的语法。
所以,Schema 目前在 XML 约束这块更具备优势,也在逐渐替代 DTD。
大家在日常开发中,这两种约束可能都见过,但是有的人可能没注意。我给大家简单举一个例子。
早期的 Spring 配置头部是这样的(Spring2.x),这就是 DTD 约束:
现在大家看到的 Spring 配置头部一般都是这样,这就是 Schema 约束:
schema 约束对命名空间有着很好的支持,命名空间可以防止命名冲突,schema 中的名称空间和约束文件都是成对出现的。
有了约束,XML 文件中该写什么不该写什么就固定下来了,这样框架才能成功解析出 XML 文件。
但是大家同时也发现了一个新的问题,无论是 DTD 还是 Schema 约束,给出的约束文件地址都是一个在线地址,这就意味着项目启动时必须能够访问到该在线地址,才能加载到约束文件,如果访问在线约束文件失败,那么项目启动也会失败。
为了解决这个问题,框架一般都是将约束文件放在本地的,在本地哪里呢?实际上就在你下载的 jar 包里。以 spring-beans 为例,在下载的 jar 包里有如下两个文件:
spring.handlers 文件内容如下: http\://www.springframework.org/schema/c=org.springframework.beans.factory.xml.SimpleConstructorNamespaceHandler http\://www.springframework.org/schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler http\://www.springframework.org/schema/util=org.springframework.beans.factory.xml.UtilNamespaceHandler
这其实一个映射配置,每一个名称空间对应的处理类在这里进行配置。
spring.schemas 文件内容如下(部分): http\://www.springframework.org/schema/beans/spring-beans-2.0.xsd=org/springframework/beans/factory/xml/spring-beans.xsd http\://www.springframework.org/schema/beans/spring-beans-2.5.xsd=org/springframework/beans/factory/xml/spring-beans.xsd http\://www.springframework.org/schema/beans/spring-beans-3.0.xsd=org/springframework/beans/factory/xml/spring-beans.xsd http\://www.springframework.org/schema/beans/spring-beans-3.1.xsd=org/springframework/beans/factory/xml/spring-beans.xsd http\://www.springframework.org/schema/beans/spring-beans-3.2.xsd=org/springframework/beans/factory/xml/spring-beans.xsd http\://www.springframework.org/schema/beans/spring-beans-4.0.xsd=org/springframework/beans/factory/xml/spring-beans.xsd http\://www.springframework.org/schema/beans/spring-beans-4.1.xsd=org/springframework/beans/factory/xml/spring-beans.xsd http\://www.springframework.org/schema/beans/spring-beans-4.2.xsd=org/springframework/beans/factory/xml/spring-beans.xsd http\://www.springframework.org/schema/beans/spring-beans-4.3.xsd=org/springframework/beans/factory/xml/spring-beans.xsd http\://www.springframework.org/schema/beans/spring-beans.xsd=org/springframework/beans/factory/xml/spring-beans.xsd
可以看到,各种版本以及没有版本号的约束文件,都对应了同一个文件,就是 org/springframework/beans/factory/xml/spring-beans.xsd,打开这个文件目录,我们就可以看到约束文件:
所以我们虽然在 Spring 的 XML 配置中看到的约束文件是一个在线地址,实际上约束文件是从本地 jar 中读取的。
2.两种解析器
EntityResolver 就是用来处理 XML 验证的。我们先来看下 EntityResolver 接口的定义: public interface EntityResolver { public abstract InputSource resolveEntity (String publicId, String systemId) throws SAXException, IOException; }
接口中就只有一个方法,就是加载约束文件。在 Spring 中,EntityResolver 的实现类是 DelegatingEntityResolver: public class DelegatingEntityResolver implements EntityResolver { public static final String DTD_SUFFIX = ".dtd"; public static final String XSD_SUFFIX = ".xsd"; private final EntityResolver dtdResolver; private final EntityResolver schemaResolver; public DelegatingEntityResolver(@Nullable ClassLoader classLoader) { this.dtdResolver = new BeansDtdResolver(); this.schemaResolver = new PluggableSchemaResolver(classLoader); } public DelegatingEntityResolver(EntityResolver dtdResolver, EntityResolver schemaResolver) { this.dtdResolver = dtdResolver; this.schemaResolver = schemaResolver; } @Override @Nullable public InputSource resolveEntity(@Nullable String publicId, @Nullable String systemId) throws SAXException, IOException { if (systemId != null) { if (systemId.endsWith(DTD_SUFFIX)) { return this.dtdResolver.resolveEntity(publicId, systemId); } else if (systemId.endsWith(XSD_SUFFIX)) { return this.schemaResolver.resolveEntity(publicId, systemId); } } return null; } @Override public String toString() { return "EntityResolver delegating " + XSD_SUFFIX + " to " + this.schemaResolver + " and " + DTD_SUFFIX + " to " + this.dtdResolver; } }
在 DelegatingEntityResolver 类中: 首先通过两种不同的后缀来区分不同的约束。 然后定义了 dtdResolver 和 schemaResolver 两个不同的变量,对应的类型分别是 BeansDtdResolver 和 PluggableSchemaResolver,也就是 dtd 和 schema 的约束验证分别由这两个类来处理。 在 resolveEntity 方法中,根据解析出来不同的后缀,分别交由不同的 EntityResolver 来处理。resolveEntity 解析中有两个参数,如果是 dtd 解析的话,publicId 是有值的,如果是 schema 解析,publicId 为 null,而 systemId 则始终指向具体的约束文件。
由于现在大部分都是 schema 约束,所以这里我们就来重点看下 PluggableSchemaResolver 类的实现: public class PluggableSchemaResolver implements EntityResolver { public static final String DEFAULT_SCHEMA_MAPPINGS_LOCATION = "META-INF/spring.schemas"; private static final Log logger = LogFactory.getLog(PluggableSchemaResolver.class); @Nullable private final ClassLoader classLoader; private final String schemaMappingsLocation; @Nullable private volatile Map schemaMappings; public PluggableSchemaResolver(@Nullable ClassLoader classLoader) { this.classLoader = classLoader; this.schemaMappingsLocation = DEFAULT_SCHEMA_MAPPINGS_LOCATION; } public PluggableSchemaResolver(@Nullable ClassLoader classLoader, String schemaMappingsLocation) { Assert.hasText(schemaMappingsLocation, "'schemaMappingsLocation' must not be empty"); this.classLoader = classLoader; this.schemaMappingsLocation = schemaMappingsLocation; } @Override @Nullable public InputSource resolveEntity(@Nullable String publicId, @Nullable String systemId) throws IOException { if (logger.isTraceEnabled()) { logger.trace("Trying to resolve XML entity with public id [" + publicId + "] and system id [" + systemId + "]"); } if (systemId != null) { String resourceLocation = getSchemaMappings().get(systemId); if (resourceLocation == null && systemId.startsWith("https:")) { resourceLocation = getSchemaMappings().get("http:" + systemId.substring(6)); } if (resourceLocation != null) { Resource resource = new ClassPathResource(resourceLocation, this.classLoader); try { InputSource source = new InputSource(resource.getInputStream()); source.setPublicId(publicId); source.setSystemId(systemId); if (logger.isTraceEnabled()) { logger.trace("Found XML schema [" + systemId + "] in classpath: " + resourceLocation); } return source; } catch (FileNotFoundException ex) { if (logger.isDebugEnabled()) { logger.debug("Could not find XML schema [" + systemId + "]: " + resource, ex); } } } } return null; } private Map getSchemaMappings() { Map schemaMappings = this.schemaMappings; if (schemaMappings == null) { synchronized (this) { schemaMappings = this.schemaMappings; if (schemaMappings == null) { try { Properties mappings = PropertiesLoaderUtils.loadAllProperties(this.schemaMappingsLocation, this.classLoader); schemaMappings = new ConcurrentHashMap<>(mappings.size()); CollectionUtils.mergePropertiesIntoMap(mappings, schemaMappings); this.schemaMappings = schemaMappings; } catch (IOException ex) { throw new IllegalStateException( "Unable to load schema mappings from location [" + this.schemaMappingsLocation + "]", ex); } } } } return schemaMappings; } @Override public String toString() { return "EntityResolver using schema mappings " + getSchemaMappings(); } } 在这个类中,一上来先通过 DEFAULT_SCHEMA_MAPPINGS_LOCATION 变量定义了 spring.schemas 文件的位置。 getSchemaMappings 方法则是将 spring.schemas 文件中的内容读取成一个 Map 加载进来。 在 resolveEntity 方法中,根据 systemId 找到文件路径,systemId 是 http\://www.springframework.org/schema/beans/spring-beans.xsd 格式,文件路径则是 org/springframework/beans/factory/xml/spring-beans.xsd ,如果第一次没有加载到,就把用户的 https: 替换成 http: 再去加载。 有了文件路径,接下来调用 ClassPathResource 去获取一个 Resource 对象,这块可以参考本系列 第二篇 ,这里我就不再赘述。 最后构造一个 InputSource 返回即可。
在 上篇文章 中,我们获取 EntityResolver 是通过 getEntityResolver 方法来获取的: protected EntityResolver getEntityResolver() { if (this.entityResolver == null) { // Determine default EntityResolver to use. ResourceLoader resourceLoader = getResourceLoader(); if (resourceLoader != null) { this.entityResolver = new ResourceEntityResolver(resourceLoader); } else { this.entityResolver = new DelegatingEntityResolver(getBeanClassLoader()); } } return this.entityResolver; }
这里最终返回的是 ResourceEntityResolver,ResourceEntityResolver 继承自 DelegatingEntityResolver,当调用 resolveEntity 方法时,也是先调用父类的该方法,进行处理,如果父类方法处理成功了,就直接返回父类方法给出的结果,如果父类方法处理失败了,则在 ResourceEntityResolver 中通过资源的相对路径再次尝试加载。
3.小结
好啦,经过上面的介绍,相信大家对于 XMl 约束和 EntityResolver 都有一定的了解啦。
后记
本文刚写完,微信群里就有小伙伴问了一个一模一样的问题:
松哥不禁感叹, 源码并非离我们很远的东西,阅读源码可以有效解决我们日常开发中一些实实在在的问题!
如果觉得有收获,记得点个在看鼓励下松哥哦~搜索微信公众号【江南一点雨】,回复 888 获取超 17k star 开源项目学习文档~
软件开发
2020-06-29 09:33:00
一、函数式接口是什么?
所谓的函数式接口,实际上就是接口里面 只能有一个抽象方法的接口 。我们上一节用到的Comparator接口就是一个典型的函数式接口,它只有一个抽象方法compare。
只有一个抽象方法?那上图中的equals方法不是也没有函数体么?不急,和我一起往下看!
二、函数式接口的特点 接口有且仅有一个抽象方法,如上图的抽象方法compare 允许定义静态非抽象方法 允许定义默认defalut非抽象方法(default方法也是java8才有的,见下文) 允许java.lang.Object中的public方法,如上图的方法equals。 FunctionInterface注解不是必须的,如果一个接口符合"函数式接口"定义,那么加不加该注解都没有影响。加上该注解能够更好地让编译器进行检查。如果编写的不是函数式接口,但是加上了@FunctionInterface,那么编译器会报错
甚至可以说:函数式接口是专门为lambda表达式准备的, lambda表达式是只实现接口中唯一的抽象方法的匿名实现类 。
三、default关键字
顺便讲一下default关键字,在java8之前 接口是不能有方法的实现,所有方法全都是抽象方法 实现接口就必须实现接口里面的所有方法
这就导致一个问题: 当一个接口有很多的实现类的时候,修改这个接口就变成了一个非常麻烦的事,需要修改这个接口的所有实现类 。
这个问题困扰了java工程师许久,不过在java8中这个问题得到了解决,没错就是default方法 default方法可以有自己的默认实现,即有方法体。 接口实现类可以不去实现default方法,并且可以使用default方法。
四、JDK中的函数式接口举例
java.lang.Runnable,
java.util.Comparator,
java.util.concurrent.Callable
java.util.function包下的接口,如Consumer、Predicate、Supplier等
五、自定义Comparator排序
我们自定义一个排序器,实现compare函数(函数式接口Comparator唯一的抽象方法)。返回0表示元素相等,-1表示前一个元素小于后一个元素,1表示前一个元素大于后一个元素。这个规则和java 8之前没什么区别。
下面代码用自定义接口实现类的的方式实现:按照年龄的倒序排序! employees.sort(new Comparator() { [@Override](https://my.oschina.net/u/1162528) public int compare(Employee em1, Employee em2) { if(em1.getAge() == em2.getAge()){ return 0; } return em1.getAge() - em2.getAge() > 0 ? -1:1; } }); employees.forEach(System.out::println);
最终的打印结果如下,按照年龄的自定义规则进行排序。 Employee(id=8, age=79, gender=M, firstName=Alex, lastName=Gussin) Employee(id=7, age=68, gender=F, firstName=Melissa, lastName=Roy) Employee(id=10, age=45, gender=M, firstName=Naveen, lastName=Jain) Employee(id=3, age=43, gender=M, firstName=Ricky, lastName=Martin) Employee(id=4, age=26, gender=M, firstName=Jon, lastName=Lowman) Employee(id=1, age=23, gender=M, firstName=Rick, lastName=Beethovan) Employee(id=5, age=19, gender=F, firstName=Cristine, lastName=Maria) Employee(id=9, age=15, gender=F, firstName=Neetu, lastName=Singh) Employee(id=6, age=15, gender=M, firstName=David, lastName=Feezor) Employee(id=2, age=13, gender=F, firstName=Martina, lastName=Hengis)
这段代码如果以lambda表达式简写。箭头左侧是参数,右侧是函数体,参数类型和返回值根据上下文自动判断。如下: employees.sort((em1,em2) -> { if(em1.getAge() == em2.getAge()){ return 0; } return em1.getAge() - em2.getAge() > 0 ? -1:1; }); employees.forEach(System.out::println);
欢迎关注我的博客,里面有很多精品合集 本文转载注明出处(必须带连接,不能只转文字): 字母哥博客 。
觉得对您有帮助的话,帮我点赞、分享!您的支持是我不竭的创作动力! 。另外,笔者最近一段时间输出了如下的精品内容,期待您的关注。 《手摸手教你学Spring Boot2.0》 《Spring Security-JWT-OAuth2一本通》 《实战前后端分离RBAC权限管理系统》 《实战SpringCloud微服务从青铜到王者》 《VUE深入浅出系列》
软件开发
2020-06-29 08:26:00
作者:wt_better 链接: http://blog.csdn.net/wt_better/article/details/80992014
MyBatis的trim标签一般用于去除sql语句中多余的and关键字,逗号,或者给sql语句前拼接 “where“、“set“以及“values(“ 等前缀,或者添加“)“等后缀,可用于选择性插入、更新、删除或者条件查询等操作。
以下是trim标签中涉及到的属性:
下面使用几个例子来说明trim标签的使用。
1、使用trim标签去除多余的and关键字
有这样的一个例子:
如果这些条件没有一个能匹配上会发生什么?最终这条 SQL 会变成这样: SELECT * FROM BLOG WHERE
这会导致查询失败。如果仅仅第二个条件匹配又会怎样?这条 SQL 最终会是这样: SELECT * FROM BLOG WHERE AND title like ‘someTitle’
你可以使用where标签来解决这个问题,where 元素只会在至少有一个子元素的条件返回 SQL 子句的情况下才去插入“WHERE”子句。而且,若语句的开头为“AND”或“OR”,where 元素也会将它们去除。
trim标签也可以完成相同的功能,写法如下: state = #{state} AND title like #{title} AND author_name like #{author.name}
2、使用trim标签去除多余的逗号
有如下的例子:
如果红框里面的条件没有匹配上,sql语句会变成如下: INSERT INTO role(role_name,) VALUES(roleName,)
插入将会失败。
使用trim标签可以解决此问题,只需做少量的修改,如下所示:
其中最重要的属性是 suffixOverrides=","
表示去除sql语句结尾多余的逗号.
注:如果你有兴趣的话,也可以研究下Mybatis逆向工程生成的Mapper文件,其中也使用了trim标签,但结合了foreach、choose等标签,更多的是牵扯到Criterion的源码研究。不过研究完之后,你将熟练掌握mybatis各种标签的使用,学到Criterion的设计思想,对自己的启发将会很大。 微信搜索:【Java小咖秀】,更多精彩等着你,回复“手册”获取两份开心。
软件开发
2020-06-29 08:07:00
目录
上一篇 深入浅出Android NDK之崩溃分析
为了能在native层打印函数的调用堆栈,找了好久的资料,最后终于找到一个靠谱的链接:
https://www.jianshu.com/p/4a5eeeee6d29
主要通过调用_Unwind_Backtrace函数来获得函数的调用堆栈,但是原文的并不好用,地址通过addr2line转换以后得不到函数名和行号,主要原因我们得到的地址是运行时地址,应该减去SO的基地址再来转换,下面看我改造后的例子,更好用。
#include
#include
#include
#include
#include
static _Unwind_Reason_Code unwindCallback(struct _Unwind_Context* context, void* arg)
{
std::vector<_Unwind_Word> &stack = *(std::vector<_Unwind_Word>*)arg;
stack.push_back(_Unwind_GetIP(context));
return _URC_NO_REASON;
}
void callstackDump(std::string &dump) {
std::vector<_Unwind_Word> stack;
_Unwind_Backtrace(unwindCallback, (void*)&stack);
dump.append(" \n"
"*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***\n"
"pid: 17980, tid: 17980, name: callstack.dump >>> callstack.dump <<<\n"
"signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0\n"
"r0 00000000 r1 00000000 r2 00000001 r3 00000001\n"
"r4 e8efe008 r5 e0537b99 r6 ff970b88 r7 ff970a98\n"
"r8 ff970de0 r9 e7904400 sl e790448c fp ff970b14\n"
"ip e8ef985c sp ff970a60 lr e8eca00f pc e0537d86 cpsr 200b0030\n"
"backtrace:\n");
char buff[256];
for (size_t i = 0; i < stack.size(); i++) {
Dl_info info;
if (!dladdr((void*)stack[i], &info)) {
continue;
}
int addr = (char*)stack[i] - (char*)info.dli_fbase - 1;
if (info.dli_sname == NULL || strlen(info.dli_sname) == 0) {
sprintf(buff, "#%02x pc %08x %s\n", i, addr, info.dli_fname);
} else {
sprintf(buff, "#%02x pc %08x %s (%s+00)\n", i, addr, info.dli_fname, info.dli_sname);
}
dump.append(buff);
}
}
void callstackLogcat(int prio, const char* tag) {
std::string dump;
callstackDump(dump);
__android_log_print(prio, tag, "%s", dump.c_str());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
把调用堆栈拼凑成崩溃日志的样子,主要是为了方便我们使用ndk-stack来解析。
下面来测试一下:
void fun1() {
callstackLogcat(ANDROID_LOG_DEBUG, "MD_DEBUG");
}
void fun2() {
fun1();
}
void fun3() {
fun2();
}
extern "C" JNIEXPORT void Java_com_example_threadtest_PThread_start(JNIEnv *env, jclass clazz) {
fun3();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
编译运行,得到以下输出:
2019-10-16 12:55:04.839 20856-20856/? D/MD_DEBUG:
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
pid: 17980, tid: 17980, name: callstack.dump >>> callstack.dump <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0
r0 00000000 r1 00000000 r2 00000001 r3 00000001
r4 e8efe008 r5 e0537b99 r6 ff970b88 r7 ff970a98
r8 ff970de0 r9 e7904400 sl e790448c fp ff970b14
ip e8ef985c sp ff970a60 lr e8eca00f pc e0537d86 cpsr 200b0030
backtrace:
#00 pc 00009fef /data/app/com.example.strtest-1/lib/arm/libstrtest.so
#01 pc 0000a207 /data/app/com.example.strtest-1/lib/arm/libstrtest.so
#02 pc 00009f91 /data/app/com.example.strtest-1/lib/arm/libstrtest.so
#03 pc 00009f9f /data/app/com.example.strtest-1/lib/arm/libstrtest.so
#04 pc 00009fa9 /data/app/com.example.strtest-1/lib/arm/libstrtest.so
#05 pc 00009fc1 /data/app/com.example.strtest-1/lib/arm/libstrtest.so (Java_com_example_threadtest_PThread_start+00)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
通过ndk-stack转换一下,得到:
C:
软件开发
2020-06-29 07:55:00
首先我挺喜欢这家公司的面试风格的,也是比较务实的吧。无奈自己的心理因素和技术水平都不好,导致面试失败。
一上来就是编程题三连。
**1、**大概意思就是:
俩列表a b,如果a中的元素在b中,那么就保存此元素在a中的索引值,最后统一输出所有索引值。
要求:时间复杂度小于O(n)
这个我当时想到的是循环遍历a,然后判断是否i in b,但是这个时间复杂度是O(n2),GG。最后面试官提醒了我一下hashmap,瞬间捶胸顿足……。最后自己想了一下,可以将b先转成字典,然后再使用in。 a = [5,3,1,5,4] b = [5,3] d = {} for i in b: d[i] = 0 res = l = len(a) for i in range(l): if a[i] in d: res.append(i) print(res)
**2、**如图,输入为这样,输出为那样,写吧……
输入几组数据,pid为-1的代表根节点。如果数据为非根节点,那么就要搜索此节点直至找到根节点。
这个其实我知道考的是树的遍历,但是没见过这种形式的题,要自己构建输入数据的形式,一下子就懵了,再加上面试官在那一直盯着……唉,承认自己能力不足吧。后续自己写了一遍,大家可以看一下,有问题直接下面评论就可以。 d = { "A":"-1", "A-1":"A", "A-2":"A", "A-3":"A", "A-2-1":"A-2", "A-2-2":"A-2", "A-2-3":"A-2" } res = def func(node): array.append(node[0]) if node[1] == "-1": return func((node[1],d[node[1]])) for i in d.items: array = func(i) string = "/".join(array[::-1]) res.append("/"+string) for j in res: print(j)
3、这个题太经典了。最短路径和。
要注意是从第一行走到最后一行,而且只能向下或者斜向下走(我当时没看清就写,写成了从左上走到右下……)。我这里写的是动态规划的解法。Leetcode上有原题,解法不唯一。 array = [[1,8,5,2], [4,1,7,3], [3,6,2,9]] x = len(array) y = len(array[0]) dp = [[0 for i in range(y)] for j in range(x)] # 遍历顺序是每行内的每列。所以遍历array中第一行只执行到if,dp中第一行就确定了,然后再确定dp第二行。 # 要注意两个边界条件 for i in range(x): for j in range(y): if i == 0: dp[i][j] = array[i][j] elif j == 0: dp[i][j] = array[i][j] + min(dp[i-1][j], dp[i-1][j+1]) elif j == y-1: dp[i][j] = array[i][j] + min(dp[i-1][j-1],dp[i-1][j]) else: dp[i][j] = array[i][j] + min(dp[i-1][j-1],dp[i-1][j],dp[i-1][j+1]) # [[1, 8, 5, 2], # [5, 2, 9, 5], # [5, 8, 4, 14]] print(min(dp[-1])) # 4
软件开发
2020-06-10 16:55:00
一、前言
在Laravel里面,管道这块的代码算是挺难理解的了,每个人有自己的理解方法,而我也有自己的理解,经过理解,我整理了一下,记录如下:
代码如下,参考 https://gitee.com/obamajs/php-pipeline $method($passable, $stack); } } else { throw new InvalidArgumentException('$pipe must be callback or object'); } }; } interface TestUnit { public function handle($passable, callable $next = null); } class Unit1 implements TestUnit { public function handle($passable, callable $next = null) { echo __CLASS__ . '->' . __METHOD__ . " called\n"; echo $passable . "\n"; $next($passable+1); } } class Unit2 implements TestUnit { public function handle($passable, callable $next = null) { echo __CLASS__ . '->' . __METHOD__ . " called\n"; echo $passable . "\n"; $next($passable+1); } } class InitialValue implements TestUnit { public function handle($passable, callable $next = null) { // 一般这里输出内容,所以不需要next了 echo __CLASS__ . '->' . __METHOD__ . " called\n"; //$next($passable); } } class Unit3 implements TestUnit { public function handle($passable, callable $next = null) { echo __CLASS__ . '->' . __METHOD__ . " called\n"; echo $passable . "\n"; $next($passable+1); } } $pipeline = array_reduce([new Unit1(), new Unit2(), new Unit3()], "Pipeline", function ($passable) { (new InitialValue())->handle($passable, null); echo $passable . "买买买\n"; }); $pipeline(1);
二、解释:
Pipeline 接受两个参数
$passable 是准备要从各级传送的值
第一次迭代:
return function ($passable) use (array_resuce最后那个函数, new Unit1())
第二次迭代
return function ($passable) use (第一次迭代的是一个参数为$passable的函数, new Unit2())
第三次迭代
return function ($passable) use (第二次迭代的是一个参数为$passable的函数, new Unit3())
三、执行过程
1. 执行第三次迭代(1)
2. 执行逻辑 new Unit3()->handle($passable, $next); 而handle函数里面有 $next($passable); 也就是
- 第二次迭代($passable) use (第一次迭代的是一个参数为$passable的函数, new Unit2())
3. 执行逻辑 new Unit2()->handle($passable, $next); 而handle函数里面有 $next($passable); 也就是
- 第一次迭代($passable) use (array_resuce最后那个函数, new Unit1())
4. 执行逻辑 new Unit1()->handle($passable, $next); 而handle函数里面有 $next($passable); 也就是
- array_resuce最后那个函数($passable)
5. 到这里,所有流程就执行完了
软件开发
2020-06-27 13:58:00
1. 什么是消息队列
消息队列(英语:Message queue)是一种进程间通信或同一进程的不同线程间的通信方式
2. 为什么使用消息队列
消息队列技术是分布式应用间交换信息的一种技术。消息队列可驻留在内存或磁盘上,队列存储消息直到它们被应用程序读出。通过消息队列,应用程序可独立地执行,它们不需要知道彼此的位置、或在继续执行前不需要等待接收程序接收此消息。
3. 什么场合使用消息队列
你首先需要弄清楚,消息队列与远程过程调用的区别,在很多读者咨询我的时候,我发现他们需要的是RPC(远程过程调用),而不是消息队列。
消息队列有同步或异步实现方式,通常我们采用异步方式使用消息队列,远程过程调用多采用同步方式。
MQ与RPC有什么不同? MQ通常传递无规则协议,这个协议由用户定义并且实现存储转发;而RPC通常是专用协议,调用过程返回结果。
4. 什么时候使用消息队列
同步需求,远程过程调用(PRC)更适合你。
异步需求,消息队列更适合你。
目前很多消息队列软件同时支持RPC功能,很多RPC系统也能异步调用。
消息队列用来实现下列需求 存储转发 分布式事务 发布订阅 基于内容的路由 点对点连接
以下是一个消息队列的运用实例
shmId = shmop_open ( $shmkey , "c" , 0644 , $this -> memSize ); //创建一个内存段 $this -> maxQSize = $this -> memSize / $this -> blockSize ; // 申請一个信号量 $this -> semId = sem_get ( $shmkey , 1 ); sem_acquire ( $this -> semId ); // 申请进入临界 $this -> init (); } private function init () { if ( file_exists ( $this -> filePtr ) ){ $contents = file_get_contents ( $this -> filePtr ); $data = explode ( '|' , $contents ); if ( isset ( $data [ 0 ]) && isset ( $data [ 1 ])){ $this -> front = ( int ) $data [ 0 ]; $this -> rear = ( int ) $data [ 1 ]; } } } public function getLength () { return (( $this -> rear - $this -> front + $this -> memSize ) % ( $this -> memSize ) ) / $this -> blockSize ; } public function enQueue ( $value ) { if ( $this -> ptrInc ( $this -> rear ) == $this -> front ){ // 队满 return false ; } //echo $this->front; $data = $this -> encode ( $value ); shmop_write ( $this -> shmId , $data , $this -> rear ); $this -> rear = $this -> ptrInc ( $this -> rear ); return $this -> decode ( $data ); } public function deQueue () { if ( $this -> front == $this -> rear ){ // 队空 throw new Exception ( " block size is null!" ); } $value = shmop_read ( $this -> shmId , $this -> front , $this -> blockSize - 1 ); $this -> front = $this -> ptrInc ( $this -> front ); return $this -> decode ( $value ); } private function ptrInc ( $ptr ) { return ( $ptr + $this -> blockSize ) % ( $this -> memSize ); } private function encode ( $value ) { $data = serialize ( $value ) . "__eof" ; //echo ''; //echo strlen($data); //echo ''; // echo $this->blockSize -1; // echo ''; if ( strlen ( $data ) > $this -> blockSize - 1 ){ throw new Exception ( strlen ( $data ) . " is overload block size!" ); } return $data ; } public function exist ( $value ){ //判断队头的数据 $data = shmop_read ( $this -> shmId , $this -> front , $this -> blockSize - 1 ); if ( $value == $this -> decode ( $data )){ return 1 ; } return 0 ; } private function decode ( $value ) { //return $value; $data = explode ( "__eof" , $value ); return unserialize ( $data [ 0 ]); } public function __destruct () { //保存队头和队尾指针 $data = $this -> front . '|' . $this -> rear ; file_put_contents ( $this -> filePtr , $data ); sem_release ( $this -> semId ); // 出临界区, 释放信号量 } }
如何调用
$shmq = new ShmQueue(); 入队: $data = 125; $shmq->enQueue($data); 出队: $shmq->deQueue();

软件开发
2020-06-27 13:56:00

一、线程执行的内存原理 public static void main(String[] args) { MyThread t1 = new MyThread(); MyThread t2 = new MyThread(); t1.start(); t2.start(); }
对应的 内存原理图 大致是这样:
注意事项:
1.执行线程任务的run方法是线程私有的。
2.某个线程对象出现异常不会影响其他线程的执行。
二、创建线程的方式
(1)继承Thread类
1、步骤
1.定义一个类,继承Thread类。
2.重写Thread类的run方法。
3.创建线程对象。
4.调用start方法开启新线程,内部会执行run方法。
2、代码示例 public class MyThread extends Thread { @Override public void run() { // 获取线程名称 String threadName = this.getName(); for (int i=0;i<100;i++) { // 复习异常抛出的方法,抛出一个运行时异常 if("Thread-1".equals(threadName) && i == 3){ throw new RuntimeException(threadName + "出问题了"); } System.out.println(threadName+"..."+i); } } }
3、Thread类的构造函数 public Thread():分配一个新的线程对象。 public Thread(String name):分配一个指定名字的新的线程对象。 public Thread(Runnable target):分配一个带有指定目标新的线程对象。 public Thread(Runnable target,String name):分配一个带有指定目标新的线程对象并指定名字。
4、常用成员方法 public String getName():获取当前线程名称。 public String start();导致此线程开始执行;java虚拟机调用此线程的run方法。 public void run():定义线程的任务逻辑。
5、常用静态方法 public static void sleep(long millis): 让当前正在执行的进程暂停指定毫秒数。 public static Thread currentThread():返回对当前正在执行的线程对象的引用。

(2)实现Runnable接口
1、步骤
1.定义Runnable接口的实现类
2.实现类覆盖重写run方法,指定线程任务逻辑
3.创建Runnable接口实现类对象
4.创建Thread类对象,构造函数传递Runnable实践类对象。 a) public Thread(Runnable target):分配一个带有指定目标新的线程对象。
b) public Thread(Runnable target,String name):分配一个带有指定目标新的线程对象并指定名字。
5.Thread类对象调用start方法,内部自动调用run方法。
2、代码展示 public class MyTask implements Runnable{ @Override public void run() { //任务逻辑 } } MyTask myTask = new MyTask(); Thread thread = new Thread(myTask); thread.start();
2、(重点!)实现Runnable接口来创建线程的好处
1.避免java中类单继承的局限性
2.降低线程任务对象和线程之间的耦合性 tip:换句话说,我们可以更加专注于线程的任务,先把线程的任务逻辑创建完毕。之后需要执行线程任务的地方就创建线程,执行需要的线程任务即可。并且线程任务可以多次反复使用。有点像零件插拔一样。
(3)匿名内部类方式
1、格式 new 父类/接口(){ //覆盖重写抽象方法 };
2、作用
1.创建父类子类对象的快捷方式
2.创建接口的实现类对象的快捷方式
3、注意事项
1.使用匿名内部类创建的对象只能一次性使用
2.尽量使用lambda表达式进行书写,提高代码可读性和编程效率。
三、(重点!)线程安全问题
(1)出现线程安全的情况
1.有两个以上线程同时操作共享数据
2.操作共享数据的语句有两条以上
3.线程调度是抢占式调度模式。
(2)解决方案 同一个线程,操作共享数据的多条语句全部执行,要么多条语句全部不执行。故而可以使用同步技术。 同步的原理:有锁的线程执行,没有锁的线程等待。
(3)实际解决
1、同步代码块
1.作用:用来解决多线程访问共享数据安全问题
2.格式 synchronized(任意对象){ } 注意事项 :
(1)所有操作共享数据的代码写到同步代码块{}中。
(2)任意对象:任意指的是 类型 可以任意,但要保证全局 唯一 ,被多个线程 共享使用 。 任意对象,也叫锁对象。更加专业的术语:对象监视器。
2、同步方法 格式 修饰符 synchronized 返回值类型 方法名称(参数列表...){ ... } 修饰符 synchronized 返回值类型 方法名称(参数列表...){ ... }
注意事项
(1)所有操作共享数据的代码都在{}中间添加一个
(2)同步方法的锁对象就是this
3、使用Lock接口
1.方法:
1.abstract void lock​() 获得锁。
2.abstract void unlock​() 释放锁。
2.实现类:
java.util.concurrent.locks.ReentrantLock ,空参构造函数
3.注意事项 :
释放锁的动作必须被执行。
(4)实际案例
1、卖票案例分析 (1)总共有3种途径卖票,每个途径,相当于一个线程对象 (2)每个线程对象要执行的任务: 都是在卖票 (3)3个线程对象,操作的资源 100 张票 是被共享的
2、解决策略:
(1) 定义实现类,实现Runnable接口
(2) 覆盖重写Runnable接口中的run方法.指定线程任务——卖票
(2.1)判断是否有票
(2.2)有: 出一张票
(2.3)票的数量减少1
(3) 创建Runnable接口的实现类对象
(4) 创建3个Thread对象,传递Runnable接口的实现类对象,代表,卖票的3种途径
(5) 3个Thread对象分别调用start方法,开启售票
3、代码实现 public class MyTicket implements Runnable{ private int tickets= 100; Object obj = new Object(); @Override public void run() { while (true){ // sellTicketB(); sellTicketA(); } } // 同步函数 private synchronized void sellTicketA(){ if(tickets>0){ System.out.println(Thread.currentThread().getName() + " 卖出第" + tickets-- + "张票"); }else { return; } } //同步进程快 private void sellTicketB() { synchronized(obj){ if(tickets>0){ System.out.println(Thread.currentThread().getName() + " 卖出第" + tickets-- + "张票"); }else { return; } } } } public class synchronizedTest { public static void main(String[] args) { MyTicket task = new MyTicket(); // 三个线程任务来出票 new Thread(task).start(); new Thread(task).start(); new Thread(task).start(); } }
4、线程同步的原理 线程执行的前提:
(1)cpu资源
(2)锁对象 基本规则:
线程对象执行同步代码块中的内容,要么全部执行,要么全部不执行,不能够被其他线程干扰。 拿买票案例举例说明
现在存在t0、t1和t2三个线程。
假设一:
假设t0线程获取cpu资源,执行线程任务遇到同步代码块,判断是否具有锁对象。
有:获取锁对象
进入同步代码块,执行同步代码,,假设t0在执行过程中没有被t1或者t2抢夺cpu资源,那么t0或顺利执行完同步代码块内代码,退出同步代码块,释放锁资源,继续和其他线程抢夺cpu资源和锁对象。
假设二:
假设t0线程获取cpu资源,执行线程任务遇到同步代码块,判断是否具有锁对象
有:获取锁对象
进入同步代码块,执行同步代码,假设t0在执行过程中被t1抢夺了cpu资源,那么t0线程将不能继续执行。t1线程执行任务,遇到同步代码块,判断是否具有锁对象,因为锁已经被t0拿了,因此t1进入阻塞状态,等待获取锁对象被释放。
假设三:
假设t0执行完成了同步代码块的内容,释放了锁对象,t1处于阻塞状态,但此时t2线程抢到了cpu资源,执行代码到同步代码块,然后顺利获取锁对象,进入同步代码块执行。这种情况下,t1将继续等待t2在同步代码块执行完毕,然后再去抢夺cpu资源和锁资源。
可以发现,线程如果不进行调度的管理可能会出现长时间等待的问题,因为抢占式调度具有随机性,不能获得最大的性能。
(持续更新…)
四、线程状态
java.lang.Thread.State 给出了六种线程状态

注意事项(一)
1.sleep方法可以在同步中使用
2.sleep方法可以在非同步中使用
3.sleep方法与锁对象无关(不会释放锁)
注意事项(二)
1.Object类定义wait和notify方法
2.因此任意对象可以调用wait()和notify()方法
3.锁对象可以是任意的
4.但是锁对象必须使用在同步中,因此wait和notify方法必须在同步中使用
案例分析 双线程交替执行。有一个抽奖池,该抽奖池中存放了奖励的金额,该抽奖池中的奖项为 {10,5,20,50,100,200,500,800,2,80,300,3000}; 创建两个抽奖箱(线程)设置线程名称分别为“抽奖箱1”,“抽奖箱2”,随机从抽奖池中完成抽奖。 两个线程轮流交替抽奖,每抽出一个奖项就打印出来。 【输出示例】 抽奖箱1...抽出了10元... 抽奖箱2...抽出了20元... 抽奖箱1...抽出了50元... 抽奖箱2...抽出了800元... ... ... 每次抽的过程中,不打印,抽完时一次性打印。 【输出示例】 在此次抽奖过程中,抽奖箱1总共产生了6个奖项,分别为:10,5,20,50,100,200最高奖项为200元,总计额为385元 在此次抽奖过程中,抽奖箱2总共产生了6个奖项,分别为:500,800,2,80,300,3000最高奖项为3000元,总计额为4682元 在此次抽奖过程中,抽奖项2中产生了最高奖项,该最高奖项为3000元
1. 分析
两个线程的任务都是抽奖,因此很明显只需要定义一个线程任务对象“抽奖”即可。由于两个抽奖箱共享一个奖池,且要求两个抽奖箱交替进行,很明显需要用到线程的等待(wait)和唤醒(notify)操作。因此,两个线程对于奖池中奖金的操作需要同步。前面已经说明,线程任务只有一个,故同步代码块的锁对象使用线程任务对象自身(this)即可。
2、实现思路

3、代码实现 public class RunnableImpl implements Runnable{ private List list; private Map mp = new HashMap<>(); private int count = 0; public RunnableImpl(List list) { this.list = list; } @Override public void run() { List subList = new ArrayList(); while (true){ synchronized (this){ if(list.size()<=0){ this.notifyAll(); mp.put(Thread.currentThread().getName(),subList); count++; if(count == 2){ Integer max = 0; String max_name = ""; for (Map.Entry entry : mp.entrySet()) { List t = entry.getValue(); String s = entry.getKey(); Integer tmax = 0; Integer sum = 0; StringBuilder sb = new StringBuilder(); for (Object o : t) { sb = sb.append(o).append(","); sum += (Integer)o; tmax = tmax < (Integer)o ? (Integer)o : tmax; } if(max < tmax){ max = tmax; max_name = s; } //sb = sb.deleteCharAt(sb.length()-1); String seq =sb.toString(); System.out.println("在此次抽奖过程中,"+ s +"总共产生了"+t.size()+"个奖项,分别为:" + seq +"最高奖项为"+ tmax +"元,总计额为"+ sum +"元"); } System.out.println("在此次抽奖过程中,"+max_name+"中产生了最高奖项,该最高奖项为"+max+"元"); } break; } if(list.size() > 0){ Object remove = list.remove(new Random().nextInt(list.size())); System.out.println(Thread.currentThread().getName() + "..." + "抽出了"+ remove +"元..."); subList.add(remove); this.notify(); try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } }
软件开发
2020-06-27 15:39:00
//通过符号文件获取函数地址

#include
#include
#include
#include
#include "ntdll.h"
#pragma comment(lib,"dbghelp.lib")

//注意:需要这两个文件
//dbghelp.dll
//symsrv.dll

//获取函数地址PDB
ULONG_PTR GetFunctionAddressPDB(HMODULE hMod, const WCHAR * szApiName)
{
//定义变量
BYTE memory[0x2000] = {0};

//参数效验
if (hMod == NULL)return NULL;
if (szApiName == NULL)return NULL;


ZeroMemory(memory, sizeof(memory));
SYMBOL_INFOW * syminfo = (SYMBOL_INFOW *)memory;
syminfo->SizeOfStruct = sizeof(SYMBOL_INFOW);
syminfo->MaxNameLen = MAX_SYM_NAME;
syminfo->ModBase = (ULONG_PTR)hMod;

if (!SymFromNameW(GetCurrentProcess(), szApiName, syminfo))
{
printf("SymFromName %ws returned error : %d\n", szApiName, GetLastError());
return 0;
}

return (ULONG_PTR)syminfo->Address;
}

//符号获取函数地址
PVOID SymGetProcAddress(LPCWSTR szDllName, LPCWSTR szApiName)
{
//变量定义
TCHAR symbolPath[0x2000] = { 0 };
TCHAR szPath[MAX_PATH] = { 0 };

//参数效验
if (szDllName == NULL)return NULL;
if (szApiName == NULL)return NULL;


GetModuleFileName(0, szPath, ARRAYSIZE(szPath));
TCHAR * temp = _tcsrchr(szPath, TEXT('\\'));
if (temp == NULL)return NULL;
*temp = 0;
_tcscat_s(symbolPath, TEXT("SRV*"));
_tcscat_s(symbolPath, szPath);
_tcscat_s(symbolPath, TEXT("*http://msdl.microsoft.com/download/symbols"));
SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | SYMOPT_FAVOR_COMPRESSED);
if (!SymInitializeW(GetCurrentProcess(), symbolPath, TRUE))
{
return NULL;
}

HMODULE hDll = GetModuleHandle(szDllName);
PVOID lpRet = NULL;
lpRet = (PVOID)GetFunctionAddressPDB(hDll, szApiName);
SymCleanup(GetCurrentProcess());

return lpRet;
}




int main(void)
{

PVOID lpFuntAddressRet = NULL;
if (GetModuleHandle(TEXT("kernelbase.dll")))
{
//高版本系统
lpFuntAddressRet = SymGetProcAddress(TEXT("ntdll.dll"), TEXT("ZwReadVirtualMemory"));
//lpRet = SymGetProcAddress(TEXT("ntdll.dll"), TEXT("RtlDispatchAPC"));
}
else
{
lpFuntAddressRet = SymGetProcAddress(TEXT("kernel32.dll"), TEXT("ZwReadVirtualMemory"));
//lpRet = SymGetProcAddress(TEXT("kernel32.dll"), TEXT("BaseDispatchAPC"));
}
printf("%p", lpFuntAddressRet);
return 0;
}
软件开发
2020-06-29 07:53:00
方法来源于对Sysinternals的procexp的逆向。
在通过kernel的pdb获取未导出的MmSizeOfPagedPoolInBytes和MmMaximumNonPagedPoolInBytes时,procexp执行了如下几个步骤:
1. 获取kernel的映像文件名称
调用IsProcessorFeaturePresent判断PAE是否开启,以确定使用ntoskrnl.exe还是ntkrnlpa.exe。
2. 获取kernel在内存中的基址
调用NtQuerySystemInformation,使用SystemModuleInformation(0x0B)枚举模块,并使用1中获得的文件名查找,确定kernel在内存中的基址。
3. 拼接kernel的完整路径,使用CreateFile(access:GENERIC_READ)打开。
4. 调用SetEnvironmentVariable设置_NT_SYMBOL_PATH为符号文件所在路径。
5. 调用SymInitialize。
6. 调用SymLoadModule64,用到3中得到的kernel文件的句柄。
7. 调用SymGetSymFromName,由IMAGEHLP_SYMBOL获得Address。
8. 调用SymCleanup。
这样,7中所得到Address | 0x80000000就是要找的内核地址了。
————————————————
版权声明:本文为CSDN博主「0902horn」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/xiaoxinjiang/article/details/7013488
软件开发
2020-06-29 07:52:00
jvm线程是维护了线程的状态。new,running,waiting,timed waiting,blocked,terminated。我们通过jstack等工具查看的时候,线程状态就是上面的一种。jvm本身是做了一种抽象,我们现在从一个典型的方法,来跟踪查看一下jvm内部又是怎么做状态变化的。
sleep方法入手 public static native void sleep(long millis) throws InterruptedException;
sleep是一个native 方法,我们通过jvm原来来进行跟着(源码来自openjdk11)。 根据jni的规范,我们通过包名或者是jni的注册方式找到了对应的声明。 static JNINativeMethod methods[] = { {"start0", "()V", (void *)&JVM_StartThread}, {"stop0", "(" OBJ ")V", (void *)&JVM_StopThread}, {"isAlive", "()Z", (void *)&JVM_IsThreadAlive}, {"suspend0", "()V", (void *)&JVM_SuspendThread}, {"resume0", "()V", (void *)&JVM_ResumeThread}, {"setPriority0", "(I)V", (void *)&JVM_SetThreadPriority}, {"yield", "()V", (void *)&JVM_Yield}, {"sleep", "(J)V", (void *)&JVM_Sleep}, {"currentThread", "()" THD, (void *)&JVM_CurrentThread}, {"countStackFrames", "()I", (void *)&JVM_CountStackFrames}, {"interrupt0", "()V", (void *)&JVM_Interrupt}, {"isInterrupted", "(Z)Z", (void *)&JVM_IsInterrupted}, {"holdsLock", "(" OBJ ")Z", (void *)&JVM_HoldsLock}, {"getThreads", "()[" THD, (void *)&JVM_GetAllThreads}, {"dumpThreads", "([" THD ")[[" STE, (void *)&JVM_DumpThreads}, {"setNativeName", "(" STR ")V", (void *)&JVM_SetNativeThreadName}, };
接下来我们要跟踪的就是JVM_Sleep了。 我们一点一点来解析这个方法。 首先这里有宏定义。 JVM_ENTRY(void, JVM_Sleep(JNIEnv* env, jclass threadClass, jlong millis))
我们展开JVM_ENTRY。 #define JVM_ENTRY(result_type, header) \ extern "C" { \ result_type JNICALL header { \ JavaThread* thread=JavaThread::thread_from_jni_environment(env); \ ThreadInVMfromNative __tiv(thread); \ debug_only(VMNativeEntryWrapper __vew;) \ VM_ENTRY_BASE(result_type, header, thread)
在ThreadInVMfromNative中,发生了一次线程状态的变更。 class ThreadInVMfromNative : public ThreadStateTransition { public: ThreadInVMfromNative(JavaThread* thread) : ThreadStateTransition(thread) { trans_from_native(_thread_in_vm); } ~ThreadInVMfromNative() { trans_and_fence(_thread_in_vm, _thread_in_native); } };
在构造方法中把 JavaThread 的**thread->thread_state()**状态变为了_thread_in_vm。在析构函数中把状态改成了_thread_in_native。这里的__tiv是一个本地对象,只有在栈销毁的时候才会触发析构,也就是说这里的转为_thread_in_native只不过是一瞬间的事情。 展开头结束后,我们再继续往后观察。 JavaThreadSleepState jtss(thread);
在这个构造方法中。把**java_thread->threadObj()**的状态变为了java_lang_Thread::SLEEPING static void set_thread_status(JavaThread* java_thread, java_lang_Thread::ThreadStatus state) { java_lang_Thread::set_thread_status(java_thread->threadObj(), state); }
这里对应的就是java的线程的状态了。 在往下走就直接设置**thread->osthread()**的状态为sleep。 ThreadState old_state = thread->osthread()->get_state(); thread->osthread()->set_state(SLEEPING);
状态到这里全部设置完成。
状态梳理
通过上面的代码,我们可以发现最核心的就是JavaThread的这个对象,他本身代表的jvm中的线程状态。会标识线程是在vm还是线程是在java或者在native。具体的状态如下 enum JavaThreadState { _thread_uninitialized = 0, // should never happen (missing initialization) _thread_new = 2, // just starting up, i.e., in process of being initialized _thread_new_trans = 3, // corresponding transition state (not used, included for completness) _thread_in_native = 4, // running in native code _thread_in_native_trans = 5, // corresponding transition state _thread_in_vm = 6, // running in VM _thread_in_vm_trans = 7, // corresponding transition state _thread_in_Java = 8, // running in Java or in stub code _thread_in_Java_trans = 9, // corresponding transition state (not used, included for completness) _thread_blocked = 10, // blocked in vm _thread_blocked_trans = 11, // corresponding transition state _thread_max_state = 12 // maximum thread state+1 - used for statistics allocation };
这个类同时那种java线程状态的引用。就是java_thread->threadObj()。这个对应的是java的线程状态,这个也是我们jstack看到的状态。 "main" #1 prio=5 os_prio=31 tid=0x00007fee9b809000 nid=0xe03 waiting on condition [0x0000700008d65000] java.lang.Thread.State: TIMED_WAITING (sleeping) at java.lang.Thread.sleep(Native Method) at com.company.Sleep.main(Sleep.java:7)
为什么输出的是timed_waiting(sleep),这个主要是因为格式化输出的原因。 if(status == THREAD_STATUS_NEW){ return "NEW"; }else if(status == THREAD_STATUS_RUNNABLE){ return "RUNNABLE"; }else if(status == THREAD_STATUS_SLEEPING){ return "TIMED_WAITING (sleeping)"; }else if(status == THREAD_STATUS_IN_OBJECT_WAIT){ return "WAITING (on object monitor)"; }else if(status == THREAD_STATUS_IN_OBJECT_WAIT_TIMED){ return "TIMED_WAITING (on object monitor)"; }else if(status == THREAD_STATUS_PARKED){ return "WAITING (parking)"; }else if(status == THREAD_STATUS_PARKED_TIMED){ return "TIMED_WAITING (parking)"; }else if(status == THREAD_STATUS_BLOCKED_ON_MONITOR_ENTER){ return "BLOCKED (on object monitor)"; }else if(status == THREAD_STATUS_TERMINATED){ return "TERMINATED"; } return "UNKNOWN"; }
在状态格式化的时候,把sleeping的归类成为TIMED_WAITING (sleeping)。 于此同时Java Thread还拿着系统的线程thread->osthread()。
小结
jvm中起码有三种状态的变化,一种是代表的java规范中的线程状态(java_thread->threadObj()),一种是表示的jvm代理的系统线程的状态(thread->osthread()),同时还有jvm线程在做转换的一种状态,是vm自己的状态表示。
欢迎关注公众号:肥宅英短
软件开发
2020-06-29 07:41:00
package main import ( "fmt" "go/ast" "reflect" ) type Member struct { Id int `json:"id" orm:"member_id"` Name string `json:"name"` status bool } func (m *Member) SetStatus(s bool) { m.status = s } func (m *Member) GetStatus() bool { return m.status } func (m Member) String() string { return fmt.Sprintf("id: %d, name: %s", m.Id, m.Name) } // 如果是值传递那么反射获取的对象将不能修改此值,否则 panic func Parse(v interface{}) { // 获取 v 的变量值,如果是地址传递获取的是指针,值传递则为变量值 rValue := reflect.ValueOf(v) // 判断是否为一个指针,如果是指针就通过 Elem() 获取指针指向的变量值 rValue = reflect.Indirect(rValue) // 获取 v 的变量类型 rType := rValue.Type() // rType := reflect.TypeOf(v) switch rType.Kind() { case reflect.Struct: // 遍历结构体字段 for i := 0; i < rValue.NumField(); i++ { field := rType.Field(i) // 忽略匿名字段和私有字段 if !field.Anonymous && ast.IsExported(field.Name) { // 获取结构体字段的 interface{} 变量 fmt.Println(rValue.Field(i).Interface()) // 对于值传递的结构体使用 Addr() 获取结构体字段 *interface{} 变量会报错 fmt.Println(reflect.TypeOf(rValue.Field(i).Addr().Interface())) // 获取字段 tag fmt.Println(rType.Field(i).Tag.Get("json")) } } // 根据字段名获取字段 interface{} fmt.Println(rValue.FieldByName("Name").Interface()) // 获取非指针方法数量,案例中的 Member 的 String 方法 fmt.Println(rValue.NumMethod()) // 获取所有方法数量 fmt.Println(rValue.Addr().NumMethod()) // 遍历结构体方法 rValue.Addr().MethodByName("SetStatus").Call([]reflect.Value{reflect.ValueOf(true)}) fmt.Println(rValue.Addr().MethodByName("GetStatus").Call(nil)) // 排序默认是按照函数名的排序(ASCII 码) fmt.Println(rValue.Addr().Method(2).Call(nil)) fmt.Println(rValue.Method(0).Call(nil)) // 创建一个新反射对象 fmt.Println(reflect.New(rType).Elem().FieldByName("Id")) case reflect.Func: num := 2 argv := make([]reflect.Value, rType.NumIn()) for i := range argv { if rType.In(i).Kind() == reflect.Int { argv[i] = reflect.ValueOf(num) } } result := rValue.Call(argv) if len(result) == 1 { fmt.Println(result[0].Int()) } case reflect.Int: // 修改值 rValue.SetInt(127) // 从反射对象获取 interface{} 变量 rIntf := rValue.Interface() // 根据 interface{} 进行类型断言 if num, ok := rIntf.(*int); ok { fmt.Println(num) } case reflect.Slice: // 切片类型 fmt.Println(rType.Kind()) // 切片内元素类型 // Elem() 作用是返回接口 v 包含的值或 v 指向的指针 fmt.Println(rType.Elem().Kind()) } } func Add(a, b int) int { return a + b } func main() { m := &Member{ Id: 23, Name: "jack", } Parse(m) fmt.Println(m.GetStatus()) // 函数调用 Parse(Add) num := 6 // 如果为值传递修改时会 panic Parse(&num) fmt.Println(num) var ms []Member Parse(&ms) } /* 23 *int id jack *string name jack 1 3 [] [id: 23, name: jack] [id: 23, name: jack] 0 true 4 127 slice struct */
软件开发
2020-06-29 00:17:00
init 函数是在文件包首次被加载的时候执行,且只执行一次。 sync.Once 是在代码运行中需要的时候执行,且只执行一次。 package main import ( "fmt" "sync" ) var once sync.Once func Test() { fmt.Println("Only once") } func main() { done := make(chan bool) for i := 0; i < 5; i++ { go func() { once.Do(Test) done <- true }() } for i := 0; i < 5; i++ { <-done } } /* Only once */
软件开发
2020-06-29 00:15:00
package main import ( "fmt" "math/rand" "sort" "time" ) // 从小到大 func Order(arr []int, left, right int) { if left >= right { return } // 以 pivot 为轴,分别将小于、大于 pivot 的元素放在左右两边 leftIndex, rightIndex, pivot := left, right, arr[(left+right)/2] for leftIndex < rightIndex { // 从左边开始向右找一个大于等于 pivot 的元素 // 最差到 pivot 时跳出,且每次循环会重新选定 pivot,即必然会循环到 pivot,故无需 判断 leftIndex <= right for /*leftIndex < right &&*/ arr[leftIndex] < pivot { leftIndex++ } // 从右边向左找一个小于等于 pivot 的元素 for /*rightIndex > left &&*/ arr[rightIndex] > pivot { rightIndex-- } // 左侧的均已小于 pivot 且 右侧的均已大于 pivot 时跳出 if leftIndex >= rightIndex { break } // 避免元素值相等导致死循环 if arr[leftIndex] == arr[rightIndex] { leftIndex++ continue } // 交换这两个元素 arr[leftIndex], arr[rightIndex] = arr[rightIndex], arr[leftIndex] // fmt.Println(leftIndex, rightIndex, arr) } // 左边 Order(arr, left, rightIndex-1) // 右边 Order(arr, leftIndex+1, right) } func main() { rand.Seed(time.Now().UnixNano()) testData1 := make([]int, 0, 100000000) times := 100000000 for i := 0; i < times; i++ { val := rand.Intn(20000000) testData1 = append(testData1, val) } // arr := []int{9, 3, 5, 1, 6, 0, 2, 7, 4, 8} // arr := []int{9, 9, 9, 9, 9, 9, 9, 9, 9, 9} // Order(&arr, 0, len(arr)-1) // fmt.Println(arr) start := time.Now() Order(testData1, 0, len(testData1)-1) fmt.Println("single goroutine: ", time.Now().Sub(start)) if !sort.IntsAreSorted(testData1) { fmt.Println("wrong quick_sort implementation") } }
软件开发
2020-06-29 00:13:00
package main import "fmt" // 从小到大 func Order(arr []int) { // 依次选出每位最小值 for i := 0; i < len(arr); i++ { // 从 i 的下一位开始依次与 i 比对,若小于 i 的值就交换 for j := i + 1; j < len(arr); j++ { if arr[j] < arr[i] { // 交换两个值 arr[i], arr[j] = arr[j], arr[i] } } fmt.Println(i, arr) } } func main() { arr := []int{9, 3, 5, 1, 6, 0, 2, 7, 4, 8} Order(arr) fmt.Println(arr) } /* 0 [0 9 5 3 6 1 2 7 4 8] 1 [0 1 9 5 6 3 2 7 4 8] 2 [0 1 2 9 6 5 3 7 4 8] 3 [0 1 2 3 9 6 5 7 4 8] 4 [0 1 2 3 4 9 6 7 5 8] 5 [0 1 2 3 4 5 9 7 6 8] 6 [0 1 2 3 4 5 6 9 7 8] 7 [0 1 2 3 4 5 6 7 9 8] 8 [0 1 2 3 4 5 6 7 8 9] 9 [0 1 2 3 4 5 6 7 8 9] [0 1 2 3 4 5 6 7 8 9] */
软件开发
2020-06-29 00:10:00
file1.cpp int ext;
file2.cpp extern const int ext2=12;
apple.cpp class apple { private: int people[100]; public: apple(int i); const int apple_number; void take(int num) const; int add(int num); int add(int num) const; int getCount() const; };
const.cpp #include #include "apple.cpp" using namespace std; // construct; apple::apple(int i) :apple_number(i) {} // int apple::add(int num) { take(num); return 0; } int apple::add(int num) const { take(num); return 0; } void apple::take(int num) const { cout << "take fun" << num << endl; } int apple::getCount() const { take(1); return apple_number; } void f(const int i) { // 用于限定不可以修改参数. //i++; } extern int ext; // 特别注意这个, 在另外一个文件中, 是通过extern 和const 修饰的, // 外部 extern const int ext2=12; // extern int ext2; 报错, // const int ext2; 报错. // extern const int ext2; 正常, // 有初始值. 外部 const int ext2=12 // extern int ext2; 报错, // const int ext2; 报错. // extern const int ext2; 报错, // 无初始值. 外部 const int ext2; // extern int ext2; 报错, // const int ext2; 报错. // extern const int ext2; 报错 extern const int ext2; // 总结: // 1 在使用了const后, 必须显示的申明为 extern ,在跨文件的模式下, 说明 const 是一个局部的. // 2 在使用了const后, 必须显示的赋值. // 3 在使用了const后 ,使用的地方必须类型一致. int main() { const int a = 1 + 1; cout << ext + 10 << endl; cout << ext2 + 12 << endl; const int b = 10; //b = 0; const string s = "helloworld"; cout << s << endl; const int* ptr = &a; ptr = &b; // *ptr = 10; 这个是错误的, 如果这么写了, 实际修改的是, 指针所指对象的值 , // 说白了, 就是int对象是不可以修改的, const int p = 10; const void* q = &p; //void* m = &p; 类型不匹配, 申明不可以通过q 来修改p的值. 如果是这样就容易被修改了, int num = 10; int num1 = 12; int* const ptr1 = # int* t = # //ptr1 = &num1; 这个错误说明了, 该指针的值是不可以修改的, *ptr1 = 11; //说明指针所指对象的值是可以修改的, cout << *ptr1 << endl; // 如上所示, 重点修饰的是指针, // 总结 : 如果const在*的左边, 则说明指针指向的对象的值不可以修改, // 如果const在* 的右边, 则说明指针本身是不可以被修改的, apple app(2); cout << app.getCount() << endl; app.add(10); const apple appb(3); appb.add(100); return 0; return 0; }
博主的原文链接 : https://github.com/Light-City/CPlusPlusThings/tree/master/basic_content/const ,非常感谢博主的分享.
软件开发
2020-06-28 23:05:00
As Node.js continues to gain in popularity, new tutorials pop up teaching you to write server-side JavaScript apps and APIs. Once you’ve built your shiny new Node app, though, what then?
In this article, I’m going to take a look at a couple of options for deploying your Node applications. We’ll take a look at Now.sh and Heroku .
I’ll explain how to deploy your code to each platform and we’ll end the article with a short summary of the pros and cons. I’ll pay attention to options for monitoring, ease of use, offered functionality and what the free hosting plan includes.
Deployment with Heroku
To be able to deploy apps to Heroku, you will have to sign up at Heroku and install the Heroku CLI for your machine. I prefer working from my terminal!
Before we can start, we need to add some code to the Procfile . Heroku makes use of this file to determine how to execute the uploaded code.
The following code needs to be added to the file so Heroku knows what command should be executed to start the app: web: node app.js
Once this is done, try to log in from the terminal by typing heroku login . Heroku will ask you to enter your login credentials.
Next, navigate to the root of your project and enter the command: heroku create . This creates an app on Heroku which is ready to receive the source code of your project. The name of the app on Heroku is randomly created.
To deploy our code to Heroku, simply use git push heroku master . We can visit the app with the command heroku open which will open the generated URL.
Pushing changes to Heroku
Changes can be pushed by following the normal Github flow: git add . git commit -m "Changes made to app" git push heroku master heroku open
Useful Heroku Commands To make sure that at least one instance of the app is running: heroku ps:scale web=1
Because we are using the free platform, it is not possible to upscale your application. However, it is possible to downscale so no instances of the application are running: heroku ps:scale web=0 View the latest logs (stream) in chronological order generated by Heroku: heroku logs --tail
It’s also possible to show the app logs only. App logs are the output of console.log() statements in your code and can be viewed with heroku logs --source app-name Heroku provides the possibility to run your app locally at http://localhost:5000 : heroku local web List all Heroku apps: heroku apps

Remove a deployment: heroku apps:destroy --app app-name Add owner (account) to access the app: heroku access:add me@email.com , same for removing heroku access:remove me@email.com
Heroku Environment Variables
If you are working with a .env file locally, you might want to use other environment variables for your Heroku deployment. It is possible to set these with heroku config:set PORT=3001 . These values overwrite the variables set in you .env file.
To see all defined Heroku environment variables, just use heroku config . If you want to remove an environment variable for e.g. PORT , use heroku config:unset PORT .
Free Plan Allows up to five Heroku apps 512 MB RAM No upscaling available, only one instance of app can be running at the same time Sleeps after 30 minutes of inactivity Randomly generated app names Metrics about memory usage, response time and throughput available but not possible to add custom metrics
Deployment with now.sh
Now.sh focuses on the developer experience (DX) , which is kind of unique. They try to offer tools which are flexible and are incredibly easy to use. Now.sh is part of Zeit.co which have developed several tools .
To keep it simple, we will only install the Now.sh CLI through npm: npm install now -g
Next, we need to sign up so we can use our credentials in the console. Both login and sign up happen at the login page . Every time you sign in, you will have to confirm your login attempt by verifying through email. After confirming, you will be redirected to your dashboard where you can view your logs and deployments.
To start using now, just type now in your console. The console will prompt your email. Fill in the correct email and verify this again by clicking on the verification email.
Now we are logged in, let’s take a look at the start script in our package.json . Now.sh uses this to start the application. This is what the scripts field looks like:
"scripts" : { "start" : "node app" } ,

Let us start with deploying our code to now.sh. Make sure you are in the root of the code example. To start the deployment process, just hit now . I think you can see the developer experience there. Everything can be executed with just one keyword! If you make changes to the application and you want to redeploy it, just hit now in your console and you are good to go.
The URL of the app can be found in the console logs. More general logs about deployment or other now commands can be found at your dashboard .
Customization and defining environment variables
One way to customize your Now.sh deployment is by using a now.json file. However, since we are already using a package.json file, we can add the required customization under a the now key. This configuration allows you to customize the app name and alias, set environment variables , specify the deployment type and define the engine. "now" : { "name" : "my-first-app" , "alias" : "app1" , "type" : "npm" , "engines" : { "node" : "4.7.2" } , "env" : { "NODE_ENV" : "production" , "PORT" : "3001" } }
It’s also possible to set the environment variables through the CLI: now -e NODE_ENV="production" -e PORT="3001" .
If you want to provide a dotenv file, you can set the option now --dotenv , but maybe you want to use .env.production instead of .env ? This can be solved with --dotenv=.env.production . Lastly, you can also add the production dotenv file to your package.json . "now" : { "name" : "my-first-app" , "alias" : "app1" , "type" : "npm" , "engines" : { "node" : "4.7.2" } , "dotenv" : ".env.production" }
Useful Now.sh Commands The possibility to add an alias to your deployment: now alias deploy-url aliasname List all deployments with their unique code: now ls Remove a deployment: now rm unique-code Force a new build (in case of issues): now -f Scale your web app (free plan max 3): now scale deployment-url 3 . Sometimes, it is not easy to predict the amount of traffic. Now.sh enables you to set auto scaling with a min and max value: now scale deployment-url min max .
Monitoring Logs
Log output can be retrieved with: now logs [deployment-url | deployment-id] . More advanced logging is also possible: now logs -a -q "GET" -n 10 deployment-url : Shows the 10 latest logs containing the word GET . now logs --since=20171028 : Shows all the logs from the 28th of October 2017 (ISO 8601 format)
It is also possible to access your logs by clicking on an app in your Now.sh dashboard.
OSS plan Now.sh
The OSS plan is free to use and offers the following: Bandwidth: 1GB Log storage up to 100MB Infinite amount of deployments possible Concurrent instances is limited to 3 No support for custom domains Max file size: 1MB No auto-scaling support
The Bottom Line
Both Heroku and Now.sh offer great functionality. Now.sh focuses more on the developer experience by offering an easy to use CLI. On the other side, Heroku pays more attention to visual logging and especially monitoring with metrics.
Personally, I prefer the simplicity Now.sh offers by just using one keyword now for (re)deployment. For Node apps, I like the addition of the now property to the package.json file to customize your Now.sh deployment. No need to add extra files like the Procfile Heroku requires.
It’s hard to choose between both platforms. It just depends on your preferences and needs. Make sure to take a look at all the plans on offer. Good luck!
软件开发
2020-06-28 22:07:00
https://www.design-simulation.com/ip/
Interactive Physics allows you to model, simulate, and explore a wide variety of physical phenomena, and create nearly any experiment imaginable. If you can use a mouse, you can use Interactive Physics. Create objects by drawing circles, blocks, and polygons Measure velocity, acceleration, force, energy, etc., in metric or English units Create ropes, springs, dampers, pulleys, slots, actuators, and motors Simulate contact, collisions, and friction Vary air resistance, gravity, or material properties View results as numbers, graphs, and animated vectors Hear and measure sound volumes, sound frequencies, and Doppler effects Create visually appealing presentations by attaching graphics to objects
Discovery Oriented - Interactivity is key
Interactive Physics is a powerful tool for discovery learning and helps students visualize and learn abstract concepts. It develops inquiry skills and physics knowledge by allowing the user to vary nearly any physical parameter (e.g., gravity, force, speed, spring constants) and to measure its effect on nearly any measurable quantity (e.g., position, energy, decibel level).
软件开发
2020-06-28 21:42:00
好莱坞电影中有多少情节?一些电影评论家说只有五个。您可以采用几种架构来实现应用程序?目前大多数程序都使用下面提到的五种架构之一。
在本文中,我将五种软件架构模式的优缺点以及适合场景提炼出来作为快速参考。你可以在单个系统中使用多个架构模式,它们的组合既是计算机科学,也是一门艺术。
一、分层架构
这种方法可能是最常见的方法,因为它通常围绕数据库构建,并且业务中的许多应用程序自然会倾向于将信息存储在RDBMS的表中。许多比较大的软件框架(例如Java EE,Drupal和Express)都是在这种架构下实现的,因此使用它们构建的许多应用程序自然都来自分层体系结构。
Model-View-Controller(MVC)分层结构是大多数流行的Web框架提供的标准软件开发方法,显然是分层体系结构。数据持久层上方是服务层,它通常包含业务逻辑和有关数据库中数据类型的信息。视图层位于顶层,通常是CSS,JavaScript和带有动态嵌入式代码的HTML。在中间有一个控制层,该控制层具有用于转换在视图和模型之间移动的数据的各种规则和方法。
分层架构的优点:每个层可以只集中于自己的功能实现。这使得应用程序: 容易维护 容易单元测试 易于分配单独的“角色” 易于更新和扩展
适当的分层体系结构将开发层面进行隔离,这些层不受其他层的更改的影响,从而使重构更加容易。划分任务并定义单独的层是架构师面临的挑战。当需求很好地适应了模式时,这些层将易于解耦或分层开发。
适合: 需要快速构建的新应用程序 传统IT部门和流程的企业或业务应用程序 具有尚不了解其他架构的经验不足的开发人员的团队 需要严格的可维护性和可测试性标准的应用
二、事件驱动架构
事件驱动的体系架构根据数据生成一个“事件”,事件由“消息中间件”或“事件分发管理的中央单元”统一接收,并将事件分配特定类型的代码处理。
使用JavaScript编程网页涉及编写对诸如鼠标单击或击键之类的事件做出反应的小模块。浏览器本身会协调所有输入,并确保只有正确的代码才能得到正确的事件。浏览器中常见许多不同类型的事件,但是模块仅与相关的事件进行交互。这与分层体系结构非常不同,在分层体系结构中,所有数据通常都将穿过所有层。总体而言,事件驱动的体系结构: 容易适应复杂,混乱的业务环境 当出现新的事件类型时,很容易扩展
注意事项: 如果模块之间可以相互影响,则[测试可能会很复杂 当模块发生故障时,中央单元(或消息中间件)必须有一个事件备份计划。 消息传递开销可能会降低处理速度,消息中间件必须缓冲以突发形式到达的消息时。 当事件有非常不同的需求时,为事件开发数据结构可能会很复杂。 维护基于事务的一致性机制很困难,因为接收事件的模块是解耦和独立的。
适合: 具有异步数据流的异步系统 各个数据块仅与多模块中的少数模块交互的应用程序 用户界面
三、微内核-多插件架构
许多的应用程序都具有一组核心代码,这些代码在不同的模块下反复使用。例如,开发工具Eclipse将打开文件,批注,编辑文件并启动后台处理器。用于显示文件和对其进行编辑的代码是微内核的一部分。其他的插件扩展了Eclipse,从而扩展了其功能。
具体到解决方案就是将一些基本的核心的任务代码推入微内核。然后,不同的业务部门可以根据不同类型的声明编写插件。
注意事项: 确定哪些代码是微内核中的内容通常是一门艺术。它应该保留经常被使用的代码。 一旦许多插件依赖微内核,修改微内核可能非常困难,甚至不可能。唯一的解决方案就是修改插件。 为内核函数选择正确的粒度很难事先完成,也几乎不可能在后期进行更改。
适合: 工具类软件 在核心代码与边缘代码之间有清晰区分的应用程序 具有一组固定的核心函数和一组动态规则的应用程序
四、微服务架构
小宝宝既可爱又有趣,但是一旦变大,就很难操纵并且难以维护。微服务架构旨在帮助开发人员避免让自己的宝宝长大,笨拙,僵硬,烦人。它的目标不是创建一个大型程序,而是创建多个不同的小型程序。避免修改一个小bug,就需要重新部署整个大型应用的情况出现。
这种方法类似于事件驱动和微内核方法,但是主要用于解耦不同模块及任务。在许多情况下,不同的任务可能需要不同的处理量,并且用途可能会有所不同。所以微服务的特点是便于修改、便于扩展。使用负载均衡及服务发现的机制,在用户使用高峰期部署更多的微服务,保证服务的高可用;在用户低频服务时段缩减微服务,从而节省服务器资源。
注意事项: 并非所有应用程序都可以拆分为相对独立的微服务单元。 当任务分散在不同的微服务之间时,通信成本会更大。单个请求的响应时长会增加。
适合: 快速发展新业务团队 大型Web应用程序
五、高速缓存架构
许多网站都是围绕数据库构建的,只要数据库能够满足负载,它们就可以正常运行。但是当使用量达到顶峰,并且数据库无法跟上用户请求的速度时,整个网站就会瘫痪。将数据存储在内存中可以使许多工作更快,从而大幅度提高用户并发访问的支撑能力。
注意事项: 对于内存数据库,事务的支持更加困难。 开发专业的高速缓存数据的程序,对程序员的技术水平往往要求更高一些(至少比只会写增删改查的程序员要高)
适合: 高频点击数据流和用户日志之类的大量数据处理 低价值数据,有时可能会丢失而不会造成重大后果(比如用户访问量数据) 读多写少的数据。比如新闻数据,写完之后几乎不改,但是有很多的人看。
欢迎关注我的博客,里面有很多精品合集 本文转载注明出处(必须带连接,不能只转文字): 字母哥博客 。
觉得对您有帮助的话,帮我点赞、分享!您的支持是我不竭的创作动力! 。另外,笔者最近一段时间输出了如下的精品内容,期待您的关注。 《手摸手教你学Spring Boot2.0》 《Spring Security-JWT-OAuth2一本通》 《实战前后端分离RBAC权限管理系统》 《实战SpringCloud微服务从青铜到王者》 《VUE深入浅出系列》
软件开发
2020-07-18 07:37:00
调度算法指的是根据系统的资源分配策略所规定的的资源分配算法。
可以根据不同的环境来讨论调度算法:
批处理系统:
- 先来先服务:
非抢占式服务 。每个就绪进程都会加入就绪队列中,每次调度队头的作业。
优缺点:利于长作业但不利于短作业。
- 短作业:
非抢占式服务 。在队列中选择较短时间的进行调度。
优缺点:长作业可能会等待时间过长。
- 最短剩余时间优先:
抢占式服务 。每次到来一个新进程会跟当前执行进程的剩余时间 作比较,如果新进程时间更短就挂起当前线程去执行新进程。
- 高响应比优先:
根据响应比(进程执行时间+等待时间 /等待时间)来进行调度。
相同等待时间时,短作业优先;而随着等待时间的增长,响应比会增大,提高了优先级,避免饥饿现象。
优缺点:兼顾了长短作业,但计算响应比的开销大。
交互系统:
- 时间片轮转:
非抢占式服务 。预先规定时间片大小,同样是执行队头的进程,但当时间片用完后就会将当前进程放到队尾,执行下一进程。适合分时系统
- 优先级:
为每个进程设定优先级,大的先执行。
- 多级队列:
设置多个队列,每个队列的时间片依次增大,当上一个队列中的进程时间片用完了但还没有执行完就会放到下一队列中。
只有当上一队列没有进程在排队了,才能执行下一队列的进程。
软件开发
2020-07-18 06:55:00

1.创建状态:进程在创建时会先申请一个空白PCB,填入控制和管理进程的信息,完成资源的分配。
2.就绪状态:已完成资源分配,等到CPU时间片就可以运行。
3.运行状态:就绪的进程在由调度算法获得CPU时间片后就可以运行该进程。
4.阻塞状态:进程所需的资源不满足(等待IO、内存不足等)时会被阻塞,进程暂时无法运行。 等待资源
5.终止状态:进程结束,出现错误/被系统终止,无法在执行。
ps:阻塞状态是资源不足,而CPU时间不足时会由运行直接转为就绪。
软件开发
2020-07-18 06:21:00
继 Golang学习系列第三天 :学习数组、切片、Map、结构体、指针、函数、接口类型、channel通道,今天学习golang操作数据库,以PostgreSQL为例。

0. 安装PostgreSQL 数据库
可以参考PostgreSQL官网 https://www.postgresql.org/download/linux/redhat/ 安装该数据库
特别需要说明的是,安装完成后,自动建立了一个名为postgres的用户,默认密码为空;同时也自动创建了一个名字叫postgres的数据库。
0.1、修改默认生成的数据库用户postgres的密码。
把密码设置为12345678.
0.2 创建示例数据库
测试数据库名可以自取,
然后建一张测试表让golang使用
CREATE TABLE users ( id serial PRIMARY KEY , email VARCHAR ( 355 ) UNIQUE NOT NULL , password VARCHAR ( 50 ) NOT NULL ); insert into users ( id ,email, password ) values ( 1 , '1056764180@qq,com' , '12345678' ); insert into users ( id ,email, password ) values ( 2 , '10567@qq,com' , '1234567890' ); insert into users ( id ,email, password ) values ( 3 , '10567567@qq,com' , '12345678908' );
0.3 开启远程访问
由于数据库和应用程序不在同一机器上,故数据库要开启远程访问功能
修改配置文件,即
vim /var/lib/pgsql/12/data/postgresql.conf
找到listen_adderess配置项设为*
继续修改另一配置文件,即
vim /var/lib/pgsql/12/data/pg_hba.conf
在# IPv4 local connections:处追加客户端的连接信息
重启postgresql服务
systemctl restart postgresql-12
最后客户端测试连接


1. golang操作数据库
连接数据库会使用第三方驱动包,由于墙的缘故,可以先设置一下代理
go env -w GO111MODULE=on go env -w GOPROXY=https: //mirrors.aliyun.com/goproxy/,direct
就以基本的增删改查数据,记录如何使用go操作数据库
1. 1 Select查询数据
新建postgres.go项目,键入以下测试连接数据库的代码
package main import ( "database/sql" "fmt" "log" _ "github.com/lib/pq" //_ "github.com/bmizerany/pq" ) const ( // TODO fill this in directly or through environment variable // Build a DSN e.g. postgres://username:password@url.com:5432/dbName DB_DSN = "postgres://postgres:12345678@192.168.8.200:5432/douyin?sslmode=disable" ) type User struct { ID int Email string Password string } func main () { // Create DB pool //db, err := sql.Open("postgres", "host=192.168.8.200 port=5432 user=postgres password=12345678 dbname=douyin sslmode=disable") db, err := sql.Open( "postgres" ,DB_DSN) if err != nil { log.Fatal( "Failed to open a DB connection: " , err) } defer db.Close() // Create an empty user and make the sql query (using $1 for the parameter) var myUser User userSql := "SELECT id, email, password FROM users WHERE id = $1" err = db.QueryRow(userSql, 1 ).Scan(&myUser.ID, &myUser.Email, &myUser.Password) if err != nil { log.Fatal( "Failed to execute query: " , err) } fmt.Printf( "你好 邮箱:%s, 密码:%s, 欢迎回来!\n" , myUser.Email, myUser.Password) }
然后创建一个模块依赖文件
go mod init pluginModel
安装具体的依赖包
go get github.com/lib/pq

最后运行测试代码
[root@master goworkspace]# go run postgres. go
从数据库查询id等于1的记录,如图
和数据库里的数据是对应的

1.2 增加数据
接上1.1示例代码,稍作更改即可,文件命名为postgres-create.go
package main import ( "database/sql" "fmt" "log" _ "github.com/lib/pq" //_ "github.com/bmizerany/pq" ) const ( // TODO fill this in directly or through environment variable // Build a DSN e.g. postgres://username:password@url.com:5432/dbName DB_DSN = "postgres://postgres:12345678@192.168.8.200:5432/douyin?sslmode=disable" ) type User struct { ID int Email string Password string } func main() { // Create DB pool //db, err := sql.Open("postgres", "host=192.168.8.200 port=5432 user=postgres password=12345678 dbname=douyin sslmode=disable") db, err := sql.Open("postgres",DB_DSN) if err != nil { log.Fatal("Failed to open a DB connection: ", err) } defer db.Close() //创建一个用户,预要插入到数据库里 var user User = User{ID:4,Email:"110@qq.com",Password:"1234567890"} //执行插入操作 _, err = db.Exec("INSERT INTO users (id,email,password) VALUES($1,$2,$3)", user.ID,user.Email,user.Password) if err != nil { log.Fatal(err) } //打印日志 log.Printf("create ok!!!") //测试数据是否插入成功,执行具体的查询语句 var myUser User userSql := "SELECT id, email, password FROM users WHERE id = $1" //设置查询参数为4,即创建数据时的ID值 err = db.QueryRow(userSql, 4).Scan(&myUser.ID, &myUser.Email, &myUser.Password) if err != nil { log.Fatal("Failed to execute query: ", err) } //输出查询结果 fmt.Printf("hello email: %s, password: %s, welcome back!\n", myUser.Email,myUser.Password) }
执行程序代码,输出结果

1.3 update修改数据
接上1.2示例代码,稍作更改即可,文件命名为postgres-update.go
package main import ( "database/sql" "fmt" "log" _ "github.com/lib/pq" //_ "github.com/bmizerany/pq" ) const ( // TODO fill this in directly or through environment variable // Build a DSN e.g. postgres://username:password@url.com:5432/dbName DB_DSN = "postgres://postgres:12345678@192.168.8.200:5432/douyin?sslmode=disable" ) type User struct { ID int Email string Password string } func main() { // Create DB pool //db, err := sql.Open("postgres", "host=192.168.8.200 port=5432 user=postgres password=12345678 dbname=douyin sslmode=disable") db, err := sql.Open("postgres",DB_DSN) if err != nil { log.Fatal("Failed to open a DB connection: ", err) } defer db.Close() //创建一个用户,预要通过主键更改到数据库里 var user User = User{ID:4,Email:"dong@qq.com",Password:"abcdedf120"} //执行更改操作 _, err = db.Exec("UPDATE users SET email=$1, password=$2 where id=$3", user.Email,user.Password,user.ID) if err != nil { log.Fatal(err) } //打印日志 log.Printf("update ok!!!") //测试数据是否更改成功,执行具体的查询语句 var myUser User userSql := "SELECT id, email, password FROM users WHERE id = $1" //设置查询参数为4,即要更改数据的ID值 err = db.QueryRow(userSql, 4).Scan(&myUser.ID, &myUser.Email, &myUser.Password) if err != nil { log.Fatal("Failed to execute query: ", err) } //输出查询结果 fmt.Printf("hello email: %s, password: %s, welcome back!\n", myUser.Email,myUser.Password) }

执行程序代码,输出结果

1.4 delete删除数据记录
接上1.3示例代码,稍作更改即可,文件命名为postgres-delete.go

package main import ( "database/sql" "fmt" "log" _ "github.com/lib/pq" //_ "github.com/bmizerany/pq" ) const ( // TODO fill this in directly or through environment variable // Build a DSN e.g. postgres://username:password@url.com:5432/dbName DB_DSN = "postgres://postgres:12345678@192.168.8.200:5432/douyin?sslmode=disable" ) type User struct { ID int Email string Password string } func main() { // Create DB pool //db, err := sql.Open("postgres", "host=192.168.8.200 port=5432 user=postgres password=12345678 dbname=douyin sslmode=disable") db, err := sql.Open("postgres",DB_DSN) if err != nil { log.Fatal("Failed to open a DB connection: ", err) } defer db.Close() //执行更改操作 _, err = db.Exec("DELETE FROM users where id=$1", 4) if err != nil { log.Fatal(err) } //打印日志 log.Printf("delete ok!!!") //测试数据是否更改成功,执行具体的查询语句 var myUser User userSql := "SELECT id, email, password FROM users WHERE id = $1" //设置查询参数为4,即要更改数据的ID值 err = db.QueryRow(userSql, 4).Scan(&myUser.ID, &myUser.Email, &myUser.Password) if err != nil { log.Fatal("Failed to execute query: ", err) } //输出查询结果 fmt.Printf("hello email: %s, password: %s, welcome back!\n", myUser.Email,myUser.Password) }
执行以上程序代码,执行输出结果

至此到这里关于golang操作数据库postgresql就告一段落了,收工。
代码已上传到github: https://github.com/dongguangming/golang-learn/tree/master/go-postgresql
注:由于我没有用可视化编程工具,是用vi编写的go代码,请你们自行排版其结构。

参考: Postgresql 密码设置 http://www.mamicode.com/info-detail-1977540.html golang连接postgresql数据库 https://msd.misuland.com/pd/3181438578597038522 cannot find module providing package github.com/xxx: working directory is not part of a module https://www.sunzhongwei.com/cannot-find-module-providing-package-githubcomxxx-working-directory-is-not-part-of-a-module SSL is not enabled on the server https://stackoverflow.com/questions/21959148/ssl-is-not-enabled-on-the-server Resolve "FATAL:no pg_hba.conf entry for host" Error when you Connect from PGAdmin4 https://www.cisco.com/c/en/us/support/docs/cloud-systems-management/cloudcenter/212585-resolve-fatal-no-pg-hba-conf-entry-for.html Connect to PostgreSQL and Run a Query https://golangcode.com/postgresql-connect-and-query/ golang postgresql CRUD https://www.cnblogs.com/ibgo/p/6010245.html
软件开发
2020-07-18 01:08:00
Mac安装svn(解决新系统Xcode不支持问题)
博客说明 文章所涉及的资料来自互联网整理和个人总结,意在于个人学习和经验汇总,如有什么地方侵权,请联系本人删除,谢谢!
说明
在新版的系统里面xcode不再支持svn
所以我们要使用的话,请使用brew来安装
安装
请使用国内中科大的源(解决报错问题)
https://guizimo.blog.csdn.net/article/details/107419919 brew install subversion
测试 svn --version
感谢 百度百科
以及勤劳的自己
软件开发
2020-07-17 23:14:00
Brew发现自动更新homebrew时卡住
博客说明 文章所涉及的资料来自互联网整理和个人总结,意在于个人学习和经验汇总,如有什么地方侵权,请联系本人删除,谢谢!
原因
因为我之前换过源,换成了是阿里的源,然后阿里的源没有cask,所以出错在这里
解决
换成中科大的源 // 替换brew.git: cd "$(brew --repo)" git remote set-url origin https://mirrors.ustc.edu.cn/brew.git // 替换homebrew-core.git: cd "$(brew --repo)/Library/Taps/homebrew/homebrew-core" git remote set-url origin https://mirrors.ustc.edu.cn/homebrew-core.git // 替换homebrew-cask.git: cd "$(brew --repo)"/Library/Taps/homebrew/homebrew-cask git remote set-url origin https://mirrors.ustc.edu.cn/homebrew-cask.git
更新源 brew update
之后再次安装就解决了问题
感谢 百度百科
以及勤劳的自己
软件开发
2020-07-17 23:06:00
Mac安装oracle(使用Docker)
博客说明 文章所涉及的资料来自互联网整理和个人总结,意在于个人学习和经验汇总,如有什么地方侵权,请联系本人删除,谢谢!
安装
拉取oracle 11g 镜像,这个版本可以自行选择 docker pull registry.cn-hangzhou.aliyuncs.com/helowin/oracle_11g
安装oracle容器 docker run -dp 9090:8080 -p 1521:1521 registry.cn-hangzhou.aliyuncs.com/helowin/oracle_11g
进入容器 docker exec -it 容器id /bin/bash
容器id可以通过一下命令查询 docker ps -a
使用navicat连接
默认值
服务名:helowin
用户名:system
密码:helowin
测试
停止/启动oracle服务: docker stop oracle docker start oracle
感谢 百度百科
以及勤劳的自己
软件开发
2020-07-17 22:49:00
基数排序(Java)
博客说明 文章所涉及的资料来自互联网整理和个人总结,意在于个人学习和经验汇总,如有什么地方侵权,请联系本人删除,谢谢!
基数排序(桶排序)介绍
基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或bin sort,顾名思义,它是通过键值的各个位的值,将要排序的元素分配至某些“桶”中,达到排序的作用
基数排序法是属于稳定性的排序,基数排序法的是效率高的稳定性排序法
基数排序(Radix Sort)是桶排序的扩展
基数排序是1887年赫尔曼·何乐礼发明的。它是这样实现的:将整数按位数切割成不同的数字,然后按每个位数分别比较。
基数排序基本思想
将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。
特点
空间换时间,稳定
代码 package cn.guizimo.sort; import java.util.Arrays; public class RadixSort { public static void main(String[] args) { int arr[] = {53,45,6,378,15,234,78}; System.out.println("排序前"); System.out.println(Arrays.toString(arr)); radixSort(arr); System.out.println("排序后"); System.out.println(Arrays.toString(arr)); } public static void radixSort(int arr[]) { //获取最大位数 int max = arr[0]; for (int i = 1; i < arr.length; i++) { if (arr[i] > max) { max = arr[i]; } } //计算位数 int maxLength = (max + "").length(); int[][] bucket = new int[10][arr.length]; int[] bucketElemtCounts = new int[10]; for (int i = 0, n = 1; i < maxLength; i++, n *= 10) { for (int j = 0; j < arr.length; j++) { int digitOfElemt = arr[j] / n % 10; bucket[digitOfElemt][bucketElemtCounts[digitOfElemt]] = arr[j]; bucketElemtCounts[digitOfElemt]++; } int index = 0; for (int k = 0; k < bucketElemtCounts.length; k++) { if (bucketElemtCounts[k] != 0) { for (int l = 0; l < bucketElemtCounts[k]; l++) { arr[index++] = bucket[k][l]; } } bucketElemtCounts[k] = 0; } System.out.println("第"+(i+1)+"轮排序"); System.out.println(Arrays.toString(arr)); } } }
测试
测试速度 package cn.guizimo.sort; import java.util.Arrays; public class RadixSort { public static void main(String[] args) { int max = 80000; int[] arr = new int[max]; for (int i = 0; i < max; i++) { arr[i] = (int)(Math.random() * 80000); } long date1 = System.currentTimeMillis(); radixSort(arr); long date2 = System.currentTimeMillis(); System.out.println("位移式希尔排序"+max+"数组的时间为:"+(date2-date1)); } public static void radixSort(int arr[]) { //获取最大位数 int max = arr[0]; for (int i = 1; i < arr.length; i++) { if (arr[i] > max) { max = arr[i]; } } //计算位数 int maxLength = (max + "").length(); int[][] bucket = new int[10][arr.length]; int[] bucketElemtCounts = new int[10]; for (int i = 0, n = 1; i < maxLength; i++, n *= 10) { for (int j = 0; j < arr.length; j++) { int digitOfElemt = arr[j] / n % 10; bucket[digitOfElemt][bucketElemtCounts[digitOfElemt]] = arr[j]; bucketElemtCounts[digitOfElemt]++; } int index = 0; for (int k = 0; k < bucketElemtCounts.length; k++) { if (bucketElemtCounts[k] != 0) { for (int l = 0; l < bucketElemtCounts[k]; l++) { arr[index++] = bucket[k][l]; } } bucketElemtCounts[k] = 0; } } } }
感谢 尚硅谷
万能的网络
以及勤劳的自己
软件开发
2020-06-27 15:36:00
什么是UTF-8编码
UTF-8是一种变长字节编码方式。
对于某一个字符的UTF-8编码,如果只有一个字节则其最高二进制位为0;如果是多字节,其第一个字节从最高位开始,连续的二进制位值为1的个数决定了其编码的位数,其余各字节均以10开头。UTF-8最多可用到6个字节。
如表:
1字节: 0xxxxxxx
2字节:110xxxxx 10xxxxxx
3字节:1110xxxx 10xxxxxx 10xxxxxx
4字节:11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
5字节:111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
6字节:1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
因此UTF-8中可以用来表示字符编码的实际位数最多有31位,即上表中x所表示的位。除去那些控制位(每字节开头的10等),这些x表示的位与UNICODE编码是一一对应的,位高低顺序也相同。 实际将UNICODE转换为UTF-8编码时应先去除高位0,然后根据所剩编码的位数决定所需最小的UTF-8编码位数。 因此那些基本ASCII字符集中的字符(UNICODE兼容ASCII)只需要一个字节的UTF-8编码(7个二进制位)便可以表示,而对于汉字,则一般需要3个字节表示。
取出二进制码
本文以汉字“春”为例进行说明 String str = "春"; char[] chars = str.toCharArray(); System.out.println(Integer.toString(chars[0], 2));
得到二进制码: 0110011000100101
UTF-8的编码规则
规则很简单,只有两条:
1、对于单字节的符号,字节的第一位设为0,后面7位为这个符号的unicode码。因此对于英语字母,UTF-8编码和ASCII码是相同的。
2、对于n字节的符号(n>1),第一个字节的前n位都设为1,第n+1位设为0,后面字节的前两位一律设为10。剩下的没有提及的二进制位,全部为这个符号的UNICODE码。
得到UTF8编码后字节: 11100110 10011000 10100101
UTF-8编码字节转成10进制
我们以第一个字节 11100110 为例,其他字节逻辑都是相同的。在这里11100110是以【补码】的形式存在的,那我们想转化成10进制,需要先推算出原码,要想推算出原码,我们得先知道原码转补码的逻辑,然后再逆向推理即可:
原码转补码逻辑:符号位不变,数值位按位取反,末位再加1。
那补码转原码的逻辑就是:
符号位不变,末位减1,数值位按位取反。
得出 11100110 的原码 10011010,符号位(最高位)为1,代表负数,数值位转10进制为26,那合起来就是 -26。
UTF-8编码字节转成16进制
二进制转16进制就简单许多,只需要从低位起每4位一组分别计算出值即可,仍然以11100110为例:
总结
按照上述的方法,汉字“春”最终得出的10进制为: [-26 -104 -91]
而最终的16进制为: [E6 98 A5]
汉字转16进制还是用的挺多的,浏览器url编码的时候也是默认转成16进制,只是每个编码结果前增加了%:
同时Java中的URLEncoder.encode方法也是一样:
编写测试代码: @Test public void testEncode() throws Exception { String str = "春"; System.out.println(URLEncoder.encode(str, "UTF-8")); }
运行结果:
本文仅供学习之用,版权归原作者所有,如有侵权请联系删除。
我将面试题和答案都整理成了PDF文档,还有一套学习资料,涵盖Java虚拟机、spring框架、Java线程、数据结构、设计模式等等,但不仅限于此。
关注公众号【java圈子】获取资料,还有优质文章每日送达。
软件开发
2020-06-27 15:34:00
styleguide
Google Python Style Guide
1 Background
Python is the main dynamic language used at Google. This style guide is a list of dos and don’ts for Python programs.
To help you format code correctly, we’ve created a settings file for Vim . For Emacs, the default settings should be fine.
Many teams use the yapf auto-formatter to avoid arguing over formatting.

2 Python Language Rules
2.1 Lint
Run pylint over your code.
2.1.1 Definition
pylint is a tool for finding bugs and style problems in Python source code. It finds problems that are typically caught by a compiler for less dynamic languages like C and C++. Because of the dynamic nature of Python, some warnings may be incorrect; however, spurious warnings should be fairly infrequent.
2.1.2 Pros
Catches easy-to-miss errors like typos, using-vars-before-assignment, etc.
2.1.3 Cons
pylint isn’t perfect. To take advantage of it, we’ll need to sometimes: a) Write around it b) Suppress its warnings or c) Improve it.
2.1.4 Decision
Make sure you run pylint on your code.
Suppress warnings if they are inappropriate so that other issues are not hidden. To suppress warnings, you can set a line-level comment: dict = 'something awful' # Bad Idea... pylint: disable=redefined-builtin
pylint warnings are each identified by symbolic name ( empty-docstring ) Google-specific warnings start with g- .
If the reason for the suppression is not clear from the symbolic name, add an explanation.
Suppressing in this way has the advantage that we can easily search for suppressions and revisit them.
You can get a list of pylint warnings by doing: pylint --list-msgs
To get more information on a particular message, use: pylint --help-msg = C6409
Prefer pylint: disable to the deprecated older form pylint: disable-msg .
Unused argument warnings can be suppressed by deleting the variables at the beginning of the function. Always include a comment explaining why you are deleting it. “Unused.” is sufficient. For example: def viking_cafe_order ( spam , beans , eggs = None ): del beans , eggs # Unused by vikings. return spam + spam + spam
Other common forms of suppressing this warning include using ‘ _ ’ as the identifier for the unused argument, prefixing the argument name with ‘ unused_ ’, or assigning them to ‘ _ ’. These forms are allowed but no longer encouraged. The first two break callers that pass arguments by name, while the last does not enforce that the arguments are actually unused.
2.2 Imports
Use import statements for packages and modules only, not for individual classes or functions. Note that there is an explicit exemption for imports from the typing module .
2.2.1 Definition
Reusability mechanism for sharing code from one module to another.
2.2.2 Pros
The namespace management convention is simple. The source of each identifier is indicated in a consistent way; x.Obj says that object Obj is defined in module x .
2.2.3 Cons
Module names can still collide. Some module names are inconveniently long.
2.2.4 Decision Use import x for importing packages and modules. Use from x import y where x is the package prefix and y is the module name with no prefix. Use from x import y as z if two modules named y are to be imported or if y is an inconveniently long name. Use import y as z only when z is a standard abbreviation (e.g., np for numpy ).
For example the module sound.effects.echo may be imported as follows: from sound.effects import echo ... echo . EchoFilter ( input , output , delay = 0.7 , atten = 4 )
Do not use relative names in imports. Even if the module is in the same package, use the full package name. This helps prevent unintentionally importing a package twice.
Imports from the typing module and the six.moves module are exempt from this rule.
2.3 Packages
Import each module using the full pathname location of the module.
2.3.1 Pros
Avoids conflicts in module names or incorrect imports due to the module search path not being what the author expected. Makes it easier to find modules.
2.3.2 Cons
Makes it harder to deploy code because you have to replicate the package hierarchy. Not really a problem with modern deployment mechanisms.
2.3.3 Decision
All new code should import each module by its full package name.
Imports should be as follows:
Yes: # Reference absl.flags in code with the complete name (verbose). import absl.flags from doctor.who import jodie FLAGS = absl . flags . FLAGS # Reference flags in code with just the module name (common). from absl import flags from doctor.who import jodie FLAGS = flags . FLAGS
No: (assume this file lives in doctor/who/ where jodie.py also exists) # Unclear what module the author wanted and what will be imported. The actual # import behavior depends on external factors controlling sys.path. # Which possible jodie module did the author intend to import? import jodie
The directory the main binary is located in should not be assumed to be in sys.path despite that happening in some environments. This being the case, code should assume that import jodie refers to a third party or top level package named jodie , not a local jodie.py .


2.4 Exceptions
Exceptions are allowed but must be used carefully.


2.4.1 Definition
Exceptions are a means of breaking out of the normal flow of control of a code block to handle errors or other exceptional conditions.


2.4.2 Pros
The control flow of normal operation code is not cluttered by error-handling code. It also allows the control flow to skip multiple frames when a certain condition occurs, e.g., returning from N nested functions in one step instead of having to carry-through error codes.


2.4.3 Cons
May cause the control flow to be confusing. Easy to miss error cases when making library calls.


2.4.4 Decision
Exceptions must follow certain conditions: Raise exceptions like this: raise MyError('Error message') or raise MyError() . Do not use the two-argument form ( raise MyError, 'Error message' ). Make use of built-in exception classes when it makes sense. For example, raise a ValueError to indicate a programming mistake like a violated precondition (such as if you were passed a negative number but required a positive one). Do not use assert statements for validating argument values of a public API. assert is used to ensure internal correctness, not to enforce correct usage nor to indicate that some unexpected event occurred. If an exception is desired in the latter cases, use a raise statement. For example:
Yes : def connect_to_next_port ( self , minimum ): """Connects to the next available port. Args: minimum: A port value greater or equal to 1024. Returns: The new minimum port. Raises: ConnectionError: If no available port is found. """ if minimum < 1024 : # Note that this raising of ValueError is not mentioned in the doc # string's "Raises:" section because it is not appropriate to # guarantee this specific behavioral reaction to API misuse. raise ValueError ( 'Minimum port must be at least 1024, not %d.' % ( minimum ,)) port = self . _find_next_open_port ( minimum ) if not port : raise ConnectionError ( 'Could not connect to service on %d or higher.' % ( minimum ,)) assert port >= minimum , 'Unexpected port %d when minimum was %d.' % ( port , minimum ) return port
No : def connect_to_next_port ( self , minimum ): """Connects to the next available port. Args: minimum: A port value greater or equal to 1024. Returns: The new minimum port. """ assert minimum >= 1024 , 'Minimum port must be at least 1024.' port = self . _find_next_open_port ( minimum ) assert port is not None return port Libraries or packages may define their own exceptions. When doing so they must inherit from an existing exception class. Exception names should end in Error and should not introduce stutter ( foo.FooError ). Never use catch-all except: statements, or catch Exception or StandardError , unless you are re-raising the exception, or creating an isolation point in the program where exceptions are not propagated but are recorded and suppressed instead, such as protecting a thread from crashing by guarding its outermost block.
Python is very tolerant in this regard and except: will really catch everything including misspelled names, sys.exit() calls, Ctrl+C interrupts, unittest failures and all kinds of other exceptions that you simply don’t want to catch. Minimize the amount of code in a try / except block. The larger the body of the try , the more likely that an exception will be raised by a line of code that you didn’t expect to raise an exception. In those cases, the try / except block hides a real error. Use the finally clause to execute code whether or not an exception is raised in the try block. This is often useful for cleanup, i.e., closing a file. When capturing an exception, use as rather than a comma. For example:
try : raise Error () except Error as error : pass


2.5 Global variables
Avoid global variables.


2.5.1 Definition
Variables that are declared at the module level or as class attributes.


2.5.2 Pros
Occasionally useful.


2.5.3 Cons
Has the potential to change module behavior during the import, because assignments to global variables are done when the module is first imported.


2.5.4 Decision
Avoid global variables.
While they are technically variables, module-level constants are permitted and encouraged. For example: MAX_HOLY_HANDGRENADE_COUNT = 3 . Constants must be named using all caps with underscores. See Naming below.
If needed, globals should be declared at the module level and made internal to the module by prepending an _ to the name. External access must be done through public module-level functions. See Naming below.


2.6 Nested/Local/Inner Classes and Functions
Nested local functions or classes are fine when used to close over a local variable. Inner classes are fine.


2.6.1 Definition
A class can be defined inside of a method, function, or class. A function can be defined inside a method or function. Nested functions have read-only access to variables defined in enclosing scopes.


2.6.2 Pros
Allows definition of utility classes and functions that are only used inside of a very limited scope. Very ADT -y. Commonly used for implementing decorators.


2.6.3 Cons
Instances of nested or local classes cannot be pickled. Nested functions and classes cannot be directly tested. Nesting can make your outer function longer and less readable.


2.6.4 Decision
They are fine with some caveats. Avoid nested functions or classes except when closing over a local value. Do not nest a function just to hide it from users of a module. Instead, prefix its name with an _ at the module level so that it can still be accessed by tests.


2.7 Comprehensions & Generator Expressions
Okay to use for simple cases.


2.7.1 Definition
List, Dict, and Set comprehensions as well as generator expressions provide a concise and efficient way to create container types and iterators without resorting to the use of traditional loops, map() , filter() , or lambda .


2.7.2 Pros
Simple comprehensions can be clearer and simpler than other dict, list, or set creation techniques. Generator expressions can be very efficient, since they avoid the creation of a list entirely.


2.7.3 Cons
Complicated comprehensions or generator expressions can be hard to read.


2.7.4 Decision
Okay to use for simple cases. Each portion must fit on one line: mapping expression, for clause, filter expression. Multiple for clauses or filter expressions are not permitted. Use loops instead when things get more complicated. Yes : result = [ mapping_expr for value in iterable if filter_expr ] result = [{ 'key' : value } for value in iterable if a_long_filter_expression ( value )] result = [ complicated_transform ( x ) for x in iterable if predicate ( x )] descriptive_name = [ transform ({ 'key' : key , 'value' : value }, color = 'black' ) for key , value in generate_iterable ( some_input ) if complicated_condition_is_met ( key , value ) ] result = [] for x in range ( 10 ): for y in range ( 5 ): if x * y > 10 : result . append (( x , y )) return { x : complicated_transform ( x ) for x in long_generator_function ( parameter ) if x is not None } squares_generator = ( x ** 2 for x in range ( 10 )) unique_names = { user . name for user in users if user is not None } eat ( jelly_bean for jelly_bean in jelly_beans if jelly_bean . color == 'black' ) No : result = [ complicated_transform ( x , some_argument = x + 1 ) for x in iterable if predicate ( x )] result = [( x , y ) for x in range ( 10 ) for y in range ( 5 ) if x * y > 10 ] return (( x , y , z ) for x in range ( 5 ) for y in range ( 5 ) if x != y for z in range ( 5 ) if y != z )


2.8 Default Iterators and Operators
Use default iterators and operators for types that support them, like lists, dictionaries, and files.


2.8.1 Definition
Container types, like dictionaries and lists, define default iterators and membership test operators (“in” and “not in”).


2.8.2 Pros
The default iterators and operators are simple and efficient. They express the operation directly, without extra method calls. A function that uses default operators is generic. It can be used with any type that supports the operation.


2.8.3 Cons
You can’t tell the type of objects by reading the method names (e.g. has_key() means a dictionary). This is also an advantage.


2.8.4 Decision
Use default iterators and operators for types that support them, like lists, dictionaries, and files. The built-in types define iterator methods, too. Prefer these methods to methods that return lists, except that you should not mutate a container while iterating over it. Never use Python 2 specific iteration methods such as dict.iter*() unless necessary. Yes : for key in adict : ... if key not in adict : ... if obj in alist : ... for line in afile : ... for k , v in adict . items (): ... for k , v in six . iteritems ( adict ): ... No : for key in adict . keys (): ... if not adict . has_key ( key ): ... for line in afile . readlines (): ... for k , v in dict . iteritems (): ...


2.9 Generators
Use generators as needed.


2.9 Definition
A generator function returns an iterator that yields a value each time it executes a yield statement. After it yields a value, the runtime state of the generator function is suspended until the next value is needed.


2.9.2 Pros
Simpler code, because the state of local variables and control flow are preserved for each call. A generator uses less memory than a function that creates an entire list of values at once.


2.9.3 Cons
None.


2.9.4 Decision
Fine. Use “Yields:” rather than “Returns:” in the docstring for generator functions.


2.10 Lambda Functions
Okay for one-liners.


2.10.1 Definition
Lambdas define anonymous functions in an expression, as opposed to a statement. They are often used to define callbacks or operators for higher-order functions like map() and filter() .


2.10.2 Pros
Convenient.


2.10.3 Cons
Harder to read and debug than local functions. The lack of names means stack traces are more difficult to understand. Expressiveness is limited because the function may only contain an expression.


2.10.4 Decision
Okay to use them for one-liners. If the code inside the lambda function is longer than 60-80 chars, it’s probably better to define it as a regular nested function .
For common operations like multiplication, use the functions from the operator module instead of lambda functions. For example, prefer operator.mul to lambda x, y: x * y .


2.11 Conditional Expressions
Okay for simple cases.


2.11.1 Definition
Conditional expressions (sometimes called a “ternary operator”) are mechanisms that provide a shorter syntax for if statements. For example: x = 1 if cond else 2 .


2.11.2 Pros
Shorter and more convenient than an if statement.


2.11.3 Cons
May be harder to read than an if statement. The condition may be difficult to locate if the expression is long.


2.11.4 Decision
Okay to use for simple cases. Each portion must fit on one line: true-expression, if-expression, else-expression. Use a complete if statement when things get more complicated. one_line = 'yes' if predicate ( value ) else 'no' slightly_split = ( 'yes' if predicate ( value ) else 'no, nein, nyet' ) the_longest_ternary_style_that_can_be_done = ( 'yes, true, affirmative, confirmed, correct' if predicate ( value ) else 'no, false, negative, nay' ) bad_line_breaking = ( 'yes' if predicate ( value ) else 'no' ) portion_too_long = ( 'yes' if some_long_module . some_long_predicate_function ( really_long_variable_name ) else 'no, false, negative, nay' )


2.12 Default Argument Values
Okay in most cases.


2.12.1 Definition
You can specify values for variables at the end of a function’s parameter list, e.g., def foo(a, b=0): . If foo is called with only one argument, b is set to 0. If it is called with two arguments, b has the value of the second argument.


2.12.2 Pros
Often you have a function that uses lots of default values, but on rare occasions you want to override the defaults. Default argument values provide an easy way to do this, without having to define lots of functions for the rare exceptions. As Python does not support overloaded methods/functions, default arguments are an easy way of “faking” the overloading behavior.


2.12.3 Cons
Default arguments are evaluated once at module load time. This may cause problems if the argument is a mutable object such as a list or a dictionary. If the function modifies the object (e.g., by appending an item to a list), the default value is modified.


2.12.4 Decision
Okay to use with the following caveat:
Do not use mutable objects as default values in the function or method definition. Yes : def foo ( a , b = None ): if b is None : b = [] Yes : def foo ( a , b : Optional [ Sequence ] = None ): if b is None : b = [] Yes : def foo ( a , b : Sequence = ()): # Empty tuple OK since tuples are immutable ... No : def foo ( a , b = []): ... No : def foo ( a , b = time . time ()): # The time the module was loaded??? ... No : def foo ( a , b = FLAGS . my_thing ): # sys.argv has not yet been parsed... ... No : def foo ( a , b : Mapping = {}): # Could still get passed to unchecked code ...


2.13 Properties
Use properties for accessing or setting data where you would normally have used simple, lightweight accessor or setter methods.


2.13.1 Definition
A way to wrap method calls for getting and setting an attribute as a standard attribute access when the computation is lightweight.


2.13.2 Pros
Readability is increased by eliminating explicit get and set method calls for simple attribute access. Allows calculations to be lazy. Considered the Pythonic way to maintain the interface of a class. In terms of performance, allowing properties bypasses needing trivial accessor methods when a direct variable access is reasonable. This also allows accessor methods to be added in the future without breaking the interface.


2.13.3 Cons
Must inherit from object in Python 2. Can hide side-effects much like operator overloading. Can be confusing for subclasses.


2.13.4 Decision
Use properties in new code to access or set data where you would normally have used simple, lightweight accessor or setter methods. Properties should be created with the @property decorator .
Inheritance with properties can be non-obvious if the property itself is not overridden. Thus one must make sure that accessor methods are called indirectly to ensure methods overridden in subclasses are called by the property (using the Template Method DP). Yes : import math class Square ( object ): """A square with two properties: a writable area and a read-only perimeter. To use: >>> sq = Square(3) >>> sq.area 9 >>> sq.perimeter 12 >>> sq.area = 16 >>> sq.side 4 >>> sq.perimeter 16 """ def __init__ ( self , side ): self . side = side @ property def area ( self ): """Area of the square.""" return self . _get_area () @ area . setter def area ( self , area ): return self . _set_area ( area ) def _get_area ( self ): """Indirect accessor to calculate the 'area' property.""" return self . side ** 2 def _set_area ( self , area ): """Indirect setter to set the 'area' property.""" self . side = math . sqrt ( area ) @ property def perimeter ( self ): return self . side * 4


2.14 True/False Evaluations
Use the “implicit” false if at all possible.


2.14.1 Definition
Python evaluates certain values as False when in a boolean context. A quick “rule of thumb” is that all “empty” values are considered false, so 0, None, [], {}, '' all evaluate as false in a boolean context.


2.14.2 Pros
Conditions using Python booleans are easier to read and less error-prone. In most cases, they’re also faster.


2.14.3 Cons
May look strange to C/C++ developers.


2.14.4 Decision
Use the “implicit” false if possible, e.g., if foo: rather than if foo != []: . There are a few caveats that you should keep in mind though: Always use if foo is None: (or is not None ) to check for a None value-e.g., when testing whether a variable or argument that defaults to None was set to some other value. The other value might be a value that’s false in a boolean context! Never compare a boolean variable to False using == . Use if not x: instead. If you need to distinguish False from None then chain the expressions, such as if not x and x is not None: . For sequences (strings, lists, tuples), use the fact that empty sequences are false, so if seq: and if not seq: are preferable to if len(seq): and if not len(seq): respectively. When handling integers, implicit false may involve more risk than benefit (i.e., accidentally handling None as 0). You may compare a value which is known to be an integer (and is not the result of len() ) against the integer 0.
Yes : if not users : print ( 'no users' ) if foo == 0 : self . handle_zero () if i % 10 == 0 : self . handle_multiple_of_ten () def f ( x = None ): if x is None : x = []
No : if len ( users ) == 0 : print ( 'no users' ) if foo is not None and not foo : self . handle_zero () if not i % 10 : self . handle_multiple_of_ten () def f ( x = None ): x = x or [] Note that '0' (i.e., 0 as string) evaluates to true.


2.15 Deprecated Language Features
Use string methods instead of the string module where possible. Use function call syntax instead of apply . Use list comprehensions and for loops instead of filter and map when the function argument would have been an inlined lambda anyway. Use for loops instead of reduce .


2.15.1 Definition
Current versions of Python provide alternative constructs that people find generally preferable.


2.15.2 Decision
We do not use any Python version which does not support these features, so there is no reason not to use the new styles. Yes : words = foo . split ( ':' ) [ x [ 1 ] for x in my_list if x [ 2 ] == 5 ] map ( math . sqrt , data ) # Ok. No inlined lambda expression. fn ( * args , ** kwargs ) No : words = string . split ( foo , ':' ) map ( lambda x : x [ 1 ], filter ( lambda x : x [ 2 ] == 5 , my_list )) apply ( fn , args , kwargs )


2.16 Lexical Scoping
Okay to use.


2.16.1 Definition
A nested Python function can refer to variables defined in enclosing functions, but can not assign to them. Variable bindings are resolved using lexical scoping, that is, based on the static program text. Any assignment to a name in a block will cause Python to treat all references to that name as a local variable, even if the use precedes the assignment. If a global declaration occurs, the name is treated as a global variable.
An example of the use of this feature is: def get_adder ( summand1 ): """Returns a function that adds numbers to a given number.""" def adder ( summand2 ): return summand1 + summand2 return adder


2.16.2 Pros
Often results in clearer, more elegant code. Especially comforting to experienced Lisp and Scheme (and Haskell and ML and …) programmers.


2.16.3 Cons
Can lead to confusing bugs. Such as this example based on PEP-0227 : i = 4 def foo ( x ): def bar (): print ( i , end = '' ) # ... # A bunch of code here # ... for i in x : # Ah, i *is* local to foo, so this is what bar sees print ( i , end = '' ) bar ()
So foo([1, 2, 3]) will print 1 2 3 3 , not 1 2 3 4 .


2.16.4 Decision
Okay to use.


2.17 Function and Method Decorators
Use decorators judiciously when there is a clear advantage. Avoid @staticmethod and limit use of @classmethod .


2.17.1 Definition
Decorators for Functions and Methods (a.k.a “the @ notation”). One common decorator is @property , used for converting ordinary methods into dynamically computed attributes. However, the decorator syntax allows for user-defined decorators as well. Specifically, for some function my_decorator , this: class C ( object ): @ my_decorator def method ( self ): # method body ...
is equivalent to: class C ( object ): def method ( self ): # method body ... method = my_decorator ( method )


2.17.2 Pros
Elegantly specifies some transformation on a method; the transformation might eliminate some repetitive code, enforce invariants, etc.


2.17.3 Cons
Decorators can perform arbitrary operations on a function’s arguments or return values, resulting in surprising implicit behavior. Additionally, decorators execute at import time. Failures in decorator code are pretty much impossible to recover from.


2.17.4 Decision
Use decorators judiciously when there is a clear advantage. Decorators should follow the same import and naming guidelines as functions. Decorator pydoc should clearly state that the function is a decorator. Write unit tests for decorators.
Avoid external dependencies in the decorator itself (e.g. don’t rely on files, sockets, database connections, etc.), since they might not be available when the decorator runs (at import time, perhaps from pydoc or other tools). A decorator that is called with valid parameters should (as much as possible) be guaranteed to succeed in all cases.
Decorators are a special case of “top level code” - see main for more discussion.
Never use @staticmethod unless forced to in order to integrate with an API defined in an existing library. Write a module level function instead.
Use @classmethod only when writing a named constructor or a class-specific routine that modifies necessary global state such as a process-wide cache.


2.18 Threading
Do not rely on the atomicity of built-in types.
While Python’s built-in data types such as dictionaries appear to have atomic operations, there are corner cases where they aren’t atomic (e.g. if __hash__ or __eq__ are implemented as Python methods) and their atomicity should not be relied upon. Neither should you rely on atomic variable assignment (since this in turn depends on dictionaries).
Use the Queue module’s Queue data type as the preferred way to communicate data between threads. Otherwise, use the threading module and its locking primitives. Learn about the proper use of condition variables so you can use threading.Condition instead of using lower-level locks.


2.19 Power Features
Avoid these features.


2.19.1 Definition
Python is an extremely flexible language and gives you many fancy features such as custom metaclasses, access to bytecode, on-the-fly compilation, dynamic inheritance, object reparenting, import hacks, reflection (e.g. some uses of getattr() ), modification of system internals, etc.


2.19.2 Pros
These are powerful language features. They can make your code more compact.


2.19.3 Cons
It’s very tempting to use these “cool” features when they’re not absolutely necessary. It’s harder to read, understand, and debug code that’s using unusual features underneath. It doesn’t seem that way at first (to the original author), but when revisiting the code, it tends to be more difficult than code that is longer but is straightforward.


2.19.4 Decision
Avoid these features in your code.
Standard library modules and classes that internally use these features are okay to use (for example, abc.ABCMeta , collections.namedtuple , dataclasses , and enum ).


2.20 Modern Python: Python 3 and from __future__ imports
Python 3 is here! While not every project is ready to use it yet, all code should be written to be 3 compatible (and tested under 3 when possible).


2.20.1 Definition
Python 3 is a significant change in the Python language. While existing code is often written with 2.7 in mind, there are some simple things to do to make code more explicit about its intentions and thus better prepared for use under Python 3 without modification.


2.20.2 Pros
Code written with Python 3 in mind is more explicit and easier to get running under Python 3 once all of the dependencies of your project are ready.


2.20.3 Cons
Some people find the additional boilerplate to be ugly. It’s unusual to add imports to a module that doesn’t actually require the features added by the import.


2.20.4 Decision
from __future__ imports
Use of from __future__ import statements is encouraged. All new code should contain the following and existing code should be updated to be compatible when possible: from __future__ import absolute_import from __future__ import division from __future__ import print_function
If you are not already familiar with those, read up on each here: absolute imports , new / division behavior , and the print function .
Please don’t omit or remove these imports, even if they’re not currently used in the module, unless the code is Python 3 only. It is better to always have the future imports in all files so that they are not forgotten during later edits when someone starts using such a feature.
There are other from __future__ import statements. Use them as you see fit. We do not include unicode_literals in our recommendations as it is not a clear win due to implicit default codec conversion consequences it introduces in many places within Python 2.7. Most code is better off with explicit use of b'' and u'' bytes and unicode string literals as necessary.
The six, future, or past libraries
When your project needs to actively support use under both Python 2 and 3, use the six , future , and past libraries as you see fit. They exist to make your code cleaner and life easier.


2.21 Type Annotated Code
You can annotate Python 3 code with type hints according to PEP-484 , and type-check the code at build time with a type checking tool like pytype .
Type annotations can be in the source or in a stub pyi file . Whenever possible, annotations should be in the source. Use pyi files for third-party or extension modules.


2.21.1 Definition
Type annotations (or “type hints”) are for function or method arguments and return values: def func ( a : int ) -> List [ int ]:
You can also declare the type of a variable using a special comment: a = SomeFunc () # type: SomeType


2.21.2 Pros
Type annotations improve the readability and maintainability of your code. The type checker will convert many runtime errors to build-time errors, and reduce your ability to use Power Features .


2.21.3 Cons
You will have to keep the type declarations up to date. You might see type errors that you think are valid code. Use of a type checker may reduce your ability to use Power Features .


2.21.4 Decision
You are strongly encouraged to enable Python type analysis when updating code. When adding or modifying public APIs, include type annotations and enable checking via pytype in the build system. As static analysis is relatively new to Python, we acknowledge that undesired side-effects (such as wrongly inferred types) may prevent adoption by some projects. In those situations, authors are encouraged to add a comment with a TODO or link to a bug describing the issue(s) currently preventing type annotation adoption in the BUILD file or in the code itself as appropriate.


3 Python Style Rules


3.1 Semicolons
Do not terminate your lines with semicolons, and do not use semicolons to put two statements on the same line.


3.2 Line length
Maximum line length is 80 characters .
Explicit exceptions to the 80 character limit: Long import statements. URLs, pathnames, or long flags in comments. Long string module level constants not containing whitespace that would be inconvenient to split across lines such as URLs or pathnames. Pylint disable comments. (e.g.: # pylint: disable=invalid-name )
Do not use backslash line continuation except for with statements requiring three or more context managers.
Make use of Python’s implicit line joining inside parentheses, brackets and braces . If necessary, you can add an extra pair of parentheses around an expression. Yes : foo_bar ( self , width , height , color = 'black' , design = None , x = 'foo' , emphasis = None , highlight = 0 ) if ( width == 0 and height == 0 and color == 'red' and emphasis == 'strong' ):
When a literal string won’t fit on a single line, use parentheses for implicit line joining. x = ( 'This will build a very long long ' 'long long long long long long string' )
Within comments, put long URLs on their own line if necessary. Yes : # See details at # http://www.example.com/us/developer/documentation/api/content/v2.0/csv_file_name_extension_full_specification.html No : # See details at # http://www.example.com/us/developer/documentation/api/content/\ # v2.0/csv_file_name_extension_full_specification.html
It is permissible to use backslash continuation when defining a with statement whose expressions span three or more lines. For two lines of expressions, use a nested with statement: Yes : with very_long_first_expression_function () as spam , \ very_long_second_expression_function () as beans , \ third_thing () as eggs : place_order ( eggs , beans , spam , beans ) No : with VeryLongFirstExpressionFunction () as spam , \ VeryLongSecondExpressionFunction () as beans : PlaceOrder ( eggs , beans , spam , beans ) Yes : with very_long_first_expression_function () as spam : with very_long_second_expression_function () as beans : place_order ( beans , spam )
Make note of the indentation of the elements in the line continuation examples above; see the indentation section for explanation.
In all other cases where a line exceeds 80 characters, and the yapf auto-formatter does not help bring the line below the limit, the line is allowed to exceed this maximum.


3.3 Parentheses
Use parentheses sparingly.
It is fine, though not required, to use parentheses around tuples. Do not use them in return statements or conditional statements unless using parentheses for implied line continuation or to indicate a tuple. Yes : if foo : bar () while x : x = bar () if x and y : bar () if not x : bar () # For a 1 item tuple the ()s are more visually obvious than the comma. onesie = ( foo ,) return foo return spam , beans return ( spam , beans ) for ( x , y ) in dict . items (): ... No : if ( x ): bar () if not ( x ): bar () return ( foo )


3.4 Indentation
Indent your code blocks with 4 spaces .
Never use tabs or mix tabs and spaces. In cases of implied line continuation, you should align wrapped elements either vertically, as per the examples in the line length section; or using a hanging indent of 4 spaces, in which case there should be nothing after the open parenthesis or bracket on the first line. Yes : # Aligned with opening delimiter foo = long_function_name ( var_one , var_two , var_three , var_four ) meal = ( spam , beans ) # Aligned with opening delimiter in a dictionary foo = { long_dictionary_key : value1 + value2 , ... } # 4-space hanging indent; nothing on first line foo = long_function_name ( var_one , var_two , var_three , var_four ) meal = ( spam , beans ) # 4-space hanging indent in a dictionary foo = { long_dictionary_key : long_dictionary_value , ... } No : # Stuff on first line forbidden foo = long_function_name ( var_one , var_two , var_three , var_four ) meal = ( spam , beans ) # 2-space hanging indent forbidden foo = long_function_name ( var_one , var_two , var_three , var_four ) # No hanging indent in a dictionary foo = { long_dictionary_key : long_dictionary_value , ... }


3.4.1 Trailing commas in sequences of items?
Trailing commas in sequences of items are recommended only when the closing container token ] , ) , or } does not appear on the same line as the final element. The presence of a trailing comma is also used as a hint to our Python code auto-formatter YAPF to direct it to auto-format the container of items to one item per line when the , after the final element is present. Yes : golomb3 = [ 0 , 1 , 3 ] Yes : golomb4 = [ 0 , 1 , 4 , 6 , ] No : golomb4 = [ 0 , 1 , 4 , 6 ]


3.5 Blank Lines
Two blank lines between top-level definitions, be they function or class definitions. One blank line between method definitions and between the class line and the first method. No blank line following a def line. Use single blank lines as you judge appropriate within functions or methods.


3.6 Whitespace
Follow standard typographic rules for the use of spaces around punctuation.
No whitespace inside parentheses, brackets or braces. Yes : spam ( ham [ 1 ], { eggs : 2 }, []) No : spam ( ham [ 1 ], { eggs : 2 }, [ ] )
No whitespace before a comma, semicolon, or colon. Do use whitespace after a comma, semicolon, or colon, except at the end of the line. Yes : if x == 4 : print ( x , y ) x , y = y , x No : if x == 4 : print ( x , y ) x , y = y , x
No whitespace before the open paren/bracket that starts an argument list, indexing or slicing. Yes : spam ( 1 ) No : spam ( 1 ) Yes : dict [ 'key' ] = list [ index ] No : dict [ 'key' ] = list [ index ]
No trailing whitespace.
Surround binary operators with a single space on either side for assignment ( = ), comparisons ( ==, <, >, !=, <>, <=, >=, in, not in, is, is not ), and Booleans ( and, or, not ). Use your better judgment for the insertion of spaces around arithmetic operators ( + , - , * , / , // , % , ** , @ ). Yes : x == 1 No : x < 1
Never use spaces around = when passing keyword arguments or defining a default parameter value, with one exception: when a type annotation is present , do use spaces around the = for the default parameter value. Yes : def complex ( real , imag = 0.0 ): return Magic ( r = real , i = imag ) Yes : def complex ( real , imag : float = 0.0 ): return Magic ( r = real , i = imag ) No : def complex ( real , imag = 0.0 ): return Magic ( r = real , i = imag ) No : def complex ( real , imag : float = 0.0 ): return Magic ( r = real , i = imag )
Don’t use spaces to vertically align tokens on consecutive lines, since it becomes a maintenance burden (applies to : , # , = , etc.): Yes : foo = 1000 # comment long_name = 2 # comment that should not be aligned dictionary = { 'foo' : 1 , 'long_name' : 2 , } No : foo = 1000 # comment long_name = 2 # comment that should not be aligned dictionary = { 'foo' : 1 , 'long_name' : 2 , }


3.7 Shebang Line
Most .py files do not need to start with a #! line. Start the main file of a program with #!/usr/bin/python with an optional single digit 2 or 3 suffix per PEP-394 .
This line is used by the kernel to find the Python interpreter, but is ignored by Python when importing modules. It is only necessary on a file that will be executed directly.


3.8 Comments and Docstrings
Be sure to use the right style for module, function, method docstrings and inline comments.


3.8.1 Docstrings
Python uses docstrings to document code. A docstring is a string that is the first statement in a package, module, class or function. These strings can be extracted automatically through the __doc__ member of the object and are used by pydoc . (Try running pydoc on your module to see how it looks.) Always use the three double-quote """ format for docstrings (per PEP 257 ). A docstring should be organized as a summary line (one physical line) terminated by a period, question mark, or exclamation point, followed by a blank line, followed by the rest of the docstring starting at the same cursor position as the first quote of the first line. There are more formatting guidelines for docstrings below.


3.8.2 Modules
Every file should contain license boilerplate. Choose the appropriate boilerplate for the license used by the project (for example, Apache 2.0, BSD, LGPL, GPL)
Files should start with a docstring describing the contents and usage of the module. """A one line summary of the module or program, terminated by a period. Leave one blank line. The rest of this docstring should contain an overall description of the module or program. Optionally, it may also contain a brief description of exported classes and functions and/or usage examples. Typical usage example: foo = ClassFoo() bar = foo.FunctionBar() """


3.8.3 Functions and Methods
In this section, “function” means a method, function, or generator.
A function must have a docstring, unless it meets all of the following criteria: not externally visible very short obvious
A docstring should give enough information to write a call to the function without reading the function’s code. The docstring should be descriptive-style ( """Fetches rows from a Bigtable.""" ) rather than imperative-style ( """Fetch rows from a Bigtable.""" ), except for @property data descriptors, which should use the same style as attributes . A docstring should describe the function’s calling syntax and its semantics, not its implementation. For tricky code, comments alongside the code are more appropriate than using docstrings.
A method that overrides a method from a base class may have a simple docstring sending the reader to its overridden method’s docstring, such as """See base class.""" . The rationale is that there is no need to repeat in many places documentation that is already present in the base method’s docstring. However, if the overriding method’s behavior is substantially different from the overridden method, or details need to be provided (e.g., documenting additional side effects), a docstring with at least those differences is required on the overriding method.
Certain aspects of a function should be documented in special sections, listed below. Each section begins with a heading line, which ends with a colon. All sections other than the heading should maintain a hanging indent of two or four spaces (be consistent within a file). These sections can be omitted in cases where the function’s name and signature are informative enough that it can be aptly described using a one-line docstring.
Args: List each parameter by name. A description should follow the name, and be separated by a colon and a space. If the description is too long to fit on a single 80-character line, use a hanging indent of 2 or 4 spaces (be consistent with the rest of the file).
The description should include required type(s) if the code does not contain a corresponding type annotation. If a function accepts *foo (variable length argument lists) and/or **bar (arbitrary keyword arguments), they should be listed as *foo and **bar .
Returns: (or Yields: for generators)
Describe the type and semantics of the return value. If the function only returns None, this section is not required. It may also be omitted if the docstring starts with Returns or Yields (e.g. """Returns row from Bigtable as a tuple of strings.""" ) and the opening sentence is sufficient to describe return value.
Raises:
List all exceptions that are relevant to the interface. You should not document exceptions that get raised if the API specified in the docstring is violated (because this would paradoxically make behavior under violation of the API part of the API). def fetch_bigtable_rows ( big_table , keys , other_silly_variable = None ): """Fetches rows from a Bigtable. Retrieves rows pertaining to the given keys from the Table instance represented by big_table. Silly things may happen if other_silly_variable is not None. Args: big_table: An open Bigtable Table instance. keys: A sequence of strings representing the key of each table row to fetch. other_silly_variable: Another optional variable, that has a much longer name than the other args, and which does nothing. Returns: A dict mapping keys to the corresponding table row data fetched. Each row is represented as a tuple of strings. For example: {'Serak': ('Rigel VII', 'Preparer'), 'Zim': ('Irk', 'Invader'), 'Lrrr': ('Omicron Persei 8', 'Emperor')} If a key from the keys argument is missing from the dictionary, then that row was not found in the table. Raises: IOError: An error occurred accessing the bigtable.Table object. """


3.8.4 Classes
Classes should have a docstring below the class definition describing the class. If your class has public attributes, they should be documented here in an Attributes section and follow the same formatting as a function’s Args section. class SampleClass ( object ): """Summary of class here. Longer class information.... Longer class information.... Attributes: likes_spam: A boolean indicating if we like SPAM or not. eggs: An integer count of the eggs we have laid. """ def __init__ ( self , likes_spam = False ): """Inits SampleClass with blah.""" self . likes_spam = likes_spam self . eggs = 0 def public_method ( self ): """Performs operation blah."""


3.8.5 Block and Inline Comments
The final place to have comments is in tricky parts of the code. If you’re going to have to explain it at the next code review , you should comment it now. Complicated operations get a few lines of comments before the operations commence. Non-obvious ones get comments at the end of the line. # We use a weighted dictionary search to find out where i is in # the array. We extrapolate position based on the largest num # in the array and the array size and then do binary search to # get the exact number. if i & ( i - 1 ) == 0 : # True if i is 0 or a power of 2.
To improve legibility, these comments should start at least 2 spaces away from the code with the comment character # , followed by at least one space before the text of the comment itself.
On the other hand, never describe the code. Assume the person reading the code knows Python (though not what you’re trying to do) better than you do. # BAD COMMENT: Now go through the b array and make sure whenever i occurs # the next element is i+1


3.8.6 Punctuation, Spelling, and Grammar
Pay attention to punctuation, spelling, and grammar; it is easier to read well-written comments than badly written ones.
Comments should be as readable as narrative text, with proper capitalization and punctuation. In many cases, complete sentences are more readable than sentence fragments. Shorter comments, such as comments at the end of a line of code, can sometimes be less formal, but you should be consistent with your style.
Although it can be frustrating to have a code reviewer point out that you are using a comma when you should be using a semicolon, it is very important that source code maintain a high level of clarity and readability. Proper punctuation, spelling, and grammar help with that goal.


3.9 Classes
If a class inherits from no other base classes, explicitly inherit from object . This also applies to nested classes. Yes : class SampleClass ( object ): pass class OuterClass ( object ): class InnerClass ( object ): pass class ChildClass ( ParentClass ): """Explicitly inherits from another class already.""" No : class SampleClass : pass class OuterClass : class InnerClass : pass
Inheriting from object is needed to make properties work properly in Python 2 and can protect your code from potential incompatibility with Python 3. It also defines special methods that implement the default semantics of objects including __new__ , __init__ , __delattr__ , __getattribute__ , __setattr__ , __hash__ , __repr__ , and __str__ .


3.10 Strings
Use the format method or the % operator for formatting strings, even when the parameters are all strings. Use your best judgment to decide between + and % (or format ) though. Yes : x = a + b x = '%s, %s!' % ( imperative , expletive ) x = '{}, {}' . format ( first , second ) x = 'name: %s; score: %d' % ( name , n ) x = 'name: {}; score: {}' . format ( name , n ) x = f'name: { name } ; score: { n } ' # Python 3.6+ No : x = '%s%s' % ( a , b ) # use + in this case x = '{}{}' . format ( a , b ) # use + in this case x = first + ', ' + second x = 'name: ' + name + '; score: ' + str ( n )
Avoid using the + and += operators to accumulate a string within a loop. Since strings are immutable, this creates unnecessary temporary objects and results in quadratic rather than linear running time. Instead, add each substring to a list and ''.join the list after the loop terminates (or, write each substring to a io.BytesIO buffer). Yes : items = [ '' ] for last_name , first_name in employee_list : items . append ( '' % ( last_name , first_name )) items . append ( '
%s, %s
' ) employee_table = '' . join ( items ) No : employee_table = '' for last_name , first_name in employee_list : employee_table += '' % ( last_name , first_name ) employee_table += '
%s, %s
'
Be consistent with your choice of string quote character within a file. Pick ' or " and stick with it. It is okay to use the other quote character on a string to avoid the need to \\ escape within the string. Yes : Python ( 'Why are you hiding your eyes?' ) Gollum ( "I'm scared of lint errors." ) Narrator ( '"Good!" thought a happy Python reviewer.' ) No : Python ( "Why are you hiding your eyes?" ) Gollum ( 'The lint. It burns. It burns us.' ) Gollum ( "Always the great lint. Watching. Watching." )
Prefer """ for multi-line strings rather than ''' . Projects may choose to use ''' for all non-docstring multi-line strings if and only if they also use ' for regular strings. Docstrings must use """ regardless.
Multi-line strings do not flow with the indentation of the rest of the program. If you need to avoid embedding extra space in the string, use either concatenated single-line strings or a multi-line string with textwrap.dedent() to remove the initial space on each line: No : long_string = """This is pretty ugly. Don't do this. """ Yes : long_string = """This is fine if your use case can accept extraneous leading spaces.""" Yes : long_string = ( "And this is fine if you can not accept \n " + "extraneous leading spaces." ) Yes : long_string = ( "And this too is fine if you can not accept \n " "extraneous leading spaces." ) Yes : import textwrap long_string = textwrap . dedent ( """ \ This is also fine, because textwrap.dedent() will collapse common leading spaces in each line.""" )


3.11 Files and Sockets
Explicitly close files and sockets when done with them.
Leaving files, sockets or other file-like objects open unnecessarily has many downsides: They may consume limited system resources, such as file descriptors. Code that deals with many such objects may exhaust those resources unnecessarily if they’re not returned to the system promptly after use. Holding files open may prevent other actions such as moving or deleting them. Files and sockets that are shared throughout a program may inadvertently be read from or written to after logically being closed. If they are actually closed, attempts to read or write from them will throw exceptions, making the problem known sooner.
Furthermore, while files and sockets are automatically closed when the file object is destructed, tying the lifetime of the file object to the state of the file is poor practice: There are no guarantees as to when the runtime will actually run the file’s destructor. Different Python implementations use different memory management techniques, such as delayed Garbage Collection, which may increase the object’s lifetime arbitrarily and indefinitely. Unexpected references to the file, e.g. in globals or exception tracebacks, may keep it around longer than intended.
The preferred way to manage files is using the “with” statement : with open ( "hello.txt" ) as hello_file : for line in hello_file : print ( line )
For file-like objects that do not support the “with” statement, use contextlib.closing() : import contextlib with contextlib . closing ( urllib . urlopen ( "http://www.python.org/" )) as front_page : for line in front_page : print ( line )


3.12 TODO Comments
Use TODO comments for code that is temporary, a short-term solution, or good-enough but not perfect.
A TODO comment begins with the string TODO in all caps and a parenthesized name, e-mail address, or other identifier of the person or issue with the best context about the problem. This is followed by an explanation of what there is to do.
The purpose is to have a consistent TODO format that can be searched to find out how to get more details. A TODO is not a commitment that the person referenced will fix the problem. Thus when you create a TODO , it is almost always your name that is given. # TODO(kl@gmail.com): Use a "*" here for string repetition. # TODO(Zeke) Change this to use relations.
If your TODO is of the form “At a future date do something” make sure that you either include a very specific date (“Fix by November 2009”) or a very specific event (“Remove this code when all clients can handle XML responses.”).


3.13 Imports formatting
Imports should be on separate lines.
E.g.: Yes : import os import sys No : import os , sys
Imports are always put at the top of the file, just after any module comments and docstrings and before module globals and constants. Imports should be grouped from most generic to least generic: Python future import statements. For example:
from __future__ import absolute_import from __future__ import division from __future__ import print_function
See above for more information about those. Python standard library imports. For example:
import sys third-party module or package imports. For example:
import tensorflow as tf Code repository sub-package imports. For example:
from otherproject.ai import mind Deprecated: application-specific imports that are part of the same top level sub-package as this file. For example:
from myproject.backend.hgwells import time_machine
You may find older Google Python Style code doing this, but it is no longer required. New code is encouraged not to bother with this. Simply treat application-specific sub-package imports the same as other sub-package imports.
Within each grouping, imports should be sorted lexicographically, ignoring case, according to each module’s full package path. Code may optionally place a blank line between import sections. import collections import queue import sys from absl import app from absl import flags import bs4 import cryptography import tensorflow as tf from book.genres import scifi from myproject.backend.hgwells import time_machine from myproject.backend.state_machine import main_loop from otherproject.ai import body from otherproject.ai import mind from otherproject.ai import soul # Older style code may have these imports down here instead: #from myproject.backend.hgwells import time_machine #from myproject.backend.state_machine import main_loop


3.14 Statements
Generally only one statement per line.
However, you may put the result of a test on the same line as the test only if the entire statement fits on one line. In particular, you can never do so with try / except since the try and except can’t both fit on the same line, and you can only do so with an if if there is no else . Yes : if foo : bar ( foo ) No : if foo : bar ( foo ) else : baz ( foo ) try : bar ( foo ) except ValueError : baz ( foo ) try : bar ( foo ) except ValueError : baz ( foo )


3.15 Accessors
If an accessor function would be trivial, you should use public variables instead of accessor functions to avoid the extra cost of function calls in Python. When more functionality is added you can use property to keep the syntax consistent.
On the other hand, if access is more complex, or the cost of accessing the variable is significant, you should use function calls (following the Naming guidelines) such as get_foo() and set_foo() . If the past behavior allowed access through a property, do not bind the new accessor functions to the property. Any code still attempting to access the variable by the old method should break visibly so they are made aware of the change in complexity.


3.16 Naming
module_name , package_name , ClassName , method_name , ExceptionName , function_name , GLOBAL_CONSTANT_NAME , global_var_name , instance_var_name , function_parameter_name , local_var_name .
Function names, variable names, and filenames should be descriptive; eschew abbreviation. In particular, do not use abbreviations that are ambiguous or unfamiliar to readers outside your project, and do not abbreviate by deleting letters within a word.
Always use a .py filename extension. Never use dashes.


3.16.1 Names to Avoid single character names except for counters or iterators. You may use “e” as an exception identifier in try/except statements. dashes ( - ) in any package/module name __double_leading_and_trailing_underscore__ names (reserved by Python)


3.16.2 Naming Conventions “Internal” means internal to a module, or protected or private within a class. Prepending a single underscore ( _ ) has some support for protecting module variables and functions (not included with from module import * ). While prepending a double underscore ( __ aka “dunder”) to an instance variable or method effectively makes the variable or method private to its class (using name mangling) we discourage its use as it impacts readability and testability and isn’t really private. Place related classes and top-level functions together in a module. Unlike Java, there is no need to limit yourself to one class per module. Use CapWords for class names, but lower_with_under.py for module names. Although there are some old modules named CapWords.py, this is now discouraged because it’s confusing when the module happens to be named after a class. (“wait – did I write import StringIO or from StringIO import StringIO ?”) Underscores may appear in unittest method names starting with test to separate logical components of the name, even if those components use CapWords. One possible pattern is test_ ; for example testPop_EmptyStack is okay. There is no One Correct Way to name test methods.


3.16.3 File Naming
Python filenames must have a .py extension and must not contain dashes ( - ). This allows them to be imported and unittested. If you want an executable to be accessible without the extension, use a symbolic link or a simple bash wrapper containing exec "$0.py" "$@" .


3.16.4 Guidelines derived from Guido’s Recommendations Type Public Internal
Packages lower_with_under
Modules lower_with_under _lower_with_under
Classes CapWords _CapWords
Exceptions CapWords
Functions lower_with_under() _lower_with_under()
Global/Class Constants CAPS_WITH_UNDER _CAPS_WITH_UNDER
Global/Class Variables lower_with_under _lower_with_under
Instance Variables lower_with_under _lower_with_under (protected)
Method Names lower_with_under() _lower_with_under() (protected)
Function/Method Parameters
Local Variables
lower_with_under
lower_with_under
While Python supports making things private by using a leading double underscore __ (aka. “dunder”) prefix on a name, this is discouraged. Prefer the use of a single underscore. They are easier to type, read, and to access from small unittests. Lint warnings take care of invalid access to protected members.


3.17 Main
Even a file meant to be used as an executable should be importable and a mere import should not have the side effect of executing the program’s main functionality. The main functionality should be in a main() function.
In Python, pydoc as well as unit tests require modules to be importable. Your code should always check if __name__ == '__main__' before executing your main program so that the main program is not executed when the module is imported. def main (): ... if __name__ == '__main__' : main ()
All code at the top level will be executed when the module is imported. Be careful not to call functions, create objects, or perform other operations that should not be executed when the file is being pydoc ed.


3.18 Function length
Prefer small and focused functions.
We recognize that long functions are sometimes appropriate, so no hard limit is placed on function length. If a function exceeds about 40 lines, think about whether it can be broken up without harming the structure of the program.
Even if your long function works perfectly now, someone modifying it in a few months may add new behavior. This could result in bugs that are hard to find. Keeping your functions short and simple makes it easier for other people to read and modify your code.
You could find long and complicated functions when working with some code. Do not be intimidated by modifying existing code: if working with such a function proves to be difficult, you find that errors are hard to debug, or you want to use a piece of it in several different contexts, consider breaking up the function into smaller and more manageable pieces.


3.19 Type Annotations


3.19.1 General Rules Familiarize yourself with PEP-484 . In methods, only annotate self , or cls if it is necessary for proper type information. e.g., @classmethod def create(cls: Type[T]) -> T: return cls() If any other variable or a returned type should not be expressed, use Any . You are not required to annotate all the functions in a module. At least annotate your public APIs. Use judgment to get to a good balance between safety and clarity on the one hand, and flexibility on the other. Annotate code that is prone to type-related errors (previous bugs or complexity). Annotate code that is hard to understand. Annotate code as it becomes stable from a types perspective. In many cases, you can annotate all the functions in mature code without losing too much flexibility.


3.19.2 Line Breaking
Try to follow the existing indentation rules.
After annotating, many function signatures will become “one parameter per line”. def my_method ( self , first_var : int , second_var : Foo , third_var : Optional [ Bar ]) -> int : ...
Always prefer breaking between variables, and not for example between variable names and type annotations. However, if everything fits on the same line, go for it. def my_method ( self , first_var : int ) -> int : ...
If the combination of the function name, the last parameter, and the return type is too long, indent by 4 in a new line. def my_method ( self , first_var : int ) -> Tuple [ MyLongType1 , MyLongType1 ]: ...
When the return type does not fit on the same line as the last parameter, the preferred way is to indent the parameters by 4 on a new line and align the closing parenthesis with the def. Yes : def my_method ( self , other_arg : Optional [ MyLongType ] ) -> Dict [ OtherLongType , MyLongType ]: ...
pylint allows you to move the closing parenthesis to a new line and align with the opening one, but this is less readable. No : def my_method ( self , other_arg : Optional [ MyLongType ] ) -> Dict [ OtherLongType , MyLongType ]: ...
As in the examples above, prefer not to break types. However, sometimes they are too long to be on a single line (try to keep sub-types unbroken). def my_method ( self , first_var : Tuple [ List [ MyLongType1 ], List [ MyLongType2 ]], second_var : List [ Dict [ MyLongType3 , MyLongType4 ]]) -> None : ...
If a single name and type is too long, consider using an alias for the type. The last resort is to break after the colon and indent by 4. Yes : def my_function ( long_variable_name : long_module_name . LongTypeName , ) -> None : ... No : def my_function ( long_variable_name : long_module_name . LongTypeName , ) -> None : ...


3.19.3 Forward Declarations
If you need to use a class name from the same module that is not yet defined – for example, if you need the class inside the class declaration, or if you use a class that is defined below – use a string for the class name. class MyClass ( object ): def __init__ ( self , stack : List [ "MyClass" ]) -> None :


3.19.4 Default Values
As per PEP-008 , use spaces around the = only for arguments that have both a type annotation and a default value. Yes : def func ( a : int = 0 ) -> int : ... No : def func ( a : int = 0 ) -> int : ...


3.19.5 NoneType
In the Python type system, NoneType is a “first class” type, and for typing purposes, None is an alias for NoneType . If an argument can be None , it has to be declared! You can use Union , but if there is only one other type, use Optional .
Use explicit Optional instead of implicit Optional . Earlier versions of PEP 484 allowed a: Text = None to be interpretted as a: Optional[Text] = None , but that is no longer the preferred behavior. Yes : def func ( a : Optional [ Text ], b : Optional [ Text ] = None ) -> Text : ... def multiple_nullable_union ( a : Union [ None , Text , int ]) -> Text ... No : def nullable_union ( a : Union [ None , Text ]) -> Text : ... def implicit_optional ( a : Text = None ) -> Text : ...


3.19.6 Type Aliases
You can declare aliases of complex types. The name of an alias should be CapWorded. If the alias is used only in this module, it should be _Private.
For example, if the name of the module together with the name of the type is too long: _ShortName = module_with_long_name . TypeWithLongName ComplexMap = Mapping [ Text , List [ Tuple [ int , int ]]]
Other examples are complex nested types and multiple return variables from a function (as a tuple).


3.19.7 Ignoring Types
You can disable type checking on a line with the special comment # type: ignore .
pytype has a disable option for specific errors (similar to lint): # pytype: disable=attribute-error


3.19.8 Typing Variables
If an internal variable has a type that is hard or impossible to infer, you can specify its type in a couple ways.
Type Comments:
Use a # type: comment on the end of the line a = SomeUndecoratedFunction () # type: Foo
Annotated Assignments
Use a colon and type between the variable name and value, as with function arguments. a : Foo = SomeUndecoratedFunction ()


3.19.9 Tuples vs Lists
Unlike Lists, which can only have a single type, Tuples can have either a single repeated type or a set number of elements with different types. The latter is commonly used as return type from a function. a = [ 1 , 2 , 3 ] # type: List[int] b = ( 1 , 2 , 3 ) # type: Tuple[int, ...] c = ( 1 , "2" , 3.5 ) # type: Tuple[int, Text, float]


3.19.10 TypeVars
The Python type system has generics . The factory function TypeVar is a common way to use them.
Example: from typing import List , TypeVar T = TypeVar ( "T" ) ... def next ( l : List [ T ]) -> T : return l . pop ()
A TypeVar can be constrained: AddableType = TypeVar ( "AddableType" , int , float , Text ) def add ( a : AddableType , b : AddableType ) -> AddableType : return a + b
A common predefined type variable in the typing module is AnyStr . Use it for multiple annotations that can be bytes or unicode and must all be the same type. from typing import AnyStr def check_length ( x : AnyStr ) -> AnyStr : if len ( x ) <= 42 : return x raise ValueError ()


3.19.11 String types
The proper type for annotating strings depends on what versions of Python the code is intended for.
For Python 3 only code, prefer to use str . Text is also acceptable. Be consistent in using one or the other.
For Python 2 compatible code, use Text . In some rare cases, str may make sense; typically to aid compatibility when the return types aren’t the same between the two Python versions. Avoid using unicode : it doesn’t exist in Python 3.
The reason this discrepancy exists is because str means different things depending on the Python version. No : def py2_code ( x : str ) -> unicode : ...
For code that deals with binary data, use bytes . def deals_with_binary_data ( x : bytes ) -> bytes : ...
For Python 2 compatible code that processes text data ( str or unicode in Python 2, str in Python 3), use Text . For Python 3 only code that process text data, prefer str . from typing import Text ... def py2_compatible ( x : Text ) -> Text : ... def py3_only ( x : str ) -> str : ...
If the type can be either bytes or text, use Union , with the appropriate text type. from typing import Text , Union ... def py2_compatible ( x : Union [ bytes , Text ]) -> Union [ bytes , Text ]: ... def py3_only ( x : Union [ bytes , str ]) -> Union [ bytes , str ]: ...
If all the string types of a function are always the same, for example if the return type is the same as the argument type in the code above, use AnyStr .
Writing it like this will simplify the process of porting the code to Python 3.


3.19.12 Imports For Typing
For classes from the typing module, always import the class itself. You are explicitly allowed to import multiple specific classes on one line from the typing module. Ex: from typing import Any , Dict , Optional
Given that this way of importing from typing adds items to the local namespace, any names in typing should be treated similarly to keywords, and not be defined in your Python code, typed or not. If there is a collision between a type and an existing name in a module, import it using import x as y . from typing import Any as AnyType


3.19.13 Conditional Imports
Use conditional imports only in exceptional cases where the additional imports needed for type checking must be avoided at runtime. This pattern is discouraged; alternatives such as refactoring the code to allow top level imports should be preferred.
Imports that are needed only for type annotations can be placed within an if TYPE_CHECKING: block. Conditionally imported types need to be referenced as strings, to be forward compatible with Python 3.6 where the annotation expressions are actually evaluated. Only entities that are used solely for typing should be defined here; this includes aliases. Otherwise it will be a runtime error, as the module will not be imported at runtime. The block should be right after all the normal imports. There should be no empty lines in the typing imports list. Sort this list as if it were a regular imports list.
import typing if typing . TYPE_CHECKING : import sketch def f ( x : "sketch.Sketch" ): ...


3.19.14 Circular Dependencies
Circular dependencies that are caused by typing are code smells. Such code is a good candidate for refactoring. Although technically it is possible to keep circular dependencies, the build system will not let you do so because each module has to depend on the other.
Replace modules that create circular dependency imports with Any . Set an alias with a meaningful name, and use the real type name from this module (any attribute of Any is Any). Alias definitions should be separated from the last import by one line. from typing import Any some_mod = Any # some_mod.py imports this module. ... def my_method ( self , var : some_mod . SomeType ) -> None : ...
3.19.15 Generics
When annotating, prefer to specify type parameters for generic types; otherwise, the generics’ parameters will be assumed to be Any . def get_names ( employee_ids : List [ int ]) -> Dict [ int , Any ]: ... # These are both interpreted as get_names(employee_ids: List[Any]) -> Dict[Any, Any] def get_names ( employee_ids : list ) -> Dict : ... def get_names ( employee_ids : List ) -> Dict : ...
If the best type parameter for a generic is Any , make it explicit, but remember that in many cases TypeVar might be more appropriate: def get_names ( employee_ids : List [ Any ]) -> Dict [ Any , Text ]: """Returns a mapping from employee ID to employee name for given IDs.""" T = TypeVar ( 'T' ) def get_names ( employee_ids : List [ T ]) -> Dict [ T , Text ]: """Returns a mapping from employee ID to employee name for given IDs."""


4 Parting Words
BE CONSISTENT .
If you’re editing code, take a few minutes to look at the code around you and determine its style. If they use spaces around all their arithmetic operators, you should too. If their comments have little boxes of hash marks around them, make your comments have little boxes of hash marks around them too.
The point of having style guidelines is to have a common vocabulary of coding so people can concentrate on what you’re saying rather than on how you’re saying it. We present global style rules here so people know the vocabulary, but local style is also important. If code you add to a file looks drastically different from the existing code around it, it throws readers out of their rhythm when they go to read it. Avoid this.
软件开发
2020-06-27 15:32:00
代码优化 ,一个很重要的课题。可能有些人觉得没用,一些细小的地方有什么好修改的,改与不改对于代码的运行效率有什么影响呢?这个问题我是这么考虑的,就像大海里面的鲸鱼一样,它吃一条小虾米有用吗?没用,但是,吃的小虾米一多之后,鲸鱼就被喂饱了。
代码优化也是一样,如果项目着眼于尽快无BUG上线,那么此时可以抓大放小,代码的细节可以不精打细磨;但是如果有足够的时间开发、维护代码,这时候就必须考虑每个可以优化的细节了,一个一个细小的优化点累积起来,对于代码的运行效率绝对是有提升的。
代码优化的目标是 减小代码的体积 提高代码运行的效率
代码优化细节 1、尽量指定类、方法的final修饰符
带有final修饰符的类是不可派生的。在Java核心API中,有许多应用final的例子,例如java.lang.String,整个类都是final的。为类指定final修饰符可以让类不可以被继承,为方法指定final修饰符可以让方法不可以被重写。如果指定了一个类为final,则该类所有的方法都是final的。Java编译器会寻找机会内联所有的final方法,内联对于提升Java运行效率作用重大,具体参见Java运行期优化。 此举能够使性能平均提高50% 。 2、尽量重用对象
特别是String对象的使用,出现字符串连接时应该使用StringBuilder/StringBuffer代替。由于Java虚拟机不仅要花时间生成对象,以后可能还需要花时间对这些对象进行垃圾回收和处理,因此,生成过多的对象将会给程序的性能带来很大的影响。 3、尽可能使用局部变量
调用方法时传递的参数以及在调用中创建的临时变量都保存在栈中速度较快,其他变量,如静态变量、实例变量等,都在堆中创建,速度较慢。另外,栈中创建的变量,随着方法的运行结束,这些内容就没了,不需要额外的垃圾回收。 4、及时关闭流
Java编程过程中,进行数据库连接、I/O流操作时务必小心,在使用完毕后,及时关闭以释放资源。因为对这些大对象的操作会造成系统大的开销,稍有不慎,将会导致严重的后果。 5、尽量减少对变量的重复计算
明确一个概念,对方法的调用,即使方法中只有一句语句,也是有消耗的,包括创建栈帧、调用方法时保护现场、调用方法完毕时恢复现场等。所以例如下面的操作: for (int i = 0; i < list.size(); i++) {...}
建议替换为: for (int i = 0, int length = list.size(); i < length; i++) {...}
这样,在list.size()很大的时候,就减少了很多的消耗 6、尽量采用懒加载的策略,即在需要的时候才创建
例如: String str = "aaa";if (i == 1) { list.add(str); }
建议替换为: if (i == 1) { String str = "aaa"; list.add(str); } 7、慎用异常
异常对性能不利。抛出异常首先要创建一个新的对象,Throwable接口的构造函数调用名为fillInStackTrace()的本地同步方法,fillInStackTrace()方法检查堆栈,收集调用跟踪信息。只要有异常被抛出,Java虚拟机就必须调整调用堆栈,因为在处理过程中创建了一个新的对象。异常只能用于错误处理,不应该用来控制程序流程。 8、不要在循环中使用try…catch…,应该把其放在最外层
除非不得已。如果毫无理由地这么写了,只要你的领导资深一点、有强迫症一点,八成就要骂你为什么写出这种垃圾代码来了。 9、如果能估计到待添加的内容长度,为底层以数组方式实现的集合、工具类指定初始长度
比如ArrayList、LinkedLlist、StringBuilder、StringBuffer、HashMap、HashSet等等,以StringBuilder为例:
(1)StringBuilder()      // 默认分配16个字符的空间
(2)StringBuilder(int size)  // 默认分配size个字符的空间
(3)StringBuilder(String str) // 默认分配16个字符+str.length()个字符空间
可以通过类(这里指的不仅仅是上面的StringBuilder)的来设定它的初始化容量,这样可以明显地提升性能。比如StringBuilder吧,length表示当前的StringBuilder能保持的字符数量。因为当StringBuilder达到最大容量的时候,它会将自身容量增加到当前的2倍再加2,无论何时只要StringBuilder达到它的最大容量,它就不得不创建一个新的字符数组然后将旧的字符数组内容拷贝到新字符数组中—-这是十分耗费性能的一个操作。试想,如果能预估到字符数组中大概要存放5000个字符而不指定长度,最接近5000的2次幂是4096,每次扩容加的2不管,那么:
(1)在4096 的基础上,再申请8194个大小的字符数组,加起来相当于一次申请了12290个大小的字符数组,如果一开始能指定5000个大小的字符数组,就节省了一倍以上的空间;
(2)把原来的4096个字符拷贝到新的的字符数组中去。
这样,既浪费内存空间又降低代码运行效率。所以,给底层以数组实现的集合、工具类设置一个合理的初始化容量是错不了的,这会带来立竿见影的效果。但是,注意,像HashMap这种是以数组+链表实现的集合,别把初始大小和你估计的大小设置得一样,因为一个table上只连接一个对象的可能性几乎为0。初始大小建议设置为2的N次幂,如果能估计到有2000个元素,设置成new HashMap(128)、new HashMap(256)都可以。 10、当复制大量数据时,使用System.arraycopy()命令 11、乘法和除法使用移位操作
例如: for (val = 0; val < 100000; val += 5) { a = val * 8; b = val / 2; }
用移位操作可以极大地提高性能,因为在计算机底层,对位的操作是最方便、最快的,因此建议修改为: for (val = 0; val < 100000; val += 5) { a = val << 3; b = val >> 1; }
移位操作虽然快,但是可能会使代码不太好理解,因此最好加上相应的注释。 12、循环内不要不断创建对象引用
例如: for (int i = 1; i <= count; i++) {Object obj = new Object(); }
这种做法会导致内存中有count份Object对象引用存在,count很大的话,就耗费内存了,建议为改为: Object obj = null;for (int i = 0; i <= count; i++) { obj = new Object(); }
这样的话,内存中只有一份Object对象引用,每次new Object()的时候,Object对象引用指向不同的Object罢了,但是内存中只有一份,这样就大大节省了内存空间了。 13、基于效率和类型检查的考虑,应该尽可能使用array,无法确定数组大小时才使用ArrayList 14、尽量使用HashMap、ArrayList、StringBuilder,除非线程安全需要,否则不推荐使用Hashtable、Vector、StringBuffer,后三者由于使用同步机制而导致了性能开销 15、不要将数组声明为public static final
因为这毫无意义,这样只是定义了引用为static final,数组的内容还是可以随意改变的,将数组声明为public更是一个安全漏洞,这意味着这个数组可以被外部类所改变。 16、尽量在合适的场合使用单例
使用单例可以减轻加载的负担、缩短加载的时间、提高加载的效率,但并不是所有地方都适用于单例,简单来说,单例主要适用于以下三个方面:
(1)控制资源的使用,通过线程同步来控制资源的并发访问
(2)控制实例的产生,以达到节约资源的目的
(3)控制数据的共享,在不建立直接关联的条件下,让多个不相关的进程或线程之间实现通信 17、尽量避免随意使用静态变量
要知道,当某个对象被定义为static的变量所引用,那么gc通常是不会回收这个对象所占有的堆内存的,如: public class A { private static B b = new B(); }
此时静态变量b的生命周期与A类相同,如果A类不被卸载,那么引用B指向的B对象会常驻内存,直到程序终止 18、及时清除不再需要的会话
为了清除不再活动的会话,许多应用服务器都有默认的会话超时时间,一般为30分钟。当应用服务器需要保存更多的会话时,如果内存不足,那么操作系统会把部分数据转移到磁盘,应用服务器也可能根据MRU(最近最频繁使用)算法把部分不活跃的会话转储到磁盘,甚至可能抛出内存不足的异常。如果会话要被转储到磁盘,那么必须要先被序列化,在大规模集群中,对对象进行序列化的代价是很昂贵的。因此,当会话不再需要时,应当及时调用HttpSession的invalidate()方法清除会话。 19、实现RandomAccess接口的集合比如ArrayList,应当使用最普通的for循环而不是foreach循环来遍历
这是JDK推荐给用户的。JDK API对于RandomAccess接口的解释是:实现RandomAccess接口用来表明其支持快速随机访问,此接口的主要目的是允许一般的算法更改其行为,从而将其应用到随机或连续访问列表时能提供良好的性能。实际经验表明,实现RandomAccess接口的类实例,假如是随机访问的,使用普通for循环效率将高于使用foreach循环;反过来,如果是顺序访问的,则使用Iterator会效率更高。可以使用类似如下的代码作判断: if (list instanceof RandomAccess) { for (int i = 0; i < list.size(); i++){} }else{ Iterator iterator = list.iterable(); while (iterator.hasNext()){iterator.next()} }
foreach循环的底层实现原理就是迭代器Iterator,参见Java语法糖1:可变长度参数以及foreach循环原理。所以后半句”反过来,如果是顺序访问的,则使用Iterator会效率更高”的意思就是顺序访问的那些类实例,使用foreach循环去遍历。 20、使用同步代码块替代同步方法
这点在多线程模块中的synchronized锁方法块一文中已经讲得很清楚了,除非能确定一整个方法都是需要进行同步的,否则尽量使用同步代码块,避免对那些不需要进行同步的代码也进行了同步,影响了代码执行效率。 21、将常量声明为static final,并以大写命名
这样在编译期间就可以把这些内容放入常量池中,避免运行期间计算生成常量的值。另外,将常量的名字以大写命名也可以方便区分出常量与变量 22、不要创建一些不使用的对象,不要导入一些不使用的类
这毫无意义,如果代码中出现”The value of the local variable i is not used”、”The import java.util is never used”,那么请删除这些无用的内容 23、程序运行过程中避免使用反射
关于,请参见反射。反射是Java提供给用户一个很强大的功能,功能强大往往意味着效率不高。不建议在程序运行过程中使用尤其是频繁使用反射机制,特别是Method的invoke方法,如果确实有必要,一种建议性的做法是将那些需要通过反射加载的类在项目启动的时候通过反射实例化出一个对象并放入内存—-用户只关心和对端交互的时候获取最快的响应速度,并不关心对端的项目启动花多久时间。 24、使用数据库连接池和线程池
这两个池都是用于重用对象的,前者可以避免频繁地打开和关闭连接,后者可以避免频繁地创建和销毁线程 25、使用带缓冲的输入输出流进行IO操作
带缓冲的输入输出流,即BufferedReader、BufferedWriter、BufferedInputStream、BufferedOutputStream,这可以极大地提升IO效率 26、顺序插入和随机访问比较多的场景使用ArrayList,元素删除和中间插入比较多的场景使用LinkedList
这个,理解ArrayList和LinkedList的原理就知道了 27、不要让public方法中有太多的形参
public方法即对外提供的方法,如果给这些方法太多形参的话主要有两点坏处:
1、违反了面向对象的编程思想,Java讲求一切都是对象,太多的形参,和面向对象的编程思想并不契合
2、参数太多势必导致方法调用的出错概率增加
至于这个”太多”指的是多少个,3、4个吧。比如我们用JDBC写一个insertStudentInfo方法,有10个学生信息字段要插如Student表中,可以把这10个参数封装在一个实体类中,作为insert方法的形参。 28、字符串变量和字符串常量equals的时候将字符串常量写在前面
这是一个比较常见的小技巧了,如果有以下代码: String str = "123"; if (str.equals("123")) {...}
建议修改为: String str = "123"; if ("123".equals(str)) { ... }
这么做主要是可以避免空指针异常 29、请知道,在java中if (i == 1)和if (1 == i)是没有区别的,但从阅读习惯上讲,建议使用前者
平时有人问,”if (i == 1)”和”if (1== i)”有没有区别,这就要从C/C++讲起。
在C/C++中,”if (i == 1)”判断条件成立,是以0与非0为基准的,0表示false,非0表示true,如果有这么一段代码: int i = 2; if (i == 1) { ... }else{ ... }
C/C++判断”i==1″不成立,所以以0表示,即false。但是如果: int i = 2;if (i = 1) { ... }else{ ... }
万一程序员一个不小心,把”if (i == 1)”写成”if (i = 1)”,这样就有问题了。在if之内将i赋值为1,if判断里面的内容非0,返回的就是true了,但是明明i为2,比较的值是1,应该返回的false。这种情况在C/C++的开发中是很可能发生的并且会导致一些难以理解的错误产生,所以,为了避免开发者在if语句中不正确的赋值操作,建议将if语句写为: int i = 2;if (1 == i) { ... }else{ ... }
这样,即使开发者不小心写成了”1 = i”,C/C++编译器也可以第一时间检查出来,因为我们可以对一个变量赋值i为1,但是不能对一个常量赋值1为i。
但是,在Java中,C/C++这种”if (i = 1)”的语法是不可能出现的,因为一旦写了这种语法,Java就会编译报错”Type mismatch: cannot convert from int to boolean”。但是,尽管Java的”if (i == 1)”和”if (1 == i)”在语义上没有任何区别,但是从阅读习惯上讲,建议使用前者会更好些。 30、不要对数组使用toString()方法
看一下对数组使用toString()打印出来的是什么: public static void main(String[] args) { int[] is = new int[]{1, 2, 3}; System.out.println(is.toString()); }
结果是: [I@18a992f
本意是想打印出数组内容,却有可能因为数组引用is为空而导致空指针异常。不过虽然对数组toString()没有意义,但是对集合toString()是可以打印出集合里面的内容的,因为集合的父类AbstractCollections重写了Object的toString()方法。 31、不要对超出范围的基本数据类型做向下强制转型
这绝不会得到想要的结果: public static void main(String[] args) { long l = 12345678901234L;int i = (int)l; System.out.println(i); }
我们可能期望得到其中的某几位,但是结果却是: 1942892530
解释一下。Java中long是8个字节64位的,所以12345678901234在计算机中的表示应该是:
0000 0000 0000 0000 0000 1011 0011 1010 0111 0011 1100 1110 0010 1111 1111 0010
一个int型数据是4个字节32位的,从低位取出上面这串二进制数据的前32位是:
0111 0011 1100 1110 0010 1111 1111 0010
这串二进制表示为十进制1942892530,所以就是我们上面的控制台上输出的内容。从这个例子上还能顺便得到两个结论:
1、整型默认的数据类型是int,long l = 12345678901234L,这个数字已经超出了int的范围了,所以最后有一个L,表示这是一个long型数。顺便,浮点型的默认类型是double,所以定义float的时候要写成””float f = 3.5f”
2、接下来再写一句”int ii = l + i;”会报错,因为long + int是一个long,不能赋值给int 32、公用的集合类中不使用的数据一定要及时remove掉
如果一个集合类是公用的(也就是说不是方法里面的属性),那么这个集合里面的元素是不会自动释放的,因为始终有引用指向它们。所以,如果公用集合里面的某些数据不使用而不去remove掉它们,那么将会造成这个公用集合不断增大,使得系统有内存泄露的隐患。 33、把一个基本数据类型转为字符串,基本数据类型.toString()是最快的方式、String.valueOf(数据)次之、数据+””最慢
把一个基本数据类型转为一般有三种方式,我有一个Integer型数据i,可以使用i.toString()、String.valueOf(i)、i+””三种方式,三种方式的效率如何,看一个测试: public static void main(String[] args) { int loopTime = 50000; Integer i = 0; long startTime = System.currentTimeMillis(); for (int j = 0; j < loopTime; j++) { String str = String.valueOf(i); } System.out.println("String.valueOf():" + (System.currentTimeMillis() - startTime) + "ms"); startTime = System.currentTimeMillis(); for (int j = 0; j < loopTime; j++) { String str = i.toString(); } System.out.println("Integer.toString():" + (System.currentTimeMillis() - startTime) + "ms"); startTime = System.currentTimeMillis(); for (int j = 0; j < loopTime; j++) { String str = i + ""; } System.out.println("i + \"\":" + (System.currentTimeMillis() - startTime) + "ms"); }
运行结果为: String.valueOf():11ms Integer.toString():5ms i + "":25ms
所以以后遇到把一个基本数据类型转为String的时候,优先考虑使用toString()方法。至于为什么,很简单:
1、String.valueOf()方法底层调用了Integer.toString()方法,但是会在调用前做空判断
2、Integer.toString()方法就不说了,直接调用了
3、i + “”底层使用了StringBuilder实现,先用append方法拼接,再用toString()方法获取字符串
三者对比下来,明显是2最快、1次之、3最慢 34、使用最有效率的方式去遍历Map
遍历Map的方式有很多,通常场景下我们需要的是遍历Map中的Key和Value,那么推荐使用的、效率最高的方式是: public static void main(String[] args) { HashMap hm = new HashMap(); hm.put("111", "222");Set> entrySet = hm.entrySet(); Iterator> iter = entrySet.iterator(); while (iter.hasNext()) { Map.Entry entry = iter.next(); System.out.println(entry.getKey() + "\t" + entry.getValue()); } }
如果你只是想遍历一下这个Map的key值,那用”Set keySet = hm.keySet();”会比较合适一些 35、对资源的close()建议分开操作
意思是,比如我有这么一段代码: try{ XXX.close(); YYY.close(); }catch (Exception e) {...}
建议修改为: try{ XXX.close(); }catch (Exception e) { ... }try{ YYY.close(); }catch (Exception e) { ... }
虽然有些麻烦,却能避免资源泄露。我想,如果没有修改过的代码,万一XXX.close()抛异常了,那么就进入了cath块中了,YYY.close()不会执行,YYY这块资源就不会回收了,一直占用着,这样的代码一多,是可能引起资源句柄泄露的。而改为上面的写法之后,就保证了无论如何XXX和YYY都会被close掉。
软件开发
2020-06-27 14:57:00
归并排序(Java)
博客说明 文章所涉及的资料来自互联网整理和个人总结,意在于个人学习和经验汇总,如有什么地方侵权,请联系本人删除,谢谢!
归并排序介绍
归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。
代码 package cn.guizimo.sort; import java.util.Arrays; public class MergetSort { public static void main(String[] args) { int arr[] = {8, 4, 5, 7, 1, 3, 6, 2}; int temp[] = new int[arr.length]; System.out.println("排序前"); System.out.println(Arrays.toString(arr)); mergeSort(arr, 0, arr.length - 1, temp); System.out.println("排序后"); System.out.println(Arrays.toString(arr)); } public static void mergeSort(int[] arr, int left, int right, int[] temp) { if (left < right) { int mid = (left + right) / 2; mergeSort(arr, left, mid, temp); mergeSort(arr, mid + 1, right, temp); merge(arr, left, mid, right, temp); } } public static void merge(int[] arr, int left, int mid, int right, int[] temp) { int i = left; int j = mid + 1; int t = 0; while (i <= mid && j <= right) { if (arr[i] <= arr[j]) { temp[t] = arr[i]; t += 1; i += 1; } else { temp[t] = arr[j]; t += 1; j += 1; } } while (i <= mid) { temp[t] = arr[i]; t += 1; i += 1; } while (j <= right) { temp[t] = arr[j]; t += 1; j += 1; } t = 0; int tempIndex = left; while (tempIndex <= right) { arr[tempIndex] = temp[t]; t += 1; tempIndex += 1; } } }
测试
速度测试 package cn.guizimo.sort; import java.util.Arrays; public class MergetSort { public static void main(String[] args) { int max = 80000; int[] arr = new int[max]; for (int i = 0; i < max; i++) { arr[i] = (int)(Math.random() * 8000000); } int temp[] = new int[arr.length]; long date1 = System.currentTimeMillis(); mergeSort(arr, 0, arr.length - 1, temp); long date2 = System.currentTimeMillis(); System.out.println("归并排序"+max+"数组的时间为:"+(date2-date1)); } public static void mergeSort(int[] arr, int left, int right, int[] temp) { if (left < right) { int mid = (left + right) / 2; mergeSort(arr, left, mid, temp); mergeSort(arr, mid + 1, right, temp); merge(arr, left, mid, right, temp); } } public static void merge(int[] arr, int left, int mid, int right, int[] temp) { int i = left; int j = mid + 1; int t = 0; while (i <= mid && j <= right) { if (arr[i] <= arr[j]) { temp[t] = arr[i]; t += 1; i += 1; } else { temp[t] = arr[j]; t += 1; j += 1; } } while (i <= mid) { temp[t] = arr[i]; t += 1; i += 1; } while (j <= right) { temp[t] = arr[j]; t += 1; j += 1; } t = 0; int tempIndex = left; while (tempIndex <= right) { arr[tempIndex] = temp[t]; t += 1; tempIndex += 1; } } }
感谢 尚硅谷
万能的网络
以及勤劳的自己
软件开发
2020-06-27 14:42:00