程序员Sherwin

有温度的程序员


  • 首页

  • 关于

  • 标签

  • 分类

  • java高级系列

  • java基础

  • java面试

  • 归档

  • 读书

  • 电影

  • 搜索

3.LinkedHashMap的实现原理

发表于 2018-06-04 | 更新于 2019-06-13 | 分类于 java高级系列

1. LinkedHashMap概述:

    LinkedHashMap是Map接口的哈希表和链接列表实现,具有可预知的迭代顺序。此实现提供所有可选的映射操作,并允许使用null值和null键。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。

    LinkedHashMap实现与HashMap的不同之处在于,前者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,该迭代顺序可以是插入顺序或者是访问顺序。

  • 注意,此实现不是同步的。如果多个线程同时访问链接的哈希映射,而其中至少一个线程从结构上修改了该映射,则它必须保持外部同步。
    阅读全文 »

2.HashMap的实现原理

发表于 2018-06-02 | 更新于 2019-06-13 | 分类于 java高级系列

1.HashMap概述:

    HashMap是基于哈希表的Map接口的非同步实现。此实现提供所有可选的映射操作,并允许使用null值和null键。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。

2.HashMap的数据结构:

   在java编程语言中,最基本的结构就是两种,一个是数组,另外一个是模拟指针(引用),所有的数据结构都可以用这两个基本结构来构造的,HashMap也不例外。HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。

阅读全文 »

1.ArrayList的实现原理

发表于 2018-06-01 | 更新于 2019-06-13 | 分类于 java高级系列

1. ArrayList概述:

   ArrayList是List接口的可变数组的实现。实现了所有可选列表操作,并允许包括 null 在内的所有元素。除了实现 List 接口外,此类还提供一些方法来操作内部用来存储列表的数组的大小。

   每个ArrayList实例都有一个容量,该容量是指用来存储列表元素的数组的大小。它总是至少等于列表的大小。随着向ArrayList中不断添加元素,其容量也自动增长。自动增长会带来数据向新数组的重新拷贝,因此,如果可预知数据量的多少,可在构造ArrayList时指定其容量。在添加大量元素前,应用程序也可以使用ensureCapacity操作来增加ArrayList实例的容量,这可以减少递增式再分配的数量。

  • 注意,此实现不是同步的。如果多个线程同时访问一个ArrayList实例,而其中至少一个线程从结构上修改了列表,那么它必须保持外部同步。
    阅读全文 »

5.编译器自动优化导致代码可以先后写的问题

发表于 2018-05-24 | 更新于 2019-06-13 | 分类于 java基础
1
2
3
4
5
6
7
8
9
10
11
public class Demo1 {
static {
test = 1;
// System.out.println(test);
}
static int test;

public static void main(String[] args) {
System.out.println("Hello World!");
}
}

其实上面的代码,编译器会自动编译为:

1
2
3
4
5
6
7
8
9
10
11
import java.io.PrintStream;

public class Demo1
{
static int test = 1;

public static void main(String[] args)
{
System.out.println("Hello World!");
}
}

所以导致没有定义的,也可以先赋值。

  • 注意问题:要有static的包围着,static会让编译器自动优化。

3.JVM内存区域详解(Eden Space、Survivor Space、Old Gen、Code Cache和Perm Gen)

发表于 2018-04-18 | 更新于 2019-06-13 | 分类于 java基础

JVM区域总体分两类,heap区和非heap区。

  • heap区又分为:

    • Eden Space(伊甸园)、
    • Survivor Space(幸存者区)、
    • Old Gen(老年代)。
  • 非heap区又分:

    • Code Cache(代码缓存区);
    • Perm Gen(永久代);
    • Jvm Stack(java虚拟机栈);
    • Local Method Statck(本地方法栈);

下面我们对每一个内存区域做详细介绍。

阅读全文 »

程序员最艰巨的十大任务

发表于 2018-04-11 | 更新于 2019-01-23 | 分类于 生活

程序员最艰巨的十大任务

