月份:2018年11月

JAVA学习之HASHMAP底层(二)

这篇文章让我们了解下高并发下的HashMap。

HashMap随着插入的数据的增多,使得key值的冲突会越来越多,这时候HashMap就会进行扩容。

先看一下扩容的条件:

HashMap.size()>=Capacity*LoadFactor

其中HashMap.size()是HashMap的长度,Capacity是HashMap的容量,LoadFactor是负载因子(默认值为0.75f)

当满足扩容条件时,HashMap会先新建一个容量为之前2倍的Entry,然后遍历之前的Entry,重新Hash到新的数组

因为index = HashCode(key) & (length -1 ) ,新的数组长度变了,所以都得重新计算。

HashMap在多线程下会出现死循环,死循环发生在扩容的第二步,也就是重新Hash那一步,先把源码附上,以后能看懂了再分析。。。

final Node<K,V>[] resize() {
    Node<K,V>[] oldTab = table;
    int oldCap = (oldTab == null) ? 0 : oldTab.length;
    int oldThr = threshold;
    int newCap, newThr = 0;
    if (oldCap > 0) {
        if (oldCap >= MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return oldTab;
        }
        else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                 oldCap >= DEFAULT_INITIAL_CAPACITY)
            newThr = oldThr << 1; // double threshold
    }
    else if (oldThr > 0) // initial capacity was placed in threshold
        newCap = oldThr;
    else {               // zero initial threshold signifies using defaults
        newCap = DEFAULT_INITIAL_CAPACITY;
        newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
    }
    if (newThr == 0) {
        float ft = (float)newCap * loadFactor;
        newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                  (int)ft : Integer.MAX_VALUE);
    }
    threshold = newThr;
    @SuppressWarnings({"rawtypes","unchecked"})
        Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
    table = newTab;
    if (oldTab != null) {
        for (int j = 0; j < oldCap; ++j) {
            Node<K,V> e;
            if ((e = oldTab[j]) != null) {
                oldTab[j] = null;
                if (e.next == null)
                    newTab[e.hash & (newCap - 1)] = e;
                else if (e instanceof TreeNode)
                    ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                else { // preserve order
                    Node<K,V> loHead = null, loTail = null;
                    Node<K,V> hiHead = null, hiTail = null;
                    Node<K,V> next;
                    do {
                        next = e.next;
                        if ((e.hash & oldCap) == 0) {
                            if (loTail == null)
                                loHead = e;
                            else
                                loTail.next = e;
                            loTail = e;
                        }
                        else {
                            if (hiTail == null)
                                hiHead = e;
                            else
                                hiTail.next = e;
                            hiTail = e;
                        }
                    } while ((e = next) != null);
                    if (loTail != null) {
                        loTail.next = null;
                        newTab[j] = loHead;
                    }
                    if (hiTail != null) {
                        hiTail.next = null;
                        newTab[j + oldCap] = hiHead;
                    }
                }
            }
        }
    }
    return newTab;
}

MySQL必知必会(一)

和题目一样,通过《MySQL必知必会》这本书记录一下MySQL应该掌握的知识。

前提当然是你已经安装了MySQL,至于如何安装MySQL,网上也有很多,这里不做记录

1.使用MySQL
打开终端,输入:                                                               

mysql -u username -p

然后回车,输入相应的密码即可;

#显示当前所有的数据库
show databases;
#选择数据库,dataname就是你要选择的数据库名字
use dataname;
#显示当前数据库下的所有表
show tables;
#显示tablename这个表的列
show columns from tablename;
#显示tablename列的第二种方法(更加简洁)
describe tablename;

关于show语句还有很多,我们可以输入help show来显示允许使用的show的语句,同时,我们也可以使用 help  xxx 来显示所支持的xxx的相关语句。

上面几行命令简单的介绍了MySQL的基本操作;

2.检索数据

#检索单个列,表示从studnet表中查出名为id的列
select id from student;
#检索多个列,表示从student中查出名为id和name的列,多个字段用,隔开
select id,name from student;
#检索所有列,使用 * 表示返回表中所有的列
select * from student;
#去重,使用distinct可以只显示不同的id
select distinct id from student;
#限制结果,返回不多于5行的数据
select id from student limit 5;
#返回从行3开始的5行数据,第一个数字为开始位置,第二个数字为检索的行数
select id from student limit 3,5;

