Eric Chang's Blog


  • 首页

  • 归档

  • 分类

  • 标签

  • 生活

  • 关于

  • 搜索

九宫格样式的实现

发表于 2016-03-09 | 分类于 Web |

九宫格样式

  • 页面显示九宫格,鼠标放在哪一个格子上,相应的格子的外边框变色;
  • 具体效果如下:

suduku

  • 下面先贴出代码:
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
38
39
40
41
42
43
<html>
<head>
<style>
#outer>div{
width: 100px;
height: 100px;
float: left;
border:5px solid blue;
text-align: center;
line-height: 100px;
position: relative;
}


#outer>div:hover{
border:5px solid red;
z-index:999;
}


.middle{
margin-left: -5px;
margin-right: -5px;
}

.middle1{
margin-top: -5px;
margin-bottom: -5px;
}


</style>

</head>

<body>
<div style="width:320px; height:320px;background-color:#eee" id="outer">
<div>1</div>
<div class="middle">2</div>
<div>3</div>
<div class="middle1">4</div>
<div class="middle middle1">5</div>
<div class="middle1">6</div>
<div>7</div>
<div class="middle">8</div>
<div>9</div>
</div>
</body>

研究

  • div换行问题:
    • <div>是块级元素,前后有换行符,如果不加任何设置,九个div将垂直排列在页面;
    • 解决方法是,先设置好外层、内层div的宽高,然后对内层div加入float: left;属性,使其向左浮动,去掉div前后的换行符,由于外层div有宽度限制,内层div在达到外层div宽度后,会自动换行,实现九宫格基本样式;
  • 文字的居中问题:
    • 对于文字的左右居中,不需要使用position属性配合left、margin-left属性来设置,只需用特定作用于文字的属性text-align: center;即可,这个属性不仅可以作用于文字,而且还能作用于很多块级元素,美中不足的是,如果父元素设置了text-align属性,它的所有子元素将继承这个属性的值;
    • 对于文字的上下居中,本来也想使用position属性配合left、margin-left属性来设置,但看到网上有牛人直接用line-height就实现了,这个属性用于设置行间距,由于九宫格中的文字只有一行,所以将line-height设置为div的高度即可实现垂直居中效果;
  • 负外边距的问题:
    • 如果不对.middle和.middle1类设置负外边距,效果是下面的样子,即处于上下中间或左右中间的格子,只对自己的外边框变化了颜色,与我们要求的效果有差距;
    • 通过将2、5、8格子的左右的margin设置为负值,4、5、6格子的上下的margin设置为负值,可有效避免相邻格子间的外边距叠加的问题;

suduku_error1

  • 将格子的position属性设置为relative,并且将鼠标悬浮时的格子的z-index属性设置为较大的值,可保证鼠标悬浮时,当前格子总是处于最高的层叠位置,否则会出现下图的情况:

suduku_error2

几种排序算法的实现

发表于 2016-01-05 | 分类于 Algorithm |

引言

  • 排序算法是重要的基础算法;
  • 排序算法的效率至关重要;
  • 任何算法的空间效率和时间效率不可兼得,所以没有最好的算法,只有最合适的算法;
  • 本文总结了几种排序算法,并分析了他们的适用场景,使用的语言为java和js;

选择排序(selection sort)

  • 思想:对于一个数组a,找出数组中的最小元素并和a[0]交换,然后忽略a[0],再次找出数组中的最小元素并与a[1]交换,以此类推;
  • java实现:
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
package algorithm;

public class SelectionSort {

public static void main(String[] args) {
int[] nums = { 1, 54, 6, 3, 78, 34, 12, 45 };
sort(nums);
for (int i = 0; i < nums.length; i++) {
System.out.println(nums[i]);
}
}

public static void sort(int[] myArray) {
for (int i = 0; i < myArray.length; i++) {
int temp = myArray[i];
int position = i;
for (int j = i + 1; j < myArray.length; j++) {
if (myArray[j] < temp) {
temp = myArray[j];
position = j;
}
}
myArray[position] = myArray[i];
myArray[i] = temp;
}
}
}
  • js实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
var nums = [1, 54, 6, 3, 78, 34, 12, 45];
var selectionSort = function(nums){
for(var i=0; i<nums.length; i++){
var temp = nums[i];
var position = i;
for(var j=i; j<nums.length; j++){
if(nums[j]<temp){
temp = nums[j];
position = j;
}
}
nums[position] = nums[i];
nums[i] = temp;
}
}

selectionSort(nums);
for(var number in nums){
console.log(nums[number]);
}

插入排序(Insertion sort)

  • 思想:从数组的第二个元素开始,将数组中的每一个元素按照规则插入到已排好序的数组中以达到排序的目的;
  • java排序:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package algorithm;

public class InsertionSort {

public static void main(String[] args) {
int[] nums = { 1, 54, 6, 3, 78, 34, 12, 45 };
sort(nums);
for (int i = 0; i < nums.length; i++) {
System.out.println(nums[i]);
}
}

public static void sort(int[] myArray) {
int temp = 0;
for (int i = 1; i < myArray.length; i++) {
int j = i - 1;
temp = myArray[i];
for (; j >= 0 && temp < myArray[j]; j--) {
myArray[j + 1] = myArray[j]; // 将大于temp的值整体后移一个单位
}
myArray[j + 1] = temp;
}
}
}
  • js实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var nums = [3,4,1,7,2,9,10,0];
var insertionSort = function(nums){
for(var i=1; i<nums.length; i++){
var temp = nums[i];
for(var j = i-1; j>=0&&nums[j]>temp; j--){
nums[j+1] = nums[j];
}
nums[j+1]=temp;
}
}

insertionSort(nums);
for(var number in nums){
console.log(nums[number]);
}

希尔排序(shell sort)

  • 希尔排序是改进后的插入排序;
  • 思想:由于数组越接近有序,插入排序时间复杂度越低,所以希尔排序不是直接对数组进行插入排序,而是先对数组分成若干组,对每一组分别进行插入排序,使得数组变得”更加有序”之后再扩大每组的元素个数,再对每组进行插入排序,以此类推,直至元素全部有序,这是一种为了降低时间复杂度的”渐进式”的插入排序;
  • java实现:
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
package algorithm;

public class InsertionSort {

public static void main(String[] args) {
int[] nums = { 1, 54, 6, 3, 78, 34, 12, 45 };
sort(nums);
for (int i = 0; i < nums.length; i++) {
System.out.println(nums[i]);
}
}

public static void sort(int[] myArray) {
double d = myArray.length;
while(true){
d = Math.ceil(d/2);
int inteval = (int)d;
for(int i =0; i<inteval; i++){
for(int j=i+inteval; j<myArray.length; j+=inteval){
int temp = myArray[j];
int k = j-inteval;
for(; k>=0&&myArray[k]>temp;k-=inteval){
myArray[k+inteval] = myArray[k];
}
myArray[k+inteval] = temp;
}
}

if(inteval==1){
break;
}
}
}
}
  • js实现:
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
var nums = [3,1,4,2,7,2,3,10];
var shellSort = function(nums){
var d = nums.length;
while(true){
d = Math.ceil(d/2);

for(var i=0; i<d; i++){
for(var j=i+d; j<nums.length; j++){
var temp = nums[j];
for(var k = j-d; k>=0&&nums[k]>temp;k-=d){
nums[k+d] = nums[k];
}
nums[k+d] = temp;
}
}
if(d==1){
break;
}
}
}

