陈志军

物来顺应,未来不迎,当时不杂,既过不恋


  • 首页

  • 分类

  • 归档

  • 标签

  • 搜索

随笔20180306

发表于 2018-03-07 | 分类于 随笔 | 阅读次数

随笔20180306

时间算算是真快啊,确实,转眼毕业快两年了。参加工作已经两年了。很多事情真是感觉像是昨天才发生的一样。
这几天静下心来想想自己。每日的计划做的很满,却是非常飘。很多时候我在对各种新技术去了解,去有想法,却忽略了一个很简单的事情,技术的更新特别快,今天有A,明天就有B,如果只是跟着潮流,那么未来的方向是哪里呢?我反思,我这一年里经历了很多,自己也在周末,下班都在弄一些技术相关的新的东西。然而,我却只教会了自己怎么用。没有教会自己为什么。过去的日子已经追不回来,努力的过好当下的日子才是最重要的。2018年立了很多的flag,其实最大的flag就只有一个,那就是-行动。

重温Java编程思想的一些感悟-20180305

发表于 2018-03-05 | 分类于 Java | 阅读次数

重温Java编程思想的一些感悟-20180305

JIT(just-in-time) 即时编译技术:将程序全部或者部分翻译成本地机器码,程序运行速度因此得到提升。
对于hotspot来说,代码每次被执行,都会做一些优化,执行的次数越多,速度也就越快。

java会给所有的默认域做一个初始化,非基本类型对象不初始化值的情况下,默认为null。
初始化顺序:static->{}->构造器;

static的数据指向同一份存储区域,不能用于局部变量。

final对于基本类型,其数值永不改变。final对于引用类型,其引用永不改变。
必须在定义final或者在构造器中对final进行赋值。

final类无法继承,方法无法重写或修改。private的方法其实是隐式的final。

compareTo,如果是两个int,最好不要直接返回i-i2,如果是有符号的int类型,一个正数最大,一个负数最大,那么将永远返回负数。所以如果重写compareTo,最好是不要直接返回return i-i2;

集合是fail-fast的,比如:

1
2
3
list.add(aa);
list.iterator();
list.add(cc);

需要明确在添加或者修改完容器所有元素之后再获取迭代器。

Effective Java 0228

发表于 2018-02-28 | 分类于 Java | 阅读次数

Effective Java 0228

覆盖equals时候请遵守通用规定

翻开书,看到了书的第3章-对于所有对象都通用的方法。我们知道,java中一切类都继承自Object,在Object类里面有
两个非常重要的方法:`hashCode()`,`equals()`;

其实Object类里面很多方法的注释非常详细,有一些比较通用的约定。作为我们在重写的时候我们最好是遵守这些约定。

如果我们要覆盖equals方法,我们需要思考几个问题:

  1. 我们为什么要覆盖?可不可以不覆盖?各自场景是什么?
  2. 怎么覆盖?
  3. 有什么需要特别注意的?

覆盖equals看起来非常简单,其实坑有蛮多。最好的避免方式当然就是不覆盖了,这样我们可以看到在Object里面的实现:

1
2
3
public boolean equals(Object obj) {
return (this == obj);
}

默认equals比较的就是类只与它自身的相等。在何种情况下我们不需要覆盖呢?书上说有四点:

  1. 类的每个实例本质上是唯一的。
  2. 不关心类是否提供“逻辑相等”的测试功能。
  3. 超类已经覆盖了equals,从超类继承过来的行为对于子类也合适。
  4. 类是私有的或是包级私有的,可以确定它的equals方法永远不会被调用。

什么时候需要覆盖,那么肯定就是上面四个的反例或者其它的情况了。一般我们需要比较逻辑上的关系时候,我们可能需要重写equals。这种被称为“值类”。值类也有不需要覆盖的场景:比如单例模式,每个值至多存在一个对象。那么比较值相等的意义就不大了;另一种就是枚举,枚举类型逻辑相同与对象等同是一个意义,因此这两个方式就算不覆盖Object的equals方法也可以。

覆盖equals时候最好也是必须遵守它的通用约定:

  1. 自反性,x.equals(x) 返回true
  2. 对称性,x.eq(y),y.eq(x) 返回true
  3. 传递性,x.eq(y),y.eq(z),x.eq(z) 返回true
  4. 一致性,多次调用x.eq(y),都应该返回true
  5. 与null进行equals(null)的时候必须返回false

高质量equals方法的建议:

  1. 使用==操作符号,查看是否当前比较参数是本身这个对象的引用。如果是返回true。
  2. 使用instanceof操作符检查“参数是否是正确的类型”,这个可以帮助我们排序非当前类型的比较,也可以排除null值。
  3. 转换instanceof之后的类型为当前this指向的对象的类型。
  4. 检查参数中的每个域,是否和该对象中对应的域相等。

