第1 章 计算机基础
大道至简,盘古生其中。计算机的基础世界一切都是0和1组成的。
组成原理
0与1
无论是图形图像的渲染、网络远程共享 还是大数据计算,归根结底都是 0与1的信号处理。
信息存储和逻辑计算的元数据 ,只能是0和1. 但是它们在不同介质里的物理表现方式却是不
一样的 ,如:极管的断电与通电、 CPU 的低电平与高电平、碰盘的电荷左右方向。
二级制和十进制 二进制逢二进一
32路的电路 能够表示的最大数 2^32-1=4294967295
平时所说的32位机器就是能够同时处理字长位32位的电路信号
为什么要用补码?
加法器是高频运算,使用用同一个运算器,可以减少变量存储的开销。
降低了cpu内部的复杂度,对于寄存器和运算器 都会减少很大的负担。
一条电路成为1位,记1bit
位移运算
向右移1位,近似表示除于2(奇数并不是完全除2)
在左移<<与 移>>两种
算中 符号位均参与移动 除负数往右移动 高位补 1之外,其他情况均在空位处补0
无符号位移运算
对于三个大于号的>>> 无符号向右移动 注意不存在<<<无符号向左移动的运
算方式) 当向右移动时 正负数高位均补 0,正数不断向右移动的最小值是0
负数不断向 移动的最小值是1 。(场景高位转地位的场景)
怎么高效地交换两个值?
取出特定为用& &0 无论是什么都是 0 &1 还是本身
当一个整数%2的n次方 完全等价与&这个数的2的n-1次方 %2 &1
*2 «1 *4«2 *8«3
/2 »1 /4»2 /8»3
位移运算可以快速地实现乘除运算,那位移时要注意什么?
浮点数
浮点数 ,符号位 +幂指位+数值位
32(1+8+23)64(1+11+52)
float a = lf;
float b = 0 . 9f ;
//结果为0.100000024
float f = a - b;
和实际结果存在误差
浮点数的存储与计算为什么总会产生微小的误差?
-
存在精度问题的原因其实就在于浮点数十进制转二进制的过程中,由于位数限制(有效数字只有23位),所以会存在丢失位数的情况,在计算过程中的对阶和规格化过程中,也存在着某些位数丢失的情况。
- 在要求绝对精确表示的业务场景下,比如金融行业的货币表示,推荐使用整型存储其最小单元的值,展示时可以转换成该货币的常用单位,比如人民币使用分存储,美元使用美分存储;
- 在要求精确表示小数点n位的业务场景下,比如圆周率要求存储小数点后1000位数字,使用单精度和双精度浮点数类型保存是难以做到的,这时推荐采用数组保存小数部分的数据;
- 在比较浮点数时,由于存在误差,往往会出现意料之外的结果,所以禁止通过判断两个浮点数是否相等来控制某些业务流程; 在数据库中保存小数时,推荐使用demical类型,禁止使用float类型和double类型,因为这两种类型在存储的时候存在精度损失的问题。
字符集与乱码
为了减少麻烦,所有情况下的字符集设置最好是一致的。
CPU 与内存
cpu内部十分复杂,总的来说是由控制器 和 运算器组成的。内部寄存器使这两者跟加高效
Java操作内存交给JVM,付出的代价是慢
TCP/IP
发送和接收数据的过程需要相应的协议来支撑,按互相可以理解的方式进行数据的打包与解包
链路层报文结构
mac地址全球唯一 使用16进制表示
总结:
程序在发送消息时,应用层接既定的协议打包数据 随后由传输层加
上双方的端口 ,由网络层加上双方的 IP 地址,由链路层加上双方的 MAC 地址
将数据拆分成数据帧 经过多个路由器 网关后到 达目标机器。简而言之 就是按
端口→ IP 地址→ MAC 地址 这样的路径进行数据的封装和发送 解包的时候反过
来操作即可
IP协议
IP是面向无连接,无状态的,没有额外的机制保持包是否送达
为什么有mac地址 还要IP协议?
分层管理
数据包生存时间 TTL 他是数据包可经过最多的路由器总数。 没经过减1 为0时抛弃。 并发送ICMP保温到源主机。
ICMP检测网络是否通畅
TCP
是 一种面向连接、确保数据在端到端间可靠传输的协议
由于TCP包头中存在扩展字段,所以通过长度为4个bit的头部长度表示tcp的报文的大小,这样才能计算数据部分的开始位置
SYN:( Synchronize Sequence Numbers )用作建立连接时的同步信号
ACK: ( Acknowledgement )用于对收到的数据进行确认,所确认的数据由确认序列号表示;
FIN:( Finish )表示后面没有数据需要发送,通常意昧着所建立的连接需要关闭了。
TCP 建立连接:
三次握手
为什么要三次握手?
主要为了两个主要目的:信息对等和防止超时
三次握手 也是防止超时导致脏链接
从编程的角度,TCP的连接 是通过文件描述符(fd)完成, 当fd不足时会出现 “open too many
files 错误而使得无法建立更多的连接。
TCP四次挥手
tcp是全双工通信,双方都能作为数据的发送方 和 接收方。
四次挥手断开连接用通俗的说法可以形象化地这样描述。
男生我们分手吧。
女生好的,我的东西收拾完,发信息给你。( 此时男生不能再拥抱女生了。)
( 个小时后)
女生,我收拾好了,分手吧。 (此时 女生也不能再拥抱男生了。)
男生。好的。 (此时 双方约定经过 个月的过渡期,双方才可以分别找新的
对象。)
TIME_WAIT:RFC793规定为2分钟,极大造成资源的浪费 为何不直接进入closed状态
1.确保被动关闭方顺利进入closed状态
防止失效请求。这样做是为了防止己失效连接的请求数据包与正常连接的
请求数据包混淆而发生异常。
所以,建议将高并发服务器 TIME WAIT 超时时间调小。
连接池
连接的频繁创建与断开,是非常消耗资源的
连接池最小连接数: 一直保持的连接数
连接池最大连接数: 连接池能申请的最大连接数,如果超过则被放到等待队列中
-般可以把连接池的最大连接数设置在 30个左右
在数据库层面的请求应答时间必须在 I00ms 以内,秒级的 SQL
查询通常存在巨大的性能提升空间,有如下应对方案,
- 建立高效且合适的索引 explain 还要考虑索引不生效的情况
- 排查连接资源 未显式关闭的·情况 特别注意ThreadLocal 或 流式计算中 使用数据库链接的地方
- 合并短的请求。
- 合理拆分多个表 join SQL 是超过三个表则禁止 join 多表查询的时候 要确定被关联字段添加索引
- 使用临时表
- 应用层改造 数据结构优化 并发线程改造
- 改用其它数据库
信息安全
DDos csrf xss
CIA原则
保密性 加密
完整性 通常使用MD5和数字签名
可用性 访问控制 限流
SQL注入
SQL 注入式攻击是未将代码与数据进行严格的隔离 ,导致在读取用户数据的时候 错误地把数据作为代码的一部分执行
防止:
( I )过滤异户输入参&中的阿弥字符 ,从而降低被 SQL ,t 入的风险。
( )禁止通过字符南拼撞的 SQL 吾旬 ,严恪使用参数绑定传人的 SQL 参数
( 3 )合理使月数据库击可框豆、提供的防左入机制。
CSRF
在用户并不知惰的情况下 冒充用户发起请求 在当前已经登录的 Web
用程序上执行恶意操作,如恶意发帖、修改密码、发邮件等。
防范 CSRF 漏洞主要通过以下方式
( I ) CSRF Token 验证,利用浏览器的同源限制,在 HTTP 接口执行前验证页面
或者 Cookie 中设置的 Token ,只有验证通过才继续执行请求。
( 人机交互 ,比如在调用上述网上银行转账接口时校验短信验证码。
XSS
跨站脚本攻击,通过向正常用户请求的 HTML 页面中插入恶意脚本,
从而可以执行任意脚本。
xss 主要分为反射型 xss 、存储型 xss DOM xss
xss 主要用于信息窃取、破坏等目的。
HTTPS
传输层本身是没有加密的
SSL 协议工作于传输层与应用层之间,为应用提供数
据的加密传输。
而 HTTPS 的全称是 HTTP over SSL ,简单的理解就是在之前的 HTTP
传输上增加了 SSL 协议的加密能力。
加密
我们可以通过对称加密算法对数据进行加密,比如 DES
密钥几乎没有什么保密性可言,如果与每一个用户之间
都约定一个独立的密钥,如何把密钥传输给对方,又是 个安全难题。
RSA 出现了。它把密码革命性地分成公钥和私钥,由于两个密钥并不相同,所以称
为非对称加密。私钥是用来对公钥加密的信息、进行解密的,是需要严格保密的。公钥
是对信息进行加密,任何人都可以知道,包括黑客。
非对称加密的安全性是基于大质数分解的困难性
RSA存在的问题· 信任危机 (即公钥不是真正的发送方 发送的)
CA ( Certificate Authority )就是颁发 HTTPS 证书的组织。
访问一个 HTTPS 的网站的大致流程如下,
( I )浏览器向服务器发送请求,请求中包括浏览器支持的协议,并附带一个随
机数。
( )服务器收到请求后,选择某种非对称加密算法,把数字证书签有公钥、身
份信息发送给浏览器,同时也附带一个随机数。
( )浏览器收到后、验证证书的真实性,用服务器的公铝发送握手信息给服务器。
( 服务器解密后 使用主前的随机数计算出,个对称加密的密钥 以此作为
加密信息并发送。
( 后续所有的信息发送都是以对称加密方式进行的。
TSL式SSL的3.0版本
第2 章 面向对象
OOP理念
OOP (面向对象编程)是面向过程的进一步发展,它推动了高级语言的发展。OOP的抽象、封装、继承、多态,使软件大规模化成为可能。
OOP实践了软件的三个主要目标:可维护,可扩展和可重用性
抽象
正确而严谨的业务抽象和建模分析能力是后续的封装、继承、多态的基础
在面向对象的思维中,抽象分为归纳和演绎。
前者是从具体到本质,从个性到共性,将一类对象的共同特征进行
归一化的逻辑思维过程 后者则是从本质到具体,从共性到个性,逐步形象化的过程。
封装
是在抽象基础上决定信息是否公开,以及公开等级,核心问题是以什么样的方式暴露哪些信息。
抽象只为了找到属性和行为的共性,属性是行为的基础,具有一定的敏感性,不能直接对外暴露
设计模式的七大原则之一的 迪米特法则就是对 封装的具体要求
继承
封装使软件在多变的情况下,基础模块可以直接被复用,或者增强复用。
继承是is-a关系,怎么满足?
里氏置换原则:任何父类能出现的地方,子类都能够出现
继承要注意 方法污染 和 方法爆炸
所以:优先采用组合或聚合的类关系来复用其他类的能力,而不是继承。
多态
同一个方法产生不同的运行结果,使同一个行为具有不同的表现形式。
是以覆写为基础实现面向对象的特性。
类
抽象类 和接口
抽象类和接口都是对实体类进行更高层次的抽象,仅定义公共行为和特性。
共同点: 都不能被实例化
抽象类在被继承时体现的是 is 关系,接口在被实 时体现的是 can do 关系。
抽象类通常是对同类事物相对具体的抽象,通常包含抽象方法、实体方法、属性变量。
can do 关系要符合接口 隔离 实现类要有能力去实现并执行接口 中定义的行为,
抽象类是 模板类设计,接口是契约式设计
优先定义为接口
内部类
内部类本身是类的属性
可以为 class enum ,甚至是 interface ,
·静态内部类,如 static class StaticinnerC!ass {} ;
·成员内部类,如: private class InstancelnnerC!ass {} ;
·局部内部类,定义在方法或者表达式内部,
·匿名内部类,如: (new Thread(){} ).start()。
访问权限
this和super
类关系
-
[ 继承 ] extends (is-a)
-
[ 实现 ] implements (can do)
-
[ 组合 ] 类是成员变量 contain-a)
-
{ 聚合 ] 类是成员变量(has a)
-
[ 依赖 ] import (use-a)
序列化
将对象转化为二进制流 称为对象的序列化
将二进制流恢复为对象的过程,称为反序列化
1.Java原生序列化
不支持跨语言
2.Hessian 序列化
协议简单 ,高效
跨语言
缩短二进制流
3.JSON序列化 抛弃了类型信息
json可读性好,方便调试
transient 序列化要有安全的防范意识
攻击者可以利用反序列化构造恶意代码
方法
入参保护
常见于批量接口 不然会导致服务内存被塞满,无任何处理能力
参数校验
构造方法
构造方法不要包含业务逻辑
初始化业务逻辑 放到某个方法内,当完成初始化 ,再显示调用
静态方法
当类加载时,即分配相应的内存空间
通常静态方法用于定义工具类的方法等,静态方法如果使用了可修改的对象,那
么在并发时会存在线程安全问题。所以,工具类的静态方法与单例通常是相伴而生的。
非静态代码块又称为局部代码块,是极不推荐的处理方式
可以使用静态代码块实现类加载判断、属性初始化、环境配置等。
覆盖
方法的覆写可以总结成容易记忆的口诀 大两小两同
。
-
一大 子类的方法访问权限控制符只能相同或变大。
-
两小 抛出异常和返回值只能变小 能够转型成父类对象。子类的返回值、
抛出异常类型必须与父类的返回值、抛出异常类型存在继承关系。
- 两同 方法名和参数必须完全相同。
泛型
类型参数话,解决不确定具体对象类型的问题。
没有泛型往往存在类型安全问题
泛型可以定义再类、接口和方法中
E括代表 Element ,用于集合中的元素;
T代表 the Type of object ,表示某个类; K代表key、V 代表 Value ,用于键值对元素。
(1)尖捂号里的每个元素都指代一种未知类型。 而仅仅是一个代号
(2)尖括号的位置非常讲究,必须在类名之后 方法返回值之前。
(3)泛型定义只具备Object的能力
泛型的好处:
1.代码安全
2.提高可读性
3.代码重用
基本数据类型
对象头占12个字节
包装类型
应为Java设计的初衷是一切皆对象, 很多时候需要对象来做
包装类的存在解决了基本数据类型无法做到的事情泛型类型参数、序列化、类型转换、高频区间数据缓存。
什么时候使用包装类还是基本数据类型?
1.所有的POJO类属性,必须使用包装类
2.RPC方法返回值和参数必须使用包装类
3.所有局部变量推荐使用基础数据类型
字符串
Strin 对象赋值操作后 会在常量池中进行缓存,如果下次申请创建对象时 缓存中已经存在,则直接返回相应引用给创建者。
StringBuilder 线程不安全
第3 章 代码风格
存储一对多的关系
JSON方式,XML方式,逗号隔开,多字段存储
推荐使用JSON方式
删除字段名称
is_deleted 使用1/0表示已删除和未删除状态
第4 章 走进JVM
字节码
字节码必须通过类加载过程加载到 JVM 环境后,才可以执行
执行有三种模式
第一,解释执行
第二, JIT 编译执行
第三, JIT 编译与解释混合执行(主流 JVM默认执行模式)
混合执行模式的优势在于解释器在启动时先解释执行,省去编译时间。
随着时间推进 JVM 通过热点代码统计分析 识别热点数据,将热点数据转化为机器码,直接交由CPU执行。
类加载过程
newInstance( )是一个方法,而new是一个关键字,其次,Class下的newInstance()的使用有局限,因为它生成对象只能调用无参的构造函数,而使用new关键字生成对象没有这个限制。
类加载器 怎么定位具体文件并读取?
为什么使用类加载器?
(1) 隔离加载类 在某些框架内进行中间件 与 应用的模块隔离,把类加载到不同的环境
(2)修改类的加载方法 类的加载模型非强制,并非一定要引入。
(3)扩展加载源
(4)防止源码泄露
继承classLoader 重写 findClass 调用defineClass
内存布局
元空间
JVM版本 sun Hostspot
JDK8 之前 是存在永久区的
1.它是固定大小,很难进行调优
2.如果动态加载的类过多,会永久代的OOM
3.永久代在垃圾回收方面也有很多问题
JDK8 使用元空间代替 永久代
在本地内存中分配
虚拟机栈
先进后出的数据结构,就像子弹的弹夹
栈是描述方法执行的内存区域,它是线程私有的
StackOverflowError 表示栈溢出 导致内存耗尽,通常出现在递归调用中
局部变量表:是存放方法参数和局部变量的地方
操作栈:初始状态为空的桶式结构栈
动态链接:每个栈帧中包含一个在常量池中对当前方法的引用 目的是支持方法调用过程的动态连接。
方法返回地址 :
-
返回值压入上层调用栈帧。
-
异常信息抛给能够处理的栈帧。。
-
PC 数器指向方法调用后的下一条指令。
本地方法栈
调用操作系统方法
程序计数寄存器
CPU时间片限制,任何时候只有一个线程执行指令
程序计数寄存器用来存放执行指令的偏移量和行号指示器
线程执行和恢复都需要依赖程序计数器 线程之间互不影响
从线程共享的角度:
堆和元空间是所有线程共享的,而虚拟机栈、本地方法栈、程序计数器是线程内部私有的
对象实例化
当new对象的时候
1.确认元信息是否存在
2.分配 对象内存
3.设置默认值
4.设置头信息
设置新对象的晗希码、 GC 信息、锁信息、对象所属的类元信息等。
这个过程的具体设置方式取决于 口币 实现。
5.执行init方法
初始化成员变量,执行实例化代码块,调用类的构造方法,
并把堆内对象的首地址赋值给引用变量。
垃圾回收
清楚不再使用的对象,自动释放内存空间
如何判断对象是否存活?
1.标记清理算法
会引起大量空间碎片
2.标记整理算法
把存活的整理在内存连续的一段,最后把之外的清理掉
Mark_Copy算法
Serial 回收器是一个主要应用于 YGC 的垃圾回收器,采用串行单线程的方法完成GC任务
STOP THE WORLD 即垃圾回收会暂停整个应用程序的执行
FGC时间较长,频繁会影响行用程序的性能
CMS 回收器( Concurrent Mark Sweep Collector )是回收停顿时间比较短、目前
比较常用的垃圾回收器。
Hotspot JDK7 推出了新代 1 ( Garbage-First Garbage Collector 垃圾回收