程序员最艰巨的任务跟编写代码没有多少关系。编码是逻辑思路的一种实践,这跟程序员日常工作中的其它任务比起来相对简单。如果你认为自己还是一个水平一般的程序员,在你真正的能进入到高手行列前,请确保你已经克服了下列晋级的障碍。

  • 解释你在干什么

解释软件开发过程是一个很困难的事情。那些非程序员职业的人也许知道很多关于编程的事情,但很显然,他们不会编程。对于他们来说,我们的生活就是在一间黑暗的屋子里趴在键盘前消耗着咖啡。

你会在你的朋友、家人和同事中遇到这样的人,他会认为编码不是一个正确的职业。

  • 形象的说出软件解决方案

根据一些简短的需求——通常是一知半解的,你需要设计出数据结构,软件架构,代码算法,通信协议,以及其它所有针对商业问题的解决方案各种组成部分。然后你需要用一种外行人听的懂的术语将它们表达出来,并需要在规定的时间里提交给客户。

很少有程序员能做好这些。

  • 评估工期

这是程序员痛苦的根源。在开发任务没有完成之前,你是绝对没有可能确定完成这个任务需要的时间。也许程序跟以前写的很相似,但环境变了,问题变了,限制条件变了。

经验会提供一定的判断力,但大部分的程序员都习惯于低估问题难度。这其中的原因是他们只考虑编码方面的因素,而忽略了这个任务清单上的其它事务。

  • 维护他人的代码

针对一个问题可能会有一万种解决方案,一万种写法。接手别人写的代码,意味着你要花无数的时间在成千上万的代码行里探索,理解当初作者的思路。而且,如果是一个不相信注释和文档的程序员留下的半个项目,麻烦就更大了。

  • 软件边界的模糊蔓延和让人吐血的奇怪功能需求

虽 然敏捷开发方法给软件范围的膨胀提供了一定的预备空间,但这并没有起到任何的作用——尤其是当你遇到一些由一时兴起的怪念头产生的功能需求。你知道这样做 必定会失败。你的团队知道这样做必定会失败。但客户觉得很好,而当失败不可避免的出现时,全是你的错,因为是你没有理解他们的真实意图。

  • 在缺少优化和过度优化之间找到平衡点

复杂的软件永远不会做到完美;总会有一些更好的方案。你完全可以没完没了的优化下去,这就是为什么软件项目从来都没有提前完工的。

而另一面,“这样就行了——我以后会优化它的”这种心态也是常见的。代码今天好用,但你知道明天可能会出现麻烦或不能用。当然了,你是不需要去修改它的,它将会留给下一个倒霉蛋程序员。

  • 测试你的代码

单元测试你也写了,软件也提交了测试组,但bug依旧存在…

软件是复杂的,可能包含成千上万行代码。系统中可能存在百万的各种交互和逻辑路径;你不可能完全测试它们。
类似的,软件会在不同的条件下跟不同的平台上的不同的软件交互。你不可能所有的都测到。
写出好的单元测试是一种枯燥且辛苦的工作。理想情况下,测试应该在着手开发前就已经写好——但你如何向客户解释为什么四个星期过去了仍然没有可用的软件?
单元测试并不能覆盖每个问题点。在理想的世界里,应该有一个独立的团队来写测试并积极的去发现问题。不幸的是,对大多数项目来说,这样成本太高,时间不够,于是用开发团队来写测试程序。而开发团队潜意识的会避免很多极端的边界情况。
程序员喜欢用符合逻辑的方式处理所有问题。但用户很少是这样的。他们会发现你永远意想不到的问题。

  • 写软件文档

给代码写文档是一项费力耗时的工作。很少有程序员擅长这个、喜欢这个的,并且很少有程序员会花时间去读它们。

  • 处理IT问题

你每天都在研究技术。你也许是一个HTML或PHP程序员,但你很可能会遇到一些例如硬盘损坏、驱动冲突或软件崩溃的问题。解决这些事情不是你的主要责任,但是,除非你解决了这些问题,否者你将无法继续你的开发工作。