对于不是float和double的基本类型可以用“==”比较,另两个调用他们的compare方法(为什么compare方法可以了?)。
另外可以将最有可能不一样的域提前比较。

覆盖equals的时候一定要一定要一定要覆盖**hashCode()**。

那么hashCode()该怎么覆盖了?下次分享。

重温Java编程思想的一些感悟-20180223

发表于 2018-02-23 | 分类于 Java | 阅读次数

重温Java编程思想的一些感悟-20180223

最近又重新翻开了这本书,翻开第一页,上面的第一句话让我有点感触。

2013年11月25日,购于亚马逊-陈志军

过得真快啊,今天是18年2月23日。岁月绕过谁?莫名的想起一句话:优于别人,并不高贵,真正的高贵应该是优于过去的自己。
今天的我,是否又优于过去的我了?

翻了一下书的目录,上面还有以前写的,哪天哪天要读那几章节。哈,我其实已经忘记我读过的这本书的内容了。

翻了下第一二三章,对象导论,一切都是对象,操作符。

使用java开发,我们通常会说一句话,万事皆对象。什么是对象?对象的好处?继承和组合的区别与联系?java的单继承结构-Object为终结父类。对象的创建和生命期。我们通常说创建一个对象在堆上,然后都是new出来的,那么基本类型的变量的空间又该如何算?也是在堆上么?

阅读全文 »

JDK-HashMap解析

发表于 2018-02-06 | 分类于 Java | 阅读次数

JDK-HashMap解析

jdk 类介绍

HashMap 是一个继承Map接口并且是使用哈希表的实现。提供了所有的map操作,并且允许null建和null值。除了非同步方式和允许null键值,它大致上可以等同于Hashtable。影响HashMap表现的有两个原因:初始容量和加载因子。

capacity:容量指的是hash表中的总容量。初始容量在第一次创建hashmap的时候就已经创建好了。
load factor: 加载因子,是测量capacity在hash表达到了多满的时候可以自动增长。

当键值对达到了加载因子的量并且超过了当前的capacity,hash表就会重新进行hash(意思是内部数据结构将重建),目的是为了将hash表能够达到差不多两倍的容量。

通常的规则是,默认的加载因子为0.75,它可以在时间和空间上做一个非常好的平衡。如果设置自定义capacity,map里面可能存在多少键值队就需要作为一个考虑因素,以达到最小的rehash操作。如果初始capacity在设置capacity之后还能存下更多的键值对,那么就不会出现rehash操作。

如果事先能大致确认map的容量是多少,那么给map定一个初始化大小是非常可行的,会比让它自己增长更有效率。在hash表中如果出现太多的相同的key拥有相同的hashCode()会降低效率,如果需要改善这种关系,如果键是可以比较的,类可以使用key的比较顺序来进行改善。

HashMap的实现是非线程安全的。在多线程的环境下,必须在外部上进行同步控制。如果外部没有控制,那么需要将map进行包装,也就是使用Collections.synchronizedMap(map)方法,最好是在一开始创建map的时候就进行包装。
map的iterators方法是fail-fast的。在iterator创建之后,如果map的结构被修改了,除了iterator的remove方法,iterator会抛出ConcurrentModificationException异常。所以在多线程环境下进行修改,iterator会快速的返回失败。不过也得注意在非同步环境下,迭代器的fail-fast不能得到保证。迭代器抛出这个异常一个非常好的功能,但是不应该依赖它来写程序,如果出现异常那么就应该说明程序有bug。

jdk的源码里面,介绍的非常详细。真的很详细。

阅读全文 »

Hibernate 校验参数

发表于 2018-01-31 | 分类于 Java | 阅读次数

Hibernater Validator 校验参数

使用方式

如果我们使用spring mvc 那么肯定知道在方法中我们可以使用注解对参数进行校验:

