0%

关注树莓派很久了,只是没有很感兴趣的应用场景,就没有买来玩。几个月前偶然得到一个小度音箱,发现了新大陆,各种语音控制功能,便捷性不言而喻,还买了一些外部设备可以通过小度控制,发现有红外遥控器可以控制家里的大部分红外家电,奈何码库不是很全,有些设备还是不能控制的,而且不支持定制功能。恰好在知乎看到了一些 geek 视频,想着自己也做一个,可以支持红外数据的定制,做到自由遥控。于是乎说干就干,从一个什么硬件都不懂的小白一步步的了解了点硬件知识,软件部分相对好实现一些。主要计划的功能是通过语音来控制红外家电、温湿度监控以及智能提醒等功能,先完成主体框架然后再不断开发插件形式来增强可玩性。

阅读全文 »

这个标题看起有点鸡汤文,不过我还是建议对以下总结出的几点做些深入思考,这些会在今后的工作中越来越多的感受到它的作用。

寻找你行业内的专家

找到你所属行业内的专家,这些人往往做事高效并且很有才华。你要做的是跟随他们所关注的方向,学习他们做事的方法,思考如何应用到你的工作和生活上。找到他们,和他们去交流思考,提出自己的观点和想法。不要仅仅把眼光放到身边的人身上,这样会局限住你的视野。

阅读全文 »

ORM(Object/Relational Mapper),即“对象-关系型数据映射组件”。对于O/R,即 Object(对象)和Relational(关系型数据),表示必须同时使用面向对象和关系型数据进行开发。本文简述通过 Java 动态代理机制实现关系数据与 POJO 对象的映射。

代理

静态代理

静态代理其实就是指设计模式中的代理模式。
代理模式为其他对象提供一种代理以控制对这个对象的访问。

静态代理模式在增强现有的接口业务功能方面有很大的优点,但是大量使用这种静态代理,会使我们系统内的类的规模增大,并且不易维护。

动态代理

为了解决静态代理的问题,引入动态代理的概念,在编译时或者运行时,可以在需要代理的地方动态生成代理,减轻代理类和类在系统中冗余的问题。

Java 动态代理基于经典代理模式,引入了一个 InvocationHandler,InvocationHandler 负责统一管理所有的方法调用。

InvocationHandler

InvocationHandler 接口定义:

1
2
3
4
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}

每一个动态代理类都必须要实现 InvocationHandler 这个接口,通过代理类的实例调用一个方法时,这个方法的调用就会被转发为由 InvocationHandler 这个接口的 invoke 方法来进行调用。

Proxy

Proxy 这个类的作用就是用来动态创建一个代理对象的类,它提供了许多的方法,但是我们用的最多的就是 newProxyInstance 这个方法,可以获得一个动态的代理对象:

1
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,  InvocationHandler h)  throws IllegalArgumentException
阅读全文 »

锁解决的问题是并发操作引起的脏读、数据不一致问题。

基本原理

volatile

在Java中允许线程访问共享变量,为了确保共享变量能被准确和一致的更新,线程应该确保使用排它锁来单独获得这个变量,Java中提供了 volatile,使之在多处理器开发中保证变量的可见性,当一个线程改变了共享变量,另一个线程能够及时读到这个修改的值。恰当的使用它会比 synchronized 成本更低,因为不会引起上下文的切换和调度。

synchronized

通过锁机制实现同步,在Java中每一个对象都可以作为锁,有以下三种形式:

  • 对于普通同步方法,锁的是当前实例对象。
  • 对于静态同步方法,所得是当前类 class 对象。
  • 对于同步方法块,锁的是括号内指定的对象。

为了减少获得锁和释放锁带来的性能消耗,Java SE 1.6 引入了偏向锁和轻量级锁。偏向锁 的核心思想是:如果一个线程获得了锁,就进入偏向模式,当这个线程再次请求锁时,如果没有其它线程获取过该锁,无需再做任何同步操作,可以节省大量锁申请的操作,来提高性能。如果偏向锁获取失败,会通过 轻量级锁 的方式获取,如果获取成功则进入临界区,如果失败则表示有其它线程争夺到锁,当前线程锁请求会膨胀为 重量级锁

锁粗化 是指在遇到一连串连续的对同一个锁不断的进行请求和释放的操作时,会把所有的锁操作整合成对锁的一次请求,减少锁请求的同步次数。

锁消除 是指在编译期,通过对上下文的扫描,去除不可能存在共享资源竞争的锁。

自旋锁 是指在锁膨胀后,避免线程真正的在操作系统层面被挂起,通过对线程做几个空循环,以期望在这之后能获取到锁,顺利的进入临界区,如果还获取不到,则会真正被操作系统层面挂起。

CAS

指的是比较并交换,它是一个原子操作,比较一个内存位置的值并且只有相等时修改这个内存位置的值并更新值,保证新的值总是基于最新的信息计算的。在 JVM 中 CAS 操作是利用处理器提供的 CMPXCHS 指令实现。是实现我们平时所说的自旋锁或乐观锁的核心操作。

优点是竞争小的时候使用系统开销小;对应缺点是循环时间长开销大、ABA问题、只能保证一个变量的原子操作。

ABA 问题

问题产生原因是两个线程处理的时间差导致,具体如下图:

解决 ABA 问题可以增加一个版本号,在每次修改值的时候增加一个版本号。

产生:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private static AtomicReference<Integer> atomicReference = new AtomicReference<Integer>(100);

