Yuicon
Yuicon
## OBJ_ENCODING_ZIPLIST `ziplist`也就是压缩列表,是`Redis`为了节约内存而开发的,是由一系列特殊编码的连续内存块组成的顺序型数据结构,在`Redis2.8`的时候是作为小数据量的列表底层实现。`ziplist`本身并没有定义结构体,下面是《redis设计与实现》里的说明图。   ### ziplist entry 下面是`ziplist`的`entry`逻辑定义,并不是实际的编码,但是可以用来理解`entry`是怎么组成的。 ```c /* We use this function to receive information about a ziplist entry. * Note that this is not how the...
# 字符串对象 字符串数据类型是`Redis`里最常用的类型了,它的键和值都是字符串,使用起来非常的方便。虽然字符串数据类型的值都统称为字符串了,但是在实际存储时会根据值的不同自动选择合适的编码。字符串对象的编码一共有三种:`int`、`raw`、`embstr`。 ## Redis对象 Redis用统一的数据结构来表示一个对象,具体定义如下: ```c typedef struct redisObject { unsigned type:4; unsigned encoding:4; // 当内存超限时采用LRU算法清除内存中的对象 unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or * LFU data...
# 序 `HashMap`是Java中常用的Map接口的实现类,因为在日常工作中非常频繁的出现,所以在大部分的Java面试中都会问几个关于HashMap的问题。掌握HashMap的实现原理,已经是Java程序员的基础操作了。 # Map接口 映射(Map)是一种用于存放键/值对的数据结构。如果提供了键,就能直接找到相对应的值。HashMap(哈希映射)是Map接口的一个实现类,主要使用哈希来实现键与值的映射。 ``` 定义。映射是一种用于存放键/值对的数据结构,主要支持两种操作:插入(put),即将一组新的键值对存入映射中;查找(get),即根据给定的键得到相应的值。 ``` # HashMap的底层数据结构 HashMap的底层是用散列表实现的,散列表是一种用数组来存储键值对的数据结构,它使用一个散列函数将键转换成数组的一个索引然后存储值。不过会有不同的键被散列成同个索引的情况出现,这叫做碰撞冲突。HashMap用拉链法来解决这个问题,即散列表数组中的每个元素都指向一条链表,链表中的每个节点都存储了被散列到这个索引的键值对。  # HashMap的散列函数 根据散列表的定义我们知道,想要弄清楚HashMap的实现,我们首先需要知道HashMap的散列函数是怎么实现的,即HashMap是如何将一个键映射到数组的索引的。 ```java static final int hash(Object key) { int h; return (key == null) ?...
> [我的博客](https://www.penglei.wang/) 转载请注明原创出处。 # 序 之所以会想来写泛型相关的内容,是因为看到这样的一段代码:  当时我的内心是这样的:  所以就赶紧去复习了下,记录下来。基础不扎实,源码看不懂啊。 # 泛型介绍 Java 泛型(generics)是 JDK 5 中引入的一个新特性,泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数,在Java集合框架里使用的非常广泛。 定义的重点是提供了编译时类型安全检测机制。比如有这样的一个泛型类: ``` public class Generics { private T value; public T getValue() {...
> [我的博客](https://www.penglei.wang/) 转载请注明原创出处。 # 序 在多线程环境下,访问非线程安全的变量时必须进行线程同步,例如使用`synchronized`方式访问`HashMap`实例。但是同步访问会降低并发性,影响系统性能。这时候就可以用空间换时间,如果我们给每个线程都分配一个独立的变量,就可以用非同步的方式使用非线程安全的变量,我们称这种变量为线程局部变量。 顾名思义,线程局部变量是指每个线程都有一份属于自己独立的变量副本,不会像普通局部变量一样可以被其他线程访问到。`Java`并没有提供语言级的线程局部变量,而是在类库里提供了线程局部变量的功能,也就是这次的主角`ThreadLocal`类。 # ThreadLocal的使用  `Java8`版本的`ThreadLocal`有上图所示的4个`public`方法和一个`protected`的方法,第一个方法用于返回初始值,默认是`null`。第二个静态方法`withInitial(Supplier k, Object v) { super(k); value = v; } } ``` `ThreadLocalMap`的实现与`HashMap`的实现有相似的地方,比如同样是使用数组存储数据和自动扩容,不同的是`hash`算法与`hash`碰撞后的处理不一样。 ```java // ThreadLocalMap的set方法 private void set(ThreadLocal key,...
# 序 从`Java 9`开始,在`Java`的世界里多了一个叫模块([JSR376](http://openjdk.java.net/projects/jigsaw/spec/))的特性。模块系统的前身是`Jigsaw`项目。最初,该项目仅仅是为`JDK`设计、实现一个模块系统。后来项目组也希望它能为开发者所用——虽然,一开始它并不是`Java SE`平台规范的组成部分。随着项目的不断深入,`Java`平台对标准模块系统的呼求也日益增长,`JCP`批准该项目升级为`JavaSE`平台的一部分,也能服务于`Java ME`和`Java EE`平台的需求。 ## 官方对`Java`平台模块系统是这样描述的: ### 一种新的`Java`编程组件,即模块。它是自描述的代码与数据的集合,有以下特性: * 引入了一个新的可选阶段——链接时,它介于编译时和运行时之间,在此期间可以将一组模块组装并优化为定制的运行时镜像。 * 为工具`javac`、`jlink`增加了一些选项,以及在`Java`中,你可以指定模块路径,这些路径定位模块的定义。 * 引入模块化`JAR`文件,该文件是一个`JAR`文件,其根目录中包含`module-info.class`文件。 * 引入`JMOD`格式,它是一种类似于`JAR`的打包格式,但它可以包含原生代码和配置文件。 ### JDK本身已经模块化。有以下改变: * 使你能够将`JDK`的模块组合成各种配置,包括: * 与`JRE`和`JDK`一样的配置。 * 与`Java SE 8`中定义的每个压缩配置文件的内容大致相同。 *...
# 序 第一次了解到控制反转(Inversion of Control)这个概念,是在学习`Spring`框架的时候。`IOC`和`AOP`作为`Spring`的两大特征,自然是要去好好学学的。而依赖注入(Dependency Injection,简称DI)却使得我困惑了挺久,一直想不明白他们之间的联系。 # 控制反转 控制反转顾名思义,就是要去反转控制权,那么到底是哪些控制被反转了?在2004年 `Martin fowler` 大神就提出了 >“哪些方面的控制被反转了?” 这个问题,他总结出是依赖对象的获得被反转了。 在单一职责原则的设计下,很少有单独一个对象就能完成的任务。大多数任务都需要复数的对象来协作完成,这样对象与对象之间就有了依赖。一开始对象之间的依赖关系是自己解决的,需要什么对象了就`New`一个出来用,控制权是在对象本身。但是这样耦合度就非常高,可能某个对象的一点小修改就会引起连锁反应,需要把依赖的对象一路修改过去。  如果依赖对象的获得被反转,具体生成什么依赖对象和什么时候生成都由对象之外的`IOC容器`来决定。对象只要在用到依赖对象的时候能获取到就可以了,常用的方式有依赖注入和依赖查找(Dependency Lookup)。这样对象与对象之间的耦合就被移除到了对象之外,后续即使有依赖修改也不需要去修改原代码了。  总结一下,控制反转是指把对象的依赖管理从内部转移至外部。 # 依赖注入 控制反转是把对象之间的依赖关系提到外部去管理,可依赖是提到对象外面了,对象本身还是要用到依赖对象的,这时候就要用到依赖注入了。顾名思义,应用需要把对象所需要的依赖从外部注入进来。可以是通过对象的构造函数传参注入,这种叫做`构造器注入(Constructor Injection)`。如果是通过`JavaBean`的属性方法传参注入,就叫做`设值方法注入(Setter Injection)`。 不管是通过什么方式注入的,如果是我们手动注入的话还是显得太麻烦了。这时候就需要一个容器来帮我们实现这个功能,自动的将对象所需的依赖注入进去,这个容器就是前面提到的`IOC容器`了。 控制反转和依赖注入的关系也已经清晰了,它们本质上可以说是一样的,只是具体的关注点不同。控制反转的关注点是控制权的转移,而依赖注入则内含了控制反转的意义,明确的描述了依赖对象在外部被管理然后注入到对象中。实现了依赖注入,控制也就反转了。 # 例子 *...
# 依赖倒置 在面向对象设计中有一个重要的原则是依赖倒置(Dependence Inversion Principle),主要作用是解耦,让对象与对象之间松耦合。定义如下:**高层模块不应该依赖底层模块,他们都应该依赖抽象。抽象不应该依赖于细节,细节应该依赖于抽象。** 光看定义很难理解依赖倒置到底是什么意思,先举一个简单的例子。 有以下两个类: ``` public class Dao { private MysqlConnection connection; public Dao(MysqlConnection connection) { this.connection = connection; } public void findAll() { connection.executeQuery("SELECT * FROM...
# 序 `Java 8`新增的`lambda`表达式毫无疑问是令人非常激动的,从此我们可以非常简洁的定义和使用代码块而不是用繁琐的匿名内部类来实现。而接口是`lambda`表达式的基础,要理解`lambda`表达式就要先理解接口的概念。 # 接口 在`Java`中接口是对类行为的抽象。似乎继承也能做到这件事,它们的区别在于`Java`中类只能有一个父类,而接口是可以实现多个的。所以接口更倾向于类的一部分抽象,也就是行为的抽象,而不是类本身的抽象。 ## 语法 要定义一个接口很简单,使用关键字`interface`后面再跟上接口名称就可以了。类可以用`implements`关键字来实现接口。 ```java public interface A { void test(); } ``` * 接口不允许有实例域,但可以有常量 * 接口中的域都会自动声明为`public static final` * 接口中的方法都会自动声明为`public` * 接口中可以声明抽象方法,`Java 8`以后还可以声明静态方法和默认方法...
## **- 面向对象的三个基本特征** 1. 封装 > 面向对象的程序设计中,某个类把所需要的数据(也可以说是类的属性)和对数据的操作(也可以说是类的行为)全部都封装在类中,分别称为类的成员变量和方法(或成员函数)。这种把成员变量和成员函数封装在一起的编程特性称为封装。 2. 继承 > 继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。 3. 多态 > 多态性(polymorphisn)是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。 > 实现多态,有二种方式,覆盖,重载。 > 覆盖,是指子类重新定义父类的虚函数的做法。 > 重载,是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。 ## **- 抽象类和接口有什么区别** > 接口和抽象类都是继承树的上层,他们的共同点如下: > 1. 都是上层的抽象层。 >...