shellSort(nums);
for(var number in nums){
console.log(nums[number]);
}

归并排序(Merge Sort)

  • 归并排序是分治法在排序算法中的的典型实现;
  • 归并排序是将一个数组分为两半A、B(或更多部分),对这两半分别排序后,再将他们归并为一个有序数组;
  • 合并的方法:将A数组中的一个元素与B数组中的一个元素相比较,并将较小的元素复制到第三个新数组中,当其中一个数组到达末尾时,将另一个数组中剩余元素复制到第三个数组中即可;
  • java实现:
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package algorithm;

public class InsertionSort {

public static void main(String[] args) {
int[] nums = { 1, 54, 6, 3, 78, 34, 12, 45 };
sort(nums,0,nums.length-1);
for (int i = 0; i < nums.length; i++) {
System.out.println(nums[i]);
}
}

public static void sort(int[] myArray,int left,int right) {
if(left<right){
int center = (left+right)/2;
sort(myArray,left,center); //递归
sort(myArray,center+1,right);
merge(myArray,left,center,right);
}
}

public static void merge(int[] myArray,int left,int center,int right){
int[] result = new int[myArray.length];
int mid = center+1;
int third = left; //记录新的数组result的偏移量
int tmp = left;
//归并
while(left<=center&&mid<=right){
if(myArray[left]<=myArray[mid]){
result[third++] = myArray[left++];
}else{
result[third++] = myArray[mid++];
}
}

//处理归并剩余部分
while(mid<=right){
result[third++] = myArray[mid++];
}

while(left<=center){
result[third++] = myArray[left++];
}

//复制回原数组
while(tmp<=right){
myArray[tmp] = result[tmp];
tmp++;
}
}
}
  • js实现:
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
38
39
40
41
42
var nums = [1, 54, 6, 3, 78, 34, 12, 45];
var mergeSort = function(nums,left,right){
if(left<right){
var center = Math.floor((left+right)/2);
mergeSort(nums,left,center);
mergeSort(nums,center+1,right);
merge(nums,left,center,right);
}
}

var merge = function(nums,left,center,right){
var mid = center+1;
var tmp = left;
var third = left;
var result = new Array(nums.length);

while(left<=center&&mid<=right){
if(nums[left]<nums[mid]){
result[third++] = nums[left++];
}else{
result[third++] = nums[mid++];
}
}

while(left<=center){
result[third++] = nums[left++];
}

while(mid<=right){
result[third++] = nums[mid++];
}

while(tmp<=right){
nums[tmp] = result[tmp];
tmp++;
}
}

mergeSort(nums,0,nums.length-1);
for(var number in nums){
console.log(nums[number]);
}

冒泡排序(Bubble sort)

  • 从第一个元素开始,比较相邻的元素,如果第一个比第二个大,就交换他们两个;
  • 依次往后,对每一对相邻元素作同样的工作,直到序列最后,最后的元素应该会是最大的数;
  • 从第一个元素开始,重复上面两步,直到倒数第二个元素就停止(最后一个元素肯定是序列中最大的,不用再次比较);
  • 重复上面的过程,直到序列末尾;
  • java实现:
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
package algorithm;

public class InsertionSort {

public static void main(String[] args) {
int[] nums = { 1, 54, 6, 3, 78, 34, 12, 45 };
sort(nums);
for (int i = 0; i < nums.length; i++) {
System.out.println(nums[i]);
}
}

public static void sort(int[] myArray) {
int tmp;
for(int i =0; i<myArray.length-1; i++){
for(int j=0; j<myArray.length-1-i; j++){
if(myArray[j]>myArray[j+1]){
tmp = myArray[j];
myArray[j] = myArray[j+1];
myArray[j+1] = tmp;
}
}
}
}
}
  • js实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var nums = [1,5,3,6,2,7,9];
var bubbleSort = function(nums){
var temp;
for(var i=0 ;i<nums.length-1; i++){
for(var j=0; j<nums.length-1-i; j++){
if(nums[j]>nums[j+1]){
tmp = nums[j];
nums[j] = nums[j+1];
nums[j+1] = tmp;
}
}
}
}

bubbleSort(nums);
for(var number in nums){
console.log(nums[number]);
}

快速排序(Quick Sort)

  • 思想:选择一个基准元素,通常选择第一个元素或者最后一个元素,通过一趟扫描,将待排序列分成两部分,一部分比基准元素小,一部分大于等于基准元素,此时基准元素在其排好序后的正确位置,然后再用同样的方法递归地排序划分的两部分;
  • java实现:
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
38
39
package algorithm;

public class InsertionSort {

public static void main(String[] args) {
int[] nums = { 1, 54, 6, 3, 78, 34, 12, 45 };
quickSort(nums,0,nums.length-1);
for (int i = 0; i < nums.length; i++) {
System.out.println(nums[i]);
}
}

public static void quickSort(int[] list, int low, int high) {
if (low < high) {
int middle = getMiddle(list, low, high); // 将list数组进行一分为二
quickSort(list, low, middle - 1); // 对低字表进行递归排序
quickSort(list, middle + 1, high); // 对高字表进行递归排序
}
}

public static int getMiddle(int[] list, int low, int high) {
int tmp = list[low]; // 数组的第一个作为中轴
while (low < high) {
while (low < high && list[high] >= tmp) {
high--;
}

list[low] = list[high]; // 比中轴小的记录移到低端

while (low < high && list[low] <= tmp) {
low++;
}

list[high] = list[low]; // 比中轴大的记录移到高端
}
list[low] = tmp; // 中轴记录到尾
return low; // 返回中轴的位置
}
}
  • js实现:
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
var nums = [1, 54, 6, 3, 78, 34, 12, 45];
var quickSort = function(nums,low,high){
if(low<high){
var middle = sort(nums,low,high);
quickSort(nums,low,middle-1);
quickSort(nums,middle+1,high);
}
}

var sort = function(nums, low, high){
var tmp = nums[low];
while(low<high){
while(low<high&&tmp<=nums[high]){
high--;
}
nums[low] = nums[high];
while(low<high&&tmp>=nums[low]){
low++;
}
nums[high] = nums[low];
}

nums[low] = tmp;
return low;
}

quickSort(nums,0,nums.length-1);
for(var number in nums){
console.log(nums[number]);
}

堆排序(Heap Sort)

  • 堆的基本概念:
    • 堆是节点含有可比较对象,并以如下方式组织的完全二叉树:
    • 每个节点含有的对象不小于(最大堆)/不大于(最小堆)其后代中的对象;
    • 最大(小)堆的根含有堆中最大(小)的对象;
    • 最大(小)堆中任何节点的子树也是最大(小)堆;
  • 堆排序原理:
    • 这里讲解的很清楚;
  • java实现:
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
package algorithm;

