正数的补码与原码、反码是一样的 ,而负数的补码是反码加 l 的结果。这样使减法运算可以使用加法器实现, 符号位也参与运算。
减少中间变量存储的开销,降低了CPU内部的设计复杂度,使内部结构更加精简,计算更加高效 ,无论对于指令、寄存器,还是运算器都会减轻很大的负担。
在左移<<与右移>>两种 运算中 ,符号位均参与移动 ,除负数往右移动,高位补 l 之外,其他情况均在空位处补 0。
对于三个大于号的>>> 无符号向右移动当向右移动时,正负数高位均补 0,正数不断向右移动的最小值是0,而 负数不断向 右移动的最小值是l。
整型移动的位数是个mod32的结果,即35>>1与35>>33是一样的结果。如果是长整型mod64,即35<<1与35<<65的结果是一样的。
浮点数是采用科学计数法来表示的,由符号位、有效数字(尾数)、指数(阶码)三部分组成。
当前业界流行的浮点数标准是IEEE754:
精度 | 字节数 | 正数取值范围 | 负数取值范围 |
---|---|---|---|
单精度类型 | 4 | 1.4e-45至3.4e+38 | -3.4e+38至-I.4e-45 |
双精度类型 | 8 | 4.9e-324至1.798e+308 | -l.798e+308至-4.9e-324 |
以单精度类型为例:
I. 符号位
在最高二进制位上分配 l 位表示浮点数的符号,0表示正数,1表示负数。
2. 阶码位
在符号位右侧分配8位用来存储指数,IEEE754 标准规定阶码位存储的是指数对应的移码。
假设指数的真值为 e,阶码为 E,则有E=e+(2n-1-1),其中2n-1-1是IEEE754标准规定的偏移量,n=8是阶码的二进制位数。
为什么偏移值为2n-1-1而不是2n-1 呢?
由于计算机规定阶码 全为 0 或全为 l 两种情况被当作特殊值处理(全 0 被认为是机器零,全 1 被认为是无穷大),去除这两个特殊值,阶码的取值范围变成了[ l,254]。如果偏移量不变仍为128 的话,根据换算关系公式 [x]阶= x +128 得到指数的范围变成[-127,126],指数最大只能取到 126,显然会缩小浮点数能表示的取值范围。而127能表示的指数范围为[-126,127], 指数最大值能取到127(8个二进制位能表示指数的取值范围为[-128,127])。
3. 尾数位
最右侧分配连续的23位用来存储有效数字,IEEE754标准规定尾数以原码表示。
为了节约存储空间,将符合规格化尾数的首个 l 省略,所以尾数表面上是23位,却表示了24位二进制数。
在数学中,进行两个小数的加减运算时,首先要将小数点对齐,然后同位数进行加减运算。对两个采用科学计数法表示的数做加减法运算时,为了让小数点对齐就需要确保指数一样。过程如下:
( I ) 零值检测
( 2 ) 对阶
在移动尾数时,部分二进制位将会被移出,但向左移会使高位被移出,对结果造成的误差更大。所以,IEEE754规定对阶的移动方向为向右移动,即选择阶码小的数进行操作。
( 3) 尾数求和
如果是负数则需要先转换成补码再进行运算
( 4) 结果规格化
( 5 ) 结果舍入
阶码在加减运算过程中只是用来比较大小,从而决定是否需要进行对阶操作。这也是设计成移码的原因。
( I ) 在使用浮点数时推荐使用双精度
( 2 )在要求绝对精确表示的业务场景下,推荐使用整型存储其最小单位的值。
( 3) 在要求精确表示小数点 n 位的业务场景下,推荐采用数组保存小数部分的数据。
( 4) 在数据库中保存小数时,推荐使用 decimal类型,禁止使用 float类型和 double类型。因为这两种类型在存储的时候,存在精度损失的问题。
这一节写的太凑字数了吧,有空自己补全此节吧。
这一节没必要讲,这其实是个很大的话题,书中讲的也都是基础知识,对读者是无益的。
本节我是典型的这次研究下次忘的状态,整体讲的还算可以,三次握手和四次挥手讲的算是清晰(握手状态画错了一点)。但讲TCP不搞一张状态图太不像话了,陈皓大神早有大篇TCP 的那些事儿值得参考。
查看当前系统各个进程产生了多少句柄:
lsof -n|awk '{print $2}'|sort|uniq -c|sort -nr|more
ACK、 SYN、 FIN是最基本的:
SYN ( Synchronize Sequence Numbers )用作建立连接时的同步信号;
ACK (Acknowledgement )用于对收到的数据进行确认,所确认的数据由确认序列号表示;
FIN ( Finish)表示后面没有数据需要发送,通常意昧着所建立的连接需要关闭了;
另外三个标志位URG、 PSH、 RST略表如下:
URG这样的进入段不必等待直到先前段被接收端消耗,而是直接发送并立即处理;
PSH确保数据被给予优先级,并在发送或接收端处理;
RST当段到达不用于当前连接时,使用复位标志;
好比你去买银行理财,说好的100人以上才能产品成立,而且最多允许1000人购买。结果来个搞政治的URG,直接跳过所有人拿到一份。又来了个阔少PSH,银行为了讨好他,直接产品成立了。然后你收到RST,说明你已经买不到了。
关于RST,亦可参考几种TCP连接中出现RST的情况。
建立连接的关键目的是同步序列号和窗口大小,但为什么需要第 3 次握手?
在书中提到两个主要目的信息对等和防止超时。
信息对等指确认自己发报能力、自己收报能力、对方发报能力、对方收报能力。防止出现请求超时导致脏连接(针对二次连接)。
TCP 是全双工通信,双方都能作为数据的发送方和接收方,挥手告别(断开)就需要双方分别告别,也就是四次挥手了。
主动先关闭连接的一方为何不直接关闭,进入 CLOSED 状态呢?
第一 ,确认被动关闭方能够顺利进入 CLOSED 状态。
第二,防止己失效连接的请求数据包与正常连接的请求数据包混淆而发生异常(丢掉失效连接网络中的数据包)。
TIME WAIT 和 CLOSE WAIT 分别表示主动关闭和被动关闭产生的阶段性状态,如果在线上服务器大量出现这两种状态,就会加重机器负载,也会影响有效连接的创建,因此需要进行有针对性的调优处理。
调整TIME_WAIT超时时间配置(/etc/sysctl.conf ):
net.ipv4.tcp_tw_reuse = 1 表示开启重用。允许将TIME-WAIT sockets重新用于新的TCP连接,默认为0,表示关闭;
net.ipv4.tcp_tw_recycle = 1 表示开启TCP连接中TIME-WAIT sockets的快速回收,默认为0,表示关闭。
net.ipv4.tcp_fin_timeout 修改系默认的 TIMEOUT 时间
查询TCP各个状态的连接数:
netstat -ant|awk '/^tcp/ {++S[$NF]} END {for(a in S) print (a,S[a])}'
查询HTTPS CLOSE_WAIT状态连接数:
netstat -ant|grep -i "443"lgrep CLOSE_WAITlwc -I
此处主要提到数据库连接池,需要后续找个简单源码分析。
本节着重讲了如何优化数据库,大多也是经验之谈(我略知一二,已很久未使用数据库了):
(1)建主高效且合适的索引。
(2)排查连接资源未显式关闭的情形。
(3)合并短的请求。
(4)合理拆分多个表join的SQL,若是超过三个表则禁止join。
(5) 使用临时表。
(6) 应用层优化。
(7) 改用其他数据库。
待到重新捡起数据库,再结合 MYSQL性能优化的最佳20+条经验 复习。
CIA原则 , 即保密性( Confidentiality ),完整性( Integrity ), 可用性( Availability )。
惭愧啊,曾经做过银行的后端系统,却从未考虑过SQL注入情况
(1)过滤用户输入参数中的特殊字符 ,从而降低被SQL注入的风险。
(2)禁止通过字符串拼接的SQL语句,严格使用参数绑定传人的SQL参数。
(3)合理使用数据库访问框架提供的防注入机制。比如MyBatis 提供的#{}绑定参数,从而防止SQL注入。
XSS是在正常用户请求的HTML页面中执行了黑客提供的恶意代码,CSRF是黑客直接盗用用户浏览器中的登录信息,冒充用户去执行黑客指定的操作。
在防范 XSS 上,主要通过对用户输入数据做过滤或者转义。
防范 CSRF 漏洞主要通过以下方式 :
(1) CSRF Token 验证,利用浏览器的同源限制,在 HTTP 接口执行前验证页面或者 Cookie 中设置的Token,只有验证通过才继续执行请求。
(2) 人机交互 ,比如在调用上述网上银行转账接口时校验短信验证码。
SSL 协议工作于传输层与应用层之间,为应用提供数据的加密传输。而 HTTPS 的全称是 HTTP over SSL,简单的理解就是在之前的 HTTP 传输上增加了 SSL协议的加密能力。
访问一个 HTTPS 的网站的大致流程如下:
(1)浏览器向服务器发送请求,请求中包括浏览器支持的协议,并附带一个随机数。
(2)服务器收到请求后,选择某种非对称加密算法,把数字证书签有公钥、身份信息发送给浏览器,同时也附带一个随机数。
(3)浏览器收到后、验证证书的真实性,用服务器的公钥发送握手信息给服务器。
(4) 服务器解密后 ,使用之前的随机数计算出一个对称加密的密钥,以此作为加密信息并发送。
(5) 后续所有的信息发送都是以对称加密方式进行的。
接口与抽象类的语法区别:
语法维度 | 抽象类 | 接口 |
---|---|---|
定义关键字 | abstract | interface |
子类继承或实现关键字 | extends | implements |
方法实现 | 可以有 | 不能有,但在 JDK8及之后,允许有 default 实现 方法访问控制符 |
属性访问控制符 | 无限制 | 有限制 ,默认是 public static final类型 |
静态方法 | 可以有 | 不能有 |
static{} 静态代码块 | 可以有 | 不能有 |
本类型之 间扩展 | 单继承 | 多继承 |
本类型之间扩展关键字 | extends | extends |
抽象类在被继承时体现的是 is-a 关系,接口在被实现时体现的是 can-do 关系。
无论是什么类型的内部类,都会编译成一个独立的 .class 文件:
外部类与内部类之间使用 $ 符号分隔,匿名内部类使用数字进行编号,而方法内部类,在类名前还有一个编号来标识是哪个方法。匿名内部类和静态内部类是比较常用的方式。
·[ 继承 ] extends (is-a)。
·[ 实现 ] implements (can-do)。
·[ 组合 ] 类是成员变量 (contains-a)。
·[ 聚合 ] 类是成员变量(has-a)。
·[ 依赖 ] import类(use-a)。
在类图中有一个规律,有形状的图形符号律放在权力强的这一侧。
内存中的数据对象只有转换为二进制流才可以进行数据持久化和网络传输。将数据对象转换为二进制流的过程称为对象的序列化( Serialization )。反之,将二进制流恢复为数据对象的过程称为反序列化( Deserialization )。
常见的序列化方式有三种 :
(I)Java原生序列化。
Java类通过实现Serializable接口来实现该类对象的序列化,这个接口非常特殊,没有任何方法,只起标识作用。Java序列化保留了对象类的元数据(如类、成员变量、继承类信息等),以及对象数据等,兼容性最好,但不支持跨语言,而且性能一般。
(2) Hessian 序列化。
Hessian 序列化是一种支持动态类型、跨语言、基于对象传输的网络协议。
(3) JSON序列化。
JSON ( JavaScript Object Notation )是一种轻量级的数据交换格式。JSON序列化就是将数据对象转换为JSON 字符串。
有些对象的敏感属性不需要进行序列化传输,可以加 transient 关键字,避免把此属性信息转化为序列化的二进制流。
需要进行参数校验的场景:
·调用频度低的方法。
·执行时间开销很大的方法。此情形中,参数校验时间几乎可以忽略不计,但如果因为参数错误导致中间执行回退或者错误,则得不偿失。
·需要极高稳定性和可用性的方法。
· 对外提供的开放接口。
· 敏感权限入口。
不需要进行参数校验的场景
· 极有可能被循环调用的方法。但在方法说明里必须注明外部参数检查。
·底层调用频度较高的方法。参数错误不太可能到底层才会暴露问题。
·声明成private只会被自己代码调用的方法。如果能够确定调用方法的代码传入参数已经做过检查或者肯定不会有问题。
建议在类定义中,类内方法定义顺序依次是公有方法或保护方法>私有方法> getter/setter方法。
最典型的getter与setter方法使用是在POJO( Plain Ordinary Java Object,简单的Java对象)类中。
(1) 不要在getter/setter中添加业务逻辑。
(2) 不要同时定义 isXxx()叩 getXxx()。
(3) 不要用相同的属性名,容易带来歧义。
方法的覆写可以总结成容易记忆的口诀 “一大两小两同”。
·一大:子类的方法访问权限控制符只能相同或变大。
· 两小:抛出异常和返回值只能变小,能够转型成父类对象。子类的返回值、抛出异常类型必须与父类的返回值、抛出异常类型存在继承关系。
· 两同:方法名和参数必须完全相同。
在编译器的眼里,方法名称+参数类型+参数个数,组成一个唯一键,称为方法签名,JVM通过这个唯一键决定调用哪种重载的方法。
在泛型定义时,约定俗成的符号包括E代表Element,用于集合中的元素; T代表the Type of object,表示某个类; K代表Key、 V代表Value,用于键值对元素。
( 1 ) 尖捂号里的每个元素都指代一种未知类型。
( 2 ) 尖括号的位置非常讲究,必须在类名之后或方法返回值之前。
( 3 ) 泛型在定义处只具备执行Object方法的能力。
( 4 ) 对于编译之后的字节码指令,其实没有这些花头花脑的方法签名,充分说明了泛型只是一种编写代码时的语法检查。
基本数据类型 :
类型名称 | 默认值 | 大小 | 包装类 | 缓存区间 |
---|---|---|---|---|
boolean | false | 1 B | Boolean | 无 |
byte | (byte)0 | 1 B | Byte | -128 ~ 127 |
char | ‘\u0000’ | 2 B | Character | (char)0 ~ (char)127 |
short | (short)0 | 2 B | Short | -128 ~ 127 |
int | 0 | 4 B | Integer | -128 ~ 127 |
long | 0L | 8 B | Long | -128 ~ 127 |
float | 0.0f | 4 B | Float | 无 |
double | 0.0d | 8 B | Double | 无 |
byte | (byte)0 | 1 B | Character | -128 ~ 127 |
Integer是Java数据世界里应用最广的数据类型,缓存范围是-128~127。但它是唯一可以修改缓存范围的包装类,在 VM options 加入参数-XX:AutoBoxCacheMax=7777, 即可设置最大缓存值为 7777。
字符串相关类型主要有三种 String、StringBuilder、StringBuffer。
String是只读字符串;StringBuffer可以在原对象上进行修改,是线程安全的;StringBuilder是非线程安全的,但操作效率比StringBuffer高。
抽象类命名使用 Abstract 或 Base 开头; 异常类命名使用 Exception 结尾 , 测试类命名以它要测试的类名开始,以Test结尾。枚举类名带上 Enum 后缀,枚举成员名称需要全大写,单词间用下画线隔开。
空格:
任何二目、三目运算符的左右两边必须有一个空格
关键词 if 与左侧小括号之间必须有一个空格
右括号与左大括号前加空格且不换行,左大括号后必须换行
如果是大括号内为空,则简洁地写成{}即可
在每一个参数逗号之后必须有一个空格
阅读至此实在觉得此书写的不能让人满意,我接触java并未多久,尚且觉得前三章写的过于基础了。作者的情怀是有的,第一章从二进制到信息安全,可见是想包罗万象的。然而到后面的小节却偏于介绍性了,有的甚至可以说简直空话。第二章主要复习了一遍java语言,但层次低了些,仍旧是语法的角度剖析未免不够深刻。第三章所述大概浏览即可,其实大多数还是要看项目原始代码风格。