【工作】《码出高效》读书笔记
- 这是一本兼顾深度与广度的通用技术书籍
- 列出了每节目录,与个人觉得是知识点的地方
- 以及每章概述
第1章 计算机基础
追根究底是深度分析和解决问题、提升程序员素质的关键所在,有助于编写高质
的代码。基础知识的深度认知决定着知识上层建筑的延展性。试问 对于如下的基
础知识,你的认知是否足够清晰呢?
- 位移运算可以快速地实现乘除运算 那位移时要注意什么?
- 浮点数的存储与计算为什么总会产生微小的误差?
- 乱码产生的根源是什么?
- 程序执行时 CPU 是如何与内存配合完成程序使命的?
- 网络连接资源耗尽的 题本质是 么?
- 黑客攻击的通常套路是什么?如何有效地防止?
本章从编程的角度深度探讨计算机组成原理、计算机网络、信息安全等相关内容,与具体编程语言无关。本章将不会讨论内部硬件的工作原理、网络世界的协议和底层传输方式、 全领域的攻防类型等内容。
1.1 走进0与1的世界
1.2 浮点数
1.2.1 科学计数法
1.2.2 浮点数表示
1.2.3 加减运算(解释浮点运算的偏差原因)
1.2.4 浮点数使用
1.3 字符集与乱码
- 一个byte 8个bit的原因:有一个bit是校验位
- GB2312 -> GBK -> GB18030(国家标准)
- Unicode为每个字符设置了编码
- UTF是Unicode的实现方式(或者叫压缩格式)分为UTF-8、UTF-16、UTF-32。UTF-8基本思路是越长用越短,会有1-6个字节对Unicode变长编码
1.4 CPU与内存
1.5 TCP/IP
1.5.1 网络协议
- 网络协议族
- 分层框架:应用层(HTTP、FTP等)、传输层(TCP、UDP)、网络层(IP)、链路层(IEEE)
- 报文->路由->端到端->解析
1.5.2 IP协议
1.5.3 TCP握手
需要三次握手的原因:信息对等、防止脏连接
image.png
1.5.4 TCP 断开连接
四次挥手
image.png
1.5.5 连接池
1.6 信息安全
1.6.1 黑客与安全
1.6.2 SQL注入
1.6.3 XSS与CSRF
1.6.4 CSRF(跨站请求伪造)
1.6.5 HTTPS
- 非对称性加密 RSA。核心在于对称加密在需要使用时,密钥的分发危险(不告诉别人,别人就没法加密),但是非对称加密,用于加密的公钥是随便公开的,自己解密的私钥自己保存即可。
- CA认证中心 HTTPS证书
- HTTPS通信大概可以分为双方建立加密通信并协定秘钥的过程(这里用非对称),和后续的数据传输过程(这里是对称加密)
- 非对称加密 性能慢
- TLS(传输层安全协议)是SSL(安全套接字层)的升级版
1.7 编程语言的发展
第2章 面向对象
本章开始讲解面向对象思想,并以 Java 为载体讲述面向对象思想在具体编程语
言中的运用与实践。当前主流的编程语言有50种左右,主要分为两大阵营:面向对象编程与面向过程编程。
面向对象编程( object -Oriented Programming, OOP )是划时代的编程思想变革,推动了高级语言的快速发展和工业化进程。 OOP的抽象、封装、继承、多态的理念使软件大规模化成为可能,有效地降低了软件开发成本、维护成本和复用成本。面向对象编程思想完全不同于传统的面向过程编程思想,使大型软件的开发就像搭积木那样隔离可控、高效简单,是当今编程领域的一股势不可当的潮流。 OOP 实践了软件工程的三个主要目标:可维护性、可重用性和可扩展性。
2.1 OOP理念
- 面向过程的结构相对松散 强调如何流程化地解决问题;面向对象的思维更加内聚,强调高内聚、低藕合,先抽象模型,定义共性行为再解决实际问题。
- 传统意义上 面向对象有三大特性封装、继承、多态。+ 本书强调的抽象
2.2 初识JAVA
2.3 类
2.3.1 类的定义
2.3.2 接口与抽象类
- 抽象类是模板式设计,而接口是契约式设计。
- 抽象类在被继承时体现的是 is -a 关系,接口在被实现时体现的是 can - do 关系。
- Java 言中类的继承采用单继承形式,避免继承泛滥、菱形继承、循环继承,甚至“四不像”实现类的出现。
- 当纠结定义接口还是抽象类时,优先推荐定义为接口,遵循接口隔离原则,按某个维度划分成多个接口,然后再用抽象类去 implements 某些接口,这样做可方便后续的扩展和重构。
2.3.3 内部类
- 静态内部类,如:static class StaticinnerCass {} ;
- 成员内部类,如:private class InstancelnnerCass {} ;
- 局部内部类,定义在方法或者表达式内部;
- 匿名内部类,如: (new Thread(){} ).start()。
无论是什么类型的内部类,都会编译成一个独立的class文件
类加载与外部类在同一个阶段进行。JDK源码中定义包内可见静态内部
类的方式很常见,这样做的好处是:
- 作用域不会扩散到包外。
- 可以通过”外部类 内部类”的方式直接访问。
- 内部类可以访问外部类中的所有静态属性和方法。
2.3.4 访问权限控制
2.3.5 this 与 super
2.3.6 类关系
- 继承 extends (is-a)
- 实现 implements (can do)
- 组合 类是成员变量(contains-a)
- 聚合 类是成员变量 (has-a)比组合要松散,例如小狗有腿(组合),有狗绳(聚合)
- 依赖 import类(use a)
随着业务和架构的发展,类与类的关系是会发生变化的。
2.3.7 序列化
2.4 方法
2.4.1 方法签名
2.4.2 参数
- 形参
- 实参
- 可变参数(即 ... )觉得不建议用
2.4.3 构造方法
创建类对象时,会先执行父类和子类的静态代码块 然后再执行父类和子类的构造方法。并不是执行完父类的静态代码块和构造方法后,再去执行子类。静态代码块只运行一次,在第二次对象实例化时,不会运行。
2.4.4 类内方法
2.4.5 getter setter
2.4.6 同步与异步
2.4.7 覆写
2.5 重载
JVM在重载方法中,选择合适的目标方法的顺序如下:
1)精确匹配
2)如果是基本数据类型,自动转换为更大表示类型的数据类型
3)自动拆箱与装箱
4)从子类向上转型类型依次匹配
5)通过可变参数匹配
2.6 泛型
- 泛型的本质是类型参数化,解决不确定具体对象类型的问题。
- 尖括号里的每个元素都指代一种未知类型(可以是T等,也可以是一个名字)
- 泛型擦除成Object
- 泛型与集合的联合使用,可以把泛型的功能发挥到极致。
2.7 数据类型
2.7.1 基本数据类型
- 对象头最小占用空间为12个字节
- 实例数据
- 对齐填充
2.7.2 包装类型
2.7.3 字符串
- String是只读的
- String常量池
第3章 代码风格
一致性很重要。
代码风格并不影响程序运行,没有潜在的故障风险,通常与数据结构、逻辑表达无关,是指不可见字符的展示方式、代码元素的命名方式和代码注释风格等。比如,大括号是否换行、缩进方式、常量与变量的命名方式、注释是否统一放置在代码上方等。代码风格的主要诉求是清爽统一、便于阅读和维护。统一的代码风格可以让开发工程师们没有严重的代码心理壁垒,每个人都可以轻松地阅读并快速理解代码逻辑,便于高效协作,逐步形成团队的代码“味道”。
3.1 命名规约
3.1.1 常量
3.1.2 变量
3.2 代码展示风格
3.2.1 缩进、空格与空行
3.2.2 换行与高度
3.2.3 控制语句
3.3 代码注释
3.3.1 注释三要素
3.3.2 注释格式
第4章 走进JVM
Oracle的HotSpot JVM 实现,是目前最主流的JAVA虚拟机。它采用解释与编译混合执行的模式。本章从字节码说起,分析类加载的过程,并结合内存布局,讲解对象创建与垃圾回收等各个知识点。
4.1 字节码
- 是一种中间码
- JAVA总共有200多个指令
- 因此叫ByteCode
- 解释执行 + 编译执行(热点代码直接JIT编译成机器码)
主要指令类型有
- 加载或存储指令
- 运算指令
- 类型转换指令
- 对象创建或访问指令
- 操作栈指令
- 方法调用与返回指令
- 同步指令
4.2 类加载过程
- ClassLoader类加载过程
- Load、Link、Init过程
- Class类 - 可以通过反射把实现和定义解耦,可以获取类的声明,注解,方法等
类加载器的分层体系
- 最高层 Bootstrap ClassLoader:负责JAVA的核心类型如 Object System String 等
- 第二层 Platform/Extension ClassLoader:加载扩展的系统类如加密
- 第三层 Application ClassLoader:加载用户自定义的CLASSPATH下的类
其中Bootstrap是c语言实现的,后面是JAVA实现的
类加载器具有等级制度,但是并无继承关系,以组合的方式来复用父加载器的功能。
-XX:+TraceClassLoading可以查看加载了哪个jar中哪个类。
下图说明了类的加载顺序和覆盖机制,即高层优先,低层不能覆盖高层。
用户可以自定义类加载器
- 隔离加载类,防止冲突
- 修改类加载方式 :按需动态加载
- 扩展源代码:如从网络加载
- 有时候源码加密后,同样需要自定义类加载器还原
4.3 内存布局
image.png- 堆区:新生代(Eden + S0+ S1)和老年代,在S区停留x次后,移动到老年代
- 元空间 MetaSpace(前身Permanent区)即以前的永久代。主要存储类的信息
- 虚拟机栈:执行方法的内存区,线程私有
- 本地方法栈(native方法):本地方法调用的栈数据,也是线程私有的
4.4 对象实例化
4.5 垃圾回收
- 与GC Roots失去引用(GC Roots指常量、静态对象、方法栈中的对象)
- 标记-清除是GC的基础
- 引入Mark-Copy解决碎片问题
主要GC算法:
- Serial 常用与YGC 单线程串行,stop the world
- CMS(初始标记、并发标记、重新标记、并发清除)共4个步骤。其中1和3 STW
- G1 将对划分为Regoin,优先回收垃圾较多的区域,对停顿时间更可控
第5章 异常与日志
捕获异常时需要分清稳定代码和非稳定代码。
如果异常在当前方法的处理能力范围之内且没有必要对外透出,那么就直接
捕获异常并做相应处理;否则向上抛出,由上层方法或者框架来处理。
5.1 异常分类
- 所有异常都是 Throwable 的子类
- 分为Error(致命异常)和Exception(非致命异常)
5.2 try 代码块
- finally 是在 return 表达式运行后执行的。因此finally里面的计算可能是无用的。
- 不要在finally中使用return,会使返回值判断变得复杂
5.3 异常的抛与接
5.4 日志
5.4.1 日志规范
5.4.2 日志框架
image.png- 日志门面:它只提供一套接口规范如slf4j,自身不负责日志功能的实现
- 日志库:主流的日志库有三个 分别是 log4j,log-jdk,logback(log4j的升级版)
- 曰志适配器:包括日志门面适配器如(slf4j-log4j12)和日志库适配器
第6章 数据结构与集合
本书中其他地方出现的集合概念,都指的是 Collection来保存各种各样的对象。非是数学意义上的Set(互异)。在进入高并发编程时代后,由集合引发的相关故障占比越来越高。比如,多线程共享集合时出现的脏数据问题,某些集合在数据扩容时出现节点之间的死链问题;写多读少的场景误用某些集合导致性能下降问题等。本章将从数组讲起,引申到集合框架,再到重点集合源码分析,最后介绍高并发集合框架,目的是对集合的了解成竹在胸、运用得心应手。
6.1 数据结构
- 数据结构定义:数据结构是指逻辑意义上的数据组织方式及其相应的处理方式。
- 数据结构分类:线性、树、图、哈希
6.2 集合框架图
- 框架图中主要分为两类。第一类是按照单个元素存储的 Collection,第二类是KV结构的Map
- Collection接口主要有Set、List、Queue三大类子实现
- Map主要有HashMap、TreeMap、SortedMap等
6.2.1 List 集合
- 最常用的是 ArrayList和LinkedList 两个集合类
- LinkedList 的优点在于可以将零散的内存单元通过附加引用的方式关联起来。内存利用率较高
6.2.2 Queue 集合
- 队列是一种特殊的线性表,它只允许在表的一端进行获取操作,在表的另一端进行插入操作
- 经常被作为Buffer (数据缓冲区)使用
6.2.3 Map 集合
- Map 类提供三种 Collection 视图
- KeySet、ValueSet、EntrySet
- 最早用于存储键值对的 Hashtable 因为性能瓶颈已经被淘汰
- HashMa 线程是不安全的,ConcurrentHashMap 是线程安全的
- TreeMap是Key有序的Map类集合,有headMap、lower/higher key等特别的操作接口
6.2.4 Set 集合
- Set 体系最常用的是 HashSet、TreeSet、LinkedHashSet三个集合类
- HashSet 从源码分析是使 HashMap 来实现的(Valu 固定为一个静态对象)
- LinkedHashSet维护了插入元素的顺序
6.3 集合初始化
- 合理初始大小可以避免被动扩容和数组复制的额外开销
- HashMap扩容还会重建哈希表,非常影响性能
- 默认值大小ArrayList 为10,HashMap 默认为16
6.4 数组与集合
- 数组是固定容量大小的
- 对于动态大小的数组,集合提供了 Vector和ArrayList两个类,前者是线程安全,性能较差,基本弃用
- Arrays 是针对数组对象进行操作的工具类,包括数组的排序、查找、对比、拷贝等操作。通过这个工具类也可以把数组转成集合
- Arrays asList 体现的是适配器模式,后台的数据仍是原有数组。并不是通用意义上的ArrayList,是一个内部类实现。。
- 使用集合toArray(T[] array )方法,转换为数组时注意需要传人类型完全一样的数组,并且它的容量为list.size,这样效率是最高的
6.5 集合与泛型
- (略,知识点多又不太常用。。)
6.6 元素的比较
6.6.1 Comparable和Comparator
- Comparable是表示自身有比较的能力,比较方法是compareTo
- Comparator是第三方实现的比较,比较方法是compare
6.6.2 hashCode和equals
- 注意任何时候覆写equals 都必须同时覆写 hashCode
6. 7 fail-fast 机制
- 遍历过程对集合的操作可能导致异常
- CopyOnWriteArrayList可以优化读多写少的场景
6.8 Map 类集合
image.png6.8.1 红黑树
- 树的结构
- 二叉树是经典的二分法实现
- 二叉树:平衡二叉树、查找二叉树、红黑树(TODO 一些重点数据结构,需要展开理解)
6.8.2 TreeMap
6.8.3 HashMap
- 并发写的死链问题(CPU大量占用)
6.8.4 ConcurrentHashMap
- 是学习并发编程的一个绝佳示例,此类超过 6300 行代码,涉及volatile、CAS 锁、链表、红黑树等众多知识点
- 原理与算法 (TODO 也需要展开。。)
第7章 并发与多线程
并发与并行两个概念非常容易混淆,它们的核心区别在于进程是否同时执行。以 KTV 唱歌为例,并行指的是有多少人可以使用话筒同时唱歌,并发指的是同一个话筒被多个人轮流使用。
7.1 线程安全
- 线程可以拥有自己的操作栈、程序计数器、局部变量表等资源
- 线程安全的核心理念就是“要么只读,要么加锁”
- Java 并发包( java.util.concurrent)JUC核心功能
- 线程同步类:CountDownLatch等。替换notify、wait等
- 并发集合类:ConcurrentHashMap等
- 线程管理类:Executors工厂类等
- 锁相关类:ReentrantLock等
7.2 什么是锁
- 计算机的锁也是从开始的悲观锁,发展到后来的乐观锁、偏向锁(降低无竞争开销)、分段锁等
- 锁主要提供两种特性:互斥性和不可见性
7.3 线程同步
7.3.1 同步是什么
- 原子性:不可分割,要么全部成功,要么全部失败
- 实现线程同步的方式有很多,比如同步方法、锁、阻塞队列等
7.3.2 volatile
- 指令优化,指令重排
- volatile延伸为敏感的易变的,局部阻止了指令重排的发生
- 双重锁问题(double-check):对象创建和引用赋值不是原子的(JVM的实现或者JIT的编译可能让代码不是按预期顺序执行)
7.3.3 信号量同步
7.4 线程池
7.4.1 线程池的好处
ThreadPoolExecutor 的构造方法:
public ThreadPoolExecutor(int corePoolSize, 常驻核心线程数
int maximumPoolSize, 同时执行的最大线程数
long keepAliveTime, 线程池中的线程空闲时间
TimeUnit unit, 时间单位
BlockingQueue<Runnable> workQueue, 缓存队列
ThreadFactory threadFactory, 线程工厂
RejectedExecutionHandler handler 执行拒绝策略的对象
)
7.4.2 线程池源码详解
7.5 ThreadLocal
7.5.1引用类型
7.5.2 Threadlocal 价值
7.5.3 ThreadLocal 副作用
- 脏数据(原因是线程会被重用,其附带的ThreadLocal也会留存)
- 内存泄露(没有remove)