不幸的是,对于IT圈外的人来说,程序员应该是软硬件都精通的人。当他们遇到了问题,他们自己不花时间就解决,直接会找你。不论是遇到什么问题:你是用计算机的,你一定知道如何将预算表导入Sage,如何配置Oracle,或为何在他们的黑莓手机上发不出邮件。

当然了,这些打搅绝对不能成为你完不成工作的理由,也没有报酬,不是吗?

  • 处理人的问题

上面的这些难题都可以总结为“人的问题”。很少有外行人会去建议一个飞行员如何开飞机或建议一个电器工程师如何布线。但很多人却会兴致勃勃的勇敢的建议如何开发软件。

我相信对于这些人没有什么好办法。你需要接受这样的事实:这世界上有一半的智力是低于平均水平的!

[英文原文:The Ten Toughest Tasks in Development ]

1.String的split小记

发表于 2018-03-30 | 更新于 2019-06-13 | 分类于 java基础
1
2
3
4
5
6
7
public class SplitDemo {
public static void main(String[] args) {
String a = "abcooob";
String[] as = a.split("o");
System.out.println(as.length);
}
}

运行结果是

4
abc b

因为分割成{“abc”,””,””,”b”}的值,这个正常理解。

1
2
3
4
5
6
7
8
9
10
public class SplitDemo {
public static void main(String[] args) {
String a = "abcooo";
String[] as = a.split("o");
System.out.println(as.length);
for (String string : as) {
System.out.print(string+"\t");
}
}
}

这个运行结果是:

1

abc

为什么呢?

看api

image

split
public String[] split(String regex)
根据给定正则表达式的匹配拆分此字符串。

该方法的作用就像是使用给定的表达式和限制参数 0 来调用两参数 split 方法。因此,所得数组中不包括结尾空字符串。

例如,字符串 “boo:and:foo” 使用这些表达式可生成以下结果:

Regex 结果
: { “boo”, “and”, “foo” }
o { “b”, “”, “:and:f” }

参数:

regex - 定界正则表达式

返回:

字符串数组,它是根据给定正则表达式的匹配拆分此字符串确定的

抛出:

PatternSyntaxException - 如果正则表达式的语法无效


结论:split分割所得数组中不包括结尾空字符串。


2.强引用、软引用、弱引用、虚引用

发表于 2018-03-23 | 更新于 2019-06-13 | 分类于 java基础

Java四种引用包括强引用,软引用,弱引用,虚引用。

强引用:

只要引用存在,垃圾回收器永远不会回收
Object obj = new Object();
//可直接通过obj取得对应的对象 如obj.equels(new Object());
而这样 obj对象对后面new Object的一个强引用,只有当obj这个引用被释放之后,对象才会被释放掉,这也是我们经常所用到的编码形式。

软引用:

非必须引用,内存溢出之前进行回收,可以通过以下代码实现
Object obj = new Object();
SoftReference sf = new SoftReference(obj);
obj = null;
sf.get();//有时候会返回null
这时候sf是对obj的一个软引用,通过sf.get()方法可以取到这个对象,当然,当这个对象被标记为需要回收的对象时,则返回null;
软引用主要用户实现类似缓存的功能,在内存足够的情况下直接通过软引用取值,无需从繁忙的真实来源查询数据,提升速度;当内存不足时,自动删除这部分缓存数据,从真正的来源查询这些数据。

弱引用:

第二次垃圾回收时回收,可以通过如下代码实现
Object obj = new Object();
WeakReference wf = new WeakReference(obj);
obj = null;
wf.get();//有时候会返回null
wf.isEnQueued();//返回是否被垃圾回收器标记为即将回收的垃圾
弱引用是在第二次垃圾回收时回收,短时间内通过弱引用取对应的数据,可以取到,当执行过第二次垃圾回收时,将返回null。
弱引用主要用于监控对象是否已经被垃圾回收器标记为即将回收的垃圾,可以通过弱引用的isEnQueued方法返回对象是否被垃圾回收器标记。

虚引用:

垃圾回收时回收,无法通过引用取到对象值,可以通过如下代码实现
Object obj = new Object();
PhantomReference pf = new PhantomReference(obj);
obj=null;
pf.get();//永远返回null
pf.isEnQueued();//返回是否从内存中已经删除
虚引用是每次垃圾回收的时候都会被回收,通过虚引用的get方法永远获取到的数据为null,因此也被成为幽灵引用。
虚引用主要用于检测对象是否已经从内存中删除。

centos安装常用工具

发表于 2018-03-15 | 更新于 2019-01-23 | 分类于 linux

1、jdk安装

去官网找到最新或者想要下载的版本的连接。通过wget获取。推荐用tar方式安装。

解压
tar -xvzf jdk-7u25-linux-x64.tar.gz -C /opt/jvm/
cd /opt/jvm
重命名
mv jdk... jdk7
vi /etc/profile
插入
#java env
JAVA_HOME=/home/gcs/user/java/jdk1.7.0_67
PATH=$JAVA_HOME/bin:$PATH
CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
export JAVA_HOME
export PATH
export CLASSPATH
生效
source /etc/profile
检查
java -version
javac

2、安装mysql5.7

1、配置YUM源

在MySQL官网中下载YUM源rpm安装包:http://dev.mysql.com/downloads/repo/yum/
(可以去官网找最新的)
下载mysql源安装包shell> wget http://dev.mysql.com/get/mysql57-community-release-el7-8.noarch.rpm
检查mysql源是否安装成功
shell> yum repolist enabled | grep “mysql.-community.“
看到上图所示表示安装成功

2、安装MySQL

shell> yum install mysql-community-server

3、启动MySQL服务

  • shell> systemctl start mysqld
  • 查看MySQL的启动状态

  • shell> systemctl status mysqld

  • mysqld.service - MySQL Server Loaded: loaded (/usr/lib/systemd/system/mysqld.service; disabled; vendor preset: disabled) Active: active (running) since 五 2016-06-2404:37:37 CST; 35min ago Main PID: 2888 (mysqld) CGroup: /system.slice/mysqld.service └─2888 /usr/sbin/mysqld –daemonize –pid-file=/var/run/mysqld/mysqld.pid6月 2404:37:36 localhost.localdomain systemd[1]: Starting MySQL Server…6月 2404:37:37 localhost.localdomain systemd[1]: Started MySQL Server.

    4、开机启动

shell> systemctl enable mysqldshell> systemctl daemon-reload

5、修改root默认密码

mysql安装完成之后,在/var/log/mysqld.log文件中给root生成了一个默认密码。通过下面的方式找到root默认密码,然后登录mysql进行修改:

shell> grep 'temporary password' /var/log/mysqld.log

shell> mysql -uroot-pmysql> ALTER USER 'root'@'localhost' IDENTIFIED BY'MyNewPass4!'; 

或者

mysql> set password for'root'@'localhost'=password('MyNewPass4!'); 

注意:mysql5.7默认安装了密码安全检查插件(validate_password),默认密码检查策略要求密码必须包含:大小写字母、数字和特殊符号,并且长度不能少于8位。否则会提示ERROR 1819 (HY000): Your password does not satisfy the current policy requirements错误,如下图所示:

通过msyql环境变量可以查看密码策略的相关信息:

mysql> show variables like'%password%';

validate_password_policy:密码策略,默认为MEDIUM策略 validate_password_dictionary_file:密码策略文件,策略为STRONG才需要 validate_password_length:密码最少长度 validate_password_mixed_case_count:大小写字符长度,至少1个 validate_password_number_count :数字至少1个 validate_password_special_char_count:特殊字符至少1个 上述参数是默认策略MEDIUM的密码检查规则。
共有以下几种密码策略:
策略 检查规则
0 or LOW Length
1 or MEDIUM Length; numeric, lowercase/uppercase, and special characters
2 or STRONG Length; numeric, lowercase/uppercase, and special characters; dictionary file
MySQL官网密码策略详细说明:
http://dev.mysql.com/doc/refman/5.7/en/validate-password-options-variables.html#sysvar_validate_password_policy

修改密码策略