public class InsertionSort {

public static void main(String[] args) {
int[] nums = { 4,1,2,7,3,8,4,5,10 };
heapSort(nums);
for (int i = 0; i < nums.length; i++) {
System.out.println(nums[i]);
}
}

public static void heapSort(int[] myArray) {
for (int i = myArray.length - 1; i >= 0; i--) {
// i记录了数组中参与重建最大堆的最后一个元素的索引值
buildMaxHeap(myArray, i);
// 交换了第一个元素和倒数第myArray.length-1-i个元素
swap(myArray, 0, i);
}
}

public static void swap(int[] myArray, int a, int b) {
int tmp;
tmp = myArray[a];
myArray[a] = myArray[b];
myArray[b] = tmp;
}

public static void buildMaxHeap(int[] myArray, int i) {
// (i-1)/2 是数组中最后一个具有叶子节点的节点的索引,从这个节点开始重建堆
for (int j = (i - 1) / 2; j >= 0; j--) {
int k = j;
while (k * 2 + 1 <= i) {
int leftIndex = k * 2 + 1; // 当前节点的左子节点的索引
int rightIndex = leftIndex + 1; // 当前节点的右子节点的索引
int biggerInLeftRight = myArray[leftIndex]; // 左右子节点中较大的数
int biggerIndex = leftIndex; // 左右子节点中较大的数的索引

// 判断右子节点是否参与本次重建堆,参与则执行
if (rightIndex <= i) {
// 找出左右子节点中较大的数
if (myArray[leftIndex] < myArray[rightIndex]) {
biggerInLeftRight = myArray[rightIndex];
biggerIndex = rightIndex;
}
}

// 将当前节点、当前节点的左右子节点中的最大值移到当前节点的位置
if (myArray[k] < biggerInLeftRight) {
swap(myArray, k, biggerIndex);
// 继续循环,确保当前节点的左右子节点也满足最大堆的性质
k = biggerIndex;
} else {
break;
}
}
}
}
}
  • js实现:
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
38
39
40
41
42
43
var nums = [8,3,5,0,2,7,9,10];
var heapSort = function(nums){
for(var i=nums.length-1; i>=0; i--){
buildMaxHeap(nums,i);
swap(nums,0,i);
}
}

var swap = function(nums, a, b){
var tmp;
tmp = nums[a];
nums[a] = nums[b];
nums[b] = tmp;
}

var buildMaxHeap = function(nums,i){
for(var j=Math.floor((i-1)/2); j>=0; j--){
var k = j;
while(k*2+1<=i){
var leftIndex = k*2+1;
var rightIndex = leftIndex+1;
var biggerIndex = leftIndex;

if(rightIndex<=i){
if(nums[leftIndex]<nums[rightIndex]){
biggerIndex = rightIndex;
}
}

if(nums[k]<nums[biggerIndex]){
swap(nums,k,biggerIndex);
k = biggerIndex;
}else{
break;
}
}
}
}

heapSort(nums);
for(var i=0; i<nums.length; i++){
console.log(nums[i]);
}

基数排序(radix sort)

  • 基数排序和其他排序方法的思路不同,不是以比较对象大小为基础;
  • 基础排序先将待排序对象的长度补全成相同的长度,然后从最后一位开始:将所有元素按照最后一位的大小排序,排序后元素的最后一位是有序的;再将所有元素按照倒数第二位的大小排序,排序后元素的倒数第二位是有序的,在倒数第二位相同的元素中,它们的最后一位是有序的;再将所有元素按照倒数第三位的大小排序,排序后元素的倒数第三位是有序的,在倒数第三位相同的元素中,它们的倒数第二位是有序的,在倒数第二位相同的元素中,它们的最后一位是有序的;
  • 以此类推,直到元素的第一位排序完成;
  • 此时元素已经是有序序列;
  • java实现:
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
package test8;

public class RadixSort {
public static void main(String[] args) {
int[] array = new int[] { 20, 22, 98, 45, 77, 75, 55, 25, 52, 79, 56,
44, 32, 37, 35, 21, 102, 33, 178, 187, 156, 144 };
sort(array, 3); // 传入数组和数组中长度最大的数的长度

for (int i = 0; i < array.length; i++) {
System.out.print(array[i] + " ");
}
}

public static void sort(int[] array, int d) {
int[][] bucket = new int[array.length][array.length];

for (int n = 1, m = 0; m < d; n *= 10, m++) {
int[] num = new int[array.length]; //记录二维数组bucket的横向数组的长度
for (int i = 0; i < array.length; i++) {
int position = array[i] / n % 10;
bucket[position][num[position]] = array[i];
num[position]++;
}
for (int k = 0, i = 0; k < array.length; k++) {
for (int j = 0; j < num[k]; j++, i++) {
array[i] = bucket[k][j];
}
}
}
}
}

各种排序算法对比

  • 资料:

    • 这里有各种算法的汇总,进入相应的算法的词条有动图;
    • 这里有HTML5动图,非常好看;
  • 复杂度对比:

排序算法 最好时间复杂度 平均时间复杂度 最坏时间复杂度 稳定性 空间复杂度
冒泡排序 O(n) O(n^2) O(n^2) 稳定 O(1)
选择排序 O(n^2) O(n^2) O(n^2) 不稳定 O(1)
插入排序 O(n) O(n^2) O(n^2) 稳定 O(1)
希尔排序 O(n) O(n^1.5) O(n^2) 不稳定 O(1)
归并排序 O(n*logn) O(n*logn) O(n*logn) 稳定 O(1)
快速排序 O(n*logn) O(n*logn) O(n^2) 不稳定 O(logn)~O(n)
堆排序 O(n*logn) O(n*logn) O(n*logn) 不稳定 O(1)
基数排序 约为O(n) 约为O(n) 约为O(n) 稳定 O(rd+n)
  • 各种排序方法的适用条件
    • 当待排序总数较小,用插入排序或选择排序,虽然他们的时间复杂度高,但是在排序总数较小时时间并不是主要矛盾;
    • 当待排序对象基本有序时,用插入排序、冒泡排序、快速排序,因为他们在序列接近有序的情况下的时间复杂度比序列完全无序时低;
    • 若待排序对象非常多,选择快速排序(首选)、堆排序、归并排序,因为他们的时间复杂度为O(nlgn),即用空间换取时间,以便解决“时间太长”这个主要矛盾;
    • 若要求排序算法稳定,则选用归并排序;

Java日志

发表于 2015-12-30 | 分类于 Java |

引言

  • 最近在做资源库管理系统,由于这个系统涉及一些复杂的操作,比如增删改查数据库表、用户申请新版本、审批和通过用户申请、导入和导出资源表,需要将每次操作的日志记录在专门的文件中,已被查阅和追责;
  • 需求中提出的日志记录功能不是JVM或者是glassfish层面的日志,而是业务层面的日志,需要用专门的类完成这一功能;
  • 顺便学习了java日志记录的相关知识;

log4j

  • 日志的记录一般是用Apache的Log4j包,log4j的优点在于:线程安全、可记录多个层次的日志信息、支持国际化、速度经过优化;
  • 本文参考这篇文章;

log4j下载和使用步骤

  • 在这里下载;
  • 将其中的jar包复制到eclipse工程中,并通过右键>build path>configuration>library添加jar包进入工程;
  • 在src直接下级目录创建并设置log4j.properties文件

log1

  • log4j.properties文件可写成诸如下面的结构:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
log4j.rootLogger = debug,stdout,D,E