public static void main(String[] args) {
new Thread(() -> {
atomicReference.compareAndSet(100, 101);
atomicReference.compareAndSet(101, 100);
},"t1").start();

new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(atomicReference.compareAndSet(100, 2019) + "\t修改后的值:" + atomicReference.get());
},"t2").start();
}

解决:

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
private static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference<Integer>(100,1);

public static void main(String[] args) {
new Thread(() -> {
System.out.println("t1拿到的初始版本号:" + atomicStampedReference.getStamp());

//睡眠1秒,是为了让t2线程也拿到同样的初始版本号
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
atomicStampedReference.compareAndSet(100, 101,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
atomicStampedReference.compareAndSet(101, 100,atomicStampedReference.getStamp(),atomicStampedReference.getStamp()+1);
},"t1").start();

new Thread(() -> {
int stamp = atomicStampedReference.getStamp();
System.out.println("t2拿到的初始版本号:" + stamp);

//睡眠3秒,是为了让t1线程完成ABA操作
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("最新版本号:" + atomicStampedReference.getStamp());
System.out.println(atomicStampedReference.compareAndSet(100, 2019,stamp,atomicStampedReference.getStamp() + 1) + "\t当前 值:" + atomicStampedReference.getReference());
},"t2").start();
}

在 mac 机器上可以使用 mweb 来写博客,比较好用的地方就是可以直接把剪贴板的图片粘贴上来,缺点是 mac 键盘超难用并且不支持窗口内开启命令行。平时在家的时候都用 Ubuntu 台式机,博客使用 VS Code 编写,一直以来阻挡我的是图片的粘贴特别费劲,今天发现一个很好用的插件 pasteimage,可以直接将剪贴板图片粘贴到 markdown 使用,并且支持配置保存路径。

然后按照教程配置好参数:

1
2
3
4
5
6
{
"pasteImage.path": "${projectRoot}/source/resource/img",
"pasteImage.basePath": "${projectRoot}/source",
"pasteImage.forceUnixStyleSeparator": true,
"pasteImage.prefix": "/"
}

就可以直接将图片粘贴到 markdown 中,其中遇到个问题就是配置不生效,会导致文件直接保存到当前文件目录,具体配置方法可以参考下面连接。

https://www.crifan.com/vscode_how_to_config_setting_plugin/ 这篇文章写的很详细了。
https://github.com/mushanshitiancai/vscode-paste-image 这篇是配置教程,里面有些地方比较容易被误导。

对于Linux系统需要有 xclip 支持,使用的时候会给提示的。

另外记录一下 Ubuntu 的截屏和粘贴快捷键:

1
2
Ctrl + Shift + Print Screen  // 区域截屏到剪贴板
Ctrl + Alt + s // 在 VS Code 中粘贴

设计原则

如果把设计模式理解为优秀软件系统模块设计的最小抽象,那么设计原则就是这些抽象的指导思想。目的是设计一个易于扩展和维护的系统。设计模式的六大原则有:

  • Single Responsibility Principle:单一职责原则
  • Open Closed Principle:开闭原则
  • Liskov Substitution Principle:里氏替换原则
  • Law of Demeter:迪米特法则
  • Interface Segregation Principle:接口隔离原则
  • Dependence Inversion Principle:依赖倒置原则

单一职责原则

应该有且只有一个原因引起类的变化,一个类只负责一个职责。一个功能应该要划分成多少个职责类去实现,并没有明显的限定。举例说明对于用户管理,用户信息管理和用户行为管理可以做初步拆分,用户信息管理又可以拆分成普通信息维护和敏感信息的维护。又比如用户发生一笔支付行为可以初步拆分为交易信息管理和支付信息管理。职责划分的粗细的影响因素有对于业务理解程度、项目开发阶段等,过粗会造成一个处理类包含太多职责,过细又会增加开发维护成本。单一职责是“高内聚低耦合”设计的一种实现形式,单一职责即为同一职责内部的内聚性,降低不同职责之间的耦合性。

里氏替换原则

描述继承关系,子类全部实现或继承父类方法,子类可以扩展父类方法实现,将子类替换父类不会产生异常。在重构角度来说如果多个子类拥有相同的行为,可以将相同行为提取到父类实现,子类调用扩展父类实现。在开发上基于“组合大于继承”的原则,通过定义实现接口的形成被其它类调用。违反这个原则不一定会产生严重后果,但是会对后面的开发维护造成困难。

开闭原则

描述的是对于需求产生变化后,软件实体部分应该进行扩展开发,避免修改。通过扩展实体行为来响应需求变化,而不是通过修改现有软件代码。

迪米特法则

描述的是一个对象应该进行减少对于其它对象的理解。通过封装我们可以屏蔽类内部逻辑,只提供足够用且少量的方法来给外部使用,降低对象之间的耦合性。当一个接口或者一个对象被公开,意味着后面我们进行开发和维护的时候很难再将这个对象收回,重构内部逻辑时也会更加困难。

接口隔离原则

描述的是建立单一的接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量行为统一,避免臃肿。对于支付接口来说,定义类通用支付方法,对于获取分期支付信息也属于支付行为的一个环节,但不是所有实体类必须要实现的,可以拆分出来。

依赖倒置原则

描述的是实现类之间不能直接发生依赖关系,其依赖关系是通过接口或者抽象类产生,即面向接口编程。实现类依赖接口或者抽象类,而接口或者抽象类不依赖实现类。

设计模式

https://design-patterns.readthedocs.io/zh_CN/latest/index.html