3.排序检索数据

#根据id排序查询出来的id
select id from student order by id;
#多个列排序,先按照id排序,如果id相同则按照name排序
select id,name from studnet order by id,name;
#指定排序顺序,asc 升序(默认)    desc 降序
select id from student order by id desc;
#使用order by 和 limit 组合可以找出最高或最低的数据
#查询年龄最大的学生的年龄
select age from student order by age desc limit 1;

4.过滤数据

where语句的使用;

#查询id为1的学生的年龄
select age from student where id = 1;

where语句常用的操作符

操作符 说明
= 等于
<> 不等于
!= 不等于
< 小于
<= 小于等于
> 大于
>= 大于等于
between 在指定的两个值之间

组合语句过滤:

#查询id为1和年龄小于20的所有学生的名字和年龄
select name,age from student where id = 1 and age < 20;
#查询id为1或者2的学生的姓名
select name from student where id =1 or id = 2;

where可以包含任意数目的and和or操作符,但是需要注意优先级,and的优先级高于or,但是一般推荐组合时使用()

#查询id为1和5的学生的年龄
select age from student where id in (1,5);
#查询除id为1、5的学生的年龄
select age from student where id not in (1,5);

用通配符进行过滤-like的用法

#查询名字以wjy开头的所有学生的信息
select * from student where name like 'wjy%';
#查询名字包含wjy的学生的信息
select * from student where name like '%wjy%';
#查询名字以w开头,y结尾的学生的信息
select * from student where name like 'w%y';
#查询名字为w_y,_代表一个字符
select * from student where name like 'w_y';

“_”“%”的区别就是:% 可以代表任意字符,_只可以匹配单个字符;

通配符的处理所花时间较长,不要过度使用。

5.函数

函数一般用在数据上的执行,给数据的转换和处理提供了方便。

常用的文本处理的函数:

函数 说明
Left() 返回串左边的字符
Length() 返回串的长度
Locate() 找出串的一个子串
Lower() 将串转为小写
LTrim() 去掉串左边的空格
Right() 返回串右边的字符
RTrim() 去掉串右边的空格
Soundex() 返回串的SOUNDEX值
SubString() 返回子串的字符
Upper()

将串转换为小写

当然还有时间处理的函数、数值处理的函数等等,这里不对这些介绍,接下来看看数据汇总的一些函数。

AVG()函数:求平均值

#求出student表中所有的年龄的平均值 as 用来表示结果的别名;
select avg(age) as avg_age from student;

COUNT() 函数:进行计数

#返回学生表中学生的总数
select count(*) as num_studnet from student;
#返回address的总数,表示表中登记了地址的学生数
select count(address) as num_student from student;

MAX()函数:返回指定列中的最大值

#返回学生中年龄最大的年龄值
select max(age) from student;

MIN()函数:返回最小值,不作示例

SUM()函数:返回指定列的和

#计算所有学生的年龄和
select sum(age) from student;

6.分组

#根据班级class_id分别统计出每个班级的学生数
select class_id,count(*) as num from student group by class_id;

MySQL也允许过滤分组,之前的where是对行进行过滤,对分组进行过滤需要使用having

#过滤出学生数大于20的班级
select class_id,count(*) as num from student group by class_id having count(*) > 20;

7.子查询

子查询就是嵌套在其他查询中的查询;

比如说有三张表客户表customers、订单表orders、订单明细表orderitems;

需要列出订购物品book121的所有客户,我们可以分为3步:

(1)在订单明细表中查出包含book121的所有订单编号;

(2)根据订单编号在订单表中查出客户id;

(3)根据客户id在客户表中查询详细信息;

#执行上面的第一步
select order_num from ordersitems where prod_id = 'book121';
#假设返回的结果是 10001,10005 两个订单号
#执行第二步,根据10001,10005查询客户id
select cust_id from orders where order_num in (10001,10005);
#假设返回的结果是 20001,20005另个客户id
#执行第三步,根据客户id查询客户信息
select cust_name,cust_contact from customers where cust_id in (20001,20005);

上面的sql语句是每一步的执行过程,这时候我们将它们嵌套一下:

select cust_name,cust_contact from customers
from customers
where cust_id in (select cust_id
                    from orders
                    where order_num in (select order_num
                                        from orderitems
                                        where prod_id = 'book121'));