log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target = System.out
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n

log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
log4j.appender.D.File = C:\\Users\\changsiyuan\\Desktop\\log.log
log4j.appender.D.Append = true
log4j.appender.D.Threshold = DEBUG
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n

log4j.appender.E = org.apache.log4j.DailyRollingFileAppender
log4j.appender.E.File = C:\\Users\\changsiyuan\\Desktop\\log.log
log4j.appender.E.Append = true
log4j.appender.E.Threshold = ERROR
log4j.appender.E.layout = org.apache.log4j.PatternLayout
log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
  • 程序需要先初始化log4j,然后再需要记录日志的地方调用相应的方法即可:
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
package testlog;

import java.io.File;

import org.apache.log4j.BasicConfigurator;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

/**
*
* @author changsiyuan
*
*/

public class Test {
//获取日志记录器
private static Logger logger = Logger.getLogger(Test.class);

/**
*
* @param args
*/

public static void main(String[] args) {
//配置log4j环境,自动快速地使用缺省Log4j环境
BasicConfigurator.configure();

// 记录debug级别的信息
logger.debug("This is debug message.");
// 记录info级别的信息
logger.info("This is info message.");
// 记录warn级别的信息
logger.warn ("This is warn message.") ;
// 记录error级别的信息
logger.error("This is error message.");
}
}

配置文件参数说明

  • 优先级说明
    • 日志优先级从高到低为ERROR、WARN、INFO、DEBUG;
  • 根配置信息
    • 下面的配置信息表明将登记大于等于INFO的日志信息输出到stdout和R两个目的地;
1
log4j.rootCategory=INFO,stdout,R
  • 配置日志输出目的地信息
    • 目的地参数如下:org.apache.log4j.ConsoleAppender(控制台),org.apache.log4j.FileAppender(文件),org.apache.log4j.DailyRollingFileAppender(每天产生一个日志文件), org.apache.log4j.RollingFileAppender(文件大小到达指定尺寸的时候产生一个新的文件), org.apache.log4j.WriterAppender(将日志信息以流格式发送到任意指定的地方)
    • 语法如下:
1
2
3
4
log4j.appender.appenderName = fully.qualified.name.of.appender.class  
log4j.appender.appenderName.option1 = value1
...
log4j.appender.appenderName.option = valueN
  • 布局信息的配置
    • 布局参数:org.apache.log4j.HTMLLayout(以HTML表格形式布局),org.apache.log4j.PatternLayout(可以灵活地指定布局模式),org.apache.log4j.SimpleLayout(包含日志信息的级别和信息字符串), org.apache.log4j.TTCCLayout(包含日志产生的时间、线程、类别等等信息)
    • 语法如下:
1
2
3
4
log4j.appender.appenderName.layout = fully.qualified.name.of.layout.class  
log4j.appender.appenderName.layout.option1 = value1
...
log4j.appender.appenderName.layout.option = valueN
  • 打印信息
    • 语法:log4j.appender.appenderName.layout.ConversionPattern=...;
    • 参数:
参数 含义
%m 输出代码中指定的消息
%p 输出优先级,即DEBUG,INFO,WARN,ERROR,FATAL
%r 输出自应用启动到输出该log信息耗费的毫秒数
%c 输出所属的类目,通常就是所在类的全名
%t 输出产生该日志事件的线程名
%n 输出一个回车换行符,Windows平台为“rn”,Unix平台为“n”
%d 输出日志时间点的日期或时间,默认格式为ISO8601,也可以在其后指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS},输出类似:2002年10月18日 22:10:28,921
%l 输出日志事件的发生位置,包括类目名、发生的线程,以及在代码中的行数

配置文件示例

  • log写入控制台
1
2
3
4
5
6
7
8
# Root logger option
log4j.rootLogger=INFO, stdout

# Direct log messages to stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
  • log写入文件
1
2
3
4
5
6
7
8
### set log levels ###  
log4j.rootLogger = debug,mylog

log4j.appender.mylog=org.apache.log4j.FileAppender
log4j.appender.mylog.append=true
log4j.appender.mylog.file= C://Users/changsiyuan/Desktop/log.txt
log4j.appender.mylog.layout=org.apache.log4j.PatternLayout
log4j.appender.mylog.layout.ConversionPattern=%-5p %c: %m%n

  • log写入文件并限制文件大小
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# Root logger option
log4j.rootLogger=INFO, file

# Direct log messages to a log file
log4j.appender.file=org.apache.log4j.RollingFileAppender

#Redirect to Tomcat logs folder
#log4j.appender.file.File=${catalina.home}/logs/logging.log

log4j.appender.file.File=C:\\logigng.log
log4j.appender.file.MaxFileSize=10MB
log4j.appender.file.MaxBackupIndex=10
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n
  • log同时写入日志和文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# Root logger option
log4j.rootLogger=INFO, file, stdout

# Direct log messages to a log file
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=C:\\logging.log
log4j.appender.file.MaxFileSize=10MB
log4j.appender.file.MaxBackupIndex=10
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n

# Direct log messages to stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p %c{1}:%L - %m%n

Java注解

发表于 2015-12-29 | 分类于 Java |

引言

  • 最近项目中需要在服务器端的REST层加入鉴权的机制,即客户端在HTTP请求中带上用户token,调用REST层的每一个API前需要经过鉴权的步骤,即向中心服务器发送用户token查询该用户是否具有调用此API的权限,如果有则可以调用,没有则不能调用;
  • 由于REST层的API都已经写好,且鉴权的代码和API的代码分离,故初步决定反射机制实现这一功能;
  • 由于用到了反射,顺便学习了下java注解;
  • 本文参考这里;

注解的作用

  • 注释是给人看的,注解是给程序看的;
  • Annotation可被用于packages、types(类、接口、枚举、Annotation类型)、类型成员(方法、构造方法、成员变量、枚举值)、方法参数和本地变量(如循环变量、catch参数);
  • 注解的最主要作用是为特定的数据打上一个标记,以便以后拿来使用;
  • 执行注解的原则:有什么标记(注解)就干什么事;
  • 注释本质上是一个标记,可以在编译、类加载、运行时被读取,并执行相应的操作;
  • 具体来说,注解有以下作用:生成文档、跟踪代码依赖性、在编译时进行格式检查;

元注解

  • 元注解用于注解其他的注解;
  • @Target
    • ElemenetType.CONSTRUCTOR 构造器声明
    • ElemenetType.FIELD 域声明(包括 enum 实例)
    • ElemenetType.LOCAL_VARIABLE 局部变量声明
    • ElemenetType.METHOD 方法声明
    • ElemenetType.PACKAGE 包声明
    • ElemenetType.PARAMETER 参数声明
    • ElemenetType.TYPE 类,接口(包括注解类型)或enum声明
  • @Retention
    • RetentionPolicy.SOURCE 注解将被编译器丢弃
    • RetentionPolicy.CLASS 注解在class文件中可用,但会被VM丢弃
    • RetentionPolicy.RUNTIME VM 将在运行期也保留注释,因此可以通过反射机制读取注解的信息
  • @Documented
    • @Documented 将此注解包含在javadoc中 ,它代表着此注解会被javadoc工具提取成文档;
  • @Inherited
    • @Inherited 允许子类继承父类中的注解;