在/etc/my.cnf文件添加validate_password_policy配置,指定密码策略

  • 选择0(LOW),1(MEDIUM),2(STRONG)其中一种,选择2需要提供密码字典文件validate_password_policy=0
    如果不需要密码策略,添加my.cnf文件中添加如下配置禁用即可:
    validate_password = off
    重新启动mysql服务使配置生效:

    systemctl restart mysqld

6、添加远程登录用户

默认只允许root帐户在本地登录,如果要在其它机器上连接mysql,必须修改root允许远程连接,或者添加一个允许远程连接的帐户,为了安全起见,我添加一个新的帐户:

mysql> GRANT ALL PRIVILEGES ON*.*TO'yangxin'@'%' IDENTIFIED BY'Yangxin0917!'WITH GRANT OPTION;

7、配置默认编码为utf8

修改/etc/my.cnf配置文件,在[mysqld]下添加编码配置,如下所示:

[mysqld]character_set_server=utf8init_connect='SET NAMES utf8'

重新启动mysql服务,

systemctl restart mysqld

查看数据库默认编码如下所示:

默认配置文件路径: 配置文件:/etc/my.cnf 日志文件:/var/log//var/log/mysqld.log 服务启动脚本:/usr/lib/systemd/system/mysqld.service socket文件:/var/run/mysqld/mysqld.pid

4.mybatis3 中provider的使用

发表于 2018-03-15 | 更新于 2019-06-13 | 分类于 java基础

mybatis3提供了4个**provider注解,分别对应增删改查,分别是:InsertProvider、DeleteProvider、UpdateProvider、SelectProvider;如何使用这些注解呢?

1.这些注解都有统一的2个入参,一个是type,一个是method。type参数的值是你动态sql的类(A)class文件,method是类(A)中的方法名。当然,这些方法都是动态sql,至于动态sql怎么实现,这就可以发挥你的java基础能力,我是结合了自定义注解和java反射基础知识实现了动态sql。下面结合代码讲解一下实现过程。

2.首先你需要定义一个接口类,这个类提供各种各样的增删改查方法。

下面以@SelectProvider为例,依次描述几种典型的使用场景。

1.使用@SelectProvider
@SelectProvider是声明在方法基本上的,这个方法定义在Mapper对应的的interface上。

1
2
3
4
5
public interface UserMapper {
@SelectProvider(type = SqlProvider.class, method = "selectUser")
@ResultMap("userMap")
public User getUser(long userId);
}

上例中是个很简单的Mapper接口,其中定义了一个方法:getUser,这个方法根据提供的用户id来查询用户信息,并返回一个User实体bean。
这是一个很简单很常用的查询场景:根据key来查询记录并将结果封装成实体bean。其中:
@SelectProvider注解用于生成查询用的sql语句,有别于@Select注解,@SelectProvide指定一个Class及其方法,并且通过调用Class上的这个方法来获得sql语句。在我们这个例子中,获取查询sql的方法是SqlProvider.selectUser。
@ResultMap注解用于从查询结果集RecordSet中取数据然后拼装实体bean。

2.定义拼装sql的类
@SelectProvide中type参数指定的Class类,必须要能够通过无参的构造函数来初始化。
@SelectProvide中method参数指定的方法,必须是public的,返回值必须为String,可以为static。

1
2
3
4
5
public class SqlProvider {
public String selectUser(long userId) {
return "select * from user where userId=" + userId;
}
}

上面的就是个简单使用,就可以在mybatis中拼接sql了,有这个之后就可以改造mybatis用实体来查询了,就像hibernate一样了。

附加几个mybatis中不常用但很有意思的OGNL用法。

  • e.method(args) : 调用对象的方法
  • e.property: 对象属性值,可以多层嵌套使用,
  • e1[e2] :按索引取值(list,数组和map,map使用的时候注意key一样的存在,不存在会报错的)
  • @class@method(args):调用类的静态方法
  • @class@field:调用类的静态字段值
1234
Sherwin Wei

Sherwin Wei

做一个有温度有梦想的程序员,坚持到底.
39 日志
8 分类
13 标签
GitHub Weibo
友情链接
  • sherwin博客
粤ICP备15018188号-1 © 2018 – 2019 Sherwin Wei
|