JPress使用

JPress是一个用Java语言编写的开源的快速建站平台,一般用于博客的快速搭建,类似于大名鼎鼎的WordPress。

JPress官方网站:http://www.jpress.io

首先  git clone https://gitee.com/fuhai/jpress.git  下载项目

然后用shell进入项目的目录,执行mvn package 命令

image.png

执行完毕后,可以看到在start-tomcat/target下生成stater-tomcat-1.0.war 

image.png

然后我们在MySQL中新建一个数据库,运行项目目录下的db.sql,创建表;

将刚才生成的war包拷贝到tomcat的webapps目录下,然后解压。

image.png

找到WEB-INF下的classes文件夹下的jboot.properties,修改为自己的数据库配置

image.png

然后启动tomcat 就可以访问了。。。

image.png

访问 http://127.0.0.1:8080/starter-tomcat-1.0/

跳转到初始化界面

image.png

填好自己的信息后,就可以管理自己的博客了。

默认的后台界面:

image.png

默认的主页:

image.png

剩下的东西自己探索,或者参考官方的文档,写的很是详细。

Java学习之HashMap底层(一)

HashMap作为常用和常考的Java映射,有必要在详细的学习一下,结合多篇博客和资料的理解,简单记录下个人的想法。

HashMap的每一个键值对都是一个Entry,一个个的键值对(Entry)分散存储在数组中,构成了HashMap的主干,每个Entry实际上是作为一个链表的头结点,当后面的key的index冲突的时候,就在Entry之后添加一个新的节点。

面试中,我们常常回答:HashMap 的底层是数组加链表的结合体,JDK1.8中添加了红黑树。这个以后再研究。

下面我们通过一个图来直观的感受一下:

image.png

HashMap中用来处理数组索引的哈希函数是: index  = hashCode(key) & (length – 1)  其中length是HashMap 的长度。

HashMap的默认初始长度是16,扩容的阈值是0.75,每次扩容为原表数量的2倍(必须是2的幂)

当计算出来的key的index冲突后,新的key就会充当头结点,因为HashMap的发明人认为后插入的数据可能被查找的几率更大。

再谈一下为什么初始长度是16,简单的来讲是为了让index均匀分布:

根据上面的哈希函数,length-1 = 16 -1 =15,二进制为:1111 

假设有三个key的hashCode是1001、1011、1111,它们分别与1111进行& 运算

image.png

将它们再转为10进制会得到三个不同的index

当长度为10时,10-1=9,9的二进制为 1001,将上面三个hashCode再分别与1001进行&运算

image.png

这时我们发现,得到的二进制数一样,这就意味着index的重复几率也太大了,不符合均匀分布的原则,所以长度设置为16或其它2的幂。

Get方法原理:

先将key通过index = Hash(key) 得到相应的index的值,然后根据index的值查找相对应的entry,由于可能有hash冲突,所以会在链表中匹配key。

Mac IDEA快捷键记录

Mac键盘符号和修饰键说明

⌘ Command

⇧ Shift

⌥ Option

⌃ Control

↩︎ Return/Enter

⌫ Delete

⌦ 向前删除键(Fn+Delete)

↑ 上箭头

↓ 下箭头

← 左箭头

→ 右箭头

⇞ Page Up(Fn+↑)

⇟ Page Down(Fn+↓)

Home Fn + ←

End Fn + →

⇥ 右制表符(Tab键)

⇤ 左制表符(Shift+Tab)

⎋ Escape (Esc)

一、Editing(编辑)

⌃Space 基本的代码补全(补全任何类、方法、变量)

⌃⇧Space 智能代码补全(过滤器方法列表和变量的预期类型)

⌘⇧↩ 自动结束代码,行末自动添加分号

⌘P 显示方法的参数信息

⌃J, Mid. button click 快速查看文档

⇧F1 查看外部文档(在某些代码上会触发打开浏览器显示相关文档)

⌘+鼠标放在代码上 显示代码简要信息

⌘F1 在错误或警告处显示具体描述信息

⌘N, ⌃↩, ⌃N 生成代码(getter、setter、构造函数、hashCode/equals,toString)

⌃O 覆盖方法(重写父类方法)