基本注解

  • @Override:告诉编译器检查本方法确保被重写,只能作用于方法,不能作用于其他元素;
  • @Deprecated:修饰类或者方法,告诉编译器这个被修饰的类或方法已经过时,编译时提出警告信息;
  • @Suppress Warnings:取消编译器警告信息,作用于修饰的元素及所有子元素;
  • @Safe Varargs:专门抑制堆污染警告;
  • 其他的java定义好的注解直接查阅API文档即可;

自定义注解

  • 自定义注解修饰各种语法成分
1
2
3
4
5
6
//注解的定义
package test13;

public @interface TestA {

}
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
package test13;

import java.util.HashMap;
import java.util.Map;

@TestA
public class UserAnnotation {
@TestA //使用了类成员注解
private Integer age;

@TestA //使用了构造方法注解
public UserAnnotation(){

}

@TestA //使用了类方法注解
public void a(){
@TestA //使用了局部变量注解
Map m = new HashMap(0);
}

public void b(@TestA Integer a){ //使用了方法参数注解

}
}
  • 使用元注解
1
2
3
4
5
6
7
8
9
10
11
12
package test13;

import java.lang.annotation.Target;
import java.lang.annotation.Retention;
import java.lang.annotation.ElementType;
import java.lang.annotation.RetentionPolicy;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestA {


}
  • 在注解中添加内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//定义注解
package test13;

import java.lang.annotation.Target;
import java.lang.annotation.Retention;
import java.lang.annotation.ElementType;
import java.lang.annotation.RetentionPolicy;

@Target({ElementType.TYPE,ElementType.METHOD,ElementType.FIELD,ElementType.CONSTRUCTOR})
@Retention(RetentionPolicy.RUNTIME)
public @interface TestA {
String name();
int id() default 0;
Class<?> gid();
}
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
38
39
//使用注解
package test13;

import java.util.HashMap;
import java.util.Map;

@TestA(name = "type", gid = Long.class)
// 类成员注解
public class UserAnnotation {
@TestA(name = "param", id = 1, gid = Long.class)
// 类成员注解
private Integer age;

@TestA(name = "construct", id = 2, gid = Long.class)
// 构造方法注解
public UserAnnotation() {
}

@TestA(name = "public method", id = 3, gid = Long.class)
// 类方法注解
public void a() {
Map m = new HashMap(0);
}

@TestA(name = "protected method", id = 4, gid = Long.class)
// 类方法注解
protected void b() {
Map m = new HashMap(0);
}

@TestA(name = "private method", id = 5, gid = Long.class)
// 类方法注解
private void c() {
Map m = new HashMap(0);
}

public void b(Integer a) {
}
}
  • 配合反射使用注解
    • 一般的做法是,定义annotation生命周期为运行时仍存在,运行时通过反射获取相应的annotation内容,然后根据annotation的内容决定采取下一步的动作;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//定义注解
package test13;

import java.lang.annotation.Target;
import java.lang.annotation.Retention;
import java.lang.annotation.ElementType;
import java.lang.annotation.RetentionPolicy;

@Target({ElementType.TYPE,ElementType.METHOD,ElementType.FIELD,ElementType.CONSTRUCTOR})
@Retention(RetentionPolicy.RUNTIME)
public @interface TestA {
String name();
int id() default 0;
Class<?> gid();
}
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
38
39
//被注解修饰的类
package test13;

import java.util.HashMap;
import java.util.Map;

@TestA(name = "type", gid = Long.class)
// 类成员注解
public class UserAnnotation {
@TestA(name = "param", id = 1, gid = Long.class)
// 类成员注解
private Integer age;

@TestA(name = "construct", id = 2, gid = Long.class)
// 构造方法注解
public UserAnnotation() {
}

@TestA(name = "public method", id = 3, gid = Long.class)
// 类方法注解
public void a() {
Map m = new HashMap(0);
}

@TestA(name = "protected method", id = 4, gid = Long.class)
// 类方法注解
protected void b() {
Map m = new HashMap(0);
}

@TestA(name = "private method", id = 5, gid = Long.class)
// 类方法注解
private void c() {
Map m = new HashMap(0);
}

public void b(Integer a) {
}
}
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
//用反射机制使用注解
package test13;

import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class ParseAnnotation {

public static void parseTypeAnnotation() throws ClassNotFoundException {
//通过反射获取UserAnnotation类
Class<?> clazz = Class.forName("test13.UserAnnotation");

//获取类的注解信息,由于注解信息保留到了运行时,所以这里可以获取
Annotation[] annotations = clazz.getAnnotations();
for (Annotation annotation : annotations) {
TestA testA = (TestA) annotation;

//id= 0;name= type;gid= class java.lang.Long (类注解仅有一个)
System.out.println("id= " + testA.id() + ";name= " + testA.name()
+ ";gid= " + testA.gid());
}
}

public static void parseMethodAnnotation() {
//通过反射获取类方法
Method[] methods = UserAnnotation.class.getDeclaredMethods();

for (Method method : methods) {
boolean hasAnnotation = method.isAnnotationPresent(TestA.class);
if (hasAnnotation) {
TestA annotation = method.getAnnotation(TestA.class);

/*
* method = c ; id = 5 ; description = private method; gid= class java.lang.Long
* method = a ; id = 3 ; description = public method; gid= class java.lang.Long
* method = b ; id = 4 ; description = protected method; gid= class java.lang.Long
*/
System.out.println("method = " + method.getName() + " ; id = "
+ annotation.id() + " ; description = "
+ annotation.name() + "; gid= " + annotation.gid());
}
}
}

public static void parseConstructAnnotation() {
//通过反射获取构造器
Constructor[] constructors = UserAnnotation.class.getConstructors();
for (Constructor constructor : constructors) {

boolean hasAnnotation = constructor
.isAnnotationPresent(TestA.class);

if (hasAnnotation) {
TestA annotation = (TestA) constructor
.getAnnotation(TestA.class);

/*
* constructor = test13.UserAnnotation ; id = 2 ; description = construct; gid= class java.lang.Long
*/
System.out.println("constructor = " + constructor.getName()
+ " ; id = " + annotation.id() + " ; description = "
+ annotation.name() + "; gid= " + annotation.gid());
}
}
}

public static void main(String[] args) throws ClassNotFoundException {
parseTypeAnnotation();
parseMethodAnnotation();
parseConstructAnnotation();
}
}

Java反射机制

发表于 2015-12-27 | 分类于 Java |

引言

  • 最近的项目中,需要根据客户端传入的id来动态的实例化对象,所以学习了一下java的反射机制;

反射的定义

  • 反射是指程序可以访问、检测和修改它本身状态或行为的一种能力,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义;
  • import是在程序编译时,静态引入相应类,反射是程序运行时,根据某一字符串,访问、创建这个字符串描述的类或是它的方法;

反射的作用

  • 在运行时判断任意一个对象所属的类 ;
  • 在运行时构造任意一个类的对象 ;
  • 在运行时判断任意一个类所具有的成员变量和方法 ;
  • 在运行时调用任意一个对象的方法;

反射所需的API