1
2
3
4
5
@PostMapping("/user)
public void addUser(@Valid User user){
User userP = user;
return ;
}

这个时候通常是在User对象里面使用一些注解来判断如:@NotNull,@NotEmpty。这些bean验证方法是遵循JSR303和JSR380规范的,目前的情况可以去这里查看详情:http://beanvalidation.org/2.0/。

实现规范的这些中有一个包是Hibernate Validator,这个包不是我们的ORM框架,可能是orm太出名了,以致于提到Hibernate大家都会想到SSH的Hibernate ORM,其实Hibernate Validator是一个非常完善的bean Validator。如果我们不想自己去实现一套,其实是可以引入这个包的。开源社区遇到的情况肯定会比我们自己要多。而且它也支持我们自定义注解来进行校验。

在我们的系统中,是无法采用@Valid这种方式的,因为我们有一些特别的操作处理所以不能使用它。如果你想自己使用的话,可能可以像我这样,写一个Validtor工具类,然后在需要的地方调用:

1
2
3
4
5
6
7
8
9
10
11
12
public class ValidatorUtil {
private static final Validator VALIDATOR = Validation.byProvider(HibernateValidator.class).configure().failFast(true).buildValidatorFactory().getValidator();

public static <T> void validate(T obj) {
Set<ConstraintViolation<T>> validateResult = VALIDATOR.validate(obj);
if(!validateResult.isEmpty()){
System.out.println("message:"+validateResult.iterator().next().getMessage());
System.err.println("messageKey:"+validateResult.iterator().next().getMessageTemplate());
throw new RuntimeException(String.format("参数校验失败:%s", validateResult.iterator().next().getPropertyPath().toString()+validateResult.iterator().next().getMessage()));
}
}
}

之后你可以在属性上面这样定义:

2018-01-31-20-00-07

如果需要使用校验的,只需要使用

阅读全文 »

编写属于自己的springboot-starter

发表于 2018-01-31 | 阅读次数

编写属于自己的springboot-starter

最近手痒,想实现一下自己的starter,感概于springboot的starter导入方便,要是能有一款自己的starter,那样多么好,比起之前的maven导入方式,好像更有意思。
主要可以加重对springboot的自动配置装载的理解。

实现过程

starter的简易目录如下,”autoconfiguration”,”domain”,”service”,”META-INF”:

阅读全文 »

迟到的总结

发表于 2018-01-18 | 阅读次数

转眼2017已经过去了,甚至2018也已经过去了18天。如果说今天来做一个2017的总结,我想这是扯的。最近需求已经完了,等资金账户在app的ui弄完,今年到年底过年的需求就只剩下维护了。这些天感触挺多,最大的一个感触就是2018,今年注定不是一个平静的简单的一年。

阅读全文 »

使用Logstash发送异常邮件

发表于 2018-01-09 | 分类于 ELK | 阅读次数

使用Logstash发送异常邮件

前端时间我们讲了如何使用elk搭建日志系统,以及如何使用Docker搭建ELK日志系统。虽然我们可以不用再去日志服务器找日志了,但是这样也有问题,我怎么知道什么时候会出现异常,不出现异常我也没必要去kibana查日志啊。

今天我们就要解决这个问题。当然解决的方式比较简单。如果有大神有更好的方式欢迎一起分享。

使用Logstash发送邮件

我们使用的是Logstash来发送邮件,网上我也搜了elastalert,但是感觉又多了一个服务,又要多去维护一个服务。后来发现logstash自带了邮件发送功能,那就直接用logstash就好了。

非常的简单易用,在logstash.conf中增加如下配置:

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
31
32
33
34
35
36
37
input {
beats {
host => "localhost"
port => "5043"
}
}

filter {
if [fields][doc_type] == 'order' {
grok {
match => { "message" => "%{TIMESTAMP_ISO8601:timestamp} %{LOGLEVEL:level} %{JAVALOGMESSAGE:msg}" }
}
}
}

output {
stdout { codec => rubydebug }
elasticsearch {
hosts => ["localhost:9200"]
index => "%{[fields][doc_type]}-%{+YYYY.MM.dd}"
}

if "ERROR" == [level] {
email {
to => "5228******@qq.com,chen****@163.cn"
cc => "email_chen****@163.com"
via => "smtp"
subject => "标题,ERROR: %{[fields][doc_type]}项目出现异常"
htmlbody => "消息主体:%{message}"
body => "Tags: %{tags}\\n\\Content:\\n%{message}"
from => "email_chen****@163.com"
address => "smtp.163.com"
username => "email_chen****@163.com"
password => "*****" # pop3密码或者登陆密码
}
}
}

主要是增加了下面这段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
if "ERROR" == [level] {
email {
to => "5228******@qq.com,chen****@163.cn"
cc => "email_chen****@163.com"
via => "smtp"
subject => "标题,ERROR: %{[fields][doc_type]}项目出现异常"
htmlbody => "消息主体:%{message}"
body => "Tags: %{tags}\\n\\Content:\\n%{message}"
from => "email_chen****@163.com"
address => "smtp.163.com"
username => "email_chen****@163.com"
password => "*****" # pop3密码或者登陆密码
}
}

我们只对ERROR级别的日志进行发送邮件,这里用了if条件语句。如果你看过之前的两篇文章,我想这里你是很容易就能弄懂的。当然这种方式不一定很好,如果你有更好的想法,欢迎交流。