⌃I 实现方法(实现接口中的方法)

⌘⌥T 包围代码(使用if..else, try..catch, for, synchronized等包围选中的代码)

⌘/ 注释/取消注释与行注释

⌘⌥/ 注释/取消注释与块注释

⌥↑ 连续选中代码块

⌥↓ 减少当前选中的代码块

⌃⇧Q 显示上下文信息

⌥↩ 显示意向动作和快速修复代码

⌘⌥L 格式化代码

⌃⌥O 优化import

⌃⌥I 自动缩进线

⇥ / ⇧⇥ 缩进代码 / 反缩进代码

⌘X 剪切当前行或选定的块到剪贴板

⌘C 复制当前行或选定的块到剪贴板

⌘V 从剪贴板粘贴

⌘⇧V 从最近的缓冲区粘贴

⌘D 复制当前行或选定的块

⌘⌫ 删除当前行或选定的块的行

⌃⇧J 智能的将代码拼接成一行

⌘↩ 智能的拆分拼接的行

⇧↩ 开始新的一行

⌘⇧U 大小写切换

⌘⇧] / ⌘⇧[ 选择直到代码块结束/开始

⌥⌦ 删除到单词的末尾(⌦键为Fn+Delete)

⌥⌫ 删除到单词的开头

⌘+ / ⌘- 展开 / 折叠代码块

⌘⇧+ 展开所以代码块

⌘⇧- 折叠所有代码块

⌘W 关闭活动的编辑器选项卡

二、Search/Replace(查询/替换)

Double ⇧ 查询任何东西

⌘F 文件内查找

⌘G 查找模式下,向下查找

⌘⇧G 查找模式下,向上查找

⌘R 文件内替换

⌘⇧F 全局查找(根据路径)

⌘⇧R 全局替换(根据路径)

⌘⇧S 查询结构(Ultimate Edition 版专用,需要在Keymap中设置)

⌘⇧M 替换结构(Ultimate Edition 版专用,需要在Keymap中设置)

三、Usage Search(使用查询)

⌥F7 / ⌘F7 在文件中查找用法 / 在类中查找用法

⌘⇧F7 在文件中突出显示的用法

⌘⌥F7 显示用法

四、Compile and Run(编译和运行)

⌘F9 编译Project

⌘⇧F9 编译选择的文件、包或模块

⌃⌥R 弹出 Run 的可选择菜单

⌃⌥D 弹出 Debug 的可选择菜单

⌃R 运行

⌃D 调试

⌃⇧R, ⌃⇧D 从编辑器运行上下文环境配置

五、Debugging(调试)

F8 进入下一步,如果当前行断点是一个方法,则不进入当前方法体内

F7 进入下一步,如果当前行断点是一个方法,则进入当前方法体内,如果该方法体还有方法,则不会进入该内嵌的方法中

⇧F7 智能步入,断点所在行上有多个方法调用,会弹出进入哪个方法

⇧F8 跳出

⌥F9 运行到光标处,如果光标前有其他断点会进入到该断点

⌥F8 计算表达式(可以更改变量值使其生效)

⌘⌥R 恢复程序运行,如果该断点下面代码还有断点则停在下一个断点上

⌘F8 切换断点(若光标当前行有断点则取消断点,没有则加上断点)

⌘⇧F8 查看断点信息

六、Navigation(导航)

⌘O 查找类文件

⌘⇧O 查找所有类型文件、打开文件、打开目录,打开目录需要在输入的内容前面或后面加一个反斜杠/

⌘⌥O 前往指定的变量 / 方法

⌃← / ⌃→ 左右切换打开的编辑tab页

F12 返回到前一个工具窗口

⎋ 从工具窗口进入代码文件窗口

⇧⎋ 隐藏当前或最后一个活动的窗口,且光标进入代码文件窗口

⌘⇧F4 关闭活动run/messages/find/… tab

⌘L 在当前文件跳转到某一行的指定处

⌘E 显示最近打开的文件记录列表

⌘⌥← / ⌘⌥→ 退回 / 前进到上一个操作的地方

⌘⇧⌫ 跳转到最后一个编辑的地方

⌥F1 显示当前文件选择目标弹出层,弹出层中有很多目标可以进行选择(如在代码编辑窗口可以选择显示该文件的Finder)

⌘B / ⌘ 鼠标点击 进入光标所在的方法/变量的接口或是定义处

⌘⌥B 跳转到实现处,在某个调用的方法名上使用会跳到具体的实现处,可以跳过接口

⌥ Space, ⌘Y 快速打开光标所在方法、类的定义

⌃⇧B 跳转到类型声明处

⌘U 前往当前光标所在方法的父类的方法 / 接口定义

⌃↓ / ⌃↑ 当前光标跳转到当前文件的前一个/后一个方法名位置

⌘] / ⌘[ 移动光标到当前所在代码的花括号开始/结束位置

⌘F12 弹出当前文件结构层,可以在弹出的层上直接输入进行筛选(可用于搜索类中的方法)

⌃H 显示当前类的层次结构

⌘⇧H 显示方法层次结构

⌃⌥H 显示调用层次结构

F2 / ⇧F2 跳转到下一个/上一个突出错误或警告的位置

F4 / ⌘↓ 编辑/查看代码源

⌥ Home 显示到当前文件的导航条

F3选中文件/文件夹/代码行,添加/取消书签

⌥F3 选中文件/文件夹/代码行,使用助记符添加/取消书签

⌃0…⌃9 定位到对应数值的书签位置

⌘F3 显示所有书签

七、Refactoring(重构)

F5 复制文件到指定目录

F6 移动文件到指定目录

⌘⌫ 在文件上为安全删除文件,弹出确认框

⇧F6 重命名文件

⌘F6 更改签名

⌘⌥N 一致性

⌘⌥M 将选中的代码提取为方法

⌘⌥V 提取变量

⌘⌥F 提取字段

⌘⌥C 提取常量

⌘⌥P 提取参数

八、VCS/Local History(版本控制/本地历史记录)

⌘K 提交代码到版本控制器

⌘T 从版本控制器更新代码

⌥⇧C 查看最近的变更记录

⌃C 快速弹出版本控制器操作面板

九、Live Templates(动态代码模板)

⌘⌥J 弹出模板选择窗口,将选定的代码使用动态模板包住

⌘J 插入自定义动态代码模板

十、General(通用)

⌘1…⌘9 打开相应编号的工具窗口

⌘S 保存所有

⌘⌥Y 同步、刷新

⌃⌘F 切换全屏模式

⌘⇧F12 切换最大化编辑器

⌥⇧F 添加到收藏夹

⌥⇧I 检查当前文件与当前的配置文件

§⌃, ⌃` 快速切换当前的scheme(切换主题、代码样式等)

⌘, 打开IDEA系统设置

⌘; 打开项目结构对话框

⇧⌘A 查找动作(可设置相关选项)

⌃⇥ 编辑窗口标签和工具窗口之间切换(如果在切换的过程加按上delete,则是关闭对应选中的窗口)

十一、Other(一些官方文档上没有体现的快捷键)

⌘⇧8 竖编辑模式

 

导航

⌘O 查找类文件 Ctrl + N

⌘⌥O 前往指定的变量 / 方法 Ctrl + Shift + Alt + N

⌃← / ⌃→ 左右切换打开的编辑tab页 Alt← / Alt→

⎋ 从工具窗口进入代码文件窗口 ESC

⌘L 在当前文件跳转到某一行的指定处 Ctrl + G

⌘E 显示最近打开的文件记录列表 Ctrl + E

⌘⌥← / ⌘⌥→ 退回 / 前进到上一个操作的地方 Ctrl + Alt + ← Ctrl + Alt + →

⌘⇧⌫ 跳转到最后一个编辑的地方

⌃H 显示当前类的层次结构 Ctrl + H

⌘⇧H 显示方法层次结构

⌃⌥H 显示调用层次结构

F4 / ⌘↓ 编辑/查看代码源

⌘⌥U 显示类UML图

⌃J 查看注释

编辑

⌥⌦ 删除到单词的末尾(⌦键为Fn+Delete)

⌥⌫ 删除到单词的开头

⌘+ / ⌘- 展开 / 折叠代码块

⌘F1 在错误或警告处显示具体描述信息

⌘⌥L 格式化代码

⌃⌥O 优化import

⇧↩ 开始新的一行

⌘⇧↩ 自动结束代码,行末自动添加分号

⌃I 实现方法(实现接口中的方法)

⇧F6 重命名文件或者变量

⌘N, ⌃↩, ⌃N 生成代码(getter、setter、构造函数、hashCode/equals,toString)

⌘P 显示方法的参数信息

查找

Double⇧ 查找任何东西

⌘⇧F 全局查找(根据路径)

⌘F 文件内查找

⌘G 查找模式下,向下查找

⌘⇧G 查找模式下,向上查找

导航

⌘⌥B 跳转到接口的实现

⌘U 查看接口定义

⌘⌥← / ⌘⌥→ 退回 / 前进到上一个操作的地方

⌘B / ⌘ 鼠标点击 进入光标所在的方法/变量的接口或是定义处

⌃⇧B 跳转到类型声明处

⌥ Space, ⌘Y 快速打开光标所在方法、类的定义

⌘O 查找类文件

⌘⇧O 查找所有类型文件、打开文件、打开目录,打开目录需要在输入的内容前面或后面加一个反斜杠/

F12 返回到前一个工具窗口

⎋ 从工具窗口进入代码文件窗口

⇧⎋ 隐藏当前或最后一个活动的窗口,且光标进入代码文件窗口

F3选中文件/文件夹/代码行,添加/取消书签

⌥F3 选中文件/文件夹/代码行,使用助记符添加/取消书签

⌃0…⌃9 定位到对应数值的书签位置

⌘F3 显示所有书签

⌥F1 显示当前文件选择目标弹出层,弹出层中有很多目标可以进行选择(如在代码编辑窗口可以选择显示该文件的Finder)

⌘F12 弹出当前文件结构层,可以在弹出的层上直接输入进行筛选(可用于搜索类中的方法)

通用

⌃⌘F 切换全屏模式

Java学习之异常

今天复习一下Java的异常,先看下图:

image.png

我们从图中可以看出Java中异常的顶级类是Throwable,所有的异常都继承于这个类;

Error和Exception是异常类的两个大的分类;

Error是非程序异常,是程序不能捕捉的异常,一般是编译或者是系统性的错误;

Exception是程序异常类,由程序内部产生;Exception又分为运行时异常(非检查性异常)和非运行时异常(检查性异常)。

Java中一般分为3种类型的异常:

  • 非运行时性异常:最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。例:IOException…

  • 运行时异常: 运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。例:NullPointerException…

  • 错误: 错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译也检查不到的。

日志框架-SLF4J+Logback

SLF4J全称:Simple Logging Facade for Java,即简单日志门面它是把不同的日志系统的实现进行了具体的抽象化,只提供了统一的日志使用接口,使用时只需要按照其提供的接口方法进行调用即可,由于它只是一个接口,并不是一个具体的可以直接单独使用的日志框架,所以最终日志的格式、记录级别、输出方式等都要通过接口绑定的具体的日志系统来实现,这些具体的日志系统就有log4j,logback,java.util.logging等,它们才实现了具体的日志系统的功能。


简单的说,SLF4J提供了日志的接口,日志的实现需要其它具体的框架来实现;

今天我就记录下SLF4J+Logback的组合来提供日志的框架。

新建一个Springboot的工程,Springboot中已经提供了SLF4J,所以我们不需要添加它的相关依赖;

工程目录结构:

image.png


在测试类中编写测试代码:

package com.wjy329.logdemo;/**
 * @Author wjy329
 * @Time 2018/11/68:53 PM
 * @description
 */

import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class logTest {

    private final Logger logger = LoggerFactory.getLogger(logTest.class);

    @Test
    public void testLog(){
        logger.info("这是info日志");
        logger.error("这是错误日志");
        logger.debug("这是debug日志");
    }
}

控制台输出的结果是:

image.png

我们可以看到info和error级别的日志输出了,原因是默认的级别是info级别,所以debug级别的日志就没有打印。

日志的级别:ERROR>WARN>INFO>DEBUG>TRACE   级别越高优先级越高,当级别为info时,优先级比info低的debug和trace就不打印了,当级别为error时,只会打印error级别的日志。

上面仅仅是日志的简单使用,下面我们介绍一般在项目中是如何配置日志的。

在resource目录下新建一个日志的配置文件,我这里叫logback.xml  

image.png

具体的代码:

<?xml version="1.0" encoding="utf-8" ?>

<configuration>
    <!-- 输出到控制台的日志 -->
    <appender name="consoleLog" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>
                %d - %msg%n
            </pattern>
        </encoder>

    </appender>

    <!-- 写出到日志文件 -->
    <appender name="testLog" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <!-- 滚动策略 -->
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 输出路径 -->
            <fileNamePattern>/Users/wjy329/desktop/testLog/wjy329-%d{yyyy-MM-dd}.log</fileNamePattern>
        </rollingPolicy>
        <encoder>
            <pattern>
                %d - %msg%n
            </pattern>
        </encoder>
    </appender>
    <!-- 日志级别 -->
    <root level="info">
        <appender-ref ref="consoleLog"/>
        <appender-ref ref="testLog"/>
    </root>

</configuration>

然后我们运行测试代码:

控制台输出如下:

image.png

输出到日志文件如下:

image.png

上面就是简单的演示日志系统的使用,后面如果遇到更详细的东西也会继续更新。

深入理解Java虚拟机(一)

准备利用空闲看一些技术书,先从深入理解java虚拟及开始,希望可以从中收获一些东西,顺便也记录一些东西。

一、

JRE: Java Runtime Environment
JDK:Java Development Kit

JRE即Java运行环境,可以理解为Java运行环境 。

JDK即Java开发工具包,Java开发人员使用的工具包,包括Java运行环境和开发环境等,JDK包括JRE。

二、

Java栈-存放基本数据类型和对象的引用

Java堆-存放对象实例

方法区-各个线程共享的内存区域,存储虚拟机加载的类信息、常量、静态变量、编译后的代码等。

运行时常量池-存放编译期生成的各种字面量和符号引用

对象的创建-在对象的创建过程中,即new;首先去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载、解析和初始化过。上面我们知道堆存放对象实例,也就是new对象的时候会使用堆内存。

内存分配方式:1、指针碰撞-假设Java堆中内存是绝对规整的,所有用过的内存放在一边,空闲的内存放在另一边,中间放着一个指针作为分界点的指示器,所分配的内存就仅仅是把那个指针向空闲空间那边挪动一段与对象大小相等的距离,这种方式就叫指针碰撞。

2、空闲列表-如果Java堆中的内存并不是规整的,已使用的内存和空闲的内存相互交错,那就没有办法简单的进行指针碰撞了,虚拟机就必须维护一个列表,记录上哪些内存是可用的,在分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录,这种分配方式称为空闲列表。

以上内容为18.7.22号写的,现在更新一下。

=================================================================

image.png

上图是Java虚拟机运行时数据区的图示

链接:https://www.nowcoder.com/questionTerminal/8e62ee8f66284ea3ac407197acc18a39

来源:牛客网

1、程序计数器: 线程私有,是当前线程所执行的字节码的行号指示器,如果线程正执行一个java方法,计数器记录正在执行的虚拟机字节码指令的地址,如果线程正在执行的是Native方法,则计数器值为空;

2、虚拟机栈: 即栈区, 线程私有 ,为虚拟机执行 Java 方法(字节码)服务,每个方法在执行的时会创建一个栈帧用于存放局部变量表、操作数栈、动态链接和方法出口等信息,每个方法的调用直至执行完成对应于栈帧的入栈和出栈;

3、本地方法栈: 为虚拟机使用的 N ative 方法服务,也是 线程私有 ;

4、Java 堆: 在虚拟机启动时创建, 线程共享 ,唯一目的是存放对象实例,是垃圾收集器管理的主要区域——” GC 堆“,可以细分为新生代和老年代,新生代又可以细分为 Eden 空间、 From Survivor 空间和 To Survivor 空间;物理上可以不连续,但逻辑上连续,可以选择固定大小或者扩展;

5、方法区: 线程共享 ,用于存储被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。被称为“永久代”,是因为 H otSpot 虚拟机的设计团队把 GC 分代收集扩展到方法区,即使用永久代来实现方法区,像 GC 管理 Java 堆一样管理方法区,从而省去专门为方法区编写内存管理代码,内存回收目标是针对常量池的回收和堆类型的卸载;

6、运行时常量池: 线程共享 ,是方法区的一部分, C lass 文件中存放编译期生成的各种字面量和符号引用,类加载后进入方法区的运行时常量池中。

image.png