都位于java.lang.reflect包中:

  • Class类:代表一个类,位于java.lang包下;
  • Field类:代表类的成员变量(成员变量也称为类的属性);
  • Method类:代表类的方法;
  • Constructor类:代表类的构造方法;
  • Array类:提供了动态创建数组,以及访问数组的元素的静态方法;

反射实例

  • 获取Class对象:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package test12;

public class Employee {

public static void main(String[] args) throws ClassNotFoundException {
Class c1 = Class.forName("test12.Employee");
Class c2 = Employee.class;
Employee e = new Employee();
Class c3 = e.getClass();

System.out.println(c1); //class test12.Employee
System.out.println(c2); //class test12.Employee
System.out.println(c3); //class test12.Employee
}

}
  • 获取所有成员变量:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package test12;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Employee {

public static void main(String[] args)
throws ClassNotFoundException, InstantiationException, IllegalAccessException,
NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
Class<?> c = Class.forName("test12.test");

//获取所有成员变量
Field[] f = c.getFields();
for(int i =0; i<f.length; i++){
System.out.println(f[i]); //public int test12.test.c
}
}
}
  • 获取特定的成员变量:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package test12;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Employee {

public static void main(String[] args)
throws ClassNotFoundException, InstantiationException, IllegalAccessException,
NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException, NoSuchFieldException {
Class<?> c = Class.forName("test12.test");

//获取特定成员变量
Field f = c.getField("c");
System.out.println(f); //public int test12.test.c
Field f1 = c.getField("d"); //java.lang.NoSuchFieldException
System.out.println(f1);
}
}
  • 获取所有方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package test12;

import java.lang.reflect.Method;

public class Employee {

public static void main(String[] args) throws ClassNotFoundException {
Class c = Class.forName("java.lang.String");

Method[] methods = c.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method); //输出所有方法
}
}

}
  • 获取特定的方法并调用之
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package test12;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Employee {

public static void main(String[] args)
throws ClassNotFoundException, InstantiationException, IllegalAccessException,
NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
Class c = Class.forName("test12.test");

//获取方法
Object t = c.newInstance();
System.out.println(t instanceof test); //true

//调用方法
Method stringMethod = c.getDeclaredMethod("plus", new Class[]{int.class, int.class});
Object result = stringMethod.invoke(t, new Object[]{1,2});
System.out.println(result); //3
System.out.println(result instanceof Integer); //true
}
}
1
2
3
4
5
6
7
8
//test类
package test12;

public class test {
public int plus(int a, int b){
return a+b;
}
}
  • 动态生成对象(构造器不含参数)
1
2
Class<?> classType = String.class;
Object obj = classType.newInstance();
  • 动态生成对象(构造器含参数):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package test12;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class Employee {

public static void main(String[] args)
throws ClassNotFoundException, InstantiationException, IllegalAccessException,
NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
Class c = Class.forName("test12.test");

//获取构造器并实例化类
Constructor cons = c.getConstructor(new Class[] {int.class, int.class});
Object obj = cons.newInstance(new Object[] {10,20});

//调用方法
Method stringMethod = c.getDeclaredMethod("plus");
Object result = stringMethod.invoke(obj);
System.out.println(result); //30
System.out.println(result instanceof Integer); //true
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//test类
package test12;

public class test {
private int a;
private int b;

public test(int a, int b){
this.a = a;
this.b = b;
}

public int plus(){
return a+b;
}
}

Java中的多态

发表于 2015-12-25 | 分类于 Java |

引言

  • 最近研究了一下java中的多态这一java特有的非常优秀的特性;
  • 多态增加了程序的灵活性和可扩展性,但是也对程序的安全性和稳定性提出了挑战;

多态的概念

  • 多态的定义:指允许不同类的对象对同一消息做出响应,即同一消息可以根据发送对象的不同而采用多种不同的行为方式(发送消息就是函数调用);
  • 多态的分类:编译时多态(方法的重载就是其具体实现),运行时多态(本文中的多态均指运行时多态);
  • 实现多态的技术是动态绑定(dynamic binding):是指在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法;
  • 多态的作用:消除类型之间的耦合关系;

多态存在的条件

  • 要有继承(包括接口的实现);
  • 要有重写;
  • 父类引用指向子类对象;

实例

  • 父类如下:
1
2
3
4
5
6
7
8
9
10
11
12
package test11;

public class BaseClass {
public int a = 6;
public void base(){
System.out.println("父类普通方法");
}

public void test(){
System.out.println("父类被覆盖方法");
}
}
  • 子类如下:
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
38
39
package test11;

public class SubClass extends BaseClass{
public int b = 8;
public int a = 8;
public void test(){
System.out.println("子类覆盖父类的test方法");
}

public void sub(){
System.out.println("子类普通方法");
}


public static void main(String[] args) {
BaseClass base = new BaseClass();
SubClass sub = new SubClass();
BaseClass bc = new SubClass();
//SubClass sb = new BaseClass(); //编译时报错,不能从父类到子类强制转换

System.out.println(base.a); //6
//System.out.println(base.b); //报错,因为BaseClass没有变量b
base.base(); //父类普通方法
base.test(); //父类被覆盖方法

System.out.println(sub.b); //8
System.out.println(sub.a); //8
sub.test(); //子类覆盖父类的test方法
sub.sub(); //子类普通方法
sub.base(); //父类普通方法

//System.out.println(bc.b); //编译时报错,编译时bc是BaseClass类,不含有变量b
System.out.println(bc.a); //6,因为变量没有多态的特性
bc.test(); //子类覆盖父类的test方法
//bc.sub(); //编译时报错,编译时bc是BaseClass类,不含有方法sub()
bc.base(); //父类普通方法

}

}
  • BaseClass base = new BaseClass();定义base为父类,可访问所有父类的变量和方法;
  • SubClass sub = new SubClass();定义为子类,优先访问子类中的变量和方法,如果没有,再从父类寻找;
  • 对于BaseClass bc = new SubClass();:
    • 编译时,系统首先new出来一个SubClass类的实例,系统自动将这个new出来的实例强制转换为BaseClass类型,并且赋给bc对象;
    • 运行时,bc对象名义上是BaseClass类型,实际上是SubClass类型,bc对象可访问且优先访问SubClass类中的方法,找不到的话再到它的父类BaseClass寻找;
    • 尽管bc对象在运行时是SubClass类型,bc对象在运行时是可以访问SubClass自己的方法的(比如sub()方法),但是由于bc对象在编译时是BaseClass类型,bc.sub()这一句话根本通不过编译,所以运行时bc对象实际上是不能访问sub()方法的;
    • 在编译时,base和bc对象都是BaseClass类型的对象,但是在运行时base仍为BaseClass类型,bc却是SubClass类型,这就出现了base和bc这两个编译时相同类型的变量,在运行时调用test()方法的结果却不一致的现象,就是多态;
    • 多态性只对方法有效,对于变量无效,所以访问bc对象的a变量时,输出父类的a变量而非子类的a变量值,这和java内部运行机制有关;

Latex使用方法

发表于 2015-12-20 | 分类于 others |

引言

  • 最近要写论文,所以粗浅学了下Latex的使用方法;
  • 本文将Latex常用的语法和工具做个总结,大多数是借鉴和引用别人的博客;

Latex

  • Latex是一种排版工具,借助于特定的语法实现论文、书籍等的排版;
  • 想要实现用Latex排版一篇论文,需要以下几个工具或软件:
    • 学习Latex语法;
    • 安装Latex解析器和Latex的pdf生成工具,本文选用TexLive2015;
    • 安装一款适合自己的Latex编辑器,本文选用Texmaker;

TexLive2015

  • 教程请点击这里;
  • iso镜像文件下载地址:
    • 地址一;
    • 地址二,密码txt3;
  • 安装方法:解压iso文件后,双击批处理文件install-tl-advanced,选择安装目录后,即可安装;

latex1

latex1

Latex编辑器

  • texLive软件自带编辑器teXworks editor;
  • 也可选用其它编辑器:textmarker、teXstudio、在线编辑器sharelatex、其他编辑器;

Latex常用语法

  • 教程
    • 入门教程;
    • 排版和格式;
    • 官方教程;
  • 数学常用
    • 数学公式;
    • 括号;
    • 数学符号;
    • 希腊字母;

生成参考文献

  • 进入谷歌学术;
  • 点击谷歌学术的设置按钮,选择“显示导入BibTex的链接”;

latex1

  • 在谷歌学术中所搜所需的文章,点击文章下面的导出BibTex按钮,即可自动生成该文章的BibTex的引用信息;

latex1

latex1

  • 添加一个名为”myreference.bib”的文件,放在和.tex文件的同一目录下,将上面生成的BibTex复制进去;
  • 在.tex文件的需要添加参考文献的地方(必须是\end{document}之前)添加下面的代码:
1
2
\bibliographystyle{plain}  ## 设置参考文献的类型,plain代表标准类型
\bibliography{myreference.bib} ## 设置bib文件的名称,让latex编译器能够找到bib文件
  • 我用的编辑器是Texmaker,点击工具>BibTex,编译bib文件;
  • bib文件编译好后,再编译.tex文件(运行.tex文件),即可看到效果:

latex1

  • 引用参考文献:
1
\cite{文章名称}   ## 文章名称是指写在bib文件中的@article后面的内容

JavaEE Web开发规范汇总

发表于 2015-12-15 | 分类于 Java |

引言

  • 在开发系统资源库项目的过程中,发现自己的代码有多处不规范之处;
  • 本文总结了JavaEE Web的一些开发规范,尤其是RESTful设计模式的一些常见的规范,以警示自己编写代码必须要规范、可维护、可扩展;

数据库设计和命名规范

  • 库表名称、字段名称含义明确,并且使用snake_case命名,避免使用特殊符号和sql关键字;
  • 字段应当具有原子性,不可再分;
  • Table用单数,Column用单数;
  • 代表个数、次数等数量概念的,用count,比如xxx_count;
  • 时间类型统一规定命名为xxx_date的形式,并且带上timezone;
  • boolean类型的命名要用is_xxx格式,比如在业务上删除一条记录时,并不会将本条记录真正从数据库中删除,而是将is_deleted字段置为相应的值;
  • 每个表都要有主键,名称是id,类型为bigint;
  • 一个字段不要有多个用途;
  • varchar的缺省长度是255,varchar(50)并不比varchar(255)节省空间,因为这个空间只在需要时才分配,而不会提前分配,使用varchar(255)可以提升系统的可持续性;
  • 必须避免不同数据库表的数据冗余,数据冗余很容易造成数据的不一致性;
  • 不同的数据库表间应当尽量解耦,这样可以保证每个表只存储他该存储的信息,库表间可用外键连接;
  • 关于数据库表设计,可参考这里;

RESTful设计规范

  • 我画了一张图清晰地说明了再用Java开发Web应用时,如果使用RESTful设计模式,应该遵循的架构:

RESTful_java_structure

  • 对上图的说明:
    • REST层只向client提供数据接口,不进行数据查询和处理;
    • Service层向REST提供各种数据和服务,是进行数据处理最重要的一层;
    • DAO层内含有各种SQL语句,以便查询;
    • entity是数据库中一条记录的映射对象;
    • entity manager对象根据相关的配置文件(如persistence.xml),在EJB容器中,负责数据库连接的管理、释放等工作;
    • 数据库保存数据;
    • 不同的REST和Dao之间不要传递数据,如果一个Service需要另一个Service的数据,可相互传递;
  • EntityManager实例化的两种方式:
    1
    2
    3
    4
    5
    //方法一:通过createEntityManagerFactory方法实例化
    EntityManager em = Persistence.createEntityManagerFactory("cn.com.haohandata_ResourceStore_war_1.0-SNAPSHOTPU") .createEntityManager();
    //方法二:通过@PersistenceContext注入
    @PersistenceContext(unitName = "cn.com.haohandata_ResourceStore_war_1.0-SNAPSHOTPU")
    private EntityManager em;

RESTful API设计规范(老师总结)

  • 原则:
    • 资源(URL)永远是名词,操作(HTTP方法)永远是动词;
    • GET永远不应该修改对象内容;
  • 基本形式
    • GET /weibo - 获取微博列表
    • GET /weibo/12 - 获取某个微博
    • POST /weibo - 创建微博
    • PUT /weibo/12 - 修改某个微博
    • PATCH /weibo/12 - 部分修改某个微博
    • DELETE /weibo/12 - 删除某个微博
  • 有关系的资源
    • GET /weibo/12/comment - 获取某个微博的评论列表
    • GET /weibo/12/comment/5 - 获取某个微博的某个评论
    • POST /weibo/12/comment - 创建微博的评论
    • PUT /weibo/12/comment/5 - 修改某个微博的某个评论
    • PATCH /weibo/12/comment/5 - 部分修改某个微博的某个评论
    • DELETE /weibo/12/comment/5 - 删除某个微博的某个评论
  • 不太能归类为CRUD的操作
    • PUT /weibo/12/like - 给某个微博点赞
    • DELETE /weibo/12/like - 删除某个微博的赞
  • 过滤、排序、搜索等
    • GET /weibo?deleted=true - 得到所有标记为删除的微博
    • GET /weibo?sort=-create-date - 按照创建日期反序排序得到微博列表
    • GET /weibo?sort=comment-count,-create-date - 先按微博评论数再按创建日期反序排序得到微博列表
    • GET /weibo?keyword=xxx - 得到包含关键字xxx的微博列表
    • GET /weibo?keyword=xxx&deleted=true&sort=comment-count,-create-date - 搜索关键字包含xxx的标记为删除的微博,按照评论数再按创建日期反序排序得到微博列表
  • 常用的查询可以独立出来
    • GET /weibo/top10 - 得到最热前10名的微博列表
  • 这里写得非常不错

编写Java代码需要注意

  • 参考这里
  • 保持方法简短扼要
  • 永远永远不要将同一个变量用于不同的目的
  • 尽可能让变量和方法的名称能够描述要实现的功能
  • 尽可能将变量定义在最靠近它们的地方
  • 不要出现让人费解的数字
  • 要像对待朋友一样对待你擅长的语言
  • 不要逆常规而行
  • 千万小心过早的优化代码
  • 要常常重构经过测试的代码
  • 不要沉溺于过度的设计技巧
  • 随时随地学习新的知识

Linux常用命令汇总

发表于 2015-12-12 | 分类于 Linux |

引言

  • 本文总结了我经常用到的linux命令,以后会补充;

帮助命令

  • --help
  • man

文本处理

  • grep:使用正则表达式搜索文本,适用于在话单中快速找出需要的内容
1
cat ip.csv grep *guangdong* | head -n 10
  • awk:从前面命令的结果中筛选列或行
1
hadoop fs -du /user/siyuan/test/test2/* | awk '{if($1!=0){print $2}}'

磁盘管理/目录管理

  • fdsik -l: 查看磁盘分区表
  • df: 查看磁盘分区使用情况
  • du: 查看当前目录下子目录的大小
  • mount 挂载设备名 挂载目录: 挂载设备,比如mount /dev/sda1 /mnt/disk1

文件/目录

  • cd: 切换目录
  • pwd: 查看当前目录
  • mkdir: 新建目录, -m 制定权限, -p创建多层目录
  • touch: 新建文件
  • vi: 新建、编辑文件
  • rmdir: 删除目录
  • rm: 删除文件/目录
  • mv 源文件或目录 目标文件或目录: 移动文件/文件改名
  • cp 源文件或目录 目标文件或目录: 复制文件
  • chmod: 更改权限
  • ls -lht: 查看当前目录下文件的大小
  • cat/head/tail/less/more: 查看文件,下面是详细的使用说明
  • cat 文件名:查看文件全部信息,从头开始向下滚动;
  • head 文件名:查看文件开头前10行信息; head -n 文件名:查看文件开头前n行信息;
  • tail 文件名:查看文件最后10行信息; tail -n 文件名:查看文件最后n行信息;
  • less 文件名:从文件开头显示一屏文件内容(先加载一部分文件内容、不是全部加载),按向下、向上、page up、page down、G(移动到最后一行)、g( 移动到第一行)、ctrl + F(向前移动一屏)、ctrl + B(向后移动一屏)、q(退出)可以随意查看;
  • more 文件名:从文件开头显示一屏文件内容,只能向下翻页,ctrl + F(向前移动一屏)、ctrl + B(向后移动一屏)、q(退出);

软件

  • yum install 软件包名称: 查找、安装、删除某一个或一组软件包
  • 软件名 --version: 检测软件是否正确安装
  • rpm -ql +软件名: 查看某个软件是否安装、安装路径
  • rpm -qi +软件名: 查看某个软件版本等相关信息

网络

  • ifconfig: 查看网络配置、网卡
  • netstat -a: 查看所有程序的协议、端口
  • ping: 查看网络连通情况

系统运行

  • jps -lm: 查看本机正在跑的进程
  • top: 查看CPU和内存使用率
  • free -m或cat /proc/meminfo或vmstat: 查看内存使用情况、空闲内存数量
  • iostat: 查看当前硬盘读写速率等
  • ifstat: 查看当前网络IO状况
  • netstat: 查看本机端口占用情况
  • ps aux: 查看正在进行的任务/程序,即任务管理器

系统配置

  • cat /proc/cpuinfo: 查看机器配置信息(包括CPU主频型号核心数等等)
  • cat /proc/cpuinfo |grep "physical id"|sort |uniq|wc -l: 查看物理CPU个数
  • cat /proc/cpuinfo |grep "processor"|wc -l: 查看逻辑CPU的个数
  • cat /proc/cpuinfo |grep "cores"|uniq: 查看CPU是几核
  • cat /proc/cpuinfo |grep MHz|uniq: 查看CPU的主频
  • cat /proc/version: Linux查看当前操作系统版本信息
  • cat /etc/issue: Linux查看版本当前操作系统发行版信息
  • uname -a: 查看当前操作系统内核信息
  • echo $PATH: 查看环境变量PATH的值

系统管理

  • useradd: 增加新用户
  • who: 每个用户、什么时间、从哪台机器上面连上来
  • su 用户名: 切换到某个用户
  • sudo: 以系统管理者的身份执行指令
  • exit/logout: 退出当前用户
  • reboot: 重启机器
  • halt/poweroff/shutdown: 关机

jQuery选择器

发表于 2015-12-11 | 分类于 Web |

引言

  • jQuery是一个javascript库,最为重要的作用就是动态的、灵活的操纵DOM对象;
  • jQuery选择器多种多样,满足了不同的筛选DOM对象的需求;
  • jQuery选择器和css选择器在很大程度上相似,但是jQuery选择器以及jQuery对于选择结果的处理上更加丰富和充实(比如使用回调函数处理选择结果);
  • 本文总结了各种类型的jQuery选择器,当然这还不全面,在工作中如果用到其他类型的、效率更高的选择器我会及时补充;

jQuery选择器基本语法

  • jQuery选择器的语法相当简单:
1
2
3
4
5
<script>
$("selector").function(){
...
}
</script>

  • 可以对比下css选择器的语法:
1
2
3
4
5
6
<style>
selector1,
selector2 {
...
}

</style>

最小粒度选择器

选择器 选择对象 实例
* 任意元素
tag name 选择 h1
#id 选择id名称(单一元素) #test
.class 选择class(一类元素) .test
selector(空格):伪类/伪元素 加入伪类伪元素,相当于对selector的筛选结果再做逻辑筛选 $(“#form1 :text”)

属性选择器

选择器 实例 选取
[attribute] $(“[href]”) 所有带有 href 属性的元素
[attribute=value] $(“[href=’#’]”) 所有 href 属性的值等于 “#” 的元素
[attribute!=value] $(“[href!=’#’]”) 所有 href 属性的值不等于 “#” 的元素
[attribute$=value] $(“[href$=’.jpg’]”) 所有 href 属性的值包含以 “.jpg” 结尾的元素

选择器级联

选择器 选择对象 实例
selector1, selector2 不同的选择器用逗号隔开(css),不同选择器直接相连 $(“.main”).$(“.test”)
selector1selector2selector3 元素必须满足所有选择器的条件才能被选中 $(“.main.btn”)
selector1>selector2 筛选出符合selector1的元素的符合selector2的所有直接后代元素 $(“.main>p”)
selector1(空格)selector2 筛选出符合selector1的元素的符合selector2的所有后代元素 $(“.main p”)
selector1+selector2 筛选出符合selector1的元素的紧挨着它的后面的同级(兄弟)元素,这个兄弟元素必须符合selector2 $(“#test3+#test4”)
selector1~selector2 筛选出符合selector1的元素后面的所有的同级(兄弟)元素,这个兄弟元素必须符合selector2 $(“#test3~#test4”)
$(“selector1”).filter(“selector2”) 用selector2的筛选条件去过滤selector1的筛选结果 $(“.main”).filter($(“p”))

伪类和伪元素

  • 这里借用书上的和w3cschool的几张图片加以说明;
  • 普通伪类选择器

putong

  • 文本相关

text

  • 子元素相关

children

  • 表单相关

form1

form2

1…345…11
Eric Chang

Eric Chang

www.yinshuisiyuan.net

101 日志
15 分类
51 标签
github weibo linkedin mail
  • 阮一峰
  • Azure
  • Python
  • Nodejs
© 2015 - 2020 Eric Chang
由 Hexo 强力驱动
主题 - NexT.Pisces


知识共享许可协议本作品采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可。