diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.1 \346\216\222\345\272\217\347\256\227\346\263\225/BubbleSort.java" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.1 \346\216\222\345\272\217\347\256\227\346\263\225/BubbleSort.java" new file mode 100644 index 0000000..f0f49ce --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.1 \346\216\222\345\272\217\347\256\227\346\263\225/BubbleSort.java" @@ -0,0 +1,20 @@ +package com.algorithm.leetcode.sort; + +import java.util.Arrays; + +public class BubbleSort { + public static void main(String[] args) { + int [] a = new int[]{1,5,2,9,3,7,0,5}; + + for (int i = 0; i < a.length; i++) { + for (int j = 0; j < a.length - i - 1; j++) { + if (a[j]>a[j+1]){ + int temp = a[j]; + a[j] = a[j+1]; + a[j+1] = temp; + } + } + } + Arrays.stream(a).forEach(System.out::print); + } +} diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.1 \346\216\222\345\272\217\347\256\227\346\263\225/InsertSort.java" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.1 \346\216\222\345\272\217\347\256\227\346\263\225/InsertSort.java" new file mode 100644 index 0000000..af8bbcb --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.1 \346\216\222\345\272\217\347\256\227\346\263\225/InsertSort.java" @@ -0,0 +1,20 @@ +package com.algorithm.leetcode.sort; + +import java.util.Arrays; + +public class InsertSort { + public static void main(String[] args) { + int [] a = new int[]{1,5,2,9,3,7,0,5}; + for (int i = 0; i < a.length; i++) { + int index = i; + int temp = a[i]; + while (index>0 && a[index-1]>temp){ + a[index] = a[index-1]; + index--; + } + a[index] = temp; + } + + Arrays.stream(a).forEach(System.out::print); + } +} diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.1 \346\216\222\345\272\217\347\256\227\346\263\225/MergeSort.java" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.1 \346\216\222\345\272\217\347\256\227\346\263\225/MergeSort.java" new file mode 100644 index 0000000..1504d4f --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.1 \346\216\222\345\272\217\347\256\227\346\263\225/MergeSort.java" @@ -0,0 +1,47 @@ +package com.algorithm.leetcode.sort; + +import java.util.Arrays; + +public class MergeSort { + public static void main(String[] args) { + int [] a = new int[]{1,5,2,9,3,7,0,5}; + + a = mergeSort(a,0,a.length-1); + Arrays.stream(a).forEach(System.out::print); + } + + private static int[] mergeSort(int[] a, int low, int high) { + int mid = (low+high)/2; + if (lowa[end]){ + int temp = a[end]; + a[end] = a[start]; + a[start] = temp; + } + while (start= a[start]){ + start++; + } + if (baseKeylow) quick(a,low,start); + if (end= 0) { + if (array[i][j] == target) return true; + if (array[i][j] > target) j -- ; + else i ++ ; + } + return false; + } +} +``` + diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/10. \351\207\215\345\273\272\344\272\214\345\217\211\346\240\221.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/10. \351\207\215\345\273\272\344\272\214\345\217\211\346\240\221.md" new file mode 100644 index 0000000..a671982 --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/10. \351\207\215\345\273\272\344\272\214\345\217\211\346\240\221.md" @@ -0,0 +1,49 @@ +### Title +输入一棵二叉树前序遍历和中序遍历的结果,请重建该二叉树。 + +**注意**: + +* 二叉树中每个节点的值都互不相同; +* 输入的前序遍历和中序遍历一定合法; +### Demo +``` +给定: +前序遍历是:[3, 9, 20, 15, 7] +中序遍历是:[9, 3, 15, 20, 7] + +返回:[3, 9, 20, null, null, 15, 7, null, null, null, null] +返回的二叉树如下所示: + 3 + / \ + 9 20 + / \ + 15 7 +``` +### Analysis + +前序的第一个字母肯定是父节点, 然后再去中序找该节点对应的位置, 位置前是左子树, 后是右子树。以此类推。 +### Code + +```java +class Solution { + Map map = new HashMap<>(); + public TreeNode buildTree(int[] preorder, int[] inorder) { + + for(int i = 0 ; i< inorder.length; i++){ + map.put(inorder[i],i); + } + return dfs(preorder,inorder,0,preorder.length-1,0,inorder.length-1); + } + + public TreeNode dfs(int [] preorder, int [] inorder, int pl, int pr, int il, int ir){ + if(pl > pr){ + return null; + } + TreeNode root = new TreeNode(preorder[pl]); + int k = map.get(preorder[pl])-il; + root.left = dfs(preorder,inorder,pl+1,pl+k,il,il+k-1); + root.right = dfs(preorder,inorder,pl+k+1,pr,il+k+1,ir); + return root; + } +} +``` diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/11. \351\223\276\350\241\250\344\270\255\345\200\222\346\225\260\347\254\254k\344\270\252\350\212\202\347\202\271.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/11. \351\223\276\350\241\250\344\270\255\345\200\222\346\225\260\347\254\254k\344\270\252\350\212\202\347\202\271.md" new file mode 100644 index 0000000..42ef647 --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/11. \351\223\276\350\241\250\344\270\255\345\200\222\346\225\260\347\254\254k\344\270\252\350\212\202\347\202\271.md" @@ -0,0 +1,41 @@ +### Title +输入一个链表,输出该链表中倒数第k个结点。 + +**注意**: + +* k >= 0; +* 如果k大于链表长度,则返回 NULL; +### Demo +``` +输入:链表:1->2->3->4->5 ,k=2 + +输出:4 +``` +### Analysis + +归类:前后指针算法 + +先让快指针走k步, 然后让快慢指针同时走, 当快指针next指向空, 则慢指针当前指向就是第k个节点。 +### Code + +```java + +class Solution { + public ListNode findKthToTail(ListNode pListHead, int k) { + if(pListHead == null || k < 0 ){return null;} + ListNode first = pListHead; + ListNode second = pListHead; + for(int i = 0; i< k-1 ; i++){ + if(first.next == null){ + return null; + } + first = first.next; + } + while(first.next != null){ + first = first.next; + second = second.next; + } + return second; + } +} +``` diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/12. \345\217\215\350\275\254\351\223\276\350\241\250.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/12. \345\217\215\350\275\254\351\223\276\350\241\250.md" new file mode 100644 index 0000000..2008875 --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/12. \345\217\215\350\275\254\351\223\276\350\241\250.md" @@ -0,0 +1,33 @@ +### Title +定义一个函数,输入一个链表的头结点,反转该链表并输出反转后链表的头结点。 + + +### Demo +``` +输入:1->2->3->4->5->NULL + +输出:5->4->3->2->1->NULL +``` +### Analysis + +最基础的链表算法。重在理解:交换pre、next指向, 但要注意技巧和套路。 +### Code + +```java +class Solution { + public ListNode reverseList(ListNode head) { + if(head == null){return null;} + ListNode pre = null; + ListNode cur = head; + + while(cur != null){ + ListNode next = cur.next; + cur.next = pre; + pre = cur; + cur = next; + } + // next.next = pre; + return pre; + } +} +``` diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/13. \347\237\251\345\275\242\350\246\206\347\233\226.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/13. \347\237\251\345\275\242\350\246\206\347\233\226.md" new file mode 100644 index 0000000..babfbd0 --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/13. \347\237\251\345\275\242\350\246\206\347\233\226.md" @@ -0,0 +1,35 @@ +### Title +我们可以用2*1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2*1的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法? +### Demo +``` +略 +``` +### Analysis + +归类: 斐波那契数列(递归) + +因为是2x1的方块覆盖2xn的矩形, 假设方块有两种: 1x2 2x1 +``` +target <= 0 大矩形为<= 2*0,直接return 1; +target = 1大矩形为2*1,只有一种摆放方法,return1; +target = 2 大矩形为2*2,有两种摆放方法,return2; + +所以: +第一次摆放一块 2*1 的小矩阵,则摆放方法总共为f(target - 1) +第一次摆放一块1*2的小矩阵,则摆放方法总共为f(target-2) +``` +### Code + +```java +public class Solution { + public int RectCover(int target) { + if (target <= 0){ + return 0; + } else if (target == 1 || target == 2){ + return target; + }else { + return RectCover(target-1)+RectCover(target-2); + } + } +} +``` diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/14. \350\260\203\346\225\264\346\225\260\347\273\204\351\241\272\345\272\217\344\275\277\345\245\207\346\225\260\344\275\215\344\272\216\345\201\266\346\225\260\345\211\215\351\235\242.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/14. \350\260\203\346\225\264\346\225\260\347\273\204\351\241\272\345\272\217\344\275\277\345\245\207\346\225\260\344\275\215\344\272\216\345\201\266\346\225\260\345\211\215\351\235\242.md" new file mode 100644 index 0000000..22b7549 --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/14. \350\260\203\346\225\264\346\225\260\347\273\204\351\241\272\345\272\217\344\275\277\345\245\207\346\225\260\344\275\215\344\272\216\345\201\266\346\225\260\345\211\215\351\235\242.md" @@ -0,0 +1,37 @@ +### Title +输入一个整数数组,实现一个函数来调整该数组中数字的顺序。 + +使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分。 + +### Demo +``` +输入:[1,2,3,4,5] + +输出: [1,3,5,2,4] +``` +### Analysis + +快速排序的思想, 如果发现前后两个位置互相对立, 则交换他们。 +### Code + +```java +class Solution { + public void reOrderArray(int [] array) { + // quick sort 思想 + int high = array.length-1; + int low = 0; + while(low < high){ + // low 是奇数,low++ + while(low < high && array[low] % 2 != 0){ + low++; + } + while(low < high && array[high] % 2 == 0){ + high--; + } + int temp = array[low]; + array[low] = array[high]; + array[high] = temp; + } + } +} +``` diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/15. \346\225\260\345\200\274\347\232\204\346\225\264\346\225\260\346\254\241\346\226\271.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/15. \346\225\260\345\200\274\347\232\204\346\225\264\346\225\260\346\254\241\346\226\271.md" new file mode 100644 index 0000000..41ed513 --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/15. \346\225\260\345\200\274\347\232\204\346\225\264\346\225\260\346\254\241\346\226\271.md" @@ -0,0 +1,37 @@ +### Title + +实现函数double Power(double base, int exponent),求base的 exponent次方。 + +不得使用库函数,同时不需要考虑大数问题。 + +**注意:** +不会出现底数和指数同为0的情况 +### Demo +``` +输入:10 ,2 + +输出:100 +``` + +``` +输入:10 ,-2 + +输出:0.01 +``` +### Analysis + +分辨指数是负数和正数的区别。 + +### Code + +```java +class Solution { + public double Power(double base, int exponent) { + double result = 1; + for(int i = 0; i< Math.abs(exponent); i++){ + result *= base; + } + return exponent<0?(1/result):result; + } +} +``` diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/16. \345\220\210\345\271\266\344\270\244\344\270\252\346\216\222\345\272\217\347\232\204\351\223\276\350\241\250.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/16. \345\220\210\345\271\266\344\270\244\344\270\252\346\216\222\345\272\217\347\232\204\351\223\276\350\241\250.md" new file mode 100644 index 0000000..c6a8110 --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/16. \345\220\210\345\271\266\344\270\244\344\270\252\346\216\222\345\272\217\347\232\204\351\223\276\350\241\250.md" @@ -0,0 +1,45 @@ +### Title +输入两个递增排序的链表,合并这两个链表并使新链表中的结点仍然是按照递增排序的。 +### Demo +``` +输入:1->3->5 , 2->4->5 + +输出:1->2->3->4->5->5 +``` +### Analysis +归并排序思想 + +### Code + +```java +class Solution { + public ListNode merge(ListNode l1, ListNode l2) { + if(l1 == null && l2 == null){return null;} + ListNode result = new ListNode(-1); + ListNode temp = result; + while(l1 != null && l2 != null){ + if(l1.val > l2.val){ + temp.next = l2; + l2 = l2.next; + }else{ + temp.next = l1; + l1 = l1.next; + } + temp = temp.next; + } + // 还会有l1 或者 l2 未遍历完的情况 + while(l1 != null){ + temp.next = l1; + l1 = l1.next; + temp = temp.next; + } + while(l2 != null){ + temp.next = l2; + l2 = l2.next; + temp = temp.next; + } + return result.next; + + } +} +``` diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/17. \344\272\214\345\217\211\346\240\221\347\232\204\351\225\234\345\203\217.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/17. \344\272\214\345\217\211\346\240\221\347\232\204\351\225\234\345\203\217.md" new file mode 100644 index 0000000..dfc6f50 --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/17. \344\272\214\345\217\211\346\240\221\347\232\204\351\225\234\345\203\217.md" @@ -0,0 +1,43 @@ +### Title +输入一个二叉树,将它变换为它的镜像。 +### Demo +``` +输入树: + 8 + / \ + 6 10 + / \ / \ + 5 7 9 11 + + [8,6,10,5,7,9,11,null,null,null,null,null,null,null,null] +输出树: + 8 + / \ + 10 6 + / \ / \ + 11 9 7 5 + + [8,10,6,11,9,7,5,null,null,null,null,null,null,null,null] +``` +### Analysis + +递归思想, 交换当前节点的左右子节点, 然后遍历整个树 +### Code + +```java +class Solution { + public void mirror(TreeNode root) { + if(root == null){ + return ; + } + // swap(left,right) + TreeNode temp = root.left; + root.left = root.right; + root.right = temp; + + mirror(root.left); + mirror(root.right); + + } +} +``` diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/18. \346\240\221\347\232\204\345\255\220\347\273\223\346\236\204.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/18. \346\240\221\347\232\204\345\255\220\347\273\223\346\236\204.md" new file mode 100644 index 0000000..68e9745 --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/18. \346\240\221\347\232\204\345\255\220\347\273\223\346\236\204.md" @@ -0,0 +1,62 @@ +### Title +输入两棵二叉树A,B,判断B是不是A的子结构。 + +我们规定空树不是任何树的子结构。 +### Demo +``` +树A: + + 8 + / \ + 8 7 + / \ + 9 2 + / \ + 4 7 +树B: + + 8 + / \ + 9 2 +返回 true ,因为B是A的子结构。 +``` +### Analysis +这个题是个嵌套递归的题, 所以要将'树节点比较'的功能提出来, 根据其结果来决定是否需要进行子节点的比较. + +### Code + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +class Solution { + public boolean hasSubtree(TreeNode pRoot1, TreeNode pRoot2) { + // 规定空树不是任何树的子结构。 + if(pRoot1 == null || pRoot2 == null){ + return false; + } + // 如果两节点相同, 则递归其子节点是否相同 + if(isSame(pRoot1, pRoot2)){ + return true; + }else{ + return hasSubtree(pRoot1.left, pRoot2) || hasSubtree(pRoot1.right, pRoot2); + } + } + + public boolean isSame(TreeNode pRoot1, TreeNode pRoot2){ + if(pRoot2 == null){ + return true; + } + if(pRoot1 == null || pRoot1.val != pRoot2.val){ + return false; + } + return isSame(pRoot1.left, pRoot2.left) && isSame(pRoot1.right, pRoot2.right); + } +} +``` diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/19. \344\270\215\345\210\206\350\241\214\344\273\216\344\270\212\345\276\200\344\270\213\346\211\223\345\215\260\344\272\214\345\217\211\346\240\221.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/19. \344\270\215\345\210\206\350\241\214\344\273\216\344\270\212\345\276\200\344\270\213\346\211\223\345\215\260\344\272\214\345\217\211\346\240\221.md" new file mode 100644 index 0000000..690a0a8 --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/19. \344\270\215\345\210\206\350\241\214\344\273\216\344\270\212\345\276\200\344\270\213\346\211\223\345\215\260\344\272\214\345\217\211\346\240\221.md" @@ -0,0 +1,54 @@ +### Title +从上往下打印出二叉树的每个结点,同一层的结点按照从左到右的顺序打印。 +### Demo +``` +输入如下图所示二叉树[8, 12, 2, null, null, 6, null, 4, null, null, null] + 8 + / \ + 12 2 + / + 6 + / + 4 + +输出:[8, 12, 2, 6, 4] +``` +### Analysis +用一个队列来维护树遍历的顺序性, 然后一个个poll出来放到list中。 + +### Code + +```java +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +class Solution { + public List printFromTopToBottom(TreeNode root) { + if(root == null){ + return new ArrayList<>(); + } + List res = new ArrayList<>(); + Queue queue = new LinkedList<>(); + + queue.offer(root); + while(!queue.isEmpty()){ + TreeNode temp = queue.poll(); + res.add(temp.val); + if(temp.left != null){ + queue.add(temp.left); + } + if(temp.right != null){ + queue.add(temp.right); + } + + } + return res; + } +} +``` diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/2. \346\233\277\346\215\242\347\251\272\346\240\274.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/2. \346\233\277\346\215\242\347\251\272\346\240\274.md" new file mode 100644 index 0000000..96d81a2 --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/2. \346\233\277\346\215\242\347\251\272\346\240\274.md" @@ -0,0 +1,33 @@ +### Title +请实现一个函数,把字符串中的每个空格替换成"%20"。 + +你可以假定输入字符串的长度最大是1000。 +注意输出字符串的长度可能大于1000。 + +### Demo +``` +输入:"We are happy." + +输出:"We%20are%20happy." +``` +### Analysis +考察对char的处理 + +### Code +```java +class Solution { + public String replaceSpaces(StringBuffer str) { + char[] chars = str.toString().toCharArray(); + StringBuffer result = ""; + for(char i:chars){ + if(i == ' '){ + result.append("%20"); + }else{ + result.append(i); + } + } + return result.toString(); + } +} +``` + diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/20. \346\240\210\347\232\204\345\216\213\345\205\245\343\200\201\345\274\271\345\207\272\345\272\217\345\210\227.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/20. \346\240\210\347\232\204\345\216\213\345\205\245\343\200\201\345\274\271\345\207\272\345\272\217\345\210\227.md" new file mode 100644 index 0000000..54e97be --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/20. \346\240\210\347\232\204\345\216\213\345\205\245\343\200\201\345\274\271\345\207\272\345\272\217\345\210\227.md" @@ -0,0 +1,46 @@ +### Title +输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。 + +假设压入栈的所有数字均不相等。 + +例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。 + +**注意**:若两个序列长度不等则视为并不是一个栈的压入、弹出序列。若两个序列都为空,则视为是一个栈的压入、弹出序列。 +### Demo +``` +输入:[1,2,3,4,5] + [4,5,3,2,1] + +输出:true +``` +### Analysis + +用一个真实的栈来辅助, 当pop的值和弹出序列的某个数相等&&栈不为空, 则弹出这个数。最后看栈是否为空, 如果不是正确的压入弹出顺序, 栈是空不了的。 +### Code + +```java +class Solution { + public boolean isPopOrder(int [] pushV,int [] popV) { + // 如果两个数组为空, 直接返回true, 两个数组长度不等, 返回false + if(pushV == null && popV == null){ + return true; + } + if(pushV.length != popV.length){ + return false; + } + // 新建一个栈, 将push一个一个放入, 并判断 + // 如果元素与popV的top元素相等, 则弹出popV, 否则继续在stack里放元素 + // 如果顺序正确的话, PopV应该会为空值 + Stack stack = new Stack<>(); + int index = 0; + for(int i = 0; i< popV.length; i++){ + stack.push(pushV[i]); + while(!stack.isEmpty() && stack.peek() == popV[index]){ + stack.pop(); + index++; + } + } + return stack.isEmpty(); + } +} +``` diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/21. \345\214\205\345\220\253min\345\207\275\346\225\260\347\232\204\346\240\210.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/21. \345\214\205\345\220\253min\345\207\275\346\225\260\347\232\204\346\240\210.md" new file mode 100644 index 0000000..0aa1953 --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/21. \345\214\205\345\220\253min\345\207\275\346\225\260\347\232\204\346\240\210.md" @@ -0,0 +1,62 @@ +### Title +设计一个支持push,pop,top等操作并且可以在O(1)时间内检索出最小元素的堆栈。 + +- push(x)–将元素x插入栈中 +- pop()–移除栈顶元素 +- top()–得到栈顶元素 +- getMin()–得到栈中最小元素 +### Demo +``` +MinStack minStack = new MinStack(); +minStack.push(-1); +minStack.push(3); +minStack.push(-4); +minStack.getMin(); --> Returns -4. +minStack.pop(); +minStack.top(); --> Returns 3. +minStack.getMin(); --> Returns -1. +``` +### Analysis +准备两个栈, 一个用来放当前入栈的最小值, 一个用来入栈全量数据。 + +当当前的值比min的栈顶还小, 则将该值放到min栈中去。 + +### Code + +```java +class MinStack { + + Stack stk = new Stack<>(); + Stack min = new Stack<>(); + /** initialize your data structure here. */ + public MinStack() { + + } + + public void push(int x) { + if(min.empty() || min.peek() >= x){ + min.push(x); + } + stk.push(x); + } + + public void pop() { + if(stk.peek() == min.peek()){ + min.pop(); + } + stk.pop(); + } + + public int top() { + return stk.peek(); + } + + public int getMin() { + if(!min.empty()){ + return min.peek(); + } + return stk.peek(); + } +} + +``` diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/22. \351\241\272\346\227\266\351\222\210\346\211\223\345\215\260\347\237\251\351\230\265.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/22. \351\241\272\346\227\266\351\222\210\346\211\223\345\215\260\347\237\251\351\230\265.md" new file mode 100644 index 0000000..7e599b1 --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/22. \351\241\272\346\227\266\351\222\210\346\211\223\345\215\260\347\237\251\351\230\265.md" @@ -0,0 +1,80 @@ +### Title +输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。 +### Demo +``` +输入: +[ + [1, 2, 3, 4], + [5, 6, 7, 8], + [9,10,11,12] +] + +输出:[1,2,3,4,8,12,11,10,9,5,6,7] +``` +### Analysis + +1. 获取到二维数组的列数和行数 +2. 往右方向遍历 == x不变, y++ +3. 同理每个方向都遍历一遍 +4. 以一圈为单位遍历 + +### Code + +```java +class Solution { + public int[] printMatrix(int[][] matrix) { + if(matrix == null|| matrix.length == 0){return new int[0];} + int startX = 0; + int endX = matrix.length; + int startY = 0; + int endY = matrix[0].length; + //System.out.println(startX + " " + endX + " " + startY + " " + endY); + int res[] = new int[endX * endY ]; + while((startX < endX) && (startY < endY)){ + printMatrix(res, matrix, startX, endX-1, startY, endY-1); + startX++; + startY++; + endX--; + endY--; + } + return res; + } + public int index = 0; + public void printMatrix(int res[], int[][] matrix, int startX, int endX, int startY, int endY){ + // 向右 ➡ + if(startX == endX){ + // 说明只能竖着来了, 为什么要等于, 因为怕拉下最后一个元素了 + for(int i = startY; i <= endY; i++){ + res[index++] = matrix[startX][i]; + } + return; + } + + // 向下 ⬇ + if(startY == endY){ + // 说明只能竖着来了 + for(int i = startX; i <= endX; i++){ + res[index++] = matrix[i][startY]; + } + return; + } + // 向右 ➡ + for(int i = startY; i < endY; i++){ + res[index++] = matrix[startX][i]; + } + // 向下 ⬇ + for(int i = startX; i < endX; i++){ + res[index++] = matrix[i][endY]; + } + // 向左 ⬅ + for(int i = endY; i > startY; i--){ + res[index++] = matrix[endX][i]; + } + // 向上 ⬆ y不变x变 + for(int i = endX; i > startX; i--){ + res[index++] = matrix[i][startY]; + } + + } +} +``` diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/23. \346\225\260\347\273\204\344\270\255\345\207\272\347\216\260\346\254\241\346\225\260\350\266\205\350\277\207\344\270\200\345\215\212\347\232\204\346\225\260\345\255\227.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/23. \346\225\260\347\273\204\344\270\255\345\207\272\347\216\260\346\254\241\346\225\260\350\266\205\350\277\207\344\270\200\345\215\212\347\232\204\346\225\260\345\255\227.md" new file mode 100644 index 0000000..c00ea51 --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/23. \346\225\260\347\273\204\344\270\255\345\207\272\347\216\260\346\254\241\346\225\260\350\266\205\350\277\207\344\270\200\345\215\212\347\232\204\346\225\260\345\255\227.md" @@ -0,0 +1,43 @@ +### Title +数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。 + +假设数组非空,并且一定存在满足条件的数字。 + +假设要求只能使用 O(n) 的时间和额外 O(1) 的空间,该怎么做呢? +### Demo +``` +输入:[1,2,1,1,3] + +输出:1 +``` +### Analysis +如果数组中有一个数字出现的次数超过数组长度的一半, 那这个数的数量一定大于别的数的个数总和。 + + +### Code + +```java +class Solution { + public int moreThanHalfNum_Solution(int[] nums) { + if(nums.length == 0){ + return -1; + } + int count = 1; + int val = nums[0]; + for(int i = 0; i< nums.length; i++){ + if(val == nums[i]){ + count ++; + }else{ + count --; + } + + if(count == 0){ + // 如果count为0 , 则val设置为当前值 + val = nums[i]; + count = 1; + } + } + return val; + } +} +``` diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/24. \344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\347\232\204\345\220\216\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/24. \344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\347\232\204\345\220\216\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227.md" new file mode 100644 index 0000000..944a033 --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/24. \344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\347\232\204\345\220\216\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227.md" @@ -0,0 +1,41 @@ +### Title +输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。 + +如果是则返回true,否则返回false。 + +假设输入的数组的任意两个数字都互不相同。 +### Demo +``` +输入:[4, 8, 6, 12, 16, 14, 10] + +输出:true +``` +### Analysis + +后序遍历的特点是根在最后, 拿这个为突破点, 因为是搜索树, 所以自然有序, 拿到不符合有序的交界节点, 则其前应为小数升序, 其后为大数升序, 不满足则返回false +### Code + +```java +class Solution { + public boolean verifySequenceOfBST(int [] sequence) { + // 迭代, 拿到最后一个元素 即根节点 + return dfs(sequence, 0 , sequence.length-1); + } + public boolean dfs(int [] sequence, int l, int r){ + if(l >= r){return true;} + // 拿到最后一个元素 + int root = sequence[r]; + // 遍历前面比root小的数, 拿到一个k + int k = l; + while( k < r && sequence[k] getLeastNumbers_Solution(int [] input, int k) { + PriorityQueue q = new PriorityQueue<>((o1,o2)->o2-o1); + for(int i = 0; i< input.length; i++){ + q.offer(input[i]); + if(q.size() > k){ + q.poll(); + } + } + var res = new ArrayList<>(q); + Collections.sort(res); + return res; + } +} +``` diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/26. \350\277\236\347\273\255\345\255\220\346\225\260\347\273\204\347\232\204\346\234\200\345\244\247\345\222\214.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/26. \350\277\236\347\273\255\345\255\220\346\225\260\347\273\204\347\232\204\346\234\200\345\244\247\345\222\214.md" new file mode 100644 index 0000000..ab3e1ba --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/26. \350\277\236\347\273\255\345\255\220\346\225\260\347\273\204\347\232\204\346\234\200\345\244\247\345\222\214.md" @@ -0,0 +1,40 @@ +### Title +输入一个 非空 整型数组,数组里的数可能为正,也可能为负。 + +数组中一个或连续的多个整数组成一个子数组。 + +求所有子数组的和的最大值。 + +要求时间复杂度为O(n)。 +### Demo +``` +输入:[1, -2, 3, 10, -4, 7, 2, -5] + +输出:18 +``` +### Analysis +如果sum和都比当前数字小, 则sum改为当前值 + +如果sum比res大, 则res为sum值 +### Code + +```java +class Solution { + public int maxSubArray(int[] nums) { + int sum = nums[0]; + int res = nums[0]; + for(int i = 1; i< nums.length; i++){ + sum += nums[i]; + // 如果sum和都比当前数字小, 则sum为当前值 + if(sum < nums[i]){ + sum = nums[i]; + } + // 如果sum比res大, 则res为sum值 + if(sum > res){ + res = sum; + } + } + return res; + } +} +``` diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/27. \344\272\214\345\217\211\346\240\221\344\270\255\345\222\214\344\270\272\346\237\220\344\270\200\345\200\274\347\232\204\350\267\257\345\276\204.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/27. \344\272\214\345\217\211\346\240\221\344\270\255\345\222\214\344\270\272\346\237\220\344\270\200\345\200\274\347\232\204\350\267\257\345\276\204.md" new file mode 100644 index 0000000..3de0d22 --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/27. \344\272\214\345\217\211\346\240\221\344\270\255\345\222\214\344\270\272\346\237\220\344\270\200\345\200\274\347\232\204\350\267\257\345\276\204.md" @@ -0,0 +1,45 @@ +### Title +输入一棵二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。 + +从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。 +### Demo +``` +给出二叉树如下所示,并给出num=22。 + 5 + / \ + 4 6 + / / \ + 12 13 6 + / \ / \ +9 1 5 1 + +输出:[[5,4,12,1],[5,6,6,5]] +``` +### Analysis + +### Code + +```java +class Solution { + private List> res = new ArrayList<>(); + public List> findPath(TreeNode root, int sum) { + dfs(root, sum, new ArrayList<>()); + return res; + } + + private void dfs(TreeNode root, int sum, List path){ + if(root == null){ + return; + } + path.add(root.val); + sum -= root.val; + if(sum == 0 && root.left == null && root.right == null){ + res.add(new ArrayList<>(path)); + }else{ + dfs(root.left, sum, path); + dfs(root.right, sum, path); + } + path.remove( path.size() -1); + } +} +``` diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/28. \344\272\214\345\217\211\346\240\221\347\232\204\346\267\261\345\272\246.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/28. \344\272\214\345\217\211\346\240\221\347\232\204\346\267\261\345\272\246.md" new file mode 100644 index 0000000..3fa7955 --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/28. \344\272\214\345\217\211\346\240\221\347\232\204\346\267\261\345\272\246.md" @@ -0,0 +1,34 @@ +### Title + +输入一棵二叉树的根结点,求该树的深度。 + +从根结点到叶结点依次经过的结点(含根、叶结点)形成树的一条路径,最长路径的长度为树的深度。 + +### Demo +``` +输入:二叉树[8, 12, 2, null, null, 6, 4, null, null, null, null]如下图所示: + 8 + / \ + 12 2 + / \ + 6 4 + +输出:3 +``` +### Analysis +递归基础题 + +### Code + +```java +class Solution { + public int treeDepth(TreeNode root) { + if(root == null){ + return 0; + } + int left = treeDepth(root.left); + int right = treeDepth(root.right); + return left>right?left+1:right+1; + } +} +``` diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/29. \345\255\227\347\254\246\344\270\262\344\270\255\347\254\254\344\270\200\344\270\252\345\217\252\345\207\272\347\216\260\344\270\200\346\254\241\347\232\204\345\255\227\347\254\246.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/29. \345\255\227\347\254\246\344\270\262\344\270\255\347\254\254\344\270\200\344\270\252\345\217\252\345\207\272\347\216\260\344\270\200\346\254\241\347\232\204\345\255\227\347\254\246.md" new file mode 100644 index 0000000..3c4ea55 --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/29. \345\255\227\347\254\246\344\270\262\344\270\255\347\254\254\344\270\200\344\270\252\345\217\252\345\207\272\347\216\260\344\270\200\346\254\241\347\232\204\345\255\227\347\254\246.md" @@ -0,0 +1,43 @@ +### Title + +在字符串中找出第一个只出现一次的字符。 + +如输入"abaccdeff",则输出b。 + +如果字符串中不存在只出现一次的字符,返回#字符。 + +### Demo +``` +输入:"abaccdeff" + +输出:'b' +``` +### Analysis +利用map, 将字符作为key, 出现次数作为value, 返回第一个value为1的key。 + +### Code + +```java +class Solution { + public char firstNotRepeatingChar(String s) { + char chars [] = s.toCharArray(); + Map map = new LinkedHashMap<>(); + for(int i = 0; i< s.length(); i++){ + int count = 0; + if(map.containsKey(chars[i])){ + count = map.get(chars[i]); + } + // System.out.println(chars[i] + " " + (count+1) ); + map.put(chars[i], ++count); + } + + for(Map.Entry entry:map.entrySet()){ + // System.out.println(entry.getKey() + " " + entry.getValue() ); + if(entry.getValue() == 1){ + return entry.getKey(); + } + } + return '#'; + } +} +``` diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/3. \344\273\216\345\260\276\345\210\260\345\244\264\346\211\223\345\215\260\351\223\276\350\241\250.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/3. \344\273\216\345\260\276\345\210\260\345\244\264\346\211\223\345\215\260\351\223\276\350\241\250.md" new file mode 100644 index 0000000..3424924 --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/3. \344\273\216\345\260\276\345\210\260\345\244\264\346\211\223\345\215\260\351\223\276\350\241\250.md" @@ -0,0 +1,40 @@ +### Title +输入一个链表的头结点,按照 从尾到头 的顺序返回节点的值。 + +返回的结果用数组存储。 +### Demo +``` +输入:[2, 3, 5] +返回:[5, 3, 2] +``` +### Analysis +反转链表 -- 基础题 +### Code + +```java +class Solution { + public int[] printListReversingly(ListNode head) { + // 声明三个节点 + ListNode pre = null; + ListNode node = head; + ListNode next = null; + int count = 1; + while(node.next != null){ + // 先拿出next节点, 然后next节点应该指向pre, pre指向当前节点 + next = node.next; + node.next = pre; + pre = node; + node = next; + count++; + } + node.next = pre; + int nums [] = new int[count]; + int i = 0; + while(node != null){ + nums[i++] = node.val; + node = node.next; + } + return nums; + } +} +``` diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/30. \346\234\272\345\231\250\344\272\272\347\232\204\350\277\220\345\212\250\350\214\203\345\233\264.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/30. \346\234\272\345\231\250\344\272\272\347\232\204\350\277\220\345\212\250\350\214\203\345\233\264.md" new file mode 100644 index 0000000..0c562eb --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/30. \346\234\272\345\231\250\344\272\272\347\232\204\350\277\220\345\212\250\350\214\203\345\233\264.md" @@ -0,0 +1,85 @@ +### Title +地上有一个 m 行和 n 列的方格,横纵坐标范围分别是 0∼m−1 和 0∼n−1。 + +一个机器人从坐标 (0,0) 的格子开始移动,每一次只能向左,右,上,下四个方向移动一格。 + +但是不能进入行坐标和列坐标的数位之和大于 k 的格子。 + +请问该机器人能够达到多少个格子? + +注意: +1. 0<=m<=50 +2. 0<=n<=50 +3. 0<=k<=100 + +### Demo +``` +输入:k=18, m=40, n=40 + +输出:1484 + +解释:当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。 + 但是,它不能进入方格(35,38),因为3+5+3+8 = 19。 +``` + +### Analysis +用标记法,对已走过的路径进行标记,然后递归判断上下左右是否满足下一个字符,以此类推 + + +### Coding +```java +class Solution { + private static class Node { + int first; + int second; + + public Node(int first, int second) { + this.first = first; + this.second = second; + } + } + + private boolean[][] visited = new boolean[55][55]; + private static final int[][] nxt = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; + + public int movingCount(int threshold, int rows, int cols) { + if (rows < 1 || cols < 1) + return 0; + + Queue que = new LinkedList<>(); + visited[0][0] = true; + que.add(new Node(0, 0)); + int res = 0; + + while (!que.isEmpty()) { + Node popNode = que.poll(); + res++; + + for (int i = 0; i < nxt.length; i++) { + int fx = popNode.first + nxt[i][0], fy = popNode.second + nxt[i][1]; + if (fx >= 0 && fy >= 0 && fx < rows && fy < cols && getSum(fx, fy) <= threshold && !visited[fx][fy]) { + que.add(new Node(fx, fy)); + visited[fx][fy] = true; + } + } + } + + return res; + } + + private int getSum(int rows, int cols) { + int s = 0; + while (rows > 0) { + s += rows % 10; + rows /= 10; + } + + while (cols > 0) { + s += cols % 10; + cols /= 10; + } + + return s; + } +} +``` \ No newline at end of file diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/31. \347\237\251\351\230\265\344\270\255\347\232\204\350\267\257\345\276\204.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/31. \347\237\251\351\230\265\344\270\255\347\232\204\350\267\257\345\276\204.md" new file mode 100644 index 0000000..2a679b5 --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/31. \347\237\251\351\230\265\344\270\255\347\232\204\350\267\257\345\276\204.md" @@ -0,0 +1,78 @@ +### Title +请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。 + +路径可以从矩阵中的任意一个格子开始,每一步可以在矩阵中向左,向右,向上,向下移动一个格子。 + +如果一条路径经过了矩阵中的某一个格子,则之后不能再次进入这个格子。 + +注意: + +输入的路径不为空; +所有出现的字符均为大写英文字母; +数据范围 +矩阵中元素的总个数 [0,900]。 +路径字符串的总长度 [0,900]。 + +### Demo +``` +matrix= +[ + ["A","B","C","E"], + ["S","F","C","S"], + ["A","D","E","E"] +] + + +str="BCCE" , return "true" + +str="ASAE" , return "false" +``` + + +### Analysis +用标记法,对已走过的路径进行标记,然后递归判断上下左右是否满足下一个字符,以此类推 + +### Coding +```java +class Solution { + public boolean hasPath(char[][] matrix, String str) { + if(matrix == null || str == "" || matrix.length == 0){ + return false; + } + int rows = matrix.length; + int cols = matrix[0].length; + boolean [][] visited = new boolean[rows][cols]; + int length = 0; + for(int i = 0; i< rows; i++){ + for(int j = 0; j < cols; j++){ + if(dfs(matrix, visited, str, rows, cols, i, j, length)){ + return true; + } + } + } + return false; + } + + public boolean dfs(char[][] matrix, boolean[][] visited, String str, int rows, int cols, int row, int col, int length){ + // 如果未读且 + int strLength = str.length(); + boolean flag = false; + if(row>=0 && row < rows && col >=0 && col < cols && visited[row][col] == false && str.charAt(length) == matrix[row][col]){ + length++; + visited[row][col] = true; + if(length == strLength){ + return true; + } + flag = dfs(matrix, visited, str, rows, cols, row+1, col, length)|| + dfs(matrix, visited, str, rows, cols, row, col-1, length)|| + dfs(matrix, visited, str, rows, cols, row, col+1, length)|| + dfs(matrix, visited, str, rows, cols, row-1, col, length); + if(!flag){ + length--; + visited[row][col] = false; + } + } + return flag; + } +} +``` \ No newline at end of file diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/32. \345\211\252\347\273\263\345\255\220.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/32. \345\211\252\347\273\263\345\255\220.md" new file mode 100644 index 0000000..e63a107 --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/32. \345\211\252\347\273\263\345\255\220.md" @@ -0,0 +1,70 @@ +### Title +给你一根长度为 n 绳子,请把绳子剪成 m 段(m、n 都是整数,2≤n≤58 并且 m≥2)。 + +每段的绳子的长度记为 k[1]、k[2]、……、k[m]。 + +k[1]k[2]…k[m] 可能的最大乘积是多少? + +例如当绳子的长度是 8 时,我们把它剪成长度分别为 2、3、3 的三段,此时得到最大的乘积 18。 +### Demo +``` +输入:8 + +输出:18 +``` + + +### Analysis + +```aidl +2: 1x1 +3: 1x2 +4: 2x2 +5: 2x3 +6: 2x2x2 +7: 2x2x3 +``` +根据上面来看,拆分成2和3肯定是乘积最大. +可以网上查一下数学推导,也可以用动态规划 + +### Coding +```java +class Solution { + public int maxProductAfterCutting(int length) + { + + if(length <= 3){ + return 1*(length-1); + } + int result = 1; + if(length % 3 == 1){ + result = 4; + length -= 4; + }else if(length % 3 == 2){ + result = 2; + length -= 2; + } + + while(length != 0){ + result *= 3; + length -= 3; + } + + return result; + } +} +``` + +动态规划: +``` +public int maxProductAfterCutting(int target) { + int[] dp = new int[target + 1]; + dp[1] = 1; + for (int i = 2; i <= target; i++) { + for (int j = 1; j < i; j++) { + dp[i] = Math.max(dp[i], (Math.max(j, dp[j])) * (Math.max(i - j, dp[i - j]))); + } + } + return dp[target]; + } +``` \ No newline at end of file diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/33. \346\211\223\345\215\260\344\273\2161\345\210\260\346\234\200\345\244\247\347\232\204n\344\275\215\346\225\260.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/33. \346\211\223\345\215\260\344\273\2161\345\210\260\346\234\200\345\244\247\347\232\204n\344\275\215\346\225\260.md" new file mode 100644 index 0000000..c5baf4f --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/33. \346\211\223\345\215\260\344\273\2161\345\210\260\346\234\200\345\244\247\347\232\204n\344\275\215\346\225\260.md" @@ -0,0 +1,34 @@ +### Title +输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数 999。 +1. 用返回一个整数列表来代替打印 +2. n 为正整数,0 < n <= 5 +### Demo +```aidl +输入:1 +返回值:[1,2,3,4,5,6,7,8,9] +``` + + + +### Analysis + + + +### Coding +```java +class Solution { + public int[] printNumbers (int n) { + // write code here + int high = 1; + while(n>0){ + high*=10; + n--; + } + int res[] = new int[high-1]; + for(int i=1;i 1 -> 9 +``` + + + +### Analysis + + + +### Coding +```java +class Solution { + public int[] printNumbers (int n) { + // write code here + int high = 1; + while(n>0){ + high*=10; + n--; + } + int res[] = new int[high-1]; + for(int i=1;ij){ + //这区间整体向后移动一位 + array[k] = array[k-1]; + k--; + } + //移位之后将对应的值赋值 + array[k] = temp; + j++; + } + } + //返回结果数数组 + return array; + } +} +``` diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/36. \351\223\276\350\241\250\344\270\255\347\216\257\347\232\204\345\205\245\345\217\243\347\273\223\347\202\271.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/36. \351\223\276\350\241\250\344\270\255\347\216\257\347\232\204\345\205\245\345\217\243\347\273\223\347\202\271.md" new file mode 100644 index 0000000..b1f679e --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/36. \351\223\276\350\241\250\344\270\255\347\216\257\347\232\204\345\205\245\345\217\243\347\273\223\347\202\271.md" @@ -0,0 +1,64 @@ +### Title +给定一个链表,若其中包含环,则输出环的入口节点。 + +若其中不包含环,则输出null。 + +数据范围 +节点 val 值取值范围 [1,1000]。 +链表长度 [0,500]。 + + +### Demo +``` +[1, 2, 3, 4, 5, 6] +2 +注意,这里的2表示编号是2的节点,节点编号从0开始。所以编号是2的节点就是val等于3的节点。 + +则输出环的入口节点3. +``` + +### Analysis +假设快指针在环中走了nnn圈,慢指针在环中走了mmm圈,它们才相遇,而进入环之前的距离为xxx,环入口到相遇点的距离为yyy,相遇点到环入口的距离为zzz。快指针一共走了x+n(y+z)+yx+n(y+z)+yx+n(y+z)+y步,慢指针一共走了x+m(y+z)+yx+m(y+z)+yx+m(y+z)+y,这个时候快指针走的倍数是慢指针的两倍,则x+n(y+z)+y=2(x+m(y+z)+y)x+n(y+z)+y=2(x+m(y+z)+y)x+n(y+z)+y=2(x+m(y+z)+y),这时候x+y=(n−2m)(y+z)x+y=(n-2m)(y+z)x+y=(n−2m)(y+z),因为环的大小是y+zy+zy+z,说明从链表头经过环入口到达相遇地方经过的距离等于整数倍环的大小:那我们从头开始遍历到相遇位置,和从相遇位置开始在环中遍历,会使用相同的步数,而双方最后都会经过入口到相遇位置这yyy个节点,那说明这yyy个节点它们就是重叠遍历的,那它们从入口位置就相遇了 + +### Code + +```java +class Solution { + //判断有没有环,返回相遇的地方 + public ListNode hasCycle(ListNode head) { + //先判断链表为空的情况 + if(head == null) + return null; + //快慢双指针 + ListNode fast = head; + ListNode slow = head; + //如果没环快指针会先到链表尾 + while(fast != null && fast.next != null){ + //快指针移动两步 + fast = fast.next.next; + //慢指针移动一步 + slow = slow.next; + //相遇则有环,返回相遇的位置 + if(fast == slow) + return slow; + } + //到末尾说明没有环,返回null + return null; + } + + public ListNode EntryNodeOfLoop(ListNode pHead) { + ListNode slow = hasCycle(pHead); + //没有环 + if(slow == null) + return null; + //快指针回到表头 + ListNode fast = pHead; + //再次相遇即是环入口 + while(fast != slow){ + fast = fast.next; + slow = slow.next; + } + return slow; + } +} +``` diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/36\350\277\233\345\210\266\346\225\260\347\232\204\345\212\240\346\263\225\350\277\220\347\256\227.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/36\350\277\233\345\210\266\346\225\260\347\232\204\345\212\240\346\263\225\350\277\220\347\256\227.md" new file mode 100644 index 0000000..124b291 --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/36\350\277\233\345\210\266\346\225\260\347\232\204\345\212\240\346\263\225\350\277\220\347\256\227.md" @@ -0,0 +1,97 @@ +### Title +36进制由`0-9`,`a-z`,共36个字符表示,最小为`'0'` + +`0 9`对应十进制的`09`,`'a''z'`对应十进制的`10 35` + +要求按照加法规则计算出任意两个36进制正整数的和 + +**要求**:不允许把36进制数字整体转为10进制数字,计算出10进制数字的相加结果再转回为36进制 +### Demo +`'1b' + '2x' = '48'` + +### Analysis +这个题的主要思路是巧妙的用一个字符串来实现36进制与10进制的映射关系。 + +`String symbol = "0123456789abcdefghijklmnopqrstuvwxyz";` + +比如z, z所在位置是`symbol.charAt(35);`也就是10进制的35。 + +相反, 已知36进制后, 在这个字符串中找到相应的index位置, 这个index就是10进制。 + +然后从低位数开始将10进制数进行运算并处理好36进位1. + +### Coding +```java +class Solution { + public String addFunWithStr(String param1, String param2){ + StringBuffer stringBuffer = new StringBuffer(); + String symbol = "0123456789abcdefghijklmnopqrstuvwxyz"; + int param1Len = param1.length(); + int param2Len = param2.length(); + + int i = param1Len - 1; + int j = param2Len - 1; + + if (i < 0 || j < 0) { + return null; + } + + int temp = 0; + while (i >= 0 && j >= 0) { + // 获取低位数 + char ch_1 = param1.charAt(i); + char ch_2 = param2.charAt(j); + // 根据0-z字符串获取index + int v1 = getIntFromChar(ch_1); + int v2 = getIntFromChar(ch_2); + + int ret = v1 + v2; + // 如果结果>36 则需要处理进位 + if (ret >= 36) { + int index = ret - 36 + temp; + char sv = symbol.charAt(index); + stringBuffer.append(sv); + temp = 1; //进位 + } else { + int index = ret + temp; + char sv = symbol.charAt(index); + stringBuffer.append(sv); + temp = 0; + } + + i--; + j--; + + } + + // 与归并排序类似, 需要将剩余的位数加上 + while (i >= 0) { + char ch_1 = param1.charAt(i); + stringBuffer.append(ch_1); + i--; + } + + while (j >= 0) { + char ch_2 = param2.charAt(i); + stringBuffer.append(ch_2); + j--; + } + + // 因为一直是append操作,低位在前面,所以需要逆序 + StringBuffer result = stringBuffer.reverse(); + return result.toString(); + } + public static int getIntFromChar(char ch) { + + int ret = -1; + // 如果字母是 0-9, 则直接-0 + if (ch >='0' && ch <= '9') { + ret = ch - '0'; + // 如果是大于9,则算一下是第几个index,等价于-'a'+10(数字0-9占用10个) + } else if (ch >= 'a' && ch <= 'z') { + ret = (ch - 'a') + 10; + } + return ret; + } +} +``` \ No newline at end of file diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/4. \346\226\220\346\263\242\351\202\243\345\245\221\346\225\260\345\210\227.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/4. \346\226\220\346\263\242\351\202\243\345\245\221\346\225\260\345\210\227.md" new file mode 100644 index 0000000..493a1ce --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/4. \346\226\220\346\263\242\351\202\243\345\245\221\346\225\260\345\210\227.md" @@ -0,0 +1,37 @@ +### Title +输入一个整数 n ,求斐波那契数列的第 n 项。 + +假定从0开始,第0项为0。(n<=39) +### Demo +``` +输入整数 n=5 + +返回 5 +``` +### Analysis +最基础的做法就是用递归, 进阶做法用动态规划 +### Code + +```java +class Solution { + public int Fibonacci(int n) { + // 如果是 1 和 2 则直接返回 + if(n == 1 || n == 2){ + return 1; + } + // 否则的话动态规划 + n -= 2; + + int a = 1; + int b = 1; + int c = 0; + while(n > 0){ + c = a + b; + a = b; + b = c; + n--; + } + return c; + } +} +``` diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/5. \350\267\263\345\217\260\351\230\266.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/5. \350\267\263\345\217\260\351\230\266.md" new file mode 100644 index 0000000..cc48b6d --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/5. \350\267\263\345\217\260\351\230\266.md" @@ -0,0 +1,33 @@ +### Title +一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。 +### Demo +``` +略 +``` +### Analysis +与《4. 斐波那契数列》类似, 就是第二个数变成了2而不是1 +### Code + +```java +public class Solution { + public int JumpFloor(int n) { + // 如果是 1 和 2 则直接返回 + if(n <= 2){ + return n; + } + // 否则的话动态规划 + n -= 2; + + int a = 1; + int b = 2; + int c = 0; + while(n > 0){ + c = a + b; + a = b; + b = c; + n--; + } + return c; + } +} +``` diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/6. \347\224\250\344\270\244\344\270\252\346\240\210\345\256\236\347\216\260\351\230\237\345\210\227.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/6. \347\224\250\344\270\244\344\270\252\346\240\210\345\256\236\347\216\260\351\230\237\345\210\227.md" new file mode 100644 index 0000000..24025e7 --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/6. \347\224\250\344\270\244\344\270\252\346\240\210\345\256\236\347\216\260\351\230\237\345\210\227.md" @@ -0,0 +1,48 @@ +### Title +请用栈实现一个队列,支持如下四种操作: + +push(x) – 将元素x插到队尾; +pop() – 将队首的元素弹出,并返回该元素; +peek() – 返回队首元素; +empty() – 返回队列是否为空; + +**注意**: + +你只能使用栈的标准操作:push to top,peek/pop from top, size 和 is empty; +如果你选择的编程语言没有栈的标准库,你可以使用list或者deque等模拟栈的操作; +输入数据保证合法,例如,在队列为空时,不会进行pop或者peek等操作; +### Demo +``` +MyQueue queue = new MyQueue(); + +queue.push(1); +queue.push(2); +queue.peek(); // returns 1 +queue.pop(); // returns 1 +queue.empty(); // returns false +``` +### Analysis +使用两个栈, push的时候都往栈1push, pop的时候将栈1的东西pop出来push到栈2 +### Code + +```java +import java.util.Stack; + +public class Solution { + Stack stack1 = new Stack(); + Stack stack2 = new Stack(); + + public void push(int node) { + stack1.push(node); + } + + public int pop() { + if(stack2.empty()){ + while(!stack1.empty()){ + stack2.push(stack1.pop()); + } + } + return stack2.pop(); + } +} +``` diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/7. \346\227\213\350\275\254\346\225\260\347\273\204\347\232\204\346\234\200\345\260\217\346\225\260\345\255\227.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/7. \346\227\213\350\275\254\346\225\260\347\273\204\347\232\204\346\234\200\345\260\217\346\225\260\345\255\227.md" new file mode 100644 index 0000000..98518bd --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/7. \346\227\213\350\275\254\346\225\260\347\273\204\347\232\204\346\234\200\345\260\217\346\225\260\345\255\227.md" @@ -0,0 +1,59 @@ +### Title +把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 + +输入一个升序的数组的一个旋转,输出旋转数组的最小元素。 + +例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 + +数组可能包含重复项。 + +**注意**:数组内所含元素非负,若数组大小为0,请返回-1。 +### Demo +``` +输入:nums=[2,2,2,0,1] + +输出:0 +``` +### Analysis +``` +情景1: + _____ + / +___/ + +情景2: + \ +____ \______ + \ + \ + +最后方案: + 如果有前后相等的情况, 则将相等的数字区间去掉, 然后使用二分法进行搜索。 +``` + +### Code + +```java +class Solution { + public int findMin(int[] nums) { + if(nums.length == 0){ + return -1; + } + int start = 0; + int end = nums.length-1; + // 消除前后相平的场景 + while(nums[0] == nums[end]){ + end--; + } + while(start < end){ + int mid = start + end >> 1; + if(nums[mid] > nums[end]){ + start = mid+1; + }else{ + end = mid; + } + } + return nums[end]; + } +} +``` diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/8. \345\217\230\346\200\201\350\267\263\345\217\260\351\230\266.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/8. \345\217\230\346\200\201\350\267\263\345\217\260\351\230\266.md" new file mode 100644 index 0000000..acdc9f9 --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/8. \345\217\230\346\200\201\350\267\263\345\217\260\351\230\266.md" @@ -0,0 +1,42 @@ +### Title +一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。 + +### Demo +``` +略 +``` +### Analysis + +``` +f(1) = 1 +f(2) = f(2-1) + f(2-2) //f(2-2) 表示2阶一次跳2阶的次数。 +f(3) = f(3-1) + f(3-2) + f(3-3) +... + +∵ f(n) = f(n-1) + f(n-2) + f(n-3) + ... + f(n-(n-1)) + f(n-n) + = f(0) + f(1) + f(2) + f(3) + ... + f(n-1) + f(n-1) = f(0) + f(1)+f(2)+f(3) + ... + f((n-1)-1) = f(0) + f(1) + f(2) + f(3) + ... + f(n-2) +∴ f(n) = 2*f(n-1) + + | 1 ,(n=0 ) +f(n) = | 1 ,(n=1 ) + | 2*f(n-1),(n>=2) + +``` +### Code + +```java + +public class Solution { + public int JumpFloorII(int target) { + if (target <= 0) { + return -1; + } else if (target == 1) { + return 1; + } else { + return 2 * JumpFloorII(target - 1); + } + } +} + +``` diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/9. \344\272\214\350\277\233\345\210\266\344\270\2551\347\232\204\344\270\252\346\225\260.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/9. \344\272\214\350\277\233\345\210\266\344\270\2551\347\232\204\344\270\252\346\225\260.md" new file mode 100644 index 0000000..130a6ea --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/9. \344\272\214\350\277\233\345\210\266\344\270\2551\347\232\204\344\270\252\346\225\260.md" @@ -0,0 +1,36 @@ +### Title +输入一个32位整数,输出该数二进制表示中1的个数。 + +**注意** + +负数在计算机中用其绝对值的补码来表示。 +### Demo +``` +样例1 +输入:9 +输出:2 +解释:9的二进制表示是1001,一共有2个1。 + +样例2 +输入:-2 +输出:31 +解释:-2在计算机里会被表示成11111111111111111111111111111110, + 一共有31个1。 +``` +### Analysis +将n与n-1二进制取与(同时该位就变成0了), 看一共能取几次 +### Code + +```java +class Solution { + public int NumberOf1(int n) + { + int count = 0; + while(n != 0){ + n = n&(n-1); + count++; + } + return count; + } +} +``` diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/\344\272\214\345\217\211\346\240\221\344\270\255\345\272\217\351\201\215\345\216\206\351\235\236\351\200\222\345\275\222\350\247\243\346\263\225.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/\344\272\214\345\217\211\346\240\221\344\270\255\345\272\217\351\201\215\345\216\206\351\235\236\351\200\222\345\275\222\350\247\243\346\263\225.md" new file mode 100644 index 0000000..0ed7bd9 --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/\344\272\214\345\217\211\346\240\221\344\270\255\345\272\217\351\201\215\345\216\206\351\235\236\351\200\222\345\275\222\350\247\243\346\263\225.md" @@ -0,0 +1,24 @@ + +``` + public static List midOrderTraverse(TreeNode root) { + LinkedList res = new LinkedList(); + + if (root == null) + return res; + + Stack aux = new Stack(); + TreeNode node = root;//node指向待处理节点 + + while (node != null || !aux.isEmpty()) { + while (node != null) { + //当前节点不为null,将当前节点入栈等到该节点的左子树全部处理完后在处理当前节点 + aux.add(node); + node = node.left;//先处理左孩子节点 + } + TreeNode temp = aux.pop(); + res.add(temp.val);//node没有左孩子,则输出当前node节点 + node = temp.right;//处理node的右子树 + } + return res; + } +``` \ No newline at end of file diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/\344\272\214\345\217\211\346\240\221\347\232\204\345\217\263\350\247\206\345\233\276.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/\344\272\214\345\217\211\346\240\221\347\232\204\345\217\263\350\247\206\345\233\276.md" new file mode 100644 index 0000000..517e2ec --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/\344\272\214\345\217\211\346\240\221\347\232\204\345\217\263\350\247\206\345\233\276.md" @@ -0,0 +1,50 @@ +### Title +二叉树的右视图 + + +### 分析 + +思路可以参考按行打印二叉树。 + +按层放到queue中, 然后取queue每层最右边的节点, 也就是每行的最后一个节点。 + +### Coding +```java +class Main{ + public int[] getRightNode(TreeNode node){ + // check null + if (node == null){ + return new int[0]; + } + // 借助queue + Queue queue = new LinkedList<>(); + List result = new ArrayList<>(); + List path = new ArrayList<>(); + // 定义每行的节点个数 + int curCount = 0; + int nextCount = 1; + queue.offer(node); + + while(!queue.isEmpty()){ + curCount = nextCount; + nextCount = 0; + for (int i = 0; i< curCount; i++){ + TreeNode temp = queue.poll(); + path.add(temp.val); + if(temp.left != null){ + queue.offer(temp.left); + nextCount++; + } + if(temp.right != null){ + queue.offer(temp.right); + nextCount++; + } + } + // 记录path的最后一个节点并重置 + result.add(path.get(path.size()-1)); + path = new ArrayList<>(); + } + return result; + } +} +``` diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/\345\233\276\347\232\204\351\201\215\345\216\206.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/\345\233\276\347\232\204\351\201\215\345\216\206.md" new file mode 100644 index 0000000..58f8e84 --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/\345\233\276\347\232\204\351\201\215\345\216\206.md" @@ -0,0 +1,86 @@ +### Title + 1 + / \ + 2 3 + / \ / \ + 4 5 6 7 + \ | / \ / + 8 9 + +遍历该图的所有节点。 + +### Analysis +深度遍历需要借助Stack、Set来完成回归和记录。当然也可以用标记法, 那样的时间复杂度高, 是递归的做法。 + +广度遍历需要借助Queue、Set来完成, 比深度遍历简单。思想一样。 +### Coding + +#### DFS +```java +public class Main { + + public static void dfs(Node node) { + if (node == null) { + return; + } + Stack stack = new Stack<>(); + Set set = new HashSet<>(); + // 首先先放入头节点, 并打印 + stack.push(node); + set.add(node); + System.out.println(node); + + while(!stack.isEmpty()){ + // 进入到这里,我们需要弹出一个节点 + Node cur = stack.pop(); + // 遍历当前节点的下一个节点 + while(cur.next != null){ + // 如果set中没有这个节点, 证明没走过 + // 如果走过的话, 跳过遍历另一个next节点 + if (!set.contains(cur)){ + stack.push(cur); + stack.push(cur.next); + set.add(cur); + System.out.println(cur.next.val); + // 为什么要break, 因为是深度遍历, 先往深处走 + break; + } + } + } + } +} +``` + +#### BFS + +```java +public class Main { + + public static void bfs(Node node) { + if (node == null) { + return; + } + Queue queue = new LinkedList<>(); + Set set = new HashSet<>(); + // 首先先放入头节点, 并打印 + queue.offer(node); + set.add(node); + System.out.println(node); + + while(!queue.isEmpty()){ + // 进入到这里,我们需要弹出一个节点 + Node cur = queue.poll(); + System.out.println(cur); + // 遍历当前节点的下一个节点 + while(cur.next != null){ + // 如果set中没有这个节点, 证明没走过 + // 如果走过的话, 跳过遍历另一个next节点 + if (!set.contains(cur)){ + queue.push(cur.next); + set.add(cur); + } + } + } + } +} +``` \ No newline at end of file diff --git "a/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/\345\257\271\345\215\225\351\223\276\350\241\250\347\232\204\346\216\222\345\272\217.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/\345\257\271\345\215\225\351\223\276\350\241\250\347\232\204\346\216\222\345\272\217.md" new file mode 100644 index 0000000..9db5244 --- /dev/null +++ "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.2 \345\211\221\346\214\207offer/\345\257\271\345\215\225\351\223\276\350\241\250\347\232\204\346\216\222\345\272\217.md" @@ -0,0 +1,53 @@ +### Title +给定一个单链表,对这个单链表进行排序,要求时间复杂度O(nlogn),空间复杂度O(1)。 + +### Demo + +`2->1->3->5->4` 经过排序后链表结构为`1->2->3->4->5` + +### Analysis +这个题的主要思路是如果发现cur>pre节点, 那么如何将cur放到正确的位置。 +1. 如何去找正确的位置, 从头遍历判断与cur的大小关系 +2. 如何去存放的正确位置, 需要在遍历第一点的时候将前后节点临时缓存。 + +### Coding +```java +class Solution { + public ListNode sortList(ListNode node){ + // check null + if(node == null || node.next == null){ + return node; + } + // 记录链表头位置 + ListNode result = new ListNode(-1); + result.next = node; + + // 记录pre 和 cur节点 + ListNode pre = node; + ListNode cur = node.next; + while(cur != null){ + // 如果pre大于cur, 则需要将cur放到正确的位置上去 + if (pre.val > cur.val){ + pre.next = cur.next; + // 拿到头节点 + ListNode temp1 = result; + ListNode temp2 = result.next; + // 找到cur应该放的位置 + while(cur.val > temp2.val){ + // temp1用来记录cur应该放的位置的前一个位置 + temp1 = temp2; + temp2 = temp2.next; + } + // 将cur放到temp1与temp2之间 + temp1.next = cur; + cur.next = temp2; + cur = cur.next; + }else{ + pre = cur; + cur = cur.next; + } + } + return result.next; + } +} +``` \ No newline at end of file diff --git "a/leetcode/136.\345\217\252\345\207\272\347\216\260\344\270\200\346\254\241\347\232\204\346\225\260\345\255\227.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/136.\345\217\252\345\207\272\347\216\260\344\270\200\346\254\241\347\232\204\346\225\260\345\255\227.md" similarity index 100% rename from "leetcode/136.\345\217\252\345\207\272\347\216\260\344\270\200\346\254\241\347\232\204\346\225\260\345\255\227.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/136.\345\217\252\345\207\272\347\216\260\344\270\200\346\254\241\347\232\204\346\225\260\345\255\227.md" diff --git "a/leetcode/168. Excel\350\241\250\345\210\227\345\220\215\347\247\260(\346\212\200\345\267\247).md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/168. Excel\350\241\250\345\210\227\345\220\215\347\247\260(\346\212\200\345\267\247).md" similarity index 100% rename from "leetcode/168. Excel\350\241\250\345\210\227\345\220\215\347\247\260(\346\212\200\345\267\247).md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/168. Excel\350\241\250\345\210\227\345\220\215\347\247\260(\346\212\200\345\267\247).md" diff --git a/leetcode/171.Excel.md "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/171.Excel.md" similarity index 100% rename from leetcode/171.Excel.md rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/171.Excel.md" diff --git "a/leetcode/926. \345\260\206\345\255\227\347\254\246\344\270\262\347\277\273\350\275\254\345\210\260\345\215\225\350\260\203\351\200\222\345\242\236.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/926. \345\260\206\345\255\227\347\254\246\344\270\262\347\277\273\350\275\254\345\210\260\345\215\225\350\260\203\351\200\222\345\242\236.md" similarity index 100% rename from "leetcode/926. \345\260\206\345\255\227\347\254\246\344\270\262\347\277\273\350\275\254\345\210\260\345\215\225\350\260\203\351\200\222\345\242\236.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/926. \345\260\206\345\255\227\347\254\246\344\270\262\347\277\273\350\275\254\345\210\260\345\215\225\350\260\203\351\200\222\345\242\236.md" diff --git "a/leetcode/970. \345\274\272\346\225\264\346\225\260.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/970. \345\274\272\346\225\264\346\225\260.md" similarity index 100% rename from "leetcode/970. \345\274\272\346\225\264\346\225\260.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/970. \345\274\272\346\225\264\346\225\260.md" diff --git "a/leetcode/LeetCode--38. \346\212\245\346\225\260\357\274\210Java\357\274\211.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/LeetCode--38. \346\212\245\346\225\260\357\274\210Java\357\274\211.md" similarity index 100% rename from "leetcode/LeetCode--38. \346\212\245\346\225\260\357\274\210Java\357\274\211.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/LeetCode--38. \346\212\245\346\225\260\357\274\210Java\357\274\211.md" diff --git "a/leetcode/LeetCode--58. \346\234\200\345\220\216\344\270\200\344\270\252\345\215\225\350\257\215\347\232\204\351\225\277\345\272\246\357\274\210Java\357\274\211.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/LeetCode--58. \346\234\200\345\220\216\344\270\200\344\270\252\345\215\225\350\257\215\347\232\204\351\225\277\345\272\246\357\274\210Java\357\274\211.md" similarity index 100% rename from "leetcode/LeetCode--58. \346\234\200\345\220\216\344\270\200\344\270\252\345\215\225\350\257\215\347\232\204\351\225\277\345\272\246\357\274\210Java\357\274\211.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/LeetCode--58. \346\234\200\345\220\216\344\270\200\344\270\252\345\215\225\350\257\215\347\232\204\351\225\277\345\272\246\357\274\210Java\357\274\211.md" diff --git "a/leetcode/Netty\347\275\221\347\273\234\350\201\212\345\244\251(\344\270\200) \350\201\212\345\244\251\345\256\244\347\232\204\345\256\236\346\210\230(\346\234\200\346\230\223\346\207\202).md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/Netty\347\275\221\347\273\234\350\201\212\345\244\251(\344\270\200) \350\201\212\345\244\251\345\256\244\347\232\204\345\256\236\346\210\230(\346\234\200\346\230\223\346\207\202).md" similarity index 100% rename from "leetcode/Netty\347\275\221\347\273\234\350\201\212\345\244\251(\344\270\200) \350\201\212\345\244\251\345\256\244\347\232\204\345\256\236\346\210\230(\346\234\200\346\230\223\346\207\202).md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/Netty\347\275\221\347\273\234\350\201\212\345\244\251(\344\270\200) \350\201\212\345\244\251\345\256\244\347\232\204\345\256\236\346\210\230(\346\234\200\346\230\223\346\207\202).md" diff --git "a/leetcode/sql/175. \347\273\204\345\220\210\344\270\244\344\270\252\350\241\250.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/sql/175. \347\273\204\345\220\210\344\270\244\344\270\252\350\241\250.md" similarity index 100% rename from "leetcode/sql/175. \347\273\204\345\220\210\344\270\244\344\270\252\350\241\250.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/sql/175. \347\273\204\345\220\210\344\270\244\344\270\252\350\241\250.md" diff --git "a/leetcode/sql/176. \347\254\254\344\272\214\351\253\230\347\232\204\350\226\252\346\260\264.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/sql/176. \347\254\254\344\272\214\351\253\230\347\232\204\350\226\252\346\260\264.md" similarity index 100% rename from "leetcode/sql/176. \347\254\254\344\272\214\351\253\230\347\232\204\350\226\252\346\260\264.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/sql/176. \347\254\254\344\272\214\351\253\230\347\232\204\350\226\252\346\260\264.md" diff --git "a/leetcode/sql/181. \350\266\205\350\277\207\347\273\217\347\220\206\346\224\266\345\205\245\347\232\204\345\221\230\345\267\245.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/sql/181. \350\266\205\350\277\207\347\273\217\347\220\206\346\224\266\345\205\245\347\232\204\345\221\230\345\267\245.md" similarity index 100% rename from "leetcode/sql/181. \350\266\205\350\277\207\347\273\217\347\220\206\346\224\266\345\205\245\347\232\204\345\221\230\345\267\245.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/sql/181. \350\266\205\350\277\207\347\273\217\347\220\206\346\224\266\345\205\245\347\232\204\345\221\230\345\267\245.md" diff --git "a/leetcode/sql/182. \346\237\245\346\211\276\351\207\215\345\244\215\347\232\204\347\224\265\345\255\220\351\202\256\347\256\261.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/sql/182. \346\237\245\346\211\276\351\207\215\345\244\215\347\232\204\347\224\265\345\255\220\351\202\256\347\256\261.md" similarity index 100% rename from "leetcode/sql/182. \346\237\245\346\211\276\351\207\215\345\244\215\347\232\204\347\224\265\345\255\220\351\202\256\347\256\261.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/sql/182. \346\237\245\346\211\276\351\207\215\345\244\215\347\232\204\347\224\265\345\255\220\351\202\256\347\256\261.md" diff --git "a/leetcode/sql/183. \344\273\216\344\270\215\350\256\242\350\264\255\347\232\204\345\256\242\346\210\267.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/sql/183. \344\273\216\344\270\215\350\256\242\350\264\255\347\232\204\345\256\242\346\210\267.md" similarity index 100% rename from "leetcode/sql/183. \344\273\216\344\270\215\350\256\242\350\264\255\347\232\204\345\256\242\346\210\267.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/sql/183. \344\273\216\344\270\215\350\256\242\350\264\255\347\232\204\345\256\242\346\210\267.md" diff --git "a/leetcode/sql/196. \345\210\240\351\231\244\351\207\215\345\244\215\347\232\204\347\224\265\345\255\220\351\202\256\347\256\261.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/sql/196. \345\210\240\351\231\244\351\207\215\345\244\215\347\232\204\347\224\265\345\255\220\351\202\256\347\256\261.md" similarity index 100% rename from "leetcode/sql/196. \345\210\240\351\231\244\351\207\215\345\244\215\347\232\204\347\224\265\345\255\220\351\202\256\347\256\261.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/sql/196. \345\210\240\351\231\244\351\207\215\345\244\215\347\232\204\347\224\265\345\255\220\351\202\256\347\256\261.md" diff --git "a/leetcode/z LeetCode--28. \345\256\236\347\216\260strStr()\357\274\210Java\357\274\211.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/z LeetCode--28. \345\256\236\347\216\260strStr()\357\274\210Java\357\274\211.md" similarity index 100% rename from "leetcode/z LeetCode--28. \345\256\236\347\216\260strStr()\357\274\210Java\357\274\211.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/z LeetCode--28. \345\256\236\347\216\260strStr()\357\274\210Java\357\274\211.md" diff --git "a/leetcode/\344\275\215\350\277\220\347\256\227/190. \351\242\240\345\200\222\344\272\214\350\277\233\345\210\266\344\275\215.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\344\275\215\350\277\220\347\256\227/190. \351\242\240\345\200\222\344\272\214\350\277\233\345\210\266\344\275\215.md" similarity index 100% rename from "leetcode/\344\275\215\350\277\220\347\256\227/190. \351\242\240\345\200\222\344\272\214\350\277\233\345\210\266\344\275\215.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\344\275\215\350\277\220\347\256\227/190. \351\242\240\345\200\222\344\272\214\350\277\233\345\210\266\344\275\215.md" diff --git "a/leetcode/\344\275\215\350\277\220\347\256\227/191. \344\275\2151\347\232\204\344\270\252\346\225\260.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\344\275\215\350\277\220\347\256\227/191. \344\275\2151\347\232\204\344\270\252\346\225\260.md" similarity index 100% rename from "leetcode/\344\275\215\350\277\220\347\256\227/191. \344\275\2151\347\232\204\344\270\252\346\225\260.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\344\275\215\350\277\220\347\256\227/191. \344\275\2151\347\232\204\344\270\252\346\225\260.md" diff --git "a/leetcode/\344\275\215\350\277\220\347\256\227/\346\227\240\346\240\207\351\242\230Markdown14-56-40.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\344\275\215\350\277\220\347\256\227/\346\227\240\346\240\207\351\242\230Markdown14-56-40.md" similarity index 100% rename from "leetcode/\344\275\215\350\277\220\347\256\227/\346\227\240\346\240\207\351\242\230Markdown14-56-40.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\344\275\215\350\277\220\347\256\227/\346\227\240\346\240\207\351\242\230Markdown14-56-40.md" diff --git "a/leetcode/\344\275\215\350\277\220\347\256\227/\347\224\250\344\270\200\346\235\241\350\257\255\345\217\245\345\210\244\346\226\255\344\270\200\344\270\252\346\225\264\346\225\260\346\230\257\344\270\215\346\230\2572\347\232\204\346\225\264\346\225\260\346\254\241\346\226\271.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\344\275\215\350\277\220\347\256\227/\347\224\250\344\270\200\346\235\241\350\257\255\345\217\245\345\210\244\346\226\255\344\270\200\344\270\252\346\225\264\346\225\260\346\230\257\344\270\215\346\230\2572\347\232\204\346\225\264\346\225\260\346\254\241\346\226\271.md" similarity index 100% rename from "leetcode/\344\275\215\350\277\220\347\256\227/\347\224\250\344\270\200\346\235\241\350\257\255\345\217\245\345\210\244\346\226\255\344\270\200\344\270\252\346\225\264\346\225\260\346\230\257\344\270\215\346\230\2572\347\232\204\346\225\264\346\225\260\346\254\241\346\226\271.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\344\275\215\350\277\220\347\256\227/\347\224\250\344\270\200\346\235\241\350\257\255\345\217\245\345\210\244\346\226\255\344\270\200\344\270\252\346\225\264\346\225\260\346\230\257\344\270\215\346\230\2572\347\232\204\346\225\264\346\225\260\346\254\241\346\226\271.md" diff --git "a/leetcode/\344\275\215\350\277\220\347\256\227/\350\276\223\345\205\245\344\270\244\344\270\252\346\225\264\346\225\260m\345\222\214n,\350\256\241\347\256\227\351\234\200\350\246\201\346\224\271\345\217\230n\347\232\204\345\207\240\344\275\215\345\217\257\344\273\245\345\276\227\345\210\260m\343\200\202.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\344\275\215\350\277\220\347\256\227/\350\276\223\345\205\245\344\270\244\344\270\252\346\225\264\346\225\260m\345\222\214n,\350\256\241\347\256\227\351\234\200\350\246\201\346\224\271\345\217\230n\347\232\204\345\207\240\344\275\215\345\217\257\344\273\245\345\276\227\345\210\260m\343\200\202.md" similarity index 100% rename from "leetcode/\344\275\215\350\277\220\347\256\227/\350\276\223\345\205\245\344\270\244\344\270\252\346\225\264\346\225\260m\345\222\214n,\350\256\241\347\256\227\351\234\200\350\246\201\346\224\271\345\217\230n\347\232\204\345\207\240\344\275\215\345\217\257\344\273\245\345\276\227\345\210\260m\343\200\202.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\344\275\215\350\277\220\347\256\227/\350\276\223\345\205\245\344\270\244\344\270\252\346\225\264\346\225\260m\345\222\214n,\350\256\241\347\256\227\351\234\200\350\246\201\346\224\271\345\217\230n\347\232\204\345\207\240\344\275\215\345\217\257\344\273\245\345\276\227\345\210\260m\343\200\202.md" diff --git "a/leetcode/\345\212\250\346\200\201\350\247\204\345\210\222/LeetCode--70. \347\210\254\346\245\274\346\242\257\357\274\210Java\357\274\211.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\345\212\250\346\200\201\350\247\204\345\210\222/LeetCode--70. \347\210\254\346\245\274\346\242\257\357\274\210Java\357\274\211.md" similarity index 100% rename from "leetcode/\345\212\250\346\200\201\350\247\204\345\210\222/LeetCode--70. \347\210\254\346\245\274\346\242\257\357\274\210Java\357\274\211.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\345\212\250\346\200\201\350\247\204\345\210\222/LeetCode--70. \347\210\254\346\245\274\346\242\257\357\274\210Java\357\274\211.md" diff --git "a/leetcode/\345\240\206\346\240\210+\351\230\237\345\210\227/LeetCode--155. \346\234\200\345\260\217\346\240\210.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\345\240\206\346\240\210+\351\230\237\345\210\227/LeetCode--155. \346\234\200\345\260\217\346\240\210.md" similarity index 100% rename from "leetcode/\345\240\206\346\240\210+\351\230\237\345\210\227/LeetCode--155. \346\234\200\345\260\217\346\240\210.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\345\240\206\346\240\210+\351\230\237\345\210\227/LeetCode--155. \346\234\200\345\260\217\346\240\210.md" diff --git "a/leetcode/\345\240\206\346\240\210+\351\230\237\345\210\227/LeetCode--20. \346\234\211\346\225\210\347\232\204\346\213\254\345\217\267\357\274\210Java\357\274\211.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\345\240\206\346\240\210+\351\230\237\345\210\227/LeetCode--20. \346\234\211\346\225\210\347\232\204\346\213\254\345\217\267\357\274\210Java\357\274\211.md" similarity index 100% rename from "leetcode/\345\240\206\346\240\210+\351\230\237\345\210\227/LeetCode--20. \346\234\211\346\225\210\347\232\204\346\213\254\345\217\267\357\274\210Java\357\274\211.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\345\240\206\346\240\210+\351\230\237\345\210\227/LeetCode--20. \346\234\211\346\225\210\347\232\204\346\213\254\345\217\267\357\274\210Java\357\274\211.md" diff --git "a/leetcode/\345\240\206\346\240\210+\351\230\237\345\210\227/LeetCode--921. \344\275\277\346\213\254\345\217\267\346\234\211\346\225\210\347\232\204\346\234\200\345\260\221\346\267\273\345\212\240.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\345\240\206\346\240\210+\351\230\237\345\210\227/LeetCode--921. \344\275\277\346\213\254\345\217\267\346\234\211\346\225\210\347\232\204\346\234\200\345\260\221\346\267\273\345\212\240.md" similarity index 100% rename from "leetcode/\345\240\206\346\240\210+\351\230\237\345\210\227/LeetCode--921. \344\275\277\346\213\254\345\217\267\346\234\211\346\225\210\347\232\204\346\234\200\345\260\221\346\267\273\345\212\240.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\345\240\206\346\240\210+\351\230\237\345\210\227/LeetCode--921. \344\275\277\346\213\254\345\217\267\346\234\211\346\225\210\347\232\204\346\234\200\345\260\221\346\267\273\345\212\240.md" diff --git "a/leetcode/\346\225\260\347\273\204/121.\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\225\260\347\273\204/121.\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272.md" similarity index 100% rename from "leetcode/\346\225\260\347\273\204/121.\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\225\260\347\273\204/121.\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272.md" diff --git "a/leetcode/\346\225\260\347\273\204/122.\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272II.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\225\260\347\273\204/122.\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272II.md" similarity index 100% rename from "leetcode/\346\225\260\347\273\204/122.\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272II.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\225\260\347\273\204/122.\344\271\260\345\215\226\350\202\241\347\245\250\347\232\204\346\234\200\344\275\263\346\227\266\346\234\272II.md" diff --git "a/leetcode/\346\225\260\347\273\204/169. \346\261\202\344\274\227\346\225\260.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\225\260\347\273\204/169. \346\261\202\344\274\227\346\225\260.md" similarity index 100% rename from "leetcode/\346\225\260\347\273\204/169. \346\261\202\344\274\227\346\225\260.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\225\260\347\273\204/169. \346\261\202\344\274\227\346\225\260.md" diff --git "a/leetcode/\346\225\260\347\273\204/189. \346\227\213\350\275\254\346\225\260\347\273\204.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\225\260\347\273\204/189. \346\227\213\350\275\254\346\225\260\347\273\204.md" similarity index 100% rename from "leetcode/\346\225\260\347\273\204/189. \346\227\213\350\275\254\346\225\260\347\273\204.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\225\260\347\273\204/189. \346\227\213\350\275\254\346\225\260\347\273\204.md" diff --git "a/leetcode/\346\225\260\347\273\204/941. \346\234\211\346\225\210\347\232\204\345\261\261\350\204\211\346\225\260\347\273\204.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\225\260\347\273\204/941. \346\234\211\346\225\210\347\232\204\345\261\261\350\204\211\346\225\260\347\273\204.md" similarity index 100% rename from "leetcode/\346\225\260\347\273\204/941. \346\234\211\346\225\210\347\232\204\345\261\261\350\204\211\346\225\260\347\273\204.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\225\260\347\273\204/941. \346\234\211\346\225\210\347\232\204\345\261\261\350\204\211\346\225\260\347\273\204.md" diff --git "a/leetcode/\346\225\260\347\273\204/942. \345\242\236\345\207\217\345\255\227\347\254\246\344\270\262\345\214\271\351\205\215.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\225\260\347\273\204/942. \345\242\236\345\207\217\345\255\227\347\254\246\344\270\262\345\214\271\351\205\215.md" similarity index 100% rename from "leetcode/\346\225\260\347\273\204/942. \345\242\236\345\207\217\345\255\227\347\254\246\344\270\262\345\214\271\351\205\215.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\225\260\347\273\204/942. \345\242\236\345\207\217\345\255\227\347\254\246\344\270\262\345\214\271\351\205\215.md" diff --git "a/leetcode/\346\225\260\347\273\204/944. \345\210\240\351\231\244\345\210\227\344\273\245\344\275\277\344\271\213\346\234\211\345\272\217.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\225\260\347\273\204/944. \345\210\240\351\231\244\345\210\227\344\273\245\344\275\277\344\271\213\346\234\211\345\272\217.md" similarity index 100% rename from "leetcode/\346\225\260\347\273\204/944. \345\210\240\351\231\244\345\210\227\344\273\245\344\275\277\344\271\213\346\234\211\345\272\217.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\225\260\347\273\204/944. \345\210\240\351\231\244\345\210\227\344\273\245\344\275\277\344\271\213\346\234\211\345\272\217.md" diff --git "a/leetcode/\346\225\260\347\273\204/LeetCode--1. \344\270\244\346\225\260\344\271\213\345\222\214\357\274\210Java\357\274\211.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\225\260\347\273\204/LeetCode--1. \344\270\244\346\225\260\344\271\213\345\222\214\357\274\210Java\357\274\211.md" similarity index 100% rename from "leetcode/\346\225\260\347\273\204/LeetCode--1. \344\270\244\346\225\260\344\271\213\345\222\214\357\274\210Java\357\274\211.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\225\260\347\273\204/LeetCode--1. \344\270\244\346\225\260\344\271\213\345\222\214\357\274\210Java\357\274\211.md" diff --git "a/leetcode/\346\225\260\347\273\204/LeetCode--137. \345\217\252\345\207\272\347\216\260\344\270\200\346\254\241\347\232\204\346\225\260\345\255\227 II.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\225\260\347\273\204/LeetCode--137. \345\217\252\345\207\272\347\216\260\344\270\200\346\254\241\347\232\204\346\225\260\345\255\227 II.md" similarity index 100% rename from "leetcode/\346\225\260\347\273\204/LeetCode--137. \345\217\252\345\207\272\347\216\260\344\270\200\346\254\241\347\232\204\346\225\260\345\255\227 II.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\225\260\347\273\204/LeetCode--137. \345\217\252\345\207\272\347\216\260\344\270\200\346\254\241\347\232\204\346\225\260\345\255\227 II.md" diff --git "a/leetcode/\346\225\260\347\273\204/LeetCode--14. \346\234\200\351\225\277\345\205\254\345\205\261\345\211\215\347\274\200\357\274\210Java\357\274\211.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\225\260\347\273\204/LeetCode--14. \346\234\200\351\225\277\345\205\254\345\205\261\345\211\215\347\274\200\357\274\210Java\357\274\211.md" similarity index 100% rename from "leetcode/\346\225\260\347\273\204/LeetCode--14. \346\234\200\351\225\277\345\205\254\345\205\261\345\211\215\347\274\200\357\274\210Java\357\274\211.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\225\260\347\273\204/LeetCode--14. \346\234\200\351\225\277\345\205\254\345\205\261\345\211\215\347\274\200\357\274\210Java\357\274\211.md" diff --git "a/leetcode/\346\225\260\347\273\204/LeetCode--26. \345\210\240\351\231\244\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\351\207\215\345\244\215\351\241\271\357\274\210Java\357\274\211.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\225\260\347\273\204/LeetCode--26. \345\210\240\351\231\244\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\351\207\215\345\244\215\351\241\271\357\274\210Java\357\274\211.md" similarity index 100% rename from "leetcode/\346\225\260\347\273\204/LeetCode--26. \345\210\240\351\231\244\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\351\207\215\345\244\215\351\241\271\357\274\210Java\357\274\211.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\225\260\347\273\204/LeetCode--26. \345\210\240\351\231\244\346\216\222\345\272\217\346\225\260\347\273\204\344\270\255\347\232\204\351\207\215\345\244\215\351\241\271\357\274\210Java\357\274\211.md" diff --git "a/leetcode/\346\225\260\347\273\204/LeetCode--27. \347\247\273\351\231\244\345\205\203\347\264\240\357\274\210Java\357\274\211.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\225\260\347\273\204/LeetCode--27. \347\247\273\351\231\244\345\205\203\347\264\240\357\274\210Java\357\274\211.md" similarity index 100% rename from "leetcode/\346\225\260\347\273\204/LeetCode--27. \347\247\273\351\231\244\345\205\203\347\264\240\357\274\210Java\357\274\211.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\225\260\347\273\204/LeetCode--27. \347\247\273\351\231\244\345\205\203\347\264\240\357\274\210Java\357\274\211.md" diff --git "a/leetcode/\346\225\260\347\273\204/LeetCode--35. \346\220\234\347\264\242\346\217\222\345\205\245\344\275\215\347\275\256\357\274\210Java\357\274\211.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\225\260\347\273\204/LeetCode--35. \346\220\234\347\264\242\346\217\222\345\205\245\344\275\215\347\275\256\357\274\210Java\357\274\211.md" similarity index 100% rename from "leetcode/\346\225\260\347\273\204/LeetCode--35. \346\220\234\347\264\242\346\217\222\345\205\245\344\275\215\347\275\256\357\274\210Java\357\274\211.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\225\260\347\273\204/LeetCode--35. \346\220\234\347\264\242\346\217\222\345\205\245\344\275\215\347\275\256\357\274\210Java\357\274\211.md" diff --git "a/leetcode/\346\225\260\347\273\204/LeetCode--53. \346\234\200\345\244\247\345\255\220\345\272\217\345\222\214\357\274\210Java\357\274\211.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\225\260\347\273\204/LeetCode--53. \346\234\200\345\244\247\345\255\220\345\272\217\345\222\214\357\274\210Java\357\274\211.md" similarity index 100% rename from "leetcode/\346\225\260\347\273\204/LeetCode--53. \346\234\200\345\244\247\345\255\220\345\272\217\345\222\214\357\274\210Java\357\274\211.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\225\260\347\273\204/LeetCode--53. \346\234\200\345\244\247\345\255\220\345\272\217\345\222\214\357\274\210Java\357\274\211.md" diff --git "a/leetcode/\346\225\260\347\273\204/LeetCode--88. \345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\346\225\260\347\273\204\357\274\210Java\357\274\211.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\225\260\347\273\204/LeetCode--88. \345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\346\225\260\347\273\204\357\274\210Java\357\274\211.md" similarity index 100% rename from "leetcode/\346\225\260\347\273\204/LeetCode--88. \345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\346\225\260\347\273\204\357\274\210Java\357\274\211.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\225\260\347\273\204/LeetCode--88. \345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\346\225\260\347\273\204\357\274\210Java\357\274\211.md" diff --git "a/leetcode/\346\225\260\347\273\204/LeetCode--922. \346\214\211\345\245\207\345\201\266\346\216\222\345\272\217\346\225\260\347\273\204 II.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\225\260\347\273\204/LeetCode--922. \346\214\211\345\245\207\345\201\266\346\216\222\345\272\217\346\225\260\347\273\204 II.md" similarity index 100% rename from "leetcode/\346\225\260\347\273\204/LeetCode--922. \346\214\211\345\245\207\345\201\266\346\216\222\345\272\217\346\225\260\347\273\204 II.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\225\260\347\273\204/LeetCode--922. \346\214\211\345\245\207\345\201\266\346\216\222\345\272\217\346\225\260\347\273\204 II.md" diff --git "a/leetcode/\346\225\260\347\273\204/\345\217\252\345\207\272\347\216\260\344\270\200\346\254\241\347\232\204\346\225\260\345\255\227.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\225\260\347\273\204/\345\217\252\345\207\272\347\216\260\344\270\200\346\254\241\347\232\204\346\225\260\345\255\227.md" similarity index 100% rename from "leetcode/\346\225\260\347\273\204/\345\217\252\345\207\272\347\216\260\344\270\200\346\254\241\347\232\204\346\225\260\345\255\227.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\225\260\347\273\204/\345\217\252\345\207\272\347\216\260\344\270\200\346\254\241\347\232\204\346\225\260\345\255\227.md" diff --git "a/leetcode/\346\225\264\346\225\260+\347\256\227\346\234\257/LeetCode--13. \347\275\227\351\251\254\346\225\260\345\255\227\350\275\254\346\225\264\346\225\260\357\274\210Java\357\274\211.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\225\264\346\225\260+\347\256\227\346\234\257/LeetCode--13. \347\275\227\351\251\254\346\225\260\345\255\227\350\275\254\346\225\264\346\225\260\357\274\210Java\357\274\211.md" similarity index 100% rename from "leetcode/\346\225\264\346\225\260+\347\256\227\346\234\257/LeetCode--13. \347\275\227\351\251\254\346\225\260\345\255\227\350\275\254\346\225\264\346\225\260\357\274\210Java\357\274\211.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\225\264\346\225\260+\347\256\227\346\234\257/LeetCode--13. \347\275\227\351\251\254\346\225\260\345\255\227\350\275\254\346\225\264\346\225\260\357\274\210Java\357\274\211.md" diff --git "a/leetcode/\346\225\264\346\225\260+\347\256\227\346\234\257/LeetCode--166. \345\212\240\344\270\200\357\274\210Java\357\274\211\357\274\210Java\357\274\211.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\225\264\346\225\260+\347\256\227\346\234\257/LeetCode--166. \345\212\240\344\270\200\357\274\210Java\357\274\211\357\274\210Java\357\274\211.md" similarity index 100% rename from "leetcode/\346\225\264\346\225\260+\347\256\227\346\234\257/LeetCode--166. \345\212\240\344\270\200\357\274\210Java\357\274\211\357\274\210Java\357\274\211.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\225\264\346\225\260+\347\256\227\346\234\257/LeetCode--166. \345\212\240\344\270\200\357\274\210Java\357\274\211\357\274\210Java\357\274\211.md" diff --git "a/leetcode/\346\225\264\346\225\260+\347\256\227\346\234\257/LeetCode--69. x \347\232\204\345\271\263\346\226\271\346\240\271\357\274\210Java\357\274\211.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\225\264\346\225\260+\347\256\227\346\234\257/LeetCode--69. x \347\232\204\345\271\263\346\226\271\346\240\271\357\274\210Java\357\274\211.md" similarity index 100% rename from "leetcode/\346\225\264\346\225\260+\347\256\227\346\234\257/LeetCode--69. x \347\232\204\345\271\263\346\226\271\346\240\271\357\274\210Java\357\274\211.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\225\264\346\225\260+\347\256\227\346\234\257/LeetCode--69. x \347\232\204\345\271\263\346\226\271\346\240\271\357\274\210Java\357\274\211.md" diff --git "a/leetcode/\346\225\264\346\225\260+\347\256\227\346\234\257/LeetCode--7. \345\217\215\350\275\254\346\225\264\346\225\260\357\274\210Java\357\274\211.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\225\264\346\225\260+\347\256\227\346\234\257/LeetCode--7. \345\217\215\350\275\254\346\225\264\346\225\260\357\274\210Java\357\274\211.md" similarity index 100% rename from "leetcode/\346\225\264\346\225\260+\347\256\227\346\234\257/LeetCode--7. \345\217\215\350\275\254\346\225\264\346\225\260\357\274\210Java\357\274\211.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\225\264\346\225\260+\347\256\227\346\234\257/LeetCode--7. \345\217\215\350\275\254\346\225\264\346\225\260\357\274\210Java\357\274\211.md" diff --git "a/leetcode/\346\225\264\346\225\260+\347\256\227\346\234\257/LeetCode--9. \345\233\236\346\226\207\346\225\260\357\274\210Java\357\274\211.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\225\264\346\225\260+\347\256\227\346\234\257/LeetCode--9. \345\233\236\346\226\207\346\225\260\357\274\210Java\357\274\211.md" similarity index 100% rename from "leetcode/\346\225\264\346\225\260+\347\256\227\346\234\257/LeetCode--9. \345\233\236\346\226\207\346\225\260\357\274\210Java\357\274\211.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\225\264\346\225\260+\347\256\227\346\234\257/LeetCode--9. \345\233\236\346\226\207\346\225\260\357\274\210Java\357\274\211.md" diff --git "a/leetcode/\346\240\221/111. \344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\345\260\217\346\267\261\345\272\246.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\240\221/111. \344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\345\260\217\346\267\261\345\272\246.md" similarity index 100% rename from "leetcode/\346\240\221/111. \344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\345\260\217\346\267\261\345\272\246.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\240\221/111. \344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\345\260\217\346\267\261\345\272\246.md" diff --git "a/leetcode/\346\240\221/112. \350\267\257\345\276\204\346\200\273\345\222\214.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\240\221/112. \350\267\257\345\276\204\346\200\273\345\222\214.md" similarity index 100% rename from "leetcode/\346\240\221/112. \350\267\257\345\276\204\346\200\273\345\222\214.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\240\221/112. \350\267\257\345\276\204\346\200\273\345\222\214.md" diff --git "a/leetcode/\346\240\221/226. \347\277\273\350\275\254\344\272\214\345\217\211\346\240\221.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\240\221/226. \347\277\273\350\275\254\344\272\214\345\217\211\346\240\221.md" similarity index 100% rename from "leetcode/\346\240\221/226. \347\277\273\350\275\254\344\272\214\345\217\211\346\240\221.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\240\221/226. \347\277\273\350\275\254\344\272\214\345\217\211\346\240\221.md" diff --git "a/leetcode/\346\240\221/235. \344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\347\232\204\346\234\200\350\277\221\345\205\254\345\205\261\347\245\226\345\205\210.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\240\221/235. \344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\347\232\204\346\234\200\350\277\221\345\205\254\345\205\261\347\245\226\345\205\210.md" similarity index 100% rename from "leetcode/\346\240\221/235. \344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\347\232\204\346\234\200\350\277\221\345\205\254\345\205\261\347\245\226\345\205\210.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\240\221/235. \344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\347\232\204\346\234\200\350\277\221\345\205\254\345\205\261\347\245\226\345\205\210.md" diff --git "a/leetcode/\346\240\221/404. \345\267\246\345\217\266\345\255\220\344\271\213\345\222\214.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\240\221/404. \345\267\246\345\217\266\345\255\220\344\271\213\345\222\214.md" similarity index 100% rename from "leetcode/\346\240\221/404. \345\267\246\345\217\266\345\255\220\344\271\213\345\222\214.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\240\221/404. \345\267\246\345\217\266\345\255\220\344\271\213\345\222\214.md" diff --git "a/leetcode/\346\240\221/LeetCode--100. \347\233\270\345\220\214\347\232\204\346\240\221\357\274\210Java\357\274\211.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\240\221/LeetCode--100. \347\233\270\345\220\214\347\232\204\346\240\221\357\274\210Java\357\274\211.md" similarity index 100% rename from "leetcode/\346\240\221/LeetCode--100. \347\233\270\345\220\214\347\232\204\346\240\221\357\274\210Java\357\274\211.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\240\221/LeetCode--100. \347\233\270\345\220\214\347\232\204\346\240\221\357\274\210Java\357\274\211.md" diff --git "a/leetcode/\346\240\221/LeetCode--101. \345\257\271\347\247\260\344\272\214\345\217\211\346\240\221\357\274\210Java\357\274\211.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\240\221/LeetCode--101. \345\257\271\347\247\260\344\272\214\345\217\211\346\240\221\357\274\210Java\357\274\211.md" similarity index 100% rename from "leetcode/\346\240\221/LeetCode--101. \345\257\271\347\247\260\344\272\214\345\217\211\346\240\221\357\274\210Java\357\274\211.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\240\221/LeetCode--101. \345\257\271\347\247\260\344\272\214\345\217\211\346\240\221\357\274\210Java\357\274\211.md" diff --git "a/leetcode/\346\240\221/LeetCode--104. \344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\345\244\247\346\267\261\345\272\246\357\274\210Java\357\274\211.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\240\221/LeetCode--104. \344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\345\244\247\346\267\261\345\272\246\357\274\210Java\357\274\211.md" similarity index 100% rename from "leetcode/\346\240\221/LeetCode--104. \344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\345\244\247\346\267\261\345\272\246\357\274\210Java\357\274\211.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\240\221/LeetCode--104. \344\272\214\345\217\211\346\240\221\347\232\204\346\234\200\345\244\247\346\267\261\345\272\246\357\274\210Java\357\274\211.md" diff --git "a/leetcode/\346\240\221/LeetCode--108. \345\260\206\346\234\211\345\272\217\346\225\260\347\273\204\350\275\254\346\215\242\344\270\272\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\357\274\210Java\357\274\211.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\240\221/LeetCode--108. \345\260\206\346\234\211\345\272\217\346\225\260\347\273\204\350\275\254\346\215\242\344\270\272\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\357\274\210Java\357\274\211.md" similarity index 100% rename from "leetcode/\346\240\221/LeetCode--108. \345\260\206\346\234\211\345\272\217\346\225\260\347\273\204\350\275\254\346\215\242\344\270\272\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\357\274\210Java\357\274\211.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\240\221/LeetCode--108. \345\260\206\346\234\211\345\272\217\346\225\260\347\273\204\350\275\254\346\215\242\344\270\272\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\357\274\210Java\357\274\211.md" diff --git "a/leetcode/\346\240\221/LeetCode--110. \345\271\263\350\241\241\344\272\214\345\217\211\346\240\221.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\240\221/LeetCode--110. \345\271\263\350\241\241\344\272\214\345\217\211\346\240\221.md" similarity index 100% rename from "leetcode/\346\240\221/LeetCode--110. \345\271\263\350\241\241\344\272\214\345\217\211\346\240\221.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\346\240\221/LeetCode--110. \345\271\263\350\241\241\344\272\214\345\217\211\346\240\221.md" diff --git "a/leetcode/\350\277\233\345\210\266/LeetCode--67. \344\272\214\350\277\233\345\210\266\346\261\202\345\222\214\357\274\210Java\357\274\211.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\350\277\233\345\210\266/LeetCode--67. \344\272\214\350\277\233\345\210\266\346\261\202\345\222\214\357\274\210Java\357\274\211.md" similarity index 100% rename from "leetcode/\350\277\233\345\210\266/LeetCode--67. \344\272\214\350\277\233\345\210\266\346\261\202\345\222\214\357\274\210Java\357\274\211.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\350\277\233\345\210\266/LeetCode--67. \344\272\214\350\277\233\345\210\266\346\261\202\345\222\214\357\274\210Java\357\274\211.md" diff --git "a/leetcode/\351\223\276\350\241\250/1. \350\256\276\350\256\241\351\223\276\350\241\250.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\351\223\276\350\241\250/1. \350\256\276\350\256\241\351\223\276\350\241\250.md" similarity index 100% rename from "leetcode/\351\223\276\350\241\250/1. \350\256\276\350\256\241\351\223\276\350\241\250.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\351\223\276\350\241\250/1. \350\256\276\350\256\241\351\223\276\350\241\250.md" diff --git "a/leetcode/\351\223\276\350\241\250/141.\347\216\257\345\275\242\351\223\276\350\241\250.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\351\223\276\350\241\250/141.\347\216\257\345\275\242\351\223\276\350\241\250.md" similarity index 100% rename from "leetcode/\351\223\276\350\241\250/141.\347\216\257\345\275\242\351\223\276\350\241\250.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\351\223\276\350\241\250/141.\347\216\257\345\275\242\351\223\276\350\241\250.md" diff --git "a/leetcode/\351\223\276\350\241\250/160.\347\233\270\344\272\244\351\223\276\350\241\250.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\351\223\276\350\241\250/160.\347\233\270\344\272\244\351\223\276\350\241\250.md" similarity index 100% rename from "leetcode/\351\223\276\350\241\250/160.\347\233\270\344\272\244\351\223\276\350\241\250.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\351\223\276\350\241\250/160.\347\233\270\344\272\244\351\223\276\350\241\250.md" diff --git "a/leetcode/\351\223\276\350\241\250/LeetCode--21. \345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\351\223\276\350\241\250\357\274\210Java\357\274\211.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\351\223\276\350\241\250/LeetCode--21. \345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\351\223\276\350\241\250\357\274\210Java\357\274\211.md" similarity index 100% rename from "leetcode/\351\223\276\350\241\250/LeetCode--21. \345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\351\223\276\350\241\250\357\274\210Java\357\274\211.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\351\223\276\350\241\250/LeetCode--21. \345\220\210\345\271\266\344\270\244\344\270\252\346\234\211\345\272\217\351\223\276\350\241\250\357\274\210Java\357\274\211.md" diff --git "a/leetcode/\351\223\276\350\241\250/LeetCode--83. \345\210\240\351\231\244\346\216\222\345\272\217\351\223\276\350\241\250\344\270\255\347\232\204\351\207\215\345\244\215\345\205\203\347\264\240\357\274\210Java\357\274\211.md" "b/1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\351\223\276\350\241\250/LeetCode--83. \345\210\240\351\231\244\346\216\222\345\272\217\351\223\276\350\241\250\344\270\255\347\232\204\351\207\215\345\244\215\345\205\203\347\264\240\357\274\210Java\357\274\211.md" similarity index 100% rename from "leetcode/\351\223\276\350\241\250/LeetCode--83. \345\210\240\351\231\244\346\216\222\345\272\217\351\223\276\350\241\250\344\270\255\347\232\204\351\207\215\345\244\215\345\205\203\347\264\240\357\274\210Java\357\274\211.md" rename to "1. \347\256\227\346\263\225\345\237\272\347\241\200/1.3 leetcode/\351\223\276\350\241\250/LeetCode--83. \345\210\240\351\231\244\346\216\222\345\272\217\351\223\276\350\241\250\344\270\255\347\232\204\351\207\215\345\244\215\345\205\203\347\264\240\357\274\210Java\357\274\211.md" diff --git "a/JVM/JDK,-JRE\345\222\214JVM\347\232\204\345\214\272\345\210\253\344\270\216\350\201\224\347\263\273.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/1. JDK\343\200\201JRE\343\200\201JVM\347\232\204\345\214\272\345\210\253\344\270\216\350\201\224\347\263\273.md" similarity index 100% rename from "JVM/JDK,-JRE\345\222\214JVM\347\232\204\345\214\272\345\210\253\344\270\216\350\201\224\347\263\273.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/1. JDK\343\200\201JRE\343\200\201JVM\347\232\204\345\214\272\345\210\253\344\270\216\350\201\224\347\263\273.md" diff --git "a/JVM/JVM\345\210\206\346\236\220\345\267\245\345\205\267\346\246\202\350\277\260.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/10. JVM-\345\210\206\346\236\220\345\267\245\345\205\267\346\246\202\350\277\260.md" similarity index 100% rename from "JVM/JVM\345\210\206\346\236\220\345\267\245\345\205\267\346\246\202\350\277\260.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/10. JVM-\345\210\206\346\236\220\345\267\245\345\205\267\346\246\202\350\277\260.md" diff --git "a/JVM/jvm\345\210\235\344\275\223\351\252\214\357\274\232\345\240\206\346\272\242\345\207\272\345\244\204\347\220\206.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/11. JVM-\345\210\235\344\275\223\351\252\214\357\274\232\345\240\206\346\272\242\345\207\272\345\244\204\347\220\206.md" similarity index 92% rename from "JVM/jvm\345\210\235\344\275\223\351\252\214\357\274\232\345\240\206\346\272\242\345\207\272\345\244\204\347\220\206.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/11. JVM-\345\210\235\344\275\223\351\252\214\357\274\232\345\240\206\346\272\242\345\207\272\345\244\204\347\220\206.md" index 9892dbb..ffe3195 100644 --- "a/JVM/jvm\345\210\235\344\275\223\351\252\214\357\274\232\345\240\206\346\272\242\345\207\272\345\244\204\347\220\206.md" +++ "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/11. JVM-\345\210\235\344\275\223\351\252\214\357\274\232\345\240\206\346\272\242\345\207\272\345\244\204\347\220\206.md" @@ -25,4 +25,3 @@ public class Test { ``` ![控制台报错](https://upload-images.jianshu.io/upload_images/5786888-999e6eb041639420.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) -12G的ROM让我等了好久才报错! diff --git "a/JVM/JVM\347\261\273\345\212\240\350\275\275\346\234\272\345\210\266.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/2. JVM-\347\261\273\345\212\240\350\275\275\346\234\272\345\210\266.md" similarity index 100% rename from "JVM/JVM\347\261\273\345\212\240\350\275\275\346\234\272\345\210\266.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/2. JVM-\347\261\273\345\212\240\350\275\275\346\234\272\345\210\266.md" diff --git "a/JVM/JVM\345\206\205\345\255\230\347\256\241\347\220\206.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/3. JVM-\345\206\205\345\255\230\347\256\241\347\220\206.md" similarity index 100% rename from "JVM/JVM\345\206\205\345\255\230\347\256\241\347\220\206.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/3. JVM-\345\206\205\345\255\230\347\256\241\347\220\206.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/4. JVM-\347\250\213\345\272\217\350\256\241\346\225\260\345\231\250.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/4. JVM-\347\250\213\345\272\217\350\256\241\346\225\260\345\231\250.md" new file mode 100644 index 0000000..47dc52e --- /dev/null +++ "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/4. JVM-\347\250\213\345\272\217\350\256\241\346\225\260\345\231\250.md" @@ -0,0 +1,19 @@ +### 什么是程序计数器 +>程序计数器(program counter register)只占用了一块比较小的内存空间,至于小到什么程度呢,这样说吧,有时可以忽略不计的。 + + +程序计数器可以看作是当前线程所执行的字节码文件(class)的行号指示器。也就是我们javap -c xxx.class 反编译生成的指令行号。 + +在虚拟机的世界中,字节码解释器就是通过改变计数器的值来选取下一条执行的字节码指令,分支、循环、跳转、异常处理、线程恢复都需要借助它来实现的。 + +java虚拟机多线程是通过线程间轮流切换来分配给处理器执行时间;在确定时间节点,一个处理器(一核)只会执行一个线程的指令;为保证线程切换回来后能恢复到原执行位置,各个线程间计数器互相不影响,独立存储(称之为 线程私有 的内存); + +### 谁在操作程序计数器 +jvm中有字节码执行引擎,它会在执行代码的时候对程序计数器进行记录。 + + +### 程序计数器特点 +1. 线程隔离。每个线程都有属于自己的程序计数器。 +2. 执行native方法的时候,程序计数器值为空。也很好理解,因为它直接通过JNI调用本地C/C++库,没有经过字节码引擎处理。 +3. 占用内存很小,可以忽略不计。 +4. 永远不会发生OOM(因为设计就没有规定) \ No newline at end of file diff --git "a/JVM/JVM--\350\277\220\350\241\214\346\227\266\346\240\210\345\270\247\347\273\223\346\236\204.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/5. JVM-\350\277\220\350\241\214\346\227\266\346\240\210\345\270\247\347\273\223\346\236\204.md" similarity index 100% rename from "JVM/JVM--\350\277\220\350\241\214\346\227\266\346\240\210\345\270\247\347\273\223\346\236\204.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/5. JVM-\350\277\220\350\241\214\346\227\266\346\240\210\345\270\247\347\273\223\346\236\204.md" diff --git "a/JVM/JVM--\346\226\271\346\263\225\350\260\203\347\224\250.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/6. JVM-\346\226\271\346\263\225\350\260\203\347\224\250.md" similarity index 100% rename from "JVM/JVM--\346\226\271\346\263\225\350\260\203\347\224\250.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/6. JVM-\346\226\271\346\263\225\350\260\203\347\224\250.md" diff --git "a/JVM/JVM\350\247\206\350\247\222\347\234\213\345\257\271\350\261\241\345\210\233\345\273\272.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/7. JVM-\350\247\206\350\247\222\347\234\213\345\257\271\350\261\241\345\210\233\345\273\272.md" similarity index 100% rename from "JVM/JVM\350\247\206\350\247\222\347\234\213\345\257\271\350\261\241\345\210\233\345\273\272.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/7. JVM-\350\247\206\350\247\222\347\234\213\345\257\271\350\261\241\345\210\233\345\273\272.md" diff --git "a/JVM/JVM\345\236\203\345\234\276\345\233\236\346\224\266\346\234\272\345\210\266.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/8. JVM-\345\236\203\345\234\276\345\233\236\346\224\266\346\234\272\345\210\266.md" similarity index 100% rename from "JVM/JVM\345\236\203\345\234\276\345\233\236\346\224\266\346\234\272\345\210\266.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/8. JVM-\345\236\203\345\234\276\345\233\236\346\224\266\346\234\272\345\210\266.md" diff --git "a/JVM/Class\346\226\207\344\273\266\347\273\223\346\236\204&\345\255\227\350\212\202\347\240\201\346\214\207\344\273\244.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/9. JVM-Class\346\226\207\344\273\266\347\273\223\346\236\204&\345\255\227\350\212\202\347\240\201\346\214\207\344\273\244.md" similarity index 100% rename from "JVM/Class\346\226\207\344\273\266\347\273\223\346\236\204&\345\255\227\350\212\202\347\240\201\346\214\207\344\273\244.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/9. JVM-Class\346\226\207\344\273\266\347\273\223\346\236\204&\345\255\227\350\212\202\347\240\201\346\214\207\344\273\244.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/JVM-ZGC.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/JVM-ZGC.md" new file mode 100644 index 0000000..1e45276 --- /dev/null +++ "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/JVM-ZGC.md" @@ -0,0 +1,113 @@ +### 低延迟垃圾收集器 + +> Shenandoah和ZGC为什么被称为低延迟GC,因为它几乎整个工作过程全部都是并发的,只有初始标记、最终标记这些阶段有短暂的停顿,这部分停顿的时间基本上是固定的,与堆的容量、堆中对象的数量没有正比例关系。实际上,它们都可以在任意可管理的(譬如现在ZGC只能管理4TB以内的堆)堆容量下,实现垃圾收集的停顿都不超过十毫秒这种以前听起来是天方夜谭、匪夷所思的目标。这两款目前仍处于实验状态的收集器,被官方命名为“低延迟垃圾收集器”。 + +衡量垃圾收集器的三项最重要的指标是:内存占用(Footprint)、吞吐量(Throughput)和延迟(Latency),三者共同构成了一个“不可能三角”。 + +### 1\. Shenandoah垃圾回收器 + +> 比起稍后要介绍的有着Oracle正朔血统的ZGC,Shenandoah反而更像是G1的下一代继承者。使用转发指针(Forwarding Pointer,也常被称为Indirection Pointer)来实现对象移动与用户程序并发的一种解决方案。 + +##### 那Shenandoah相比起G1又有什么改进呢? + +虽然Shenandoah也是使用基于Region的堆内存布局,同样有着用于存放大对象的HumongousRegion,默认的回收策略也同样是优先处理回收价值最大的Region……但在管理堆内存方面,它与G1至少有三个明显的不同之处,最重要的当然是支持并发的整理算法,G1的回收阶段是可以多线程并行的,但却不能与用户线程并发,这点作为Shenandoah最核心的功能稍后笔者会着重讲解。其次,Shenandoah(目前)是默认不使用分代收集的,换言之,不会有专门的新生代Region或者老年代Region的存在,没有实现分代,并不是说分代对Shenandoah没有价值,这更多是出于性价比的权衡,基于工作量上的考虑而将其放到优先级较低的位置上。最后,Shenandoah摒弃了在G1中耗费大量内存和计算资源去维护的记忆集,改用名为“连接矩阵”(Connection Matrix)的全局数据结构来记录跨Region的引用关系,降低了处理跨代指针时的记忆集维护消耗,也降低了伪共享问题。 + +##### Shenandoah收集器的工作过程大致可以划分为以下九个阶段 + +1. 初始标记 这个阶段仍是“Stop The World”的,但停顿时间与堆大小无关,只与GC Roots的数量相关 + +2. 并发标记 与G1一样,遍历对象图,标记出全部可达的对象,这个阶段是与用户线程一起并发的,时间长短取决于堆中存活对象的数量以及对象图的结构复杂程度。 + +3. 最终标记 与G1一样,处理剩余的SATB扫描,并在这个阶段统计出回收价值最高的Region,将这些Region构成一组回收集(Collection Set)。最终标记阶段也会有一小段短暂的停顿。 + +4. 并发清理 这个阶段用于清理那些整个区域内连一个存活对象都没有找到的Region + +5. 并发回收 在这个阶段,Shenandoah要把回收集里面的存活对象先复制一份到其他未被使用的Region之中。复制对象这件事情如果将用户线程冻结起来再做那是相当简单的,但如果两者必须要同时并发进行的话,就变得复杂起来了。其困难点是在移动对象的同时,用户线程仍然可能不停对被移动的对象进行读写访问,移动对象是一次性的行为,但移动之后整个内存中所有指向该对象的引用都还是旧对象的地址,这是很难一瞬间全部改变过来的。对于并发回收阶段遇到的这些困难,Shenandoah将会通过读屏障和被称为“Brooks Pointers”的转发指针来解决(讲解完Shenandoah整个工作过程之后笔者还要再回头介绍它)。并发回收阶段运行的时间长短取决于回收集的大小 + +6. 初始引用更新 并发回收阶段复制对象结束后,还需要把堆中所有指向旧对象的引用修正到复制后的新地址,这个操作称为引用更新。 + +7. 并发引用更新 真正开始进行引用更新操作,这个阶段是与用户线程一起并发的,时间长短取决于内存中涉及的引用数量的多少。并发引用更新与并发标记不同,它不再需要沿着对象图来搜索,只需要按照内存物理地址的顺序,线性地搜索出引用类型,把旧值改为新值即可。 + +8. 最终引用更新 解决了堆中的引用更新后,还要修正存在于GC Roots中的引用。这个阶段是Shenandoah的最后一次停顿,停顿时间只与GC Roots的数量相关。 + +9. 并发清理 经过并发回收和引用更新之后,整个回收集中所有的Region已再无存活对象,最后再调用一次并发清理过程来回收这些Region的内存空间,供以后新对象分配使用。 + +##### Shenandoah收集器性能 + +2016年做该测试时的Shenandoah并没有完全达成预定目标,停顿时间比其他几款收集器确实有了质的飞跃,但也并未实现最大停顿时间控制在十毫秒以内的目标,而吞吐量方面则出现了很明显的下降。 + +![](https://user-gold-cdn.xitu.io/2020/1/11/16f931defeabda94?w=1240&h=262&f=png&s=110126) + +Shenandoah的性能在日益改善,逐步接近“Low-Pause”的目标。此外,RedHat也积极拓展Shenandoah的使用范围,将其Backport到JDK 11甚至是JDK 8之上,让更多不方便升级JDK版本的应用也能够享受到垃圾收集器技术发展的最前沿成果。 + +### 2. ZGC收集器 + +> ZGC是一款在JDK 11中新加入的具有实验性质[插图]的低延迟垃圾收集器,是由Oracle公司研发的。2018年Oracle创建了JEP 333将ZGC提交给OpenJDK,推动其进入OpenJDK 11的发布清单之中。 + +ZGC和Shenandoah的目标是高度相似的,都希望在尽可能对吞吐量影响不太大的前提下,实现在任意堆内存大小下都可以把垃圾收集的停顿时间限制在十毫秒以内的低延迟。但是ZGC和Shenandoah的实现思路又是差异显著的。 + +ZGC收集器是一款基于Region内存布局的,(暂时)不设分代的,使用了读屏障、染色指针和内存多重映射等技术来实现可并发的标记-整理算法的,以低延迟为首要目标的一款垃圾收集器。 + +##### 并发整理算法的实现 + +> Shenandoah使用转发指针和读屏障来实现并发整理,ZGC虽然同样用到了读屏障,但用的却是一条与Shenandoah完全不同,更加复杂精巧的解题思路。 + +ZGC收集器有一个标志性的设计是它采用的染色指针技术(Colored Pointer),直接把标记信息记在引用对象的指针上。指针对于计算机来讲,它也是一个信息的载体,但是目前而言,内存中的理论可访问信息是远大于实际需求的,尽管Linux高18位不能用来寻址,但剩余的46位也足以满足需求,所以ZGC团队就将指针信息载体进行染色,将其高4位用来存储四个记号信息,通过这些标志位,虚拟机可以直接从指针中看到其引用对象的三色标记状态、是否进入了重分配集(即被移动过)、是否只能通过finalize()方法才能被访问到。 + +由于这些标志位进一步压缩了原本就只有46位的地址空间,也直接导致ZGC能够管理的内存不可以超过4TB(2的42次幂)。 + +![](https://user-gold-cdn.xitu.io/2020/1/11/16f931df02d6c256?w=1073&h=289&f=png&s=115259) + +##### 染色指针的三大优势: + +1. 染色指针可以使得一旦某个Region的存活对象被移走之后,这个Region立即就能够被释放和重用掉,而不必等待整个堆中所有指向该Region的引用都被修正后才能清理。 + +2. 染色指针可以大幅减少在垃圾收集过程中内存屏障的使用数量,设置内存屏障,尤其是写屏障的目的通常是为了记录对象引用的变动情况,如果将这些信息直接维护在指针中,显然就可以省去一些专门的记录操作。 + +3. 染色指针可以作为一种可扩展的存储结构用来记录更多与对象标记、重定位过程相关的数据,以便日后进一步提高性能。 + +##### Java虚拟机作为一个普普通通的进程,这样随意重新定义内存中某些指针的其中几位,操作系统是否支持?处理器是否支持? + +这里面的解决方案要涉及虚拟内存映射技术。把染色指针中的标志位看作是地址的分段符,那只要将这些不同的地址段都映射到同一个物理内存空间,经过多重映射转换后,就可以使用染色指针正常进行寻址了。 + +![image](https://user-gold-cdn.xitu.io/2020/1/11/16f931df01f9c7ed?w=1074&h=446&f=png&s=283314) + +##### ZGC的运作过程大致可划分为以下四个大的阶段 + +1. 并发标记(Concurrent Mark):并发标记是遍历对象图做可达性分析的阶段,与G1、Shenandoah不同的是,ZGC的标记是在指针上而不是在对象上进行的,标记阶段会更新染色指针中的Marked 0、Marked 1标志位。 + +2. 并发预备重分配(Concurrent Prepare for Relocate):这个阶段需要根据特定的查询条件统计得出本次收集过程要清理哪些Region,ZGC划分Region的目的并非为了像G1那样做收益优先的增量回收,而实用范围更大的扫描成本换取省去G1中记忆集的维护成本。此外,在JDK12的ZGC中开始支持的类卸载以及弱引用的处理,也是在这个阶段中完成的。 + +3. 并发重分配(Concurrent Relocate):重分配是ZGC执行过程中的核心阶段,这个过程要把重分配集中的存活对象复制到新的Region上,并为重分配集中的每个Region维护一个转发表(Forward Table),记录从旧对象到新对象的转向关系。得益于染色指针的支持,ZGC收集器能仅从引用上就明确得知一个对象是否处于重分配集之中,如果用户线程此时并发访问了位于重分配集中的对象,这次访问将会被预置的内存屏障所截获,然后立即根据Region上的转发表记录将访问转发到新复制的对象上,并同时修正更新该引用的值,使其直接指向新对象,ZGC将这种行为称为指针的“自愈”(Self-Healing)能力。 + +4. 并发重映射(Concurrent Remap):重映射所做的就是修正整个堆中指向重分配集中旧对象的所有引用。ZGC的并发重映射并不是一个必须要“迫切”去完成的任务,因为前面提到ZGC有"自愈"能力,最坏也就多跳转一层,这时候,一旦所有指针都被修正之后,原来记录新旧对象关系的转发表就可以释放掉了。 + +##### ZGC收集器性能 + +1. ZGC的设计理念是迄今垃圾收集器研究的最前沿成果,可是,必定要有优有劣才会称作权衡,ZGC的这种选择[插图]也限制了它能承受的对象分配速率不会太高,目前唯一的办法就是尽可能地增加堆容量大小,获得更多喘息的时间。 + +2. ZGC还有一个常在技术资料上被提及的优点是支持“NUMA-Aware”非统一内存访问架构的内存分配。由于摩尔定律逐渐失效,现代处理器因频率发展受限转而向多核方向发展,这就造成了如果要访问被其他处理器核心管理的内存,就必须通过Inter-Connect通道来完成,这要比访问处理器的本地内存慢得多,ZGC收集器会优先尝试在请求线程当前所处的处理器的本地内存上分配对象,以保证高效内存访问。 + +3. 在ZGC的“弱项”吞吐量方面,以低延迟为首要目标的ZGC已经达到了以高吞吐量为目标Parallel Scavenge的99%,直接超越了G1。 + +4. ZGC均能毫不费劲地控制在十毫秒之内 + +![image](https://user-gold-cdn.xitu.io/2020/1/11/16f931df3d56bb0a?w=1063&h=708&f=png&s=195145) + +![image](https://user-gold-cdn.xitu.io/2020/1/11/16f931dfb1f140d1?w=1068&h=555&f=png&s=163271) + + +声明:本问参考书籍《深入理解Java虚拟机:JVM高级特性与最佳实践(第3版) 》,这个版本刚上市两个月,新增了一些新的GC和内存分配策略的知识,大伙有兴趣可以看看。若有侵权,请联系删除,谢谢! + + +--- + +--- +如果你喜欢我的文章,那麻烦请关注我的公众号,该公众号还处于初始阶段,谢谢大家的支持。 +![](https://user-gold-cdn.xitu.io/2019/12/3/16ecadc23e4f9bc3?w=258&h=258&f=jpeg&s=26754) +关注公众号,回复`java架构`获取架构视频资源(后期还会分享不同的优质资源噢)。回复`找对象`可以拉你进IT单身交友群噢。 + +想看往期文章, 请点击我的GitHub地址: https://github.com/fantj2016/java-reader + +--- +--- \ No newline at end of file diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/JVM-\344\270\211\347\247\215\345\270\270\351\207\217\346\261\240.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/JVM-\344\270\211\347\247\215\345\270\270\351\207\217\346\261\240.md" new file mode 100644 index 0000000..b2d88ee --- /dev/null +++ "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/JVM-\344\270\211\347\247\215\345\270\270\351\207\217\346\261\240.md" @@ -0,0 +1,35 @@ +### 常量池 + +> 我的理解是Class文件常量池先被加载到堆里,然后解析完后放到运行时常量池中,然后将运行时常量池存放到元空间。 + +Java中的常量池分为三种类型: + +- 静态常量池(也称class文件常量池)(The Constant Pool) +- 运行时常量池(The Run-Time Constant Pool) +- String常量池 + +##### 静态常量池 + +> 存在于class文件中 + +所处区域:堆 + +诞生时间:编译时 + +内容概要:符号引用和字面量 + +class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于存放**编译期**生成的各种字面量和符号引用 + +#### 运行时常量池 + +> 存在于**元空间**中 + +诞生时间:JVM运行时 + +内容概要:class文件元信息描述,**编译后的代码数据**,引用类型数据,类文件常量池。 + +#### String常量池 + +> 存在于堆中 + +在HotSpot VM里实现的string pool功能的是一个StringTable类,它是一个哈希表,里面存的是驻留字符串(也就是我们常说的用双引号括起来的)的引用(而不是驻留字符串实例本身),也就是说在堆中的某些字符串实例被这个StringTable引用之后就等同被赋予了”驻留字符串”的身份。这个StringTable在每个HotSpot VM的实例只有一份,被所有的类共享。 \ No newline at end of file diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/JVM-\345\206\205\345\255\230\346\263\204\346\274\217.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/JVM-\345\206\205\345\255\230\346\263\204\346\274\217.md" new file mode 100644 index 0000000..fd9147d --- /dev/null +++ "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/JVM-\345\206\205\345\255\230\346\263\204\346\274\217.md" @@ -0,0 +1,13 @@ +首先OOM原因有很多: + +- java.lang.OutOfMemoryError: Java heap space 。如果没有代码无限制new对象,一般可通过JVM调参解决。 +- java.lang.OutOfMemoryError: PermGen space 。 可采用-XX:MaxPermSize调节大小 +- java.lang.OutOfMemoryError: Requested array size exceeds VM limit 尝试分配比堆大的数组 +- java.lang.OutOfMemoryError: request bytes for . Out of swap space? 本机swap空间不足 +- java.lang.OutOfMemoryError: (Native method) + +1. 先根据报错确定原因。初步定位是不是调参可以解决的。 +2. 调参解决不了, 分析thread dump、heap dump。 `jmap -dump:live打印堆日志` `jstack -l 打印线程日志` 。`jstat -gc `查看gc信息 +3. 使用MAT、jhat、等分析堆日志。 也可以借助jconsole 分析线程死锁、内存使用等。 + +元空间:1.8后取消了永久代(方法区),改为元空间,类的信息存放在元空间,元空间不使用堆内存,使用的是本地内存,理论上讲本地内存有多大,元空间就有多大。 \ No newline at end of file diff --git a/JVM/2018-08-14.md "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/JVM.md" similarity index 100% rename from JVM/2018-08-14.md rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/JVM.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/arthas\345\221\275\344\273\244\345\210\227\350\241\250.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/arthas\345\221\275\344\273\244\345\210\227\350\241\250.md" new file mode 100644 index 0000000..bfcb6ce --- /dev/null +++ "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/arthas\345\221\275\344\273\244\345\210\227\350\241\250.md" @@ -0,0 +1,29 @@ +### 命令列表 +https://arthas.aliyun.com/doc/commands.html + +### 个人总结 +#### 监控类 +* `monitor`:监控方法的执行情况 +* `watch`:检测函数入参、返回值 +* `trace`:根据路径追踪,并记录消耗时间 +* `stack`:输出当前方法被调用的调用路径 +* `tt`:时间隧道,记录多个请求 +* `jad`:反编译耗时代码 + +#### JVM类 +* `dashboard`:实时数据面板 +* `Thread`:线程相关堆栈信息 +* `sysprop`:查看/修改属性 +* `sysenv`:查看JVM环境属性 +* `vmpotion`:查看JVM中选项 +* `getstatic`:获取静态成员变量 +* `ognl`:执行ognl表达式 +* `dump`:保存已加载字节码文件到本地 + +#### 类加载器 +* `sc`:查看类信息 search class +* `sm`:查看已加载方法信息 search method +* `classloader`:获取类加载器的信息 + + + diff --git "a/Java/\344\273\216JVM\350\247\206\350\247\222\345\210\206\346\236\220try---catch---\346\200\247\350\203\275.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/\344\273\216JVM\350\247\206\350\247\222\345\210\206\346\236\220try-catch\346\200\247\350\203\275.md" similarity index 99% rename from "Java/\344\273\216JVM\350\247\206\350\247\222\345\210\206\346\236\220try---catch---\346\200\247\350\203\275.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/\344\273\216JVM\350\247\206\350\247\222\345\210\206\346\236\220try-catch\346\200\247\350\203\275.md" index b542ebb..36e2ed6 100644 --- "a/Java/\344\273\216JVM\350\247\206\350\247\222\345\210\206\346\236\220try---catch---\346\200\247\350\203\275.md" +++ "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/\344\273\216JVM\350\247\206\350\247\222\345\210\206\346\236\220try-catch\346\200\247\350\203\275.md" @@ -1,4 +1,3 @@ ->try..catch..到底影响性能吗? ### 实验1:简单认识 diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/\345\255\227\350\212\202\347\240\201\346\214\207\344\273\244.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/\345\255\227\350\212\202\347\240\201\346\214\207\344\273\244.md" new file mode 100644 index 0000000..4052fbb --- /dev/null +++ "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/\345\255\227\350\212\202\347\240\201\346\214\207\344\273\244.md" @@ -0,0 +1,58 @@ +### 1.将局部变量表中的变量压入操作数栈中 + +xload_n or xload n 将数据压入栈 +iload_1:将局部变量表中下标为 1 的 int 变量压入操作数栈中。 +aload_2:将局部变量表中下标为 2 的引用数据类型变量(此时为 String)压入操作数栈中。 +lload_3:将局部变量表中下标为 3 的 long 型变量压入操作数栈中。 +iload 5:将局部变量表中下标为 5 的 int 变量(实际为 boolean)压入操作数栈中。 +### 2.将常量池中的常量压入操作数栈中 + +根据数据类型和入栈内容的不同,此类又可以细分为 const 系列、push 系列和 Idc 指令。 + +### 3.将栈顶的数据出栈并装入局部变量表中 + +xstore_(x 为 i、l、f、d、a,n 默认为 0 到 3) +xstore(x 为 i、l、f、d、a) + +### 4.算术指令 + +加法指令:iadd、ladd、fadd、dadd +减法指令:isub、lsub、fsub、dsub +乘法指令:imul、lmul、fmul、dmul +除法指令:idiv、ldiv、fdiv、ddiv +求余指令:irem、lrem、frem、drem +自增指令:iinc +### 5.对象的创建和访问指令 + +#### 5.1创建指令 + +数组也是一种对象,但它创建的字节码指令和普通的对象不同。创建数组的指令有三种: + +newarray:创建基本数据类型的数组 +anewarray:创建引用类型的数组 +multianewarray:创建多维数组 +普通对象的创建指令只有一个,就是 new,它会接收一个操作数,指向常量池中的一个索引,表示要创建的类型。 + +#### 5.2字段访问指令 + +字段可以分为两类,一类是成员变量,一类是静态变量(static 关键字修饰的),所以字段访问指令可以分为两类: + +访问静态变量:getstatic、putstatic。 +访问成员变量:getfield、putfield,需要创建对象后才能访问。 +### 6.方法调用和返回指令 + +方法调用指令有 5 个,分别用于不同的场景: + +invokevirtual:用于调用对象的成员方法,根据对象的实际类型进行分派,支持多态。 +invokeinterface:用于调用接口方法,会在运行时搜索由特定对象实现的接口方法进行调用。 +invokespecial:用于调用一些需要特殊处理的方法,包括构造方法、私有方法和父类方法。 +invokestatic:用于调用静态方法。 +invokedynamic:用于在运行时动态解析出调用点限定符所引用的方法,并执行。 +### 7.操作数栈管理指令 + +常见的操作数栈管理指令有 pop、dup 和 swap。 + +将一个或两个元素从栈顶弹出,并且直接废弃,比如 pop,pop2; +复制栈顶的一个或两个数值并将其重新压入栈顶,比如 dup,dup2,dup_×1,dup2_×1,dup_×2,dup2_×2; +将栈最顶端的两个槽中的数值交换位置,比如 swap。 +这些指令不需要指明数据类型,因为是按照位置压入和弹出的。 \ No newline at end of file diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/\345\257\271\350\261\241\347\232\204\345\210\233\345\273\272\350\277\207\347\250\213.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/\345\257\271\350\261\241\347\232\204\345\210\233\345\273\272\350\277\207\347\250\213.md" new file mode 100644 index 0000000..e3824ed --- /dev/null +++ "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/1. JVM/\345\257\271\350\261\241\347\232\204\345\210\233\345\273\272\350\277\207\347\250\213.md" @@ -0,0 +1,55 @@ +### 对象的创建过程 + +首先,写一段代码编译一下字节码,参考之前的指令知识来看一下对象创建的过程。 + +```java +public class Main { + public static void main(String[] args) { + Entry entry = new Entry(); + } +} + +public class Entry { + private int total = 10; + + public int getTotal() { + return total; + } + + public Entry setTotal(int total) { + this.total = total; + return this; + } +} + +``` +把上面的代码进行编译、解析 + +``` +fantj@FantJdeMacBook-Pro src % javap -c Main.class +Compiled from "Main.java" +public class Main { + public Main(); + Code: + 0: aload_0 + 1: invokespecial #1 // Method java/lang/Object."":()V + 4: return + + public static void main(java.lang.String[]); + Code: + 0: new #2 // class Entry + 3: dup + 4: invokespecial #3 // Method Entry."":()V + 7: astore_1 + 8: return +} + +``` + +#### 字节码解读: +* aload_0: 读取方法的第一个引用参数(或更一般地说,第一个本地引用变量)并将其推送到堆栈. +* invokespecial #1 : 调用Main的super方法,也就是Object的构造方法 +* new: 它会接收一个操作数,指向常量池中的一个索引,表示要创建的类型 +* dup: 压入栈顶 +* invokespecial #3 :调用Entry的构造方法 (半初始化状态,因为此时的变量还是默认值) +* astore_1:将栈顶值保存到局部变量 (真正初始化完成) \ No newline at end of file diff --git "a/Java\345\271\266\345\217\221/Java\345\271\266\345\217\221\347\274\226\347\250\213\357\274\210\344\270\200\357\274\211\347\272\277\347\250\213\347\232\204\345\220\204\347\247\215\345\210\233\345\273\272\346\226\271\345\274\217.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/1. \347\272\277\347\250\213\347\232\204\345\220\204\347\247\215\345\210\233\345\273\272\346\226\271\345\274\217.md" similarity index 93% rename from "Java\345\271\266\345\217\221/Java\345\271\266\345\217\221\347\274\226\347\250\213\357\274\210\344\270\200\357\274\211\347\272\277\347\250\213\347\232\204\345\220\204\347\247\215\345\210\233\345\273\272\346\226\271\345\274\217.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/1. \347\272\277\347\250\213\347\232\204\345\220\204\347\247\215\345\210\233\345\273\272\346\226\271\345\274\217.md" index 6b09c62..4a73f3a 100644 --- "a/Java\345\271\266\345\217\221/Java\345\271\266\345\217\221\347\274\226\347\250\213\357\274\210\344\270\200\357\274\211\347\272\277\347\250\213\347\232\204\345\220\204\347\247\215\345\210\233\345\273\272\346\226\271\345\274\217.md" +++ "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/1. \347\272\277\347\250\213\347\232\204\345\220\204\347\247\215\345\210\233\345\273\272\346\226\271\345\274\217.md" @@ -33,11 +33,14 @@ public class CreatThreadDemo1 extends Thread{ } ``` 常规方法,不多做介绍了,interrupted方法,是来判断该线程是否被中断。(终止线程不允许用stop方法,该方法不会施放占用的资源。所以我们在设计程序的时候,要按照中断线程的思维去设计,就像上面的代码一样)。 + ###### 让线程等待的方法 +>sleep不会释放锁、wait会释放锁然后进入等待队列。 * Thread.sleep(200); //线程休息2ms * Object.wait(); //让线程进入等待,直到调用Object的notify或者notifyAll时,线程停止休眠 #### 方法二:实现runnable接口,作为线程任务存在 +>Thread类也是Runnable的实现类。start方法最终会调用Runnable的run方法。 ``` public class CreatThreadDemo2 implements Runnable { @Override @@ -58,6 +61,7 @@ public class CreatThreadDemo2 implements Runnable { Runnable 只是来修饰线程所执行的任务,它不是一个线程对象。想要启动Runnable对象,必须将它放到一个线程对象里。 #### 方法三:匿名内部类创建线程对象 +>用lambda写更好。 ``` public class CreatThreadDemo3 extends Thread{ public static void main(String[] args) { @@ -192,20 +196,20 @@ public class CreatThreadDemo7 { //parallel 平行的,并行的 int result = values.parallelStream().mapToInt(p -> p*2).sum(); System.out.println(result); - //怎么证明它是并发处理呢 - values.parallelStream().forEach(p-> System.out.println(p)); + //证明它是并发处理 + values.parallelStream().forEach(p-> System.out.println( Thread.currentThread().getName() +" "+p)); } } ``` ``` 200 -40 -10 -20 -30 +ForkJoinPool.commonPool-worker-3 20 +ForkJoinPool.commonPool-worker-1 40 +ForkJoinPool.commonPool-worker-2 10 +main 30 ``` -怎么证明它是并发处理呢,他们并不是按照顺序输出的 。 + diff --git "a/Java\345\271\266\345\217\221/Java\345\271\266\345\217\221\347\274\226\347\250\213----AQS.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/10. AQS.md" similarity index 88% rename from "Java\345\271\266\345\217\221/Java\345\271\266\345\217\221\347\274\226\347\250\213----AQS.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/10. AQS.md" index 3658430..0a0b8aa 100644 --- "a/Java\345\271\266\345\217\221/Java\345\271\266\345\217\221\347\274\226\347\250\213----AQS.md" +++ "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/10. AQS.md" @@ -21,13 +21,12 @@ ##### getState ``` - protected final int getState() { - return state; - } +protected final int getState() { + return state; +} ``` 返回同步状态的当前值。此操作具有 volatile 读的内存语义。 -返回: -当前状态值 + ##### setState ``` @@ -42,80 +41,57 @@ newState - 新的状态值 ##### compareAndSetState ``` - protected final boolean compareAndSetState(int expect, int update) { - // See below for intrinsics setup to support this - return unsafe.compareAndSwapInt(this, stateOffset, expect, update); - } +protected final boolean compareAndSetState(int expect, int update) { + // See below for intrinsics setup to support this + return unsafe.compareAndSwapInt(this, stateOffset, expect, update); +} ``` compareAndSwap即CAS,详细可查找[Java并发编程 -- Atomic包](https://www.jianshu.com/p/288bdd29ec06)文章。 如果当前状态值等于预期值,则以原子方式将同步状态设置为给定的更新值。此操作具有 volatile 读和写的内存语义。 -###### 参数: -expect - 预期值 -update - 新值 -###### 返回: + 如果成功,则返回 true。返回 false 指示实际值与预期值不相等。 + ##### tryAcquire ``` - protected boolean tryAcquire(int arg) { - throw new UnsupportedOperationException(); - } +protected boolean tryAcquire(int arg) { + throw new UnsupportedOperationException(); +} ``` 试图在独占模式下获取对象状态。此方法应该查询是否允许它在独占模式下获取对象状态,如果允许,则获取它。 此方法总是由执行 acquire 的线程来调用。如果此方法报告失败,则 acquire 方法可以将线程加入队列(如果还没有将它加入队列),直到获得其他某个线程释放了该线程的信号。可以用此方法来实现 lock.tryLock()方法。默认实现将抛出[UnsupportedOperationException](http://tool.oschina.net/uploads/apidocs/jdk-zh/java/lang/UnsupportedOperationException.html "java.lang 中的类")。 -###### 参数: -arg - acquire 参数。该值总是传递给 acquire 方法的那个值,或者是因某个条件等待而保存在条目上的值。该值是不间断的,并且可以表示任何内容。 -###### 返回: + 如果成功,则返回 true。在成功的时候,此对象已经被获取。 -###### 抛出: -[IllegalMonitorStateException](http://tool.oschina.net/uploads/apidocs/jdk-zh/java/lang/IllegalMonitorStateException.html "java.lang 中的类")- 如果正在进行的获取操作将在非法状态下放置此同步器。必须以一致的方式抛出此异常,以便同步正确运行。 -[UnsupportedOperationException](http://tool.oschina.net/uploads/apidocs/jdk-zh/java/lang/UnsupportedOperationException.html "java.lang 中的类")- 如果不支持独占模式 ##### tryRelease ``` - protected boolean tryRelease(int arg) { - throw new UnsupportedOperationException(); - } +protected boolean tryRelease(int arg) { + throw new UnsupportedOperationException(); +} ``` 试图设置状态来反映独占模式下的一个释放。 此方法总是由正在执行释放的线程调用。 默认实现将抛出 UnsupportedOperationException。 -###### 参数: -arg - release 参数。该值总是传递给 release 方法的那个值,或者是因某个条件等待而保存在条目上的当前状态值。该值是不间断的,并且可以表示任何内容。 -###### 返回: -如果此对象现在处于完全释放状态,从而使等待的线程都可以试图获得此对象,则返回 true;否则返回 false。 -###### 抛出: -IllegalMonitorStateException - 如果正在进行的释放操作将在非法状态下放置此同步器。必须以一致的方式抛出此异常,以便同步正确运行。 -UnsupportedOperationException - 如果不支持独占模式 - ##### tryAcquireShared ``` - protected int tryAcquireShared(int arg) { - throw new UnsupportedOperationException(); - } +protected int tryAcquireShared(int arg) { + throw new UnsupportedOperationException(); +} ``` 试图在共享模式下获取对象状态。此方法应该查询是否允许它在共享模式下获取对象状态,如果允许,则获取它。 此方法总是由执行 acquire 线程来调用。如果此方法报告失败,则 acquire 方法可以将线程加入队列(如果还没有将它加入队列),直到获得其他某个线程释放了该线程的信号。 默认实现将抛出 UnsupportedOperationException。 -参数: -arg - acquire 参数。该值总是传递给 acquire 方法的那个值,或者是因某个条件等待而保存在条目上的值。该值是不间断的,并且可以表示任何内容。 -返回: -在失败时返回负值;如果共享模式下的获取成功但其后续共享模式下的获取不能成功,则返回 0;如果共享模式下的获取成功并且其后续共享模式下的获取可能够成功,则返回正值,在这种情况下,后续等待线程必须检查可用性。(对三种返回值的支持使得此方法可以在只是有时候以独占方式获取对象的上下文中使用。)在成功的时候,此对象已被获取。 -抛出: -IllegalMonitorStateException - 如果正在进行的获取操作将在非法状态下放置此同步器。必须以一致的方式抛出此异常,以便同步正确运行。 -UnsupportedOperationException - 如果不支持共享模式 - ##### tryReleaseShared ``` - protected boolean tryReleaseShared(int arg) { - throw new UnsupportedOperationException(); - } +protected boolean tryReleaseShared(int arg) { + throw new UnsupportedOperationException(); +} ``` 试图设置状态来反映共享模式下的一个释放。 此方法总是由正在执行释放的线程调用。 diff --git "a/Java\345\271\266\345\217\221/Java\345\271\266\345\217\221\347\274\226\347\250\213----AQS\345\205\245\351\227\250&\345\256\236\347\216\260\345\217\257\351\207\215\345\205\245\351\224\201.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/11. AQS\345\205\245\351\227\250&\345\256\236\347\216\260\345\217\257\351\207\215\345\205\245\351\224\201.md" similarity index 100% rename from "Java\345\271\266\345\217\221/Java\345\271\266\345\217\221\347\274\226\347\250\213----AQS\345\205\245\351\227\250&\345\256\236\347\216\260\345\217\257\351\207\215\345\205\245\351\224\201.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/11. AQS\345\205\245\351\227\250&\345\256\236\347\216\260\345\217\257\351\207\215\345\205\245\351\224\201.md" diff --git "a/Java\345\271\266\345\217\221/Java\345\271\266\345\217\221\347\274\226\347\250\213----Condition.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/12. Condition.md" similarity index 100% rename from "Java\345\271\266\345\217\221/Java\345\271\266\345\217\221\347\274\226\347\250\213----Condition.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/12. Condition.md" diff --git "a/Java\345\271\266\345\217\221/Java\345\244\232\347\272\277\347\250\213----\344\272\222\346\226\245\351\224\201-\345\205\261\344\272\253\351\224\201-\350\257\273\345\206\231\351\224\201-\345\277\253\351\200\237\345\205\245\351\227\250.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/13. \344\272\222\346\226\245\351\224\201-\345\205\261\344\272\253\351\224\201-\350\257\273\345\206\231\351\224\201.md" similarity index 100% rename from "Java\345\271\266\345\217\221/Java\345\244\232\347\272\277\347\250\213----\344\272\222\346\226\245\351\224\201-\345\205\261\344\272\253\351\224\201-\350\257\273\345\206\231\351\224\201-\345\277\253\351\200\237\345\205\245\351\227\250.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/13. \344\272\222\346\226\245\351\224\201-\345\205\261\344\272\253\351\224\201-\350\257\273\345\206\231\351\224\201.md" diff --git "a/Java\345\271\266\345\217\221/Java\345\244\232\347\272\277\347\250\213----\351\224\201\351\231\215\347\272\247.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/14. \351\224\201\351\231\215\347\272\247.md" similarity index 100% rename from "Java\345\271\266\345\217\221/Java\345\244\232\347\272\277\347\250\213----\351\224\201\351\231\215\347\272\247.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/14. \351\224\201\351\231\215\347\272\247.md" diff --git "a/Java\345\271\266\345\217\221/Java\345\244\232\347\272\277\347\250\213----\345\205\254\345\271\263\351\224\201\345\222\214\351\235\236\345\205\254\345\271\263\351\224\201\347\232\204\344\270\200\344\272\233\346\200\235\350\200\203.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/15. \345\205\254\345\271\263\351\224\201\345\222\214\351\235\236\345\205\254\345\271\263\351\224\201\347\232\204\344\270\200\344\272\233\346\200\235\350\200\203.md" similarity index 100% rename from "Java\345\271\266\345\217\221/Java\345\244\232\347\272\277\347\250\213----\345\205\254\345\271\263\351\224\201\345\222\214\351\235\236\345\205\254\345\271\263\351\224\201\347\232\204\344\270\200\344\272\233\346\200\235\350\200\203.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/15. \345\205\254\345\271\263\351\224\201\345\222\214\351\235\236\345\205\254\345\271\263\351\224\201\347\232\204\344\270\200\344\272\233\346\200\235\350\200\203.md" diff --git "a/Java\345\271\266\345\217\221/Java\345\271\266\345\217\221\347\274\226\347\250\213----\346\211\213\345\212\250\345\256\236\347\216\260\345\217\257\351\207\215\345\205\245Lock.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/16. \346\211\213\345\212\250\345\256\236\347\216\260\345\217\257\351\207\215\345\205\245Lock.md" similarity index 100% rename from "Java\345\271\266\345\217\221/Java\345\271\266\345\217\221\347\274\226\347\250\213----\346\211\213\345\212\250\345\256\236\347\216\260\345\217\257\351\207\215\345\205\245Lock.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/16. \346\211\213\345\212\250\345\256\236\347\216\260\345\217\257\351\207\215\345\205\245Lock.md" diff --git "a/Java\345\271\266\345\217\221/Java\345\271\266\345\217\221\347\274\226\347\250\213----\345\215\225\344\276\213\346\250\241\345\274\217\347\272\277\347\250\213\345\256\211\345\205\250\351\227\256\351\242\230.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/17. \345\215\225\344\276\213\346\250\241\345\274\217\347\272\277\347\250\213\345\256\211\345\205\250\351\227\256\351\242\230.md" similarity index 100% rename from "Java\345\271\266\345\217\221/Java\345\271\266\345\217\221\347\274\226\347\250\213----\345\215\225\344\276\213\346\250\241\345\274\217\347\272\277\347\250\213\345\256\211\345\205\250\351\227\256\351\242\230.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/17. \345\215\225\344\276\213\346\250\241\345\274\217\347\272\277\347\250\213\345\256\211\345\205\250\351\227\256\351\242\230.md" diff --git "a/\345\211\221\346\214\207offer/\345\271\266\345\217\221\345\267\245\345\205\267\347\261\273CountDownLatch\343\200\201CyclicBarrier\343\200\201Semaphore\345\256\236\350\267\265\345\217\212\346\272\220\347\240\201\345\210\206\346\236\220.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/18. \345\271\266\345\217\221\345\267\245\345\205\267\347\261\273CountDownLatch\343\200\201CyclicBarrier\343\200\201Semaphore\345\256\236\350\267\265\345\217\212\346\272\220\347\240\201\345\210\206\346\236\220.md" similarity index 100% rename from "\345\211\221\346\214\207offer/\345\271\266\345\217\221\345\267\245\345\205\267\347\261\273CountDownLatch\343\200\201CyclicBarrier\343\200\201Semaphore\345\256\236\350\267\265\345\217\212\346\272\220\347\240\201\345\210\206\346\236\220.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/18. \345\271\266\345\217\221\345\267\245\345\205\267\347\261\273CountDownLatch\343\200\201CyclicBarrier\343\200\201Semaphore\345\256\236\350\267\265\345\217\212\346\272\220\347\240\201\345\210\206\346\236\220.md" diff --git "a/Java\346\272\220\347\240\201\345\210\206\346\236\220/ThreadPoolExector\346\272\220\347\240\201\345\210\206\346\236\220.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/19. ThreadPoolExector\346\272\220\347\240\201\345\210\206\346\236\220.md" similarity index 100% rename from "Java\346\272\220\347\240\201\345\210\206\346\236\220/ThreadPoolExector\346\272\220\347\240\201\345\210\206\346\236\220.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/19. ThreadPoolExector\346\272\220\347\240\201\345\210\206\346\236\220.md" diff --git "a/Java\345\271\266\345\217\221/Java\345\244\232\347\272\277\347\250\213----wait()-\345\222\214-notify()-\344\275\277\347\224\250\345\205\245\351\227\250.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/2. wait\343\200\201notify\344\275\277\347\224\250\345\205\245\351\227\250.md" similarity index 90% rename from "Java\345\271\266\345\217\221/Java\345\244\232\347\272\277\347\250\213----wait()-\345\222\214-notify()-\344\275\277\347\224\250\345\205\245\351\227\250.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/2. wait\343\200\201notify\344\275\277\347\224\250\345\205\245\351\227\250.md" index c82d19f..a7c7eb4 100644 --- "a/Java\345\271\266\345\217\221/Java\345\244\232\347\272\277\347\250\213----wait()-\345\222\214-notify()-\344\275\277\347\224\250\345\205\245\351\227\250.md" +++ "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/2. wait\343\200\201notify\344\275\277\347\224\250\345\205\245\351\227\250.md" @@ -5,31 +5,18 @@ wait,notify,notifyAll 是定义在Object类的实例方法,用于控制线程状态。 -### 文档分析 - -我们找到Object类,下载它的文档,翻译每个方法的注释。 - -总结如下: - -1. wait() 和 notify() 必须由对象持有者去调用,有三种方式: -1️⃣执行该对象的synchronized实例方法 -2️⃣执行synchronized代码块 -3️⃣执行该类的synchronized静态方法 +### 使用总结: +1. wait() 和 notify() 必须由对象持有者去调用,也就是说必须进入monitor监控,有三种方式: + 1. 执行该对象的synchronized实例方法 + 2. 执行synchronized代码块 + 3. 执行该类的synchronized静态方法 2. 当想要调用wait( )进行线程等待时,必须要取得这个锁对象的控制权(对象监视器),一般是放到synchronized(obj)代码中。 - 3. 在while循环里用wait操作性能更好(比if判断) - -4. 调用obj.wait( )释放了obj的锁,否则其他线程也无法获得obj的锁,也就无法在synchronized(obj){ obj.notify() } 代码段内唤醒A。 - -5. notify( )方法只会通知等待队列中的第一个相关线程(不会通知优先级比较高的线程) - +4. 调用obj.wait()方法会释放obj的锁,并将锁放入锁池队列。(锁池队列标识在对象头) +5. obj.notify( )方法只会通知等待队列中的第一个相关线程去锁池拿obj锁, 然后进入就绪状态(不会通知优先级比较高的线程) 6. notifyAll( )通知所有等待该竞争资源的线程(也不会按照线程的优先级来执行) -7. 如果是synchronized声明的方法,wait()操作后会施放synchronized锁,相反notify()触发后会重拿起synchronized锁。 - -8. 如果当前线程不是当前对象所持有,则会报异常IllegalMonitorStateException - ### 实例 ##### 1. 通过调用对象的wait和notify实现 diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/20. wait()\344\270\216sleep()\346\226\271\346\263\225\345\214\272\345\210\253.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/20. wait()\344\270\216sleep()\346\226\271\346\263\225\345\214\272\345\210\253.md" new file mode 100644 index 0000000..b08b57c --- /dev/null +++ "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/20. wait()\344\270\216sleep()\346\226\271\346\263\225\345\214\272\345\210\253.md" @@ -0,0 +1,10 @@ +本质上的区别: sleep是对线程状态的控制, wait是线程间通讯。一个是Thread类, 一个是Object类。Thread不会影响锁的行为,锁相关的方法都定义在Object类中 + +1. sleep是Thread的方法, wait是Object的方法。 +2. sleep不会释放锁, wait会施放锁并将锁添加到一个等待队列。 +3. sleep不依赖monitor, wait依赖monitor。 +4. sleep不需要唤醒、wait需要。 + +![img](https://img-blog.csdn.net/20150309140927553) + +**注意**:锁池是对象头中的一个标记,用于存放等待锁的线程。调用wait()方法, 意味着当前线程需要释放自己的所有锁放入锁池, 然后进入该对象的等待队列。当调用notify()时, 就通知等待队列中的一个线程出列, 然后进入锁池去拿到锁。 \ No newline at end of file diff --git "a/Java\345\271\266\345\217\221/Java\345\271\266\345\217\221\347\274\226\347\250\213----\347\272\277\347\250\213\345\256\211\345\205\250\343\200\201\344\274\230\345\205\210\347\272\247\350\256\276\345\256\232.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/3. \347\272\277\347\250\213\345\256\211\345\205\250\343\200\201\344\274\230\345\205\210\347\272\247\350\256\276\345\256\232.md" similarity index 95% rename from "Java\345\271\266\345\217\221/Java\345\271\266\345\217\221\347\274\226\347\250\213----\347\272\277\347\250\213\345\256\211\345\205\250\343\200\201\344\274\230\345\205\210\347\272\247\350\256\276\345\256\232.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/3. \347\272\277\347\250\213\345\256\211\345\205\250\343\200\201\344\274\230\345\205\210\347\272\247\350\256\276\345\256\232.md" index 35ae3f8..eddb293 100644 --- "a/Java\345\271\266\345\217\221/Java\345\271\266\345\217\221\347\274\226\347\250\213----\347\272\277\347\250\213\345\256\211\345\205\250\343\200\201\344\274\230\345\205\210\347\272\247\350\256\276\345\256\232.md" +++ "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/3. \347\272\277\347\250\213\345\256\211\345\205\250\343\200\201\344\274\230\345\205\210\347\272\247\350\256\276\345\256\232.md" @@ -44,11 +44,10 @@ public class Task{ public int value = 0; - // 没有处理线程安全 public int getValue() { - return value++; - } + return value++; + } public static void main(String[] args) { Task task = new Task(); @@ -96,4 +95,4 @@ public class Task{ 既在修饰符后加上synchronized关键字。 ![image.png](http://upload-images.jianshu.io/upload_images/5786888-e0841cdc7b961279.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) -但是又有一个问题,这样的话,其实原理上是串行处理的,那我们该如果解决这个问题呢。 +但是又有一个问题,这样的话,其实原理上是串行处理的, 性能比较低, 那我们该如何更好的解决这个问题呢。下一章分析。 diff --git "a/Java\345\271\266\345\217\221/Java\345\271\266\345\217\221\347\274\226\347\250\213----\346\267\261\345\205\245\345\211\226\346\236\220volatile\345\205\263\351\224\256\345\255\227.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/4. \346\267\261\345\205\245\345\211\226\346\236\220volatile\345\205\263\351\224\256\345\255\227.md" similarity index 54% rename from "Java\345\271\266\345\217\221/Java\345\271\266\345\217\221\347\274\226\347\250\213----\346\267\261\345\205\245\345\211\226\346\236\220volatile\345\205\263\351\224\256\345\255\227.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/4. \346\267\261\345\205\245\345\211\226\346\236\220volatile\345\205\263\351\224\256\345\255\227.md" index 630c16d..cdc9306 100644 --- "a/Java\345\271\266\345\217\221/Java\345\271\266\345\217\221\347\274\226\347\250\213----\346\267\261\345\205\245\345\211\226\346\236\220volatile\345\205\263\351\224\256\345\255\227.md" +++ "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/4. \346\267\261\345\205\245\345\211\226\346\236\220volatile\345\205\263\351\224\256\345\255\227.md" @@ -1,5 +1,5 @@ ### 1.volatile关键字的两层语义 -一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义: +>一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义: 1. 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。 2. 禁止进行指令重排序。 @@ -17,20 +17,19 @@ while(!stop){ //线程2 stop = true; ``` -事实上,这段代码会真的先执行线程1,然后再执行2吗,答案是肯定的:不是。两个线程各干各的事情,没有绝对的先后问题,所以会出现两种答案(一个线程用stop=false跑线程1,一个线程用stop=true跑线程二,互不相关)。 +事实上,这段代码会真的先执行线程1,然后再执行2吗,答案是肯定的:不是。两个线程各干各的事情,没有绝对的先后问题,所以会出现两种答案(一个线程用stop=false跑线程1,一个线程用stop=true跑线程二,互不相关), 那如何保证stop在两个线程中变化可见呢?将该变量用volatile修饰。 -###### 但是用volatile修饰之后就变得不一样了: +#### volatile修饰使得变量多线程可见原理: +>这个涉及到jvm的内存模型, 这个内存模型也就是下面三点描述的: -第一:使用volatile关键字会强制将修改的值立即写入主存; +1. 使用volatile关键字会强制将修改的值立即写入主存; +2. 使用volatile关键字的话,当线程2进行修改时,会导致线程1的工作内存中缓存变量stop的缓存行无效(反映到硬件层的话,就是CPU的L1或者L2缓存中对应的缓存行无效); +3. 由于线程1的工作内存中缓存变量stop的缓存行无效,所以线程1再次读取变量stop的值时会去主存读取。 -第二:使用volatile关键字的话,当线程2进行修改时,会导致线程1的工作内存中缓存变量stop的缓存行无效(反映到硬件层的话,就是CPU的L1或者L2缓存中对应的缓存行无效); - -第三:由于线程1的工作内存中缓存变量stop的缓存行无效,所以线程1再次读取变量stop的值时会去主存读取。 - -所以肯定能保证不管 多少个线程跑的时候,stop的值是相同的。这是利用了volatile的线程可见性原理。 +所以不管多少个线程跑的时候,stop的值是各线程保持同步的。这是利用了volatile的线程可见性原理。 ### 2.volatile保证原子性吗? -从上面知道volatile关键字保证了操作的可见性,但是volatile能保证对变量的操作是原子性吗?看一段代码: +>从上面知道volatile关键字保证了操作的可见性,但是volatile不能保证对变量的操作是原子性。 ``` public class Test { public volatile int inc = 0; @@ -62,11 +61,11 @@ public class Test { * 假如线程1从住内存中获取到变量值,在执行自增的时候是阻塞性质的,这时候线程2也拿到一个相同的值,然后也进行自增,那么这两个线程最终写入的值是一样的。 -##### 那如何修改呢?有三种方式 -1. 给自增方法加上同步锁synchronized +#### 那如何保证原子性呢?有三种方式 +##### 1. 给自增方法加上同步锁synchronized ``` public class Test { - public int inc = 0; + public volatile int inc = 0; public synchronized void increase() { inc++; @@ -89,10 +88,10 @@ public class Test { } } ``` -2. 采用Lock +##### 2. 采用Lock ``` public class Test { - public int inc = 0; + public volatile int inc = 0; Lock lock = new ReentrantLock(); public void increase() { @@ -121,7 +120,9 @@ public class Test { } } ``` -3. 采用AtomicInteger +##### 3. 采用AtomicInteger +>在java 1.5的java.util.concurrent.atomic包下提供了一些原子操作类,即对基本数据类型的 自增(加1操作),自减(减1操作)、以及加法操作(加一个数),减法操作(减一个数)进行了封装,保证这些操作是原子性操作。atomic是利用CAS来实现原子性操作的(Compare And Swap),CAS实际上是利用处理器提供的CMPXCHG指令实现的,而处理器执行CMPXCHG指令是一个原子性操作。 + ``` public class Test { public AtomicInteger inc = new AtomicInteger(); @@ -147,25 +148,48 @@ public class Test { } } ``` -在java 1.5的java.util.concurrent.atomic包下提供了一些原子操作类,即对基本数据类型的 自增(加1操作),自减(减1操作)、以及加法操作(加一个数),减法操作(减一个数)进行了封装,保证这些操作是原子性操作。atomic是利用CAS来实现原子性操作的(Compare And Swap),CAS实际上是利用处理器提供的CMPXCHG指令实现的,而处理器执行CMPXCHG指令是一个原子性操作。 -### 3.volatile能保证有序性吗? -在前面提到volatile关键字能禁止指令重排序,所以volatile能在一定程度上保证有序性。 +### 3.volatile有序性 +>volatile保证有序性通过两点:也就是Happen-Before原则: +1. 当程序执行到volatile变量的读操作或者写操作时,在其前面的操作的更改肯定全部已经进行,且结果已经对后面的操作可见;在其后面的操作肯定还没有进行; +2. 在进行指令优化时,不能将在对volatile变量访问的语句放在其后面执行,也不能把volatile变量后面的语句放到其前面执行。 -### 4.volatile的原理和实现机制 -前面讲述了源于volatile关键字的一些使用,下面我们来探讨一下volatile到底如何保证可见性和禁止指令重排序的。 +##### 有序性例子 +``` +//x、y为非volatile变量 +//flag为volatile变量 + +x = 2; //语句1 +y = 0; //语句2 +flag = true; //语句3 +x = 4; //语句4 +y = -1; //语句5 +``` +由于flag变量为volatile变量,那么在进行指令重排序的过程的时候,不会将语句3放到语句1、语句2前面,也不会讲语句3放到语句4、语句5后面。但是要注意语句1和语句2的顺序、语句4和语句5的顺序是不作任何保证的。同时volatile也能保证执行顺序与前面编译顺序一样。 -下面这段话摘自《深入理解Java虚拟机》: +##### 应该保证有序性而没有使用volatile会怎样呢 +``` +//线程1: +context = loadContext(); //语句1 +inited = true; //语句2 + +//线程2: +while(!inited ){ + sleep() +} +doSomethingwithconfig(context); +``` +前面举这个例子的时候,提到有可能语句2会在语句1之前执行,那么久可能导致context还没被初始化,而线程2中就使用未初始化的context去进行操作,导致程序报空指针异常。如果用volatile关键字对inited变量进行修饰,就不会出现这种问题了。 -“观察加入volatile关键字和没有加入volatile关键字时所生成的汇编代码发现,加入volatile关键字时,会多出一个lock前缀指令” +### 4.volatile的原理和实现机制 +>前面讲述了源于volatile关键字的一些使用,下面我们来探讨一下volatile到底如何保证可见性和禁止指令重排序的。参考:《深入理解Java虚拟机》: -lock前缀指令实际上相当于一个内存屏障(也成内存栅栏),内存屏障会提供3个功能: +观察加入volatile关键字和没有加入volatile关键字时所生成的汇编代码发现,加入volatile关键字时,会多出一个lock前缀指令. +lock前缀指令实际上相当于一个内存屏障(也成内存栅栏),内存屏障会提供3个功能: 1. 它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成; - 2. 它会强制将对缓存的修改操作立即写入主存; - 3. 如果是写操作,它会导致其他CPU中对应的缓存行无效。 diff --git "a/Java\345\271\266\345\217\221/Java\345\271\266\345\217\221\347\274\226\347\250\213----synchronized\344\277\235\350\257\201\347\272\277\347\250\213\345\256\211\345\205\250\347\232\204\345\216\237\347\220\206.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/5. synchronized\344\277\235\350\257\201\347\272\277\347\250\213\345\256\211\345\205\250\347\232\204\345\216\237\347\220\206.md" similarity index 69% rename from "Java\345\271\266\345\217\221/Java\345\271\266\345\217\221\347\274\226\347\250\213----synchronized\344\277\235\350\257\201\347\272\277\347\250\213\345\256\211\345\205\250\347\232\204\345\216\237\347\220\206.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/5. synchronized\344\277\235\350\257\201\347\272\277\347\250\213\345\256\211\345\205\250\347\232\204\345\216\237\347\220\206.md" index 73ce122..6012bbe 100644 --- "a/Java\345\271\266\345\217\221/Java\345\271\266\345\217\221\347\274\226\347\250\213----synchronized\344\277\235\350\257\201\347\272\277\347\250\213\345\256\211\345\205\250\347\232\204\345\216\237\347\220\206.md" +++ "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/5. synchronized\344\277\235\350\257\201\347\272\277\347\250\213\345\256\211\345\205\250\347\232\204\345\216\237\347\220\206.md" @@ -1,18 +1,25 @@ +### 什么时候会发生线程安全问题: +>线程安全是并发编程中的重要关注点,应该注意到的是,造成线程安全问题的主要诱因有两点: +1. 存在共享数据(也称临界资源)。 +2. 存在多条线程共同操作共享数据。 -线程安全是并发编程中的重要关注点,应该注意到的是,造成线程安全问题的主要诱因有两点,一是存在共享数据(也称临界资源),二是存在多条线程共同操作共享数据。因此为了解决这个问题,我们可能需要这样一个方案,当存在多个线程操作共享数据时,需要保证同一时刻有且只有一个线程在操作共享数据,其他线程必须等到该线程处理完数据后再进行,这种方式有个高尚的名称叫互斥锁,即能达到互斥访问目的的锁,也就是说当一个共享数据被当前正在访问的线程加上互斥锁后,在同一个时刻,其他线程只能处于等待的状态,直到当前线程处理完毕释放该锁。在 Java 中,关键字 synchronized可以保证在同一个时刻,只有一个线程可以执行某个方法或者某个代码块(主要是对方法或者代码块中存在共享数据的操作),同时我们还应该注意到synchronized另外一个重要的作用,synchronized可保证一个线程的变化(主要是共享数据的变化)被其他线程所看到(保证可见性,完全可以替代Volatile功能),这点确实也是很重要的。 +那如何保证线程安全呢?java提供了一些线程安全的工具, 这里主要介绍synchronized锁。 +### synchronized的三种使用方式 +>synchronized关键字最主要有以下3种使用方式,下面分别介绍 -### synchronized的三种应用方式 -synchronized关键字最主要有以下3种应用方式,下面分别介绍 +1. 修饰实例方法。作用于当前实例加锁,进入同步代码前要获得当前实例的锁 +2. 修饰静态方法。作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁 +3. 修饰代码块。指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。 -* 修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁 +### synchronized与this锁(对象锁)、class锁(类锁)的关系 -* 修饰静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁 - -* 修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。 +1. 如果修饰的是静态方法,则拿到的是类锁。因为此时对象实例还没有生成。如果修饰普通方法,则拿到的是对象锁,同一个实例会拦截。 +2. 只要采用类锁`synchronized(xxx.class)`, 就会拦截所有线程,同时只能有一个线程访问。 +3. 只要采用对象锁`sychronized(this)`, 如果是同一个实例就会拦截, 如果是不同实例, 则可以同时访问。 ##### synchronized作用于实例方法 -所谓的实例对象锁就是用synchronized修饰实例对象中的实例方法,注意是实例方法不包括静态方法,如下 +>所谓的实例对象锁就是用synchronized修饰实例对象中的实例方法,注意是实例方法不包括静态方法,如下 ``` public class AccountingSync implements Runnable{ //共享资源(临界资源) @@ -72,11 +79,13 @@ public class AccountingSyncBad implements Runnable{ System.out.println(i); } } + +控制台结果:1452317 ``` 上述代码与前面不同的是我们同时创建了两个新实例AccountingSyncBad,然后启动两个不同的线程对共享变量i进行操作,但很遗憾操作结果是1452317而不是期望结果2000000,因为上述代码犯了严重的错误,虽然我们使用synchronized修饰了increase方法,但却new了两个不同的实例对象,这也就意味着存在着两个不同的实例对象锁,因此t1和t2都会进入各自的对象锁,也就是说t1和t2线程使用的是不同的锁,因此线程安全是无法保证的。解决这种困境的的方式是将synchronized作用于静态的increase方法,这样的话,对象锁就当前类对象,由于无论创建多少个实例对象,但对于的类对象拥有只有一个,所有在这样的情况下对象锁就是唯一的。下面我们看看如何使用将synchronized作用于静态的increase方法。 ##### synchronized作用于静态方法 -当synchronized作用于静态方法时,其锁就是当前类的class对象锁。由于静态成员不专属于任何一个实例对象,是类成员,因此通过class对象锁可以控制静态 成员的并发操作。需要注意的是如果一个线程A调用一个实例对象的非static synchronized方法,而线程B需要调用这个实例对象所属类的静态 synchronized方法,是允许的,不会发生互斥现象,因为访问静态 synchronized 方法占用的锁是当前类的class对象,而访问非静态 synchronized 方法占用的锁是当前实例对象锁,看如下代码 +>当synchronized作用于静态方法时,其锁就是当前类的class对象锁。由于静态成员不专属于任何一个实例对象,是类成员,因此通过class对象锁可以控制静态 成员的并发操作。需要注意的是如果一个线程A调用一个实例对象的非static synchronized方法,而线程B需要调用这个实例对象所属类的静态 synchronized方法,是允许的,不会发生互斥现象,因为访问静态 synchronized 方法占用的锁是当前类的class对象,而访问非静态 synchronized 方法占用的锁是当前实例对象锁,看如下代码 ``` public class AccountingSyncClass implements Runnable{ static int i=0; @@ -118,7 +127,7 @@ public class AccountingSyncClass implements Runnable{ 由于synchronized关键字修饰的是静态increase方法,与修饰实例方法不同的是,其锁对象是当前类的class对象。注意代码中的increase4Obj方法是实例方法,其对象锁是当前实例对象,如果别的线程调用该方法,将不会产生互斥现象,毕竟锁对象不同,但我们应该意识到这种情况下可能会发现线程安全问题(操作了共享静态变量i)。 ##### synchronized同步代码块 -除了使用关键字修饰实例方法和静态方法外,还可以使用同步代码块,在某些情况下,我们编写的方法体可能比较大,同时存在一些比较耗时的操作,而需要同步的代码又只有一小部分,如果直接对整个方法进行同步操作,可能会得不偿失,此时我们可以使用同步代码块的方式对需要同步的代码进行包裹,这样就无需对整个方法进行同步操作了,同步代码块的使用示例如下: +>除了使用关键字修饰实例方法和静态方法外,还可以使用同步代码块,在某些情况下,我们编写的方法体可能比较大,同时存在一些比较耗时的操作,而需要同步的代码又只有一小部分,如果直接对整个方法进行同步操作,可能会得不偿失,此时我们可以使用同步代码块的方式对需要同步的代码进行包裹,这样就无需对整个方法进行同步操作了,同步代码块的使用示例如下: ``` public class AccountingSync implements Runnable{ static AccountingSync instance=new AccountingSync(); @@ -163,7 +172,7 @@ synchronized(AccountingSync.class){ ### synchronized底层语义原理 Java 虚拟机中的同步(Synchronization)基于进入和退出管程(Monitor)对象实现, 无论是显式同步(有明确的 monitorenter 和 monitorexit 指令,即同步代码块)还是隐式同步都是如此。在 Java 语言中,同步用的最多的地方可能是被 synchronized 修饰的同步方法。同步方法 并不是由 monitorenter 和 monitorexit 指令来实现同步的,而是由方法调用指令读取运行时常量池中方法的 ACC_SYNCHRONIZED 标志来隐式实现的,关于这点,稍后详细分析。下面先来了解一个概念Java对象头,这对深入理解synchronized实现原理非常关键。 -  如果对上面的执行结果还有疑问,也先不用急,我们先来了解Synchronized的原理,再回头上面的问题就一目了然了。我们先通过反编译下面的代码来看看Synchronized是如何实现对代码块进行同步的: +如果对上面的执行结果还有疑问,也先不用急,我们先来了解Synchronized的原理,再回头上面的问题就一目了然了。我们先通过反编译下面的代码来看看Synchronized是如何实现对代码块进行同步的: ``` public class SynchronizedDemo { public void method() { @@ -180,15 +189,7 @@ public class SynchronizedDemo { 关于这两条指令的作用,我们直接参考JVM规范中描述: ##### monitorenter : -``` -Each object is associated with a monitor. A monitor is locked if and only if it has an owner. The thread that executes monitorenter attempts to gain ownership of the monitor associated with objectref, as follows: -• If the entry count of the monitor associated with objectref is zero, the thread enters the monitor and sets its entry count to one. The thread is then the owner of the monitor. -• If the thread already owns the monitor associated with objectref, it reenters the monitor, incrementing its entry count. -• If another thread already owns the monitor associated with objectref, the thread blocks until the monitor's entry count is zero, then tries again to gain ownership. -``` -这段话的大概意思为: - -每个对象有一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下: +>每个对象有一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下: 1. 如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者。 @@ -197,17 +198,11 @@ Each object is associated with a monitor. A monitor is locked if and only if it 3. 如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。 ##### monitorexit: -``` -The thread that executes monitorexit must be the owner of the monitor associated with the instance referenced by objectref. -The thread decrements the entry count of the monitor associated with objectref. If as a result the value of the entry count is zero, the thread exits the monitor and is no longer its owner. Other threads that are blocking to enter the monitor are allowed to attempt to do so. -``` -这段话的大概意思为: - -执行monitorexit的线程必须是objectref所对应的monitor的所有者。 +>执行monitorexit的线程必须是objectref所对应的monitor的所有者。 指令执行时,monitor的进入数减1,如果减1后进入数为0,那线程退出monitor,不再是这个monitor的所有者。其他被这个monitor阻塞的线程可以尝试去获取这个 monitor 的所有权。 -  通过这两段描述,我们应该能很清楚的看出Synchronized的实现原理,Synchronized的语义底层是通过一个monitor的对象来完成,其实wait/notify等方法也依赖于monitor对象,这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法,否则会抛出java.lang.IllegalMonitorStateException的异常的原因。 +通过这两段描述,我们应该能很清楚的看出Synchronized的实现原理,Synchronized的语义底层是通过一个monitor的对象来完成,其实wait/notify等方法也依赖于monitor对象,这就是为什么只有在同步的块或者方法中才能调用wait/notify等方法,否则会抛出java.lang.IllegalMonitorStateException的异常的原因。 我们再来看一下同步方法的反编译结果: @@ -435,26 +430,34 @@ synchronized (obj) { ## Java虚拟机对synchronized的优化 -锁的状态总共有四种,无锁状态、偏向锁、轻量级锁和重量级锁。随着锁的竞争,锁可以从偏向锁升级到轻量级锁,再升级的重量级锁,但是锁的升级是单向的,也就是说只能从低到高升级,不会出现锁的降级,关于重量级锁,前面我们已详细分析过,下面我们将介绍偏向锁和轻量级锁以及JVM的其他优化手段,这里并不打算深入到每个锁的实现和转换过程更多地是阐述Java虚拟机所提供的每个锁的核心优化思想,毕竟涉及到具体过程比较繁琐,如需了解详细过程可以查阅《深入理解Java虚拟机原理》。 +**锁主要存在四中状态,依次是:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态。**他们会随着竞争的激烈而逐渐升级。注意锁可以升级不可降级,这种策略是为了提高获得锁和释放锁的效率。 + +###### **锁粗化** + +就是将多个连续的加锁、解锁操作连接在一起,扩展成一个范围更大的锁。 + +如上面实例:vector每次add的时候都需要加锁操作,JVM检测到对同一个对象(vector)连续加锁、解锁操作,会合并一个更大范围的加锁、解锁操作,即加锁解锁操作会移到for循环之外。 + +##### 锁消除 + +如果JVM不可能存在资源竞争,变量不可能逃逸到方法外,则将锁消除。 -### 偏向锁 +##### 自旋锁 -偏向锁是Java 6之后加入的新锁,它是一种针对加锁操作的优化手段,经过研究发现,在大多数情况下,锁不仅不存在多线程竞争,而且总是由同一线程多次获得,因此为了减少同一线程获取锁(会涉及到一些CAS操作,耗时)的代价而引入偏向锁。偏向锁的核心思想是,如果一个线程获得了锁,那么锁就进入偏向模式,此时Mark Word 的结构也变为偏向锁结构,当这个线程再次请求锁时,无需再做任何同步操作,即获取锁的过程,这样就省去了大量有关锁申请的操作,从而也就提供程序的性能。所以,对于没有锁竞争的场合,偏向锁有很好的优化效果,毕竟极有可能连续多次是同一个线程申请相同的锁。但是对于锁竞争比较激烈的场合,偏向锁就失效了,因为这样场合极有可能每次申请锁的线程都是不相同的,因此这种场合下不应该使用偏向锁,否则会得不偿失,需要注意的是,偏向锁失败后,并不会立即膨胀为重量级锁,而是先升级为轻量级锁。下面我们接着了解轻量级锁。 +对象锁的锁状态只会持续很短一段时间**,**为了这一段很短的时间频繁地阻塞和唤醒线程是非常不值得的。 -### 轻量级锁 +##### 偏向锁 -倘若偏向锁失败,虚拟机并不会立即升级为重量级锁,它还会尝试使用一种称为轻量级锁的优化手段(1.6之后加入的),此时Mark Word 的结构也变为轻量级锁的结构。轻量级锁能够提升程序性能的依据是“对绝大部分的锁,在整个同步周期内都不存在竞争”,注意这是经验数据。需要了解的是,轻量级锁所适应的场景是线程交替执行同步块的场合,如果存在同一时间访问同一锁的场合,就会导致轻量级锁膨胀为重量级锁。 +为了在非多线程的情况下减少线程间锁的交换消耗资源(CAS操作)。偏向锁只能被动释放噢,等锁有了竞争 -### 自旋锁 +##### 轻量级锁 -轻量级锁失败后,虚拟机为了避免线程真实地在操作系统层面挂起,还会进行一项称为自旋锁的优化手段。这是基于在大多数情况下,线程持有锁的时间都不会太长,如果直接挂起操作系统层面的线程可能会得不偿失,毕竟操作系统实现线程之间的切换时需要从用户态转换到核心态,这个状态之间的转换需要相对比较长的时间,时间成本相对较高,因此自旋锁会假设在不久将来,当前的线程可以获得锁,因此虚拟机会让当前想要获取锁的线程做几个空循环(这也是称为自旋的原因),一般不会太久,可能是50个循环或100循环,在经过若干次循环后,如果得到锁,就顺利进入临界区。如果还不能获得锁,那就会将线程在操作系统层面挂起,这就是自旋锁的优化方式,这种方式确实也是可以提升效率的。最后没办法也就只能升级为重量级锁了。 +当多个线程竞争偏向锁,会升级为轻量级锁。(利用CAS操作更新对象的Mark Word,如果失败则升级为重量级锁) -### 锁消除 +##### 重量级锁 -消除锁是虚拟机另外一种锁的优化,这种优化更彻底,Java虚拟机在JIT编译时(可以简单理解为当某段代码即将第一次被执行时进行编译,又称即时编译),通过对运行上下文的扫描,去除不可能存在共享资源竞争的锁,通过这种方式消除没有必要的锁,可以节省毫无意义的请求锁时间,如下StringBuffer的append是一个同步方法,但是在add方法中的StringBuffer属于一个局部变量,并且不会被其他线程所使用,因此StringBuffer不可能存在共享资源竞争的情景,JVM会自动将其锁消除。 +重量级锁通过对象内部的监视器(monitor)实现,其中monitor的本质是依赖于底层操作系统的Mutex Lock实现,操作系统实现线程之间的切换需要从用户态到内核态的切换,切换成本非常高。 -### 重量级锁 -即synchronized,一直等待线程施放锁后才可以拿到资源。 >本篇的主要参考资料: 《Java编程思想》 《深入理解Java虚拟机》 diff --git "a/Java\345\271\266\345\217\221/Java\345\271\266\345\217\221\347\274\226\347\250\213----join.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/6. Join\346\226\271\346\263\225.md" similarity index 63% rename from "Java\345\271\266\345\217\221/Java\345\271\266\345\217\221\347\274\226\347\250\213----join.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/6. Join\346\226\271\346\263\225.md" index 3bae674..3a274c1 100644 --- "a/Java\345\271\266\345\217\221/Java\345\271\266\345\217\221\347\274\226\347\250\213----join.md" +++ "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/6. Join\346\226\271\346\263\225.md" @@ -40,35 +40,32 @@ a方法执行完毕 ### 源码分析 ``` - public final synchronized void join(long millis) - throws InterruptedException { - long base = System.currentTimeMillis(); - long now = 0; +public final synchronized void join(long millis) +throws InterruptedException { + long base = System.currentTimeMillis(); + long now = 0; - if (millis < 0) { - throw new IllegalArgumentException("timeout value is negative"); - } + if (millis < 0) { + throw new IllegalArgumentException("timeout value is negative"); + } - if (millis == 0) { - while (isAlive()) { - //wait操作挂起 调用join方法的线程锁 - wait(0); - } - } else { - while (isAlive()) { - long delay = millis - now; - if (delay <= 0) { - break; - } - wait(delay); - now = System.currentTimeMillis() - base; + if (millis == 0) { + while (isAlive()) { + //wait操作挂起 调用join方法的线程锁 + wait(0); + } + } else { + while (isAlive()) { + long delay = millis - now; + if (delay <= 0) { + break; } + wait(delay); + now = System.currentTimeMillis() - base; } } +} ``` 其中参数`long millis`是来规范join执行的时间,默认为0. `isAlive():` `A thread is alive if it has been started and has not yet died` -如果当前线程活着,就将其挂起(wait)。 - -##### 那什么时候才会施放线程锁呢? -每当线程执行完毕后,默认会调用notifyAll方法。 +如果当前线程活着,就将其挂起(wait)。每当线程执行完毕后,默认会调用notifyAll方法。 diff --git "a/Java\345\271\266\345\217\221/Java\345\271\266\345\217\221\347\274\226\347\250\213----ThreadLocal.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/7. \346\234\254\345\234\260\347\272\277\347\250\213ThreadLocal.md" similarity index 88% rename from "Java\345\271\266\345\217\221/Java\345\271\266\345\217\221\347\274\226\347\250\213----ThreadLocal.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/7. \346\234\254\345\234\260\347\272\277\347\250\213ThreadLocal.md" index 2f6a6a8..0ee8825 100644 --- "a/Java\345\271\266\345\217\221/Java\345\271\266\345\217\221\347\274\226\347\250\213----ThreadLocal.md" +++ "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/7. \346\234\254\345\234\260\347\272\277\347\250\213ThreadLocal.md" @@ -61,6 +61,7 @@ Thread-0 :2 #### 1. get() +>先获取到当前线程, 然后根据当前线程获取ThreadLocalMap, 然后返回其value值。 ``` public T get() { Thread t = Thread.currentThread(); @@ -113,16 +114,20 @@ Thread-0 :2 ``` ##### 1.4 Entry ``` - static class Entry extends WeakReference> { - /** The value associated with this ThreadLocal. */ - Object value; +static class Entry extends WeakReference> { + /** The value associated with this ThreadLocal. */ + Object value; - Entry(ThreadLocal k, Object v) { - super(k); - value = v; - } - } + Entry(ThreadLocal k, Object v) { + super(k); + value = v; + } +} ``` +注意这里继承了WeakReference, 标明了它是一个软引用。 +>为什么用软引用呢? + +假如每个key都强引用指向threadlocal,也就是上图虚线那里是个强引用,那么这个threadlocal就会因为和entry存在强引用无法被回收!造成内存泄漏 ,除非线程结束,线程被回收了,map也跟着回收。 ##### 1.5 ThreadLocal和ThreadLocalMap对应关系 ![ThreadLocal和ThreadLocalMap对应关系](https://upload-images.jianshu.io/upload_images/5786888-ea551311d7b3cf2f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/8. ThreadLocal\345\206\205\345\255\230\346\263\204\346\274\217\351\227\256\351\242\230.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/8. ThreadLocal\345\206\205\345\255\230\346\263\204\346\274\217\351\227\256\351\242\230.md" new file mode 100644 index 0000000..1d17b48 --- /dev/null +++ "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/8. ThreadLocal\345\206\205\345\255\230\346\263\204\346\274\217\351\227\256\351\242\230.md" @@ -0,0 +1,171 @@ +### ThreadLocal +>我们知道ThreadLocal可以用来做线程隔离, 但如果使用不当也会产生内存泄漏的问题。这篇文章主要通过内存泄漏来讲述。 + +#### 内存泄漏原因 + +ThreadLocal内存泄露,最主要的原因在于它的内部类ThreadLocalMap中的Entry的设计。Entry继承了WeakReference>,即Entry的key是弱引用: + +``` +static class ThreadLocalMap { + static class Entry extends WeakReference> { + Object value; + + Entry(ThreadLocal k, Object v) { + super(k); + value = v; + } + } + ... +} +``` +#### 为什么要设计为软连接 +假如每个key都强引用指向ThreadLocal,也就是上图虚线那里是个强引用,那么这个ThreadLocal就会因为和entry存在强引用无法被回收!造成内存泄漏 ,除非线程结束,线程被回收了,map也跟着回收。 + +#### 为什么已经是软连接了还是会内存泄漏 +因为我们都用线程池来提高性能,Thread并没有像预期那样执行完就销毁,所以key'会在垃圾回收的时候被回收掉, 而key对应的value则不会被回收, 这样会导致一种现象:key为null,value有值。key为空的话value是无效数据,久而久之,value累加就会导致内存泄漏。 + +##### 怎么解决这个内存泄漏问题 + +每次使用完ThreadLocal都调用它的remove()方法清除数据。 + +##### JDK开发者是如何避免内存泄漏的 + +ThreadLocal提供的get()方法中,调用了ThreadLocalMap#getEntry()方法,对key进行了校验和对null key进行擦除。 + +``` + private Entry getEntry(ThreadLocal key) { + // 拿到索引位置 + int i = key.threadLocalHashCode & (table.length - 1); + Entry e = table[i]; + if (e != null && e.get() == key) + return e; + else + return getEntryAfterMiss(key, i, e); + } +``` + +如果key为null, 则会调用getEntryAfterMiss()方法,在这个方法中,如果k == null , 则调用expungeStaleEntry(i);方法。 + +```java + private Entry getEntryAfterMiss(ThreadLocal key, int i, Entry e) { + Entry[] tab = table; + int len = tab.length; + + while (e != null) { + ThreadLocal k = e.get(); + if (k == key) + return e; + if (k == null) + expungeStaleEntry(i); + else + i = nextIndex(i, len); + e = tab[i]; + } + return null; + } +``` + +expungeStaleEntry(i)方法完成了对key=null 的key所对应的value进行赋空, 释放了空间避免内存泄漏。 + +```java + private int expungeStaleEntry(int staleSlot) { + Entry[] tab = table; + int len = tab.length; + + // expunge entry at staleSlot + tab[staleSlot].value = null; + tab[staleSlot] = null; + size--; + + // Rehash until we encounter null + Entry e; + int i; + // 遍历下一个key为空的entry, 并将value指向null + for (i = nextIndex(staleSlot, len); + (e = tab[i]) != null; + i = nextIndex(i, len)) { + ThreadLocal k = e.get(); + if (k == null) { + e.value = null; + tab[i] = null; + size--; + } else { + int h = k.threadLocalHashCode & (len - 1); + if (h != i) { + tab[i] = null; + + // Unlike Knuth 6.4 Algorithm R, we must scan until + // null because multiple entries could have been stale. + while (tab[h] != null) + h = nextIndex(h, len); + tab[h] = e; + } + } + } + return i; + } +``` + +同理, set()方法最终也是调用该方法(expungeStaleEntry), 调用路径: set(T value)->map.set(this, value)->rehash()->expungeStaleEntries() + +remove方法remove()->ThreadLocalMap.remove(this)->expungeStaleEntry(i) + + + +这样做, 也只能说尽可能避免内存泄漏, 但并不会完全解决内存泄漏这个问题。比如极端情况下我们只创建ThreadLocal但不调用set、get、remove方法等。所以最能解决问题的办法就是用完ThreadLocal后手动调用remove(). + +##### 手动remove?你怎么去设计/实现? + +如果是spring项目, 可以借助于bean的声明周期, 在拦截器的afterCompletion阶段进行调用。 + +##### 弱引用导致内存泄漏,那为什么key不设置为强引用 + +如果key设置为强引用, 当threadLocal实例释放后, threadLocal=null, 但是threadLocal会有强引用指向threadLocalMap,threadLocalMap.Entry又强引用threadLocal, 这样会导致threadLocal不能正常被GC回收。 + +弱引用虽然会引起内存泄漏, 但是也有set、get、remove方法操作对null key进行擦除的补救措施, 方案上略胜一筹。 + +#### 线程执行结束后会不会清空value + +事实上,当currentThread执行结束后, threadLocalMap变得不可达从而被回收,Entry等也就都被回收了,但这个环境就要求不对Thread进行复用,但是我们项目中经常会复用线程来提高性能, 所以currentThread一般不会处于终止状态。 + +##### Thread和ThreadLocal有什么联系呢 + +Thread和ThreadLocal是绑定的, ThreadLocal依赖于Thread去执行, Thread将需要隔离的数据存放到ThreadLocal(准确的讲是ThreadLocalMap)中, 来实现多线程处理。 + +### 相关问题扩展 + +#### spring如何处理bean多线程下的并发问题 + +ThreadLocal天生为解决相同变量的访问冲突问题, 所以这个对于spring的默认单例bean的多线程访问是一个完美的解决方案。spring也确实是用了ThreadLocal来处理多线程下相同变量并发的线程安全问题。 + +#### spring 如何保证数据库事务在同一个连接下执行的 + +> 要想实现jdbc事务, 就必须是在同一个连接对象中操作, 多个连接下事务就会不可控, 需要借助分布式事务完成。那spring 如何保证数据库事务在同一个连接下执行的呢? + +DataSourceTransactionManager 是spring的数据源事务管理器, 它会在你调用getConnection()的时候从数据库连接池中获取一个connection, 然后将其与ThreadLocal绑定, 事务完成后解除绑定。 这样就保证了事务在同一连接下完成。 + +详细源码: + +1. 事务开始阶段:org.springframework.jdbc.datasource.DataSourceTransactionManager#doBegin->TransactionSynchronizationManager#bindResource->org.springframework.transaction.support.TransactionSynchronizationManager#bindResource + +![image.png](https://user-gold-cdn.xitu.io/2019/12/3/16ecad233adb105f?w=784&h=242&f=png&s=81952) + +2. 事务结束阶段: + + org.springframework.jdbc.datasource.DataSourceTransactionManager#doCleanupAfterCompletion->TransactionSynchronizationManager#unbindResource->org.springframework.transaction.support.TransactionSynchronizationManager#unbindResource->TransactionSynchronizationManager#doUnbindResource + +![image.png](https://user-gold-cdn.xitu.io/2019/12/3/16ecad20f5f4eccf?w=782&h=277&f=png&s=78470) + + +--- + +--- +如果你喜欢我的文章,那麻烦请关注我的公众号,该公众号还处于初始阶段,谢谢大家的支持。 +![](https://user-gold-cdn.xitu.io/2019/12/3/16ecadc23e4f9bc3?w=258&h=258&f=jpeg&s=26754) +关注公众号,回复`java架构`获取架构视频资源(后期还会分享不同的优质资源噢)。回复`找对象`可以拉你进IT单身交友群噢。 + +想看往期文章, 请点击我的GitHub地址: https://github.com/fantj2016/java-reader + +--- +--- + diff --git "a/Java\345\271\266\345\217\221/Java\345\271\266\345\217\221\347\274\226\347\250\213----Atomic\345\214\205.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/9. Atomic\345\214\205.md" similarity index 93% rename from "Java\345\271\266\345\217\221/Java\345\271\266\345\217\221\347\274\226\347\250\213----Atomic\345\214\205.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/9. Atomic\345\214\205.md" index 284c726..d58317f 100644 --- "a/Java\345\271\266\345\217\221/Java\345\271\266\345\217\221\347\274\226\347\250\213----Atomic\345\214\205.md" +++ "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/2. \345\271\266\345\217\221\347\274\226\347\250\213/9. Atomic\345\214\205.md" @@ -1,8 +1,9 @@ Java从JDK1.5开始提供了java.util.concurrent.atomic包,方便程序员在多线程环境下,无锁的进行原子操作。原子变量的底层使用了处理器提供的原子指令,但是不同的CPU架构可能提供的原子指令不一样,也有可能需要某种形式的内部锁,所以该方法不能绝对保证线程不被阻塞。 ### Atomic包介绍 -官方解释:一个小型工具包,支持单变量上的无锁线程安全编程。 +>一个小型工具包,支持单变量上的无锁线程安全编程。 + ![image.png](http://upload-images.jianshu.io/upload_images/5786888-98aaa28314c8db01.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) -可以看到,它里面有17个java类。 +它里面有17个java类。 ### 原子更新基本类型类 用于通过原子的方式更新基本类型,Atomic包提供了以下三个类: @@ -77,15 +78,15 @@ public class AtomicIntegerTest { } ``` -用的lambda表达式,不懂的可以看我的文章:https://www.jianshu.com/p/3a08dc78a05f +用的lambda表达式, 没有学过的可以先看看这些文章:https://www.jianshu.com/p/3a08dc78a05f -### 餐后甜点 +### 那如何更新double类型的数据呢 Atomic包提供了三种基本类型的原子更新,但是Java的基本类型里还有char,float和double等。那么问题来了,如何原子的更新其他的基本类型呢?Atomic包里的类基本都是使用Unsafe实现的,让我们一起看下[Unsafe的源码](http://www.docjar.com/html/api/sun/misc/Unsafe.java.html),发现Unsafe只提供了三种CAS方法,compareAndSwapObject,compareAndSwapInt和compareAndSwapLong,再看AtomicBoolean源码,发现其是先把Boolean转换成整型,再使用compareAndSwapInt进行CAS,所以原子更新double也可以用类似的思路来实现。 ### 原子更新数组类 -通过原子的方式更新数组里的某个元素,Atomic包提供了以下三个类: +>通过原子的方式更新数组里的某个元素,Atomic包提供了以下三个类: * AtomicIntegerArray:原子更新整型数组里的元素。 * AtomicLongArray:原子更新长整型数组里的元素。 @@ -110,7 +111,7 @@ public class AtomicIntegerArrayTest { ### 原子更新引用类型 -原子更新基本类型的AtomicInteger,只能更新一个变量,如果要原子的更新多个变量,就需要使用这个原子更新引用类型提供的类。Atomic包提供了以下三个类: +>原子更新基本类型的AtomicInteger,只能更新一个变量,如果要原子的更新多个变量,就需要使用这个原子更新引用类型提供的类。Atomic包提供了以下三个类: AtomicReference:原子更新引用类型。 AtomicReferenceFieldUpdater:原子更新引用类型里的字段。 @@ -170,7 +171,7 @@ public class AtomicReferenceTest { ``` ### 原子更新字段类 -**如果我们只需要某个类里的某个字段,那么就需要使用原子更新字段类**,Atomic包提供了以下三个类: +>**如果我们只需要某个类里的某个字段,那么就需要使用原子更新字段类**,Atomic包提供了以下三个类: * AtomicIntegerFieldUpdater:原子更新整型的字段的更新器。 * AtomicLongFieldUpdater:原子更新长整型字段的更新器。 @@ -236,7 +237,7 @@ public class AtomicIntegerFieldUpdaterTest { ``` -那最后,再来解一下CAS的疑惑 +那最后,聊一聊CAS ##### 什么是CAS diff --git "a/Java-reflection-\345\217\215\345\260\204\350\257\246\350\247\243/Java-\345\217\215\345\260\204\346\225\231\347\250\213.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/1. \345\211\215\350\250\200.md" similarity index 100% rename from "Java-reflection-\345\217\215\345\260\204\350\257\246\350\247\243/Java-\345\217\215\345\260\204\346\225\231\347\250\213.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/1. \345\211\215\350\250\200.md" diff --git "a/Java-reflection-\345\217\215\345\260\204\350\257\246\350\247\243/Java\345\217\215\345\260\204---\346\263\233\345\236\213.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/10. \346\263\233\345\236\213.md" similarity index 100% rename from "Java-reflection-\345\217\215\345\260\204\350\257\246\350\247\243/Java\345\217\215\345\260\204---\346\263\233\345\236\213.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/10. \346\263\233\345\236\213.md" diff --git "a/Java-reflection-\345\217\215\345\260\204\350\257\246\350\247\243/Java\345\217\215\345\260\204---\345\212\250\346\200\201\347\261\273\345\212\240\350\275\275\345\222\214\351\207\215\350\275\275.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/11. \345\212\250\346\200\201\347\261\273\345\212\240\350\275\275\345\222\214\351\207\215\350\275\275.md" similarity index 100% rename from "Java-reflection-\345\217\215\345\260\204\350\257\246\350\247\243/Java\345\217\215\345\260\204---\345\212\250\346\200\201\347\261\273\345\212\240\350\275\275\345\222\214\351\207\215\350\275\275.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/11. \345\212\250\346\200\201\347\261\273\345\212\240\350\275\275\345\222\214\351\207\215\350\275\275.md" diff --git "a/Java-reflection-\345\217\215\345\260\204\350\257\246\350\247\243/Java\345\217\215\345\260\204---\345\212\250\346\200\201\344\273\243\347\220\206.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/12. \345\212\250\346\200\201\344\273\243\347\220\206.md" similarity index 100% rename from "Java-reflection-\345\217\215\345\260\204\350\257\246\350\247\243/Java\345\217\215\345\260\204---\345\212\250\346\200\201\344\273\243\347\220\206.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/12. \345\212\250\346\200\201\344\273\243\347\220\206.md" diff --git "a/Java-reflection-\345\217\215\345\260\204\350\257\246\350\247\243/Java\345\217\215\345\260\204---\345\255\227\346\256\265.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/2. \345\255\227\346\256\265.md" similarity index 100% rename from "Java-reflection-\345\217\215\345\260\204\350\257\246\350\247\243/Java\345\217\215\345\260\204---\345\255\227\346\256\265.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/2. \345\255\227\346\256\265.md" diff --git "a/Java-reflection-\345\217\215\345\260\204\350\257\246\350\247\243/Java\345\217\215\345\260\204---\346\225\260\347\273\204.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/3. \346\225\260\347\273\204.md" similarity index 100% rename from "Java-reflection-\345\217\215\345\260\204\350\257\246\350\247\243/Java\345\217\215\345\260\204---\346\225\260\347\273\204.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/3. \346\225\260\347\273\204.md" diff --git "a/Java-reflection-\345\217\215\345\260\204\350\257\246\350\247\243/Java\345\217\215\345\260\204---\347\247\201\346\234\211\345\255\227\346\256\265\345\222\214\346\226\271\346\263\225.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/4. \347\247\201\346\234\211\345\255\227\346\256\265\345\222\214\346\226\271\346\263\225.md" similarity index 100% rename from "Java-reflection-\345\217\215\345\260\204\350\257\246\350\247\243/Java\345\217\215\345\260\204---\347\247\201\346\234\211\345\255\227\346\256\265\345\222\214\346\226\271\346\263\225.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/4. \347\247\201\346\234\211\345\255\227\346\256\265\345\222\214\346\226\271\346\263\225.md" diff --git "a/Java-reflection-\345\217\215\345\260\204\350\257\246\350\247\243/Java\345\217\215\345\260\204---\346\226\271\346\263\225-Methods.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/5. \346\226\271\346\263\225-Methods.md" similarity index 100% rename from "Java-reflection-\345\217\215\345\260\204\350\257\246\350\247\243/Java\345\217\215\345\260\204---\346\226\271\346\263\225-Methods.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/5. \346\226\271\346\263\225-Methods.md" diff --git "a/Java-reflection-\345\217\215\345\260\204\350\257\246\350\247\243/Java\345\217\215\345\260\204---\350\216\267\345\217\226Getters-and-Setters.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/6. \350\216\267\345\217\226Getters-and-Setters.md" similarity index 100% rename from "Java-reflection-\345\217\215\345\260\204\350\257\246\350\247\243/Java\345\217\215\345\260\204---\350\216\267\345\217\226Getters-and-Setters.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/6. \350\216\267\345\217\226Getters-and-Setters.md" diff --git "a/Java-reflection-\345\217\215\345\260\204\350\257\246\350\247\243/Java\345\217\215\345\260\204---\346\236\204\351\200\240\345\207\275\346\225\260.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/7. \346\236\204\351\200\240\345\207\275\346\225\260.md" similarity index 100% rename from "Java-reflection-\345\217\215\345\260\204\350\257\246\350\247\243/Java\345\217\215\345\260\204---\346\236\204\351\200\240\345\207\275\346\225\260.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/7. \346\236\204\351\200\240\345\207\275\346\225\260.md" diff --git "a/Java-reflection-\345\217\215\345\260\204\350\257\246\350\247\243/Java\345\217\215\345\260\204---\346\263\250\350\247\243.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/8. \346\263\250\350\247\243.md" similarity index 100% rename from "Java-reflection-\345\217\215\345\260\204\350\257\246\350\247\243/Java\345\217\215\345\260\204---\346\263\250\350\247\243.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/8. \346\263\250\350\247\243.md" diff --git "a/Java-reflection-\345\217\215\345\260\204\350\257\246\350\247\243/Java\345\217\215\345\260\204---\347\261\273\345\257\271\350\261\241.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/9. \347\261\273\345\257\271\350\261\241.md" similarity index 100% rename from "Java-reflection-\345\217\215\345\260\204\350\257\246\350\247\243/Java\345\217\215\345\260\204---\347\261\273\345\257\271\350\261\241.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/3. \345\217\215\345\260\204/9. \347\261\273\345\257\271\350\261\241.md" diff --git "a/jdbc\347\273\204\344\273\266\350\257\246\350\247\243/JDBC\357\274\210\344\270\200\357\274\211\346\246\202\350\277\260.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/4. JDBC/1. \346\246\202\350\277\260.md" similarity index 100% rename from "jdbc\347\273\204\344\273\266\350\257\246\350\247\243/JDBC\357\274\210\344\270\200\357\274\211\346\246\202\350\277\260.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/4. JDBC/1. \346\246\202\350\277\260.md" diff --git "a/jdbc\347\273\204\344\273\266\350\257\246\350\247\243/JDBC\357\274\210\344\272\214\357\274\211\351\251\261\345\212\250\347\250\213\345\272\217\347\261\273\345\236\213\345\217\221\345\261\225\345\216\206\347\250\213.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/4. JDBC/2. JDBC-\351\251\261\345\212\250\347\250\213\345\272\217\347\261\273\345\236\213\345\217\221\345\261\225\345\216\206\347\250\213.md" similarity index 100% rename from "jdbc\347\273\204\344\273\266\350\257\246\350\247\243/JDBC\357\274\210\344\272\214\357\274\211\351\251\261\345\212\250\347\250\213\345\272\217\347\261\273\345\236\213\345\217\221\345\261\225\345\216\206\347\250\213.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/4. JDBC/2. JDBC-\351\251\261\345\212\250\347\250\213\345\272\217\347\261\273\345\236\213\345\217\221\345\261\225\345\216\206\347\250\213.md" diff --git "a/jdbc\347\273\204\344\273\266\350\257\246\350\247\243/JDBC\357\274\210\344\270\211\357\274\211\346\225\260\346\215\256\345\272\223\350\277\236\346\216\245\345\222\214\346\225\260\346\215\256\345\242\236\345\210\240\346\224\271\346\237\245.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/4. JDBC/3. \346\225\260\346\215\256\345\272\223\350\277\236\346\216\245\345\222\214\346\225\260\346\215\256\345\242\236\345\210\240\346\224\271\346\237\245.md" similarity index 100% rename from "jdbc\347\273\204\344\273\266\350\257\246\350\247\243/JDBC\357\274\210\344\270\211\357\274\211\346\225\260\346\215\256\345\272\223\350\277\236\346\216\245\345\222\214\346\225\260\346\215\256\345\242\236\345\210\240\346\224\271\346\237\245.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/4. JDBC/3. \346\225\260\346\215\256\345\272\223\350\277\236\346\216\245\345\222\214\346\225\260\346\215\256\345\242\236\345\210\240\346\224\271\346\237\245.md" diff --git "a/jdbc\347\273\204\344\273\266\350\257\246\350\247\243/JDBC\357\274\210\345\233\233\357\274\211ResultSet-\350\257\246\350\247\243.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/4. JDBC/4. ResultSet-\350\257\246\350\247\243.md" similarity index 100% rename from "jdbc\347\273\204\344\273\266\350\257\246\350\247\243/JDBC\357\274\210\345\233\233\357\274\211ResultSet-\350\257\246\350\247\243.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/4. JDBC/4. ResultSet-\350\257\246\350\247\243.md" diff --git "a/jdbc\347\273\204\344\273\266\350\257\246\350\247\243/JDBC\357\274\210\344\272\224\357\274\211PreparedStatement-\350\257\246\350\247\243.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/4. JDBC/5. PreparedStatement-\350\257\246\350\247\243.md" similarity index 100% rename from "jdbc\347\273\204\344\273\266\350\257\246\350\247\243/JDBC\357\274\210\344\272\224\357\274\211PreparedStatement-\350\257\246\350\247\243.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/4. JDBC/5. PreparedStatement-\350\257\246\350\247\243.md" diff --git "a/jdbc\347\273\204\344\273\266\350\257\246\350\247\243/JDBC\357\274\210\345\205\255\357\274\211\346\211\271\351\207\217\345\244\204\347\220\206sql.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/4. JDBC/6. \346\211\271\351\207\217\345\244\204\347\220\206sql.md" similarity index 100% rename from "jdbc\347\273\204\344\273\266\350\257\246\350\247\243/JDBC\357\274\210\345\205\255\357\274\211\346\211\271\351\207\217\345\244\204\347\220\206sql.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/4. JDBC/6. \346\211\271\351\207\217\345\244\204\347\220\206sql.md" diff --git "a/jdbc\347\273\204\344\273\266\350\257\246\350\247\243/JDBC\357\274\210\344\270\203\357\274\211\344\272\213\345\212\241Transaction.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/4. JDBC/7. \344\272\213\345\212\241Transaction.md" similarity index 100% rename from "jdbc\347\273\204\344\273\266\350\257\246\350\247\243/JDBC\357\274\210\344\270\203\357\274\211\344\272\213\345\212\241Transaction.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/4. JDBC/7. \344\272\213\345\212\241Transaction.md" diff --git "a/jdbc\347\273\204\344\273\266\350\257\246\350\247\243/JDBC\357\274\210\345\205\253\357\274\211CallableStatement-\345\255\230\345\202\250\350\277\207\347\250\213\350\260\203\347\224\250.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/4. JDBC/8. CallableStatement-\345\255\230\345\202\250\350\277\207\347\250\213\350\260\203\347\224\250.md" similarity index 100% rename from "jdbc\347\273\204\344\273\266\350\257\246\350\247\243/JDBC\357\274\210\345\205\253\357\274\211CallableStatement-\345\255\230\345\202\250\350\277\207\347\250\213\350\260\203\347\224\250.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/4. JDBC/8. CallableStatement-\345\255\230\345\202\250\350\277\207\347\250\213\350\260\203\347\224\250.md" diff --git "a/jdbc\347\273\204\344\273\266\350\257\246\350\247\243/JDBC\357\274\210\344\271\235\357\274\211DatabaseMetaData-\346\225\260\346\215\256\345\272\223\345\205\203\346\225\260\346\215\256.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/4. JDBC/9. DatabaseMetaData-\346\225\260\346\215\256\345\272\223\345\205\203\346\225\260\346\215\256.md" similarity index 100% rename from "jdbc\347\273\204\344\273\266\350\257\246\350\247\243/JDBC\357\274\210\344\271\235\357\274\211DatabaseMetaData-\346\225\260\346\215\256\345\272\223\345\205\203\346\225\260\346\215\256.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/4. JDBC/9. DatabaseMetaData-\346\225\260\346\215\256\345\272\223\345\205\203\346\225\260\346\215\256.md" diff --git "a/Java-Servlet/Java-Servlet-\350\257\246\350\247\243.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/1. Servlet-\350\257\246\350\247\243.md" similarity index 100% rename from "Java-Servlet/Java-Servlet-\350\257\246\350\247\243.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/1. Servlet-\350\257\246\350\247\243.md" diff --git "a/Java-Servlet/Java-ServletContext-\350\257\246\350\247\243.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/10. ServletContext-\350\257\246\350\247\243.md" similarity index 100% rename from "Java-Servlet/Java-ServletContext-\350\257\246\350\247\243.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/10. ServletContext-\350\257\246\350\247\243.md" diff --git "a/Java-Servlet/Java-Servlet-web-xml-\351\205\215\347\275\256\350\257\246\350\247\243.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/11. Servlet-web-xml-\351\205\215\347\275\256\350\257\246\350\247\243.md" similarity index 100% rename from "Java-Servlet/Java-Servlet-web-xml-\351\205\215\347\275\256\350\257\246\350\247\243.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/11. Servlet-web-xml-\351\205\215\347\275\256\350\257\246\350\247\243.md" diff --git "a/Java-Servlet/JavaEE-Servlet-\345\271\266\345\217\221\351\227\256\351\242\230.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/12. JavaEE-Servlet-\345\271\266\345\217\221\351\227\256\351\242\230.md" similarity index 100% rename from "Java-Servlet/JavaEE-Servlet-\345\271\266\345\217\221\351\227\256\351\242\230.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/12. JavaEE-Servlet-\345\271\266\345\217\221\351\227\256\351\242\230.md" diff --git "a/Java-Servlet/Java-HttpServlet-\350\257\246\350\247\243.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/2. HttpServlet-\350\257\246\350\247\243.md" similarity index 100% rename from "Java-Servlet/Java-HttpServlet-\350\257\246\350\247\243.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/2. HttpServlet-\350\257\246\350\247\243.md" diff --git "a/Java-Servlet/Java-HttpRequest-\350\257\246\350\247\243.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/3. HttpRequest-\350\257\246\350\247\243.md" similarity index 100% rename from "Java-Servlet/Java-HttpRequest-\350\257\246\350\247\243.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/3. HttpRequest-\350\257\246\350\247\243.md" diff --git "a/Java-Servlet/Java-HttpResponse-\350\257\246\350\247\243.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/4. HttpResponse-\350\257\246\350\247\243.md" similarity index 100% rename from "Java-Servlet/Java-HttpResponse-\350\257\246\350\247\243.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/4. HttpResponse-\350\257\246\350\247\243.md" diff --git "a/Java-Servlet/Java-HttpSession-\350\257\246\350\247\243.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/5. HttpSession-\350\257\246\350\247\243.md" similarity index 100% rename from "Java-Servlet/Java-HttpSession-\350\257\246\350\247\243.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/5. HttpSession-\350\257\246\350\247\243.md" diff --git "a/Java-Servlet/Java-RequestDispatcher-\350\257\246\350\247\243.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/6. RequestDispatcher-\350\257\246\350\247\243.md" similarity index 100% rename from "Java-Servlet/Java-RequestDispatcher-\350\257\246\350\247\243.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/6. RequestDispatcher-\350\257\246\350\247\243.md" diff --git "a/Java-Servlet/Java-Servlet-Cookie-\350\257\246\350\247\243.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/7. Servlet-Cookie-\350\257\246\350\247\243.md" similarity index 100% rename from "Java-Servlet/Java-Servlet-Cookie-\350\257\246\350\247\243.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/7. Servlet-Cookie-\350\257\246\350\247\243.md" diff --git "a/Java-Servlet/Java-Servlet-Filter-\350\257\246\350\247\243.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/8. Servlet-Filter-\350\257\246\350\247\243.md" similarity index 100% rename from "Java-Servlet/Java-Servlet-Filter-\350\257\246\350\247\243.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/8. Servlet-Filter-\350\257\246\350\247\243.md" diff --git "a/Java-Servlet/Java-Servlet-GZip-Servlet-Filter-\350\257\246\350\247\243.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/9. Servlet-GZip-Servlet-Filter-\350\257\246\350\247\243.md" similarity index 100% rename from "Java-Servlet/Java-Servlet-GZip-Servlet-Filter-\350\257\246\350\247\243.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/5. Servlet/9. Servlet-GZip-Servlet-Filter-\350\257\246\350\247\243.md" diff --git "a/Java-NIO/Java-NIO\357\274\210\344\270\200\357\274\211\346\216\242\347\264\242.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/1. Java-NIO-\346\216\242\347\264\242.md" similarity index 100% rename from "Java-NIO/Java-NIO\357\274\210\344\270\200\357\274\211\346\216\242\347\264\242.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/1. Java-NIO-\346\216\242\347\264\242.md" diff --git "a/Java-NIO/Java-NIO\357\274\210\345\215\201\357\274\211-ServerSocketChannel.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/10. Java-NIO-ServerSocketChannel.md" similarity index 100% rename from "Java-NIO/Java-NIO\357\274\210\345\215\201\357\274\211-ServerSocketChannel.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/10. Java-NIO-ServerSocketChannel.md" diff --git "a/Java-NIO/Java-NIO\357\274\210\345\215\201\344\270\200\357\274\211Non-blocking-Server.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/11. Java-NIO-Non-blocking-Server.md" similarity index 100% rename from "Java-NIO/Java-NIO\357\274\210\345\215\201\344\270\200\357\274\211Non-blocking-Server.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/11. Java-NIO-Non-blocking-Server.md" diff --git "a/Java-NIO/Java-NIO\357\274\210\345\215\201\344\272\214\357\274\211-DatagramChannel.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/12. Java-NIO-DatagramChannel.md" similarity index 100% rename from "Java-NIO/Java-NIO\357\274\210\345\215\201\344\272\214\357\274\211-DatagramChannel.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/12. Java-NIO-DatagramChannel.md" diff --git "a/Java-NIO/Java-NIO\357\274\210\345\215\201\344\270\211\357\274\211-Pipe(\347\256\241\351\201\223).md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/13. Java-NIO-Pipe(\347\256\241\351\201\223).md" similarity index 100% rename from "Java-NIO/Java-NIO\357\274\210\345\215\201\344\270\211\357\274\211-Pipe(\347\256\241\351\201\223).md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/13. Java-NIO-Pipe(\347\256\241\351\201\223).md" diff --git "a/Java-NIO/Java-NIO-\357\274\210\345\215\201\345\233\233\357\274\211NIO--\345\222\214-IO-\347\232\204\345\214\272\345\210\253\345\222\214\351\200\202\347\224\250\345\234\272\346\231\257\345\210\206\346\236\220.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/14. Java-NIO-NIO\345\222\214IO\347\232\204\345\214\272\345\210\253\345\222\214\351\200\202\347\224\250\345\234\272\346\231\257\345\210\206\346\236\220.md" similarity index 100% rename from "Java-NIO/Java-NIO-\357\274\210\345\215\201\345\233\233\357\274\211NIO--\345\222\214-IO-\347\232\204\345\214\272\345\210\253\345\222\214\351\200\202\347\224\250\345\234\272\346\231\257\345\210\206\346\236\220.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/14. Java-NIO-NIO\345\222\214IO\347\232\204\345\214\272\345\210\253\345\222\214\351\200\202\347\224\250\345\234\272\346\231\257\345\210\206\346\236\220.md" diff --git "a/Java-NIO/Java-NIO\357\274\210\345\215\201\344\272\224\357\274\211-Path-\347\232\204\347\224\250\346\263\225.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/15. Java-NIO-Path\347\232\204\347\224\250\346\263\225.md" similarity index 100% rename from "Java-NIO/Java-NIO\357\274\210\345\215\201\344\272\224\357\274\211-Path-\347\232\204\347\224\250\346\263\225.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/15. Java-NIO-Path\347\232\204\347\224\250\346\263\225.md" diff --git "a/Java-NIO/Java-NIO\357\274\210\345\215\201\345\205\255\357\274\211-Files.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/16. Java-NIO-Files.md" similarity index 100% rename from "Java-NIO/Java-NIO\357\274\210\345\215\201\345\205\255\357\274\211-Files.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/16. Java-NIO-Files.md" diff --git "a/Java-NIO/Java-NIO\357\274\210\345\215\201\344\270\203\357\274\211-AsynchronousFileChannel-\345\274\202\346\255\245\346\226\207\344\273\266\351\200\232\351\201\223.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/17. Java-NIO-AsynchronousFileChannel\345\274\202\346\255\245\346\226\207\344\273\266\351\200\232\351\201\223.md" similarity index 100% rename from "Java-NIO/Java-NIO\357\274\210\345\215\201\344\270\203\357\274\211-AsynchronousFileChannel-\345\274\202\346\255\245\346\226\207\344\273\266\351\200\232\351\201\223.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/17. Java-NIO-AsynchronousFileChannel\345\274\202\346\255\245\346\226\207\344\273\266\351\200\232\351\201\223.md" diff --git "a/Java-NIO/Java-NIO\357\274\210\345\215\201\345\205\253\357\274\211\345\256\236\344\276\213.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/18. Java-NIO-\345\256\236\344\276\213.md" similarity index 100% rename from "Java-NIO/Java-NIO\357\274\210\345\215\201\345\205\253\357\274\211\345\256\236\344\276\213.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/18. Java-NIO-\345\256\236\344\276\213.md" diff --git "a/Java-NIO/BIO\343\200\201NIO\343\200\201AIO\345\214\272\345\210\253(\347\273\235\345\257\271\351\200\232\344\277\227\346\230\223\346\207\202).md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/19. BIO\343\200\201NIO\343\200\201AIO\345\214\272\345\210\253.md" similarity index 100% rename from "Java-NIO/BIO\343\200\201NIO\343\200\201AIO\345\214\272\345\210\253(\347\273\235\345\257\271\351\200\232\344\277\227\346\230\223\346\207\202).md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/19. BIO\343\200\201NIO\343\200\201AIO\345\214\272\345\210\253.md" diff --git "a/Java-NIO/Java-NIO\357\274\210\344\270\211\357\274\211\346\246\202\345\277\265.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/2. Java-NIO-\346\246\202\345\277\265.md" similarity index 100% rename from "Java-NIO/Java-NIO\357\274\210\344\270\211\357\274\211\346\246\202\345\277\265.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/2. Java-NIO-\346\246\202\345\277\265.md" diff --git "a/Java-NIO/Java\344\271\213NIO\347\254\254\344\272\214\347\253\240\357\274\232nio\347\256\200\344\273\213.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/2. Java-NIO-\347\256\200\344\273\213.md" similarity index 100% rename from "Java-NIO/Java\344\271\213NIO\347\254\254\344\272\214\347\253\240\357\274\232nio\347\256\200\344\273\213.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/2. Java-NIO-\347\256\200\344\273\213.md" diff --git "a/Java-NIO/Java-NIO\357\274\210\344\270\211\357\274\211Channel-\351\200\232\351\201\223.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/3. Java-NIO-Channel-\351\200\232\351\201\223.md" similarity index 100% rename from "Java-NIO/Java-NIO\357\274\210\344\270\211\357\274\211Channel-\351\200\232\351\201\223.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/3. Java-NIO-Channel-\351\200\232\351\201\223.md" diff --git "a/Java-NIO/Java-NIO\357\274\210\345\233\233\357\274\211Buffer.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/4. Java-NIO-Buffer.md" similarity index 100% rename from "Java-NIO/Java-NIO\357\274\210\345\233\233\357\274\211Buffer.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/4. Java-NIO-Buffer.md" diff --git "a/Java-NIO/Java-NIO\357\274\210\344\272\224\357\274\211Scatter---Gather.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/5. Java-NIO-Scatter---Gather.md" similarity index 100% rename from "Java-NIO/Java-NIO\357\274\210\344\272\224\357\274\211Scatter---Gather.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/5. Java-NIO-Scatter---Gather.md" diff --git "a/Java-NIO/Java-NIO\357\274\210\345\205\255\357\274\211Channel-\344\271\213\351\227\264\347\232\204\350\275\254\346\215\242(\344\274\240\350\276\223).md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/6. Java-NIO-Channel-\344\271\213\351\227\264\347\232\204\350\275\254\346\215\242(\344\274\240\350\276\223).md" similarity index 100% rename from "Java-NIO/Java-NIO\357\274\210\345\205\255\357\274\211Channel-\344\271\213\351\227\264\347\232\204\350\275\254\346\215\242(\344\274\240\350\276\223).md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/6. Java-NIO-Channel-\344\271\213\351\227\264\347\232\204\350\275\254\346\215\242(\344\274\240\350\276\223).md" diff --git "a/Java-NIO/Java-NIO\357\274\210\344\270\203\357\274\211Selector.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/7. Java-NIO-Selector.md" similarity index 100% rename from "Java-NIO/Java-NIO\357\274\210\344\270\203\357\274\211Selector.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/7. Java-NIO-Selector.md" diff --git "a/Java-NIO/Java-NIO\357\274\210\345\205\253\357\274\211-FileChannel.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/8. Java-NIO-FileChannel.md" similarity index 100% rename from "Java-NIO/Java-NIO\357\274\210\345\205\253\357\274\211-FileChannel.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/8. Java-NIO-FileChannel.md" diff --git "a/Java-NIO/Java-NIO\357\274\210\344\271\235\357\274\211-SocketChannel.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/9. Java-NIO-SocketChannel.md" similarity index 100% rename from "Java-NIO/Java-NIO\357\274\210\344\271\235\357\274\211-SocketChannel.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java-NIO/9. Java-NIO-SocketChannel.md" diff --git "a/Java/java8-LocalDate\347\261\273.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java8/java8-LocalDate\347\261\273.md" similarity index 100% rename from "Java/java8-LocalDate\347\261\273.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java8/java8-LocalDate\347\261\273.md" diff --git a/Java/java8-lambda.md "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java8/java8-lambda.md" similarity index 100% rename from Java/java8-lambda.md rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java8/java8-lambda.md" diff --git a/Java/java8-stream.md "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java8/java8-stream.md" similarity index 100% rename from Java/java8-stream.md rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java8/java8-stream.md" diff --git "a/Java\345\207\275\346\225\260\345\274\217\347\274\226\347\250\213/Java-\345\207\275\346\225\260\345\274\217\347\274\226\347\250\213\357\274\210\344\270\200\357\274\211\345\210\235\350\257\206\347\257\207.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java\345\207\275\346\225\260\345\274\217\347\274\226\347\250\213/1. \345\210\235\350\257\206\347\257\207.md" similarity index 100% rename from "Java\345\207\275\346\225\260\345\274\217\347\274\226\347\250\213/Java-\345\207\275\346\225\260\345\274\217\347\274\226\347\250\213\357\274\210\344\270\200\357\274\211\345\210\235\350\257\206\347\257\207.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java\345\207\275\346\225\260\345\274\217\347\274\226\347\250\213/1. \345\210\235\350\257\206\347\257\207.md" diff --git "a/Java\345\207\275\346\225\260\345\274\217\347\274\226\347\250\213/Java-\345\207\275\346\225\260\345\274\217\347\274\226\347\250\213\357\274\210\344\272\214\357\274\211Lambda\350\241\250\350\276\276\345\274\217.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java\345\207\275\346\225\260\345\274\217\347\274\226\347\250\213/2. Lambda\350\241\250\350\276\276\345\274\217.md" similarity index 100% rename from "Java\345\207\275\346\225\260\345\274\217\347\274\226\347\250\213/Java-\345\207\275\346\225\260\345\274\217\347\274\226\347\250\213\357\274\210\344\272\214\357\274\211Lambda\350\241\250\350\276\276\345\274\217.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java\345\207\275\346\225\260\345\274\217\347\274\226\347\250\213/2. Lambda\350\241\250\350\276\276\345\274\217.md" diff --git "a/Java\345\207\275\346\225\260\345\274\217\347\274\226\347\250\213/Java-\345\207\275\346\225\260\345\274\217\347\274\226\347\250\213\357\274\210\344\270\211\357\274\211\346\265\201\357\274\210Stream\357\274\211.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java\345\207\275\346\225\260\345\274\217\347\274\226\347\250\213/3. Stream\346\265\201.md" similarity index 100% rename from "Java\345\207\275\346\225\260\345\274\217\347\274\226\347\250\213/Java-\345\207\275\346\225\260\345\274\217\347\274\226\347\250\213\357\274\210\344\270\211\357\274\211\346\265\201\357\274\210Stream\357\274\211.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java\345\207\275\346\225\260\345\274\217\347\274\226\347\250\213/3. Stream\346\265\201.md" diff --git "a/Java\351\235\242\350\257\225/Java\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223\344\271\213Java\345\237\272\347\241\200(\344\270\200).md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java\345\237\272\347\241\200\346\200\273\347\273\223(\344\270\200).md" similarity index 100% rename from "Java\351\235\242\350\257\225/Java\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223\344\271\213Java\345\237\272\347\241\200(\344\270\200).md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java\345\237\272\347\241\200\346\200\273\347\273\223(\344\270\200).md" diff --git "a/Java\351\235\242\350\257\225/Java\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223\344\271\213Java\345\237\272\347\241\200(\344\270\211).md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java\345\237\272\347\241\200\346\200\273\347\273\223(\344\270\211).md" similarity index 100% rename from "Java\351\235\242\350\257\225/Java\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223\344\271\213Java\345\237\272\347\241\200(\344\270\211).md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java\345\237\272\347\241\200\346\200\273\347\273\223(\344\270\211).md" diff --git "a/Java\351\235\242\350\257\225/Java\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223\344\271\213Java\345\237\272\347\241\200(\344\272\214).md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java\345\237\272\347\241\200\346\200\273\347\273\223(\344\272\214).md" similarity index 100% rename from "Java\351\235\242\350\257\225/Java\351\235\242\350\257\225\351\242\230\346\200\273\347\273\223\344\271\213Java\345\237\272\347\241\200(\344\272\214).md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/Java\345\237\272\347\241\200\346\200\273\347\273\223(\344\272\214).md" diff --git "a/\347\261\273\345\212\240\350\275\275\345\231\250/jvm/\347\261\273\345\212\240\350\275\275\345\231\250\351\235\242\350\257\225(\344\270\200).md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\347\261\273\345\212\240\350\275\275\345\231\250/\347\261\273\345\212\240\350\275\275\345\231\250.md" similarity index 99% rename from "\347\261\273\345\212\240\350\275\275\345\231\250/jvm/\347\261\273\345\212\240\350\275\275\345\231\250\351\235\242\350\257\225(\344\270\200).md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\347\261\273\345\212\240\350\275\275\345\231\250/\347\261\273\345\212\240\350\275\275\345\231\250.md" index 79062dd..b2589ff 100644 --- "a/\347\261\273\345\212\240\350\275\275\345\231\250/jvm/\347\261\273\345\212\240\350\275\275\345\231\250\351\235\242\350\257\225(\344\270\200).md" +++ "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\347\261\273\345\212\240\350\275\275\345\231\250/\347\261\273\345\212\240\350\275\275\345\231\250.md" @@ -1,4 +1,3 @@ -### 1. 什么是类加载器 负责读取 Java 字节代码,并转换成java.lang.Class类的一个实例 diff --git "a/\350\256\241\347\275\221\351\235\242\350\257\225/\347\275\221\347\273\234(\344\270\200).md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/\347\275\221\347\273\234(\344\270\200).md" similarity index 100% rename from "\350\256\241\347\275\221\351\235\242\350\257\225/\347\275\221\347\273\234(\344\270\200).md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234/\347\275\221\347\273\234(\344\270\200).md" diff --git "a/\350\256\276\350\256\241\346\250\241\345\274\217/\350\256\276\350\256\241\346\250\241\345\274\217\345\277\253\351\200\237\345\255\246\344\271\240\357\274\210\344\270\200\357\274\211\345\267\245\345\216\202\346\250\241\345\274\217.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/1. \345\267\245\345\216\202\346\250\241\345\274\217.md" similarity index 100% rename from "\350\256\276\350\256\241\346\250\241\345\274\217/\350\256\276\350\256\241\346\250\241\345\274\217\345\277\253\351\200\237\345\255\246\344\271\240\357\274\210\344\270\200\357\274\211\345\267\245\345\216\202\346\250\241\345\274\217.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/1. \345\267\245\345\216\202\346\250\241\345\274\217.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/10. \351\200\202\351\205\215\345\231\250\346\250\241\345\274\217.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/10. \351\200\202\351\205\215\345\231\250\346\250\241\345\274\217.md" new file mode 100644 index 0000000..e69de29 diff --git "a/\350\256\276\350\256\241\346\250\241\345\274\217/\350\256\276\350\256\241\346\250\241\345\274\217\345\277\253\351\200\237\345\255\246\344\271\240\357\274\210\344\272\214\357\274\211\346\212\275\350\261\241\345\267\245\345\216\202\346\250\241\345\274\217.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/2. \346\212\275\350\261\241\345\267\245\345\216\202\346\250\241\345\274\217.md" similarity index 100% rename from "\350\256\276\350\256\241\346\250\241\345\274\217/\350\256\276\350\256\241\346\250\241\345\274\217\345\277\253\351\200\237\345\255\246\344\271\240\357\274\210\344\272\214\357\274\211\346\212\275\350\261\241\345\267\245\345\216\202\346\250\241\345\274\217.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/2. \346\212\275\350\261\241\345\267\245\345\216\202\346\250\241\345\274\217.md" diff --git "a/\350\256\276\350\256\241\346\250\241\345\274\217/\350\256\276\350\256\241\346\250\241\345\274\217\345\277\253\351\200\237\345\255\246\344\271\240\357\274\210\344\270\211\357\274\211\345\215\225\344\276\213\346\250\241\345\274\217.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/3. \345\215\225\344\276\213\346\250\241\345\274\217.md" similarity index 92% rename from "\350\256\276\350\256\241\346\250\241\345\274\217/\350\256\276\350\256\241\346\250\241\345\274\217\345\277\253\351\200\237\345\255\246\344\271\240\357\274\210\344\270\211\357\274\211\345\215\225\344\276\213\346\250\241\345\274\217.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/3. \345\215\225\344\276\213\346\250\241\345\274\217.md" index 56678fe..69e0e4e 100644 --- "a/\350\256\276\350\256\241\346\250\241\345\274\217/\350\256\276\350\256\241\346\250\241\345\274\217\345\277\253\351\200\237\345\255\246\344\271\240\357\274\210\344\270\211\357\274\211\345\215\225\344\276\213\346\250\241\345\274\217.md" +++ "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/3. \345\215\225\344\276\213\346\250\241\345\274\217.md" @@ -42,8 +42,8 @@ public class Singleton { ``` 可以在多线程环境下使用,但是效率太低。 -**优点:**一个对象初始化一次,节省内存。 -**缺点:**必须用synchronized来维持单例,没效率。 +**优点**:一个对象初始化一次,节省内存。 +**缺点**:必须用synchronized来维持单例,没效率。 ### 实现方式三:饿汉式(线程安全) ``` @@ -65,7 +65,7 @@ public class Singleton { ``` public class Singleton { - private static Singleton instance; + private static volatile Singleton instance; public static Singleton getInstance(){ if (instance == null){ @@ -81,6 +81,7 @@ public class Singleton { ``` 采用双锁机制,安全且在多线程情况下能保持高性能。详细了解请点击:[Java并发编程 -- 单例模式线程安全问题](https://www.jianshu.com/p/3707bc0fc6f0) +注意这个volatile,一方面保证了instance的可见性、一方面保证了jvm不对其编译重排序优化,避免NullPointException。 ### 实现方式五:登记式/静态内部类(线程安全) ``` diff --git "a/\350\256\276\350\256\241\346\250\241\345\274\217/\350\256\276\350\256\241\346\250\241\345\274\217\345\277\253\351\200\237\345\255\246\344\271\240\357\274\210\345\233\233\357\274\211\345\273\272\351\200\240\350\200\205\346\250\241\345\274\217.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/4. \345\273\272\351\200\240\350\200\205\346\250\241\345\274\217.md" similarity index 100% rename from "\350\256\276\350\256\241\346\250\241\345\274\217/\350\256\276\350\256\241\346\250\241\345\274\217\345\277\253\351\200\237\345\255\246\344\271\240\357\274\210\345\233\233\357\274\211\345\273\272\351\200\240\350\200\205\346\250\241\345\274\217.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/4. \345\273\272\351\200\240\350\200\205\346\250\241\345\274\217.md" diff --git "a/\350\256\276\350\256\241\346\250\241\345\274\217/\350\256\276\350\256\241\346\250\241\345\274\217\345\277\253\351\200\237\345\255\246\344\271\240\357\274\210\344\272\224\357\274\211\345\216\237\345\236\213\346\250\241\345\274\217.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/5. \345\216\237\345\236\213\346\250\241\345\274\217.md" similarity index 100% rename from "\350\256\276\350\256\241\346\250\241\345\274\217/\350\256\276\350\256\241\346\250\241\345\274\217\345\277\253\351\200\237\345\255\246\344\271\240\357\274\210\344\272\224\357\274\211\345\216\237\345\236\213\346\250\241\345\274\217.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/5. \345\216\237\345\236\213\346\250\241\345\274\217.md" diff --git "a/\350\256\276\350\256\241\346\250\241\345\274\217/\350\256\276\350\256\241\346\250\241\345\274\217\345\277\253\351\200\237\345\255\246\344\271\240\357\274\210\345\205\255\357\274\211\346\250\241\346\235\277\346\250\241\345\274\217.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/6. \346\250\241\346\235\277\346\250\241\345\274\217.md" similarity index 100% rename from "\350\256\276\350\256\241\346\250\241\345\274\217/\350\256\276\350\256\241\346\250\241\345\274\217\345\277\253\351\200\237\345\255\246\344\271\240\357\274\210\345\205\255\357\274\211\346\250\241\346\235\277\346\250\241\345\274\217.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/6. \346\250\241\346\235\277\346\250\241\345\274\217.md" diff --git "a/\350\256\276\350\256\241\346\250\241\345\274\217/\350\256\276\350\256\241\346\250\241\345\274\217\345\277\253\351\200\237\345\255\246\344\271\240\357\274\210\344\270\203\357\274\211\347\255\226\347\225\245\346\250\241\345\274\217.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/7. \347\255\226\347\225\245\346\250\241\345\274\217.md" similarity index 100% rename from "\350\256\276\350\256\241\346\250\241\345\274\217/\350\256\276\350\256\241\346\250\241\345\274\217\345\277\253\351\200\237\345\255\246\344\271\240\357\274\210\344\270\203\357\274\211\347\255\226\347\225\245\346\250\241\345\274\217.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/7. \347\255\226\347\225\245\346\250\241\345\274\217.md" diff --git "a/\350\256\276\350\256\241\346\250\241\345\274\217/\350\256\276\350\256\241\346\250\241\345\274\217\345\277\253\351\200\237\345\255\246\344\271\240\357\274\210\345\205\253\357\274\211\345\247\224\346\264\276\346\250\241\345\274\217.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/8. \345\247\224\346\264\276\346\250\241\345\274\217.md" similarity index 100% rename from "\350\256\276\350\256\241\346\250\241\345\274\217/\350\256\276\350\256\241\346\250\241\345\274\217\345\277\253\351\200\237\345\255\246\344\271\240\357\274\210\345\205\253\357\274\211\345\247\224\346\264\276\346\250\241\345\274\217.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/8. \345\247\224\346\264\276\346\250\241\345\274\217.md" diff --git "a/\350\256\276\350\256\241\346\250\241\345\274\217/Java\344\270\244\347\247\215\344\273\243\347\220\206\346\250\241\345\274\217.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/9. \344\273\243\347\220\206\346\250\241\345\274\217.md" similarity index 100% rename from "\350\256\276\350\256\241\346\250\241\345\274\217/Java\344\270\244\347\247\215\344\273\243\347\220\206\346\250\241\345\274\217.md" rename to "2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/9. \344\273\243\347\220\206\346\250\241\345\274\217.md" diff --git "a/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/\345\205\255\345\244\247\350\256\276\350\256\241\345\216\237\345\210\231.md" "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/\345\205\255\345\244\247\350\256\276\350\256\241\345\216\237\345\210\231.md" new file mode 100644 index 0000000..14b08ba --- /dev/null +++ "b/2. Java\345\237\272\347\241\200/2.1 \345\237\272\347\241\200/\350\256\276\350\256\241\346\250\241\345\274\217/\345\205\255\345\244\247\350\256\276\350\256\241\345\216\237\345\210\231.md" @@ -0,0 +1,18 @@ +1. 单一职责 + + 类的话,一个类尽量做同一类型的事情。对于方法的话,一个方法尽量做一件事情。降低维护成本。 +2. 里氏替换 + + 子类对象可以替换其父类对象,而程序执行效果不变。继承体系 +3. 开闭原则 + + 对外扩展开发,对内修改关闭。抽象类 +4. 依赖倒置 + + 高层模块不能依赖低层模块,二者都应该依赖抽象。接口编程 +5. 接口隔离 + + 多个特定的客户端接口要好于一个通用性的总接口。客户端不应该依赖它不需要实现的接口。应尽量细化接口,接口中的方法应尽量少。需要注意的是接口的力度也不能太小,如果过小,则会造成接口数量过多,使设计复杂化。 +6. 迪米特 + + 一个对象应该对尽可能少的对象有接触,也就是只接触那些真正需要接触的对象。迪米特法则也叫做最少知道原则,一个类应该只和它的成员变量,方法的输入,返回参数中的类作交流,而不应该引入其他的类(间接交流)。可以良好地降低类与类之间的耦合。 \ No newline at end of file diff --git "a/Java\346\272\220\347\240\201\345\210\206\346\236\220/ArrayList\346\272\220\347\240\201\345\210\206\346\236\220.md" "b/2. Java\345\237\272\347\241\200/2.2 \346\272\220\347\240\201\350\247\243\346\236\220/1. ArrayList\346\272\220\347\240\201\345\210\206\346\236\220.md" similarity index 100% rename from "Java\346\272\220\347\240\201\345\210\206\346\236\220/ArrayList\346\272\220\347\240\201\345\210\206\346\236\220.md" rename to "2. Java\345\237\272\347\241\200/2.2 \346\272\220\347\240\201\350\247\243\346\236\220/1. ArrayList\346\272\220\347\240\201\345\210\206\346\236\220.md" diff --git "a/Java\346\272\220\347\240\201\345\210\206\346\236\220/HashMap\346\272\220\347\240\201\345\210\206\346\236\220.md" "b/2. Java\345\237\272\347\241\200/2.2 \346\272\220\347\240\201\350\247\243\346\236\220/2. HashMap\346\272\220\347\240\201\345\210\206\346\236\220.md" similarity index 100% rename from "Java\346\272\220\347\240\201\345\210\206\346\236\220/HashMap\346\272\220\347\240\201\345\210\206\346\236\220.md" rename to "2. Java\345\237\272\347\241\200/2.2 \346\272\220\347\240\201\350\247\243\346\236\220/2. HashMap\346\272\220\347\240\201\345\210\206\346\236\220.md" diff --git "a/Java\346\272\220\347\240\201\345\210\206\346\236\220/LinkList\346\272\220\347\240\201\345\210\206\346\236\220-md.md" "b/2. Java\345\237\272\347\241\200/2.2 \346\272\220\347\240\201\350\247\243\346\236\220/3. LinkedList\346\272\220\347\240\201\345\210\206\346\236\220-md.md" similarity index 100% rename from "Java\346\272\220\347\240\201\345\210\206\346\236\220/LinkList\346\272\220\347\240\201\345\210\206\346\236\220-md.md" rename to "2. Java\345\237\272\347\241\200/2.2 \346\272\220\347\240\201\350\247\243\346\236\220/3. LinkedList\346\272\220\347\240\201\345\210\206\346\236\220-md.md" diff --git "a/2. Java\345\237\272\347\241\200/2.2 \346\272\220\347\240\201\350\247\243\346\236\220/4. ThreadPoolExector\346\272\220\347\240\201\345\210\206\346\236\220.md" "b/2. Java\345\237\272\347\241\200/2.2 \346\272\220\347\240\201\350\247\243\346\236\220/4. ThreadPoolExector\346\272\220\347\240\201\345\210\206\346\236\220.md" new file mode 100644 index 0000000..f16c39e --- /dev/null +++ "b/2. Java\345\237\272\347\241\200/2.2 \346\272\220\347\240\201\350\247\243\346\236\220/4. ThreadPoolExector\346\272\220\347\240\201\345\210\206\346\236\220.md" @@ -0,0 +1,434 @@ +>线程池源码也是面试经常被提问到的点,我会将全局源码做一分析,然后告诉你面试考啥,怎么答。 + +### 为什么要用线程池? +>简洁的答两点就行。 + +1. 降低系统资源消耗。 +2. 提高线程可控性。 + +### 如何创建使用线程池? + +JDK8提供了五种创建线程池的方法: + +1. 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。 +``` +public static ExecutorService newFixedThreadPool(int nThreads) { + return new ThreadPoolExecutor(nThreads, nThreads, + 0L, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue()); +} +``` +2. (JDK8新增)会根据所需的并发数来动态创建和关闭线程。能够合理的使用CPU进行对任务进行并发操作,所以适合使用在很耗时的任务。 +>注意返回的是ForkJoinPool对象。 +``` +public static ExecutorService newWorkStealingPool(int parallelism) { + return new ForkJoinPool + (parallelism, + ForkJoinPool.defaultForkJoinWorkerThreadFactory, + null, true); +} + +什么是ForkJoinPool: + +public ForkJoinPool(int parallelism, + ForkJoinWorkerThreadFactory factory, + UncaughtExceptionHandler handler, + boolean asyncMode) { + this(checkParallelism(parallelism), + checkFactory(factory), + handler, + asyncMode ? FIFO_QUEUE : LIFO_QUEUE, + "ForkJoinPool-" + nextPoolId() + "-worker-"); + checkPermission(); + } +使用一个无限队列来保存需要执行的任务,可以传入线程的数量, +不传入,则默认使用当前计算机中可用的cpu数量, +使用分治法来解决问题,使用fork()和join()来进行调用 +``` +3. 创建一个可缓存的线程池,可灵活回收空闲线程,若无可回收,则新建线程。 +``` +public static ExecutorService newCachedThreadPool() { + return new ThreadPoolExecutor(0, Integer.MAX_VALUE, + 60L, TimeUnit.SECONDS, + new SynchronousQueue()); +} +``` +4. 创建一个单线程的线程池。 +``` +public static ExecutorService newSingleThreadExecutor() { + return new FinalizableDelegatedExecutorService + (new ThreadPoolExecutor(1, 1, + 0L, TimeUnit.MILLISECONDS, + new LinkedBlockingQueue())); +} +``` +5. 创建一个定长线程池,支持定时及周期性任务执行。 +``` +public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { + return new ScheduledThreadPoolExecutor(corePoolSize); +} +``` + +### 上层源码结构分析 + +>Executor结构: +![](https://upload-images.jianshu.io/upload_images/5786888-3c7c75e9b406f155.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +#### Executor +>一个运行新任务的简单接口 +``` +public interface Executor { + + void execute(Runnable command); +} +``` + +#### ExecutorService +>扩展了Executor接口。添加了一些用来管理执行器生命周期和任务生命周期的方法 +![](https://upload-images.jianshu.io/upload_images/5786888-3d8ddd3eeb25b148.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +#### AbstractExecutorService +>对ExecutorService接口的抽象类实现。不是我们分析的重点。 + + +#### ThreadPoolExecutor +>Java线程池的核心实现。 + + +### ThreadPoolExecutor源码分析 + +#### 属性解释 + +``` +// AtomicInteger是原子类 ctlOf()返回值为RUNNING; +private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); +// 高3位表示线程状态 +private static final int COUNT_BITS = Integer.SIZE - 3; +// 低29位表示workerCount容量 +private static final int CAPACITY = (1 << COUNT_BITS) - 1; + +// runState is stored in the high-order bits +// 能接收任务且能处理阻塞队列中的任务 +private static final int RUNNING = -1 << COUNT_BITS; +// 不能接收新任务,但可以处理队列中的任务。 +private static final int SHUTDOWN = 0 << COUNT_BITS; +// 不接收新任务,不处理队列任务。 +private static final int STOP = 1 << COUNT_BITS; +// 所有任务都终止 +private static final int TIDYING = 2 << COUNT_BITS; +// 什么都不做 +private static final int TERMINATED = 3 << COUNT_BITS; + +// 存放任务的阻塞队列 +private final BlockingQueue workQueue; + +``` +值的注意的是状态值越大线程越不活跃。 +##### 线程池状态的转换模型: + +![](https://upload-images.jianshu.io/upload_images/5786888-97fe7aee4be7bb4c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +#### 构造器 +``` +public ThreadPoolExecutor(int corePoolSize,//线程池初始启动时线程的数量 + int maximumPoolSize,//最大线程数量 + long keepAliveTime,//空闲线程多久关闭? + TimeUnit unit,// 计时单位 + BlockingQueue workQueue,//放任务的阻塞队列 + ThreadFactory threadFactory,//线程工厂 + RejectedExecutionHandler handler// 拒绝策略) { + if (corePoolSize < 0 || + maximumPoolSize <= 0 || + maximumPoolSize < corePoolSize || + keepAliveTime < 0) + throw new IllegalArgumentException(); + if (workQueue == null || threadFactory == null || handler == null) + throw new NullPointerException(); + this.acc = System.getSecurityManager() == null ? + null : + AccessController.getContext(); + this.corePoolSize = corePoolSize; + this.maximumPoolSize = maximumPoolSize; + this.workQueue = workQueue; + this.keepAliveTime = unit.toNanos(keepAliveTime); + this.threadFactory = threadFactory; + this.handler = handler; +} +``` + +在向线程池提交任务时,会通过两个方法:execute和submit。 + +**本文着重讲解execute方法**。submit方法放在下次和Future、Callable一起分析。 +#### execute方法: +``` +public void execute(Runnable command) { + if (command == null) + throw new NullPointerException(); + // clt记录着runState和workerCount + int c = ctl.get(); + //workerCountOf方法取出低29位的值,表示当前活动的线程数 + //然后拿线程数和 核心线程数做比较 + if (workerCountOf(c) < corePoolSize) { + // 如果活动线程数<核心线程数 + // 添加到 + //addWorker中的第二个参数表示限制添加线程的数量是根据corePoolSize来判断还是maximumPoolSize来判断 + if (addWorker(command, true)) + // 如果成功则返回 + return; + // 如果失败则重新获取 runState和 workerCount + c = ctl.get(); + } + // 如果当前线程池是运行状态并且任务添加到队列成功 + if (isRunning(c) && workQueue.offer(command)) { + // 重新获取 runState和 workerCount + int recheck = ctl.get(); + // 如果不是运行状态并且 + if (! isRunning(recheck) && remove(command)) + reject(command); + else if (workerCountOf(recheck) == 0) + //第一个参数为null,表示在线程池中创建一个线程,但不去启动 + // 第二个参数为false,将线程池的有限线程数量的上限设置为maximumPoolSize + addWorker(null, false); + } + //再次调用addWorker方法,但第二个参数传入为false,将线程池的有限线程数量的上限设置为maximumPoolSize + else if (!addWorker(command, false)) + //如果失败则拒绝该任务 + reject(command); +} +``` +总结一下它的工作流程: + +1. 当`workerCount < corePoolSize`,创建线程执行任务。 + +2. 当`workerCount >= corePoolSize`&&阻塞队列`workQueue`未满,把新的任务放入阻塞队列。 + +3. 当`workQueue`已满,并且`workerCount >= corePoolSize`,并且`workerCount < maximumPoolSize`,创建线程执行任务。 + +4. 当workQueue已满,`workerCount >= maximumPoolSize`,采取拒绝策略,默认拒绝策略是直接抛异常。 + + +![](https://upload-images.jianshu.io/upload_images/5786888-654f22886b8240b3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +通过上面的execute方法可以看到,最主要的逻辑还是在addWorker方法中实现的,那我们就看下这个方法: + +#### addWorker方法 +>主要工作是在线程池中创建一个新的线程并执行 + +参数定义: +* `firstTask` the task the new thread should run first (or null if none). (指定新增线程执行的第一个任务或者不执行任务) +* `core` if true use corePoolSize as bound, else maximumPoolSize.(core如果为true则使用corePoolSize绑定,否则为maximumPoolSize。 (此处使用布尔指示符而不是值,以确保在检查其他状态后读取新值)。) +``` +private boolean addWorker(Runnable firstTask, boolean core) { + retry: + for (;;) { + + int c = ctl.get(); + // 获取运行状态 + int rs = runStateOf(c); + + // Check if queue empty only if necessary. + // 如果状态值 >= SHUTDOWN (不接新任务&不处理队列任务) + // 并且 如果 !(rs为SHUTDOWN 且 firsTask为空 且 阻塞队列不为空) + if (rs >= SHUTDOWN && + ! (rs == SHUTDOWN && + firstTask == null && + ! workQueue.isEmpty())) + // 返回false + return false; + + for (;;) { + //获取线程数wc + int wc = workerCountOf(c); + // 如果wc大与容量 || core如果为true表示根据corePoolSize来比较,否则为maximumPoolSize + if (wc >= CAPACITY || + wc >= (core ? corePoolSize : maximumPoolSize)) + return false; + // 增加workerCount(原子操作) + if (compareAndIncrementWorkerCount(c)) + // 如果增加成功,则跳出 + break retry; + // wc增加失败,则再次获取runState + c = ctl.get(); // Re-read ctl + // 如果当前的运行状态不等于rs,说明状态已被改变,返回重新执行 + if (runStateOf(c) != rs) + continue retry; + // else CAS failed due to workerCount change; retry inner loop + } + } + + boolean workerStarted = false; + boolean workerAdded = false; + Worker w = null; + try { + // 根据firstTask来创建Worker对象 + w = new Worker(firstTask); + // 根据worker创建一个线程 + final Thread t = w.thread; + if (t != null) { + // new一个锁 + final ReentrantLock mainLock = this.mainLock; + // 加锁 + mainLock.lock(); + try { + // Recheck while holding lock. + // Back out on ThreadFactory failure or if + // shut down before lock acquired. + // 获取runState + int rs = runStateOf(ctl.get()); + // 如果rs小于SHUTDOWN(处于运行)或者(rs=SHUTDOWN && firstTask == null) + // firstTask == null证明只新建线程而不执行任务 + if (rs < SHUTDOWN || + (rs == SHUTDOWN && firstTask == null)) { + // 如果t活着就抛异常 + if (t.isAlive()) // precheck that t is startable + throw new IllegalThreadStateException(); + // 否则加入worker(HashSet) + //workers包含池中的所有工作线程。仅在持有mainLock时访问。 + workers.add(w); + // 获取工作线程数量 + int s = workers.size(); + //largestPoolSize记录着线程池中出现过的最大线程数量 + if (s > largestPoolSize) + // 如果 s比它还要大,则将s赋值给它 + largestPoolSize = s; + // worker的添加工作状态改为true + workerAdded = true; + } + } finally { + mainLock.unlock(); + } + // 如果worker的添加工作完成 + if (workerAdded) { + // 启动线程 + t.start(); + // 修改线程启动状态 + workerStarted = true; + } + } + } finally { + if (! workerStarted) + addWorkerFailed(w); + } + // 返回线启动状态 + return workerStarted; + +``` +###### 为什么需要持有mainLock? +因为workers是HashSet类型的,不能保证线程安全。 + + + +那`w = new Worker(firstTask);`如何理解呢 + +#### Worker.java +``` +private final class Worker + extends AbstractQueuedSynchronizer + implements Runnable +``` +可以看到它继承了AQS并发框架还实现了Runnable。证明它还是一个线程任务类。那我们调用t.start()事实上就是调用了该类重写的run方法. + +##### Worker为什么不使用ReentrantLock来实现呢? +tryAcquire方法它是不允许重入的,而ReentrantLock是允许重入的。对于线程来说,如果线程正在执行是不允许其它锁重入进来的。 + +线程只需要两个状态,一个是独占锁,表明正在执行任务;一个是不加锁,表明是空闲状态。 +``` +public void run() { + runWorker(this); +} +``` +run方法又调用了runWorker方法: +``` +final void runWorker(Worker w) { + // 拿到当前线程 + Thread wt = Thread.currentThread(); + // 拿到当前任务 + Runnable task = w.firstTask; + // 将Worker.firstTask置空 并且释放锁 + w.firstTask = null; + w.unlock(); // allow interrupts + boolean completedAbruptly = true; + try { + // 如果task或者getTask不为空,则一直循环 + while (task != null || (task = getTask()) != null) { + // 加锁 + w.lock(); + // If pool is stopping, ensure thread is interrupted; + // if not, ensure thread is not interrupted. This + // requires a recheck in second case to deal with + // shutdownNow race while clearing interrupt + // return ctl.get() >= stop + // 如果线程池状态>=STOP 或者 (线程中断且线程池状态>=STOP)且当前线程没有中断 + // 其实就是保证两点: + // 1. 线程池没有停止 + // 2. 保证线程没有中断 + if ((runStateAtLeast(ctl.get(), STOP) || + (Thread.interrupted() && + runStateAtLeast(ctl.get(), STOP))) && + !wt.isInterrupted()) + // 中断当前线程 + wt.interrupt(); + try { + // 空方法 + beforeExecute(wt, task); + Throwable thrown = null; + try { + // 执行run方法(Runable对象) + task.run(); + } catch (RuntimeException x) { + thrown = x; throw x; + } catch (Error x) { + thrown = x; throw x; + } catch (Throwable x) { + thrown = x; throw new Error(x); + } finally { + afterExecute(task, thrown); + } + } finally { + // 执行完后, 将task置空, 完成任务++, 释放锁 + task = null; + w.completedTasks++; + w.unlock(); + } + } + completedAbruptly = false; + } finally { + // 退出工作 + processWorkerExit(w, completedAbruptly); + } + +``` +总结一下runWorker方法的执行过程: + +1. while循环中,不断地通过getTask()方法从workerQueue中获取任务 +2. 如果线程池正在停止,则中断线程。否则调用3. +3. 调用task.run()执行任务; +4. 如果task为null则跳出循环,执行processWorkerExit()方法,销毁线程`workers.remove(w);` + +这个流程图非常经典: +![](https://upload-images.jianshu.io/upload_images/5786888-4c08a0bb6a78abe1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +除此之外,`ThreadPoolExector`还提供了`tryAcquire`、`tryRelease`、`shutdown`、`shutdownNow`、`tryTerminate`、等涉及的一系列线程状态更改的方法有兴趣可以自己研究。大体思路是一样的,这里不做介绍。 + +##### Worker为什么不使用ReentrantLock来实现呢? +tryAcquire方法它是不允许重入的,而ReentrantLock是允许重入的。对于线程来说,如果线程正在执行是不允许其它锁重入进来的。 + +线程只需要两个状态,一个是独占锁,表明正在执行任务;一个是不加锁,表明是空闲状态。 + +##### 在runWorker方法中,为什么要在执行任务的时候对每个工作线程都加锁呢? +shutdown方法与getTask方法存在竞态条件.(这里不做深入,建议自己深入研究,对它比较熟悉的面试官一般会问) + + +### 高频考点 +1. 创建线程池的五个方法。 +2. 线程池的五个状态 +3. execute执行过程。 +4. runWorker执行过程。(把两个流程图记下,理解后说个大该就行。) +5. 比较深入的问题就是我在文中插入的问题。 +6. ...期望大家能在评论区补充。 + + +声明:图片来源于网络,若有侵犯请联系博主。 \ No newline at end of file diff --git "a/dubbo/Dubbo\350\270\251\345\235\221----\344\270\215\350\203\275\345\272\217\345\210\227\345\214\226HttpServletRequest.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Dubbo/Dubbo-\345\272\217\345\210\227\345\214\226HttpServletRequest\345\244\261\350\264\245.md" similarity index 100% rename from "dubbo/Dubbo\350\270\251\345\235\221----\344\270\215\350\203\275\345\272\217\345\210\227\345\214\226HttpServletRequest.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Dubbo/Dubbo-\345\272\217\345\210\227\345\214\226HttpServletRequest\345\244\261\350\264\245.md" diff --git "a/dubbo/SpringBoot-\346\225\264\345\220\210-Dubbo&Zookeeper-\345\256\236\347\216\260\345\210\206\345\270\203\345\274\217\357\274\210\347\211\271\345\210\253\345\256\214\346\225\264\357\274\211.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Dubbo/SpringBoot-\346\225\264\345\220\210Dubbo&Zookeeper.md" similarity index 100% rename from "dubbo/SpringBoot-\346\225\264\345\220\210-Dubbo&Zookeeper-\345\256\236\347\216\260\345\210\206\345\270\203\345\274\217\357\274\210\347\211\271\345\210\253\345\256\214\346\225\264\357\274\211.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Dubbo/SpringBoot-\346\225\264\345\220\210Dubbo&Zookeeper.md" diff --git "a/dubbo/\346\234\200\346\226\260\345\256\230\346\226\271\347\211\210\347\232\204SpringBoot-\346\225\264\345\220\210-Dubbo.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Dubbo/SpringBoot-\346\225\264\345\220\210Dubbo\345\256\230\346\226\271\347\211\210.md" similarity index 100% rename from "dubbo/\346\234\200\346\226\260\345\256\230\346\226\271\347\211\210\347\232\204SpringBoot-\346\225\264\345\220\210-Dubbo.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Dubbo/SpringBoot-\346\225\264\345\220\210Dubbo\345\256\230\346\226\271\347\211\210.md" diff --git "a/dubbo/Springboot-\346\225\264\345\220\210-dubbo-\347\232\204\344\270\200\344\272\233\345\235\221.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Dubbo/Springboot-\346\225\264\345\220\210dubbo-\347\232\204\344\270\200\344\272\233\345\235\221.md" similarity index 100% rename from "dubbo/Springboot-\346\225\264\345\220\210-dubbo-\347\232\204\344\270\200\344\272\233\345\235\221.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Dubbo/Springboot-\346\225\264\345\220\210dubbo-\347\232\204\344\270\200\344\272\233\345\235\221.md" diff --git "a/dubbo/Zookeeper-\347\216\257\345\242\203\346\220\255\345\273\272&zk\345\221\275\344\273\244\350\257\246\350\247\243.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Dubbo/Zookeeper-\347\216\257\345\242\203\346\220\255\345\273\272&zk\345\221\275\344\273\244\350\257\246\350\247\243.md" similarity index 100% rename from "dubbo/Zookeeper-\347\216\257\345\242\203\346\220\255\345\273\272&zk\345\221\275\344\273\244\350\257\246\350\247\243.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Dubbo/Zookeeper-\347\216\257\345\242\203\346\220\255\345\273\272&zk\345\221\275\344\273\244\350\257\246\350\247\243.md" diff --git "a/dubbo/dubbo-admin-\347\233\221\346\216\247\346\220\255\345\273\272(\346\234\200\345\256\230\346\226\271\343\200\201\346\234\200\350\257\246\347\273\206).md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Dubbo/dubbo-admin-\345\256\230\346\226\271\347\233\221\346\216\247\346\220\255\345\273\272.md" similarity index 100% rename from "dubbo/dubbo-admin-\347\233\221\346\216\247\346\220\255\345\273\272(\346\234\200\345\256\230\346\226\271\343\200\201\346\234\200\350\257\246\347\273\206).md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Dubbo/dubbo-admin-\345\256\230\346\226\271\347\233\221\346\216\247\346\220\255\345\273\272.md" diff --git "a/springboot/SpringBoot-Jpa-\344\275\277\347\224\250\351\227\256\351\242\230\350\247\243\345\206\263\346\225\264\345\220\210.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/JPA/SpringBoot-Jpa-\344\275\277\347\224\250\351\227\256\351\242\230\350\247\243\345\206\263\346\225\264\345\220\210.md" similarity index 100% rename from "springboot/SpringBoot-Jpa-\344\275\277\347\224\250\351\227\256\351\242\230\350\247\243\345\206\263\346\225\264\345\220\210.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/JPA/SpringBoot-Jpa-\344\275\277\347\224\250\351\227\256\351\242\230\350\247\243\345\206\263\346\225\264\345\220\210.md" diff --git "a/springboot/SpringBoot-\346\225\264\345\220\210JPA---PageHelper-\347\232\204\345\210\206\351\241\265\346\234\200\347\256\200\345\256\236\347\216\260.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/JPA/SpringBoot-\346\225\264\345\220\210-JPA-PageHelper-\347\232\204\345\210\206\351\241\265\346\234\200\347\256\200\345\256\236\347\216\260.md" similarity index 100% rename from "springboot/SpringBoot-\346\225\264\345\220\210JPA---PageHelper-\347\232\204\345\210\206\351\241\265\346\234\200\347\256\200\345\256\236\347\216\260.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/JPA/SpringBoot-\346\225\264\345\220\210-JPA-PageHelper-\347\232\204\345\210\206\351\241\265\346\234\200\347\256\200\345\256\236\347\216\260.md" diff --git "a/springboot/spring-boot2-\346\225\264\345\220\210\357\274\210\344\272\214\357\274\211JPA\357\274\210\347\211\271\345\210\253\345\256\214\346\225\264\357\274\201\357\274\211.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/JPA/SpringBoot2-\346\225\264\345\220\210-JPA.md" similarity index 100% rename from "springboot/spring-boot2-\346\225\264\345\220\210\357\274\210\344\272\214\357\274\211JPA\357\274\210\347\211\271\345\210\253\345\256\214\346\225\264\357\274\201\357\274\211.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/JPA/SpringBoot2-\346\225\264\345\220\210-JPA.md" diff --git "a/springboot/spring-boot2-\346\225\264\345\220\210\357\274\210\344\270\200\357\274\211Mybatis-\357\274\210\347\211\271\345\210\253\345\256\214\346\225\264\357\274\201\357\274\211.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Mybatis/1. Springboot2-\346\225\264\345\220\210Mybatis.md" similarity index 100% rename from "springboot/spring-boot2-\346\225\264\345\220\210\357\274\210\344\270\200\357\274\211Mybatis-\357\274\210\347\211\271\345\210\253\345\256\214\346\225\264\357\274\201\357\274\211.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Mybatis/1. Springboot2-\346\225\264\345\220\210Mybatis.md" diff --git "a/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Mybatis/Mybatis#\345\222\214$\345\214\272\345\210\253.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Mybatis/Mybatis#\345\222\214$\345\214\272\345\210\253.md" new file mode 100644 index 0000000..d588235 --- /dev/null +++ "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Mybatis/Mybatis#\345\222\214$\345\214\272\345\210\253.md" @@ -0,0 +1,4 @@ +* `#{}`: 根据参数的类型进行处理,比如传入String类型,则会为参数加上双引号。#{} 传参在进行SQL预编译时,会把参数部分用一个占位符 ? 代替,这样可以防止 SQL注入。 +* `${}` : 将参数取出不做任何处理,直接放入语句中,就是简单的字符串替换,并且该参数会参加SQL的预编译,需要手动过滤参数防止 SQL注入。 +- 因此 mybatis 中优先使用 #{};当需要动态传入 表名或列名时,再考虑使用 ${} 。 +- ``其中 ${} 比较特殊, 他的应用场景是 需要动态传入 表名或列名时使用。 \ No newline at end of file diff --git "a/Mybatis/Mybatis-(ParameterType)-\345\246\202\344\275\225\344\274\240\351\200\222\345\244\232\344\270\252\344\270\215\345\220\214\347\261\273\345\236\213\347\232\204\345\217\202\346\225\260.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Mybatis/Mybatis-(ParameterType)-\345\246\202\344\275\225\344\274\240\351\200\222\345\244\232\344\270\252\344\270\215\345\220\214\347\261\273\345\236\213\347\232\204\345\217\202\346\225\260.md" similarity index 100% rename from "Mybatis/Mybatis-(ParameterType)-\345\246\202\344\275\225\344\274\240\351\200\222\345\244\232\344\270\252\344\270\215\345\220\214\347\261\273\345\236\213\347\232\204\345\217\202\346\225\260.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Mybatis/Mybatis-(ParameterType)-\345\246\202\344\275\225\344\274\240\351\200\222\345\244\232\344\270\252\344\270\215\345\220\214\347\261\273\345\236\213\347\232\204\345\217\202\346\225\260.md" diff --git "a/Mybatis/Mybatis\344\270\255\345\244\247\344\272\216\347\255\211\344\272\216\345\260\217\344\272\216\347\255\211\344\272\216\347\232\204\345\206\231\346\263\225.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Mybatis/Mybatis\344\270\255\345\244\247\344\272\216\347\255\211\344\272\216\345\260\217\344\272\216\347\255\211\344\272\216\347\232\204\345\206\231\346\263\225.md" similarity index 100% rename from "Mybatis/Mybatis\344\270\255\345\244\247\344\272\216\347\255\211\344\272\216\345\260\217\344\272\216\347\255\211\344\272\216\347\232\204\345\206\231\346\263\225.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Mybatis/Mybatis\344\270\255\345\244\247\344\272\216\347\255\211\344\272\216\345\260\217\344\272\216\347\255\211\344\272\216\347\232\204\345\206\231\346\263\225.md" diff --git "a/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Mybatis/Mybatis\347\274\223\345\255\230.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Mybatis/Mybatis\347\274\223\345\255\230.md" new file mode 100644 index 0000000..f340acc --- /dev/null +++ "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Mybatis/Mybatis\347\274\223\345\255\230.md" @@ -0,0 +1,25 @@ +### Mybatis缓存 + +> mybatis支持一级和二级缓存 + +##### 一级缓存 + +一级缓存是基于SqlSession建立,当用户发起查询时,MyBatis根据当前执行的语句生成MappedStatement,在Local Cache进行查询(SqlSession接口默认实现类中有Executor接口实现类对象,该对象中存放了缓存,当有UDI操作时会清空缓存)。配置:`` + +1. MyBatis一级缓存的生命周期和SqlSession一致。 +2. MyBatis一级缓存内部设计简单,只是一个没有容量限定的HashMap,在缓存的功能性上有所欠缺。 +3. MyBatis的一级缓存最大范围是SqlSession内部,有多个SqlSession或者分布式的环境下,数据库写操作会引起脏数据,建议设定缓存级别为Statement。 + +##### 二级缓存 + +如果多个SqlSession之间需要共享缓存,则需要使用到二级缓存。开启二级缓存后,会使用CachingExecutor装饰Executor,进入一级缓存的查询流程前,先在CachingExecutor进行二级缓存的查询。 + +二级缓存开启后,同一个namespace下的所有操作语句,都影响着同一个Cache,即二级缓存被多个SqlSession共享,是一个全局的变量。 + +当开启缓存后,数据的查询执行的流程就是 二级缓存 -> 一级缓存 -> 数据库。 + +1. MyBatis的二级缓存相对于一级缓存来说,实现了SqlSession之间缓存数据的共享,同时粒度更加的细,能够到namespace级别,通过Cache接口实现类不同的组合,对Cache的可控性也更强。 +2. MyBatis在多表查询时,极大可能会出现脏数据,有设计上的缺陷,安全使用二级缓存的条件比较苛刻。 +3. 在分布式环境下,由于默认的MyBatis Cache实现都是基于本地的,分布式环境下必然会出现读取到脏数据,需要使用集中式缓存将MyBatis的Cache接口实现,有一定的开发成本,直接使用Redis,Memcached等分布式缓存可能成本更低,安全性也更高。 + +个人建议MyBatis缓存特性在生产环境中进行关闭,单纯作为一个ORM框架使用可能更为合适。 \ No newline at end of file diff --git "a/Mybatis/\347\224\250mybatis\346\211\271\351\207\217\346\233\264\346\226\260\346\225\260\346\215\256\346\212\245\351\224\231\351\227\256\351\242\230.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Mybatis/\347\224\250mybatis\346\211\271\351\207\217\346\233\264\346\226\260\346\225\260\346\215\256\346\212\245\351\224\231\351\227\256\351\242\230.md" similarity index 100% rename from "Mybatis/\347\224\250mybatis\346\211\271\351\207\217\346\233\264\346\226\260\346\225\260\346\215\256\346\212\245\351\224\231\351\227\256\351\242\230.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Mybatis/\347\224\250mybatis\346\211\271\351\207\217\346\233\264\346\226\260\346\225\260\346\215\256\346\212\245\351\224\231\351\227\256\351\242\230.md" diff --git "a/netty\350\247\243\350\257\273/Netty\346\267\261\345\205\245\346\265\205\345\207\272\357\274\210\344\270\200\357\274\211\345\205\245\351\227\250.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Netty/1. Netty\346\267\261\345\205\245\346\265\205\345\207\272.md" similarity index 100% rename from "netty\350\247\243\350\257\273/Netty\346\267\261\345\205\245\346\265\205\345\207\272\357\274\210\344\270\200\357\274\211\345\205\245\351\227\250.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Netty/1. Netty\346\267\261\345\205\245\346\265\205\345\207\272.md" diff --git "a/netty\350\247\243\350\257\273/Netty\347\275\221\347\273\234\350\201\212\345\244\251(\344\270\200)-\350\201\212\345\244\251\345\256\244\347\232\204\345\256\236\346\210\230.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Netty/2. Netty\350\201\212\345\244\251\345\256\244\347\232\204\345\256\236\346\210\230.md" similarity index 100% rename from "netty\350\247\243\350\257\273/Netty\347\275\221\347\273\234\350\201\212\345\244\251(\344\270\200)-\350\201\212\345\244\251\345\256\244\347\232\204\345\256\236\346\210\230.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Netty/2. Netty\350\201\212\345\244\251\345\256\244\347\232\204\345\256\236\346\210\230.md" diff --git "a/netty\350\247\243\350\257\273/\346\265\205\350\260\210Netty\347\232\204\347\272\277\347\250\213\346\250\241\345\236\213.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Netty/\346\265\205\350\260\210Netty\347\232\204\347\272\277\347\250\213\346\250\241\345\236\213.md" similarity index 100% rename from "netty\350\247\243\350\257\273/\346\265\205\350\260\210Netty\347\232\204\347\272\277\347\250\213\346\250\241\345\236\213.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Netty/\346\265\205\350\260\210Netty\347\232\204\347\272\277\347\250\213\346\250\241\345\236\213.md" diff --git "a/spring/spring\346\263\250\350\247\243\345\274\200\345\217\221\357\274\210\344\270\200\357\274\211Bean\346\263\250\345\205\245.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Spring/spring\346\263\250\350\247\243\345\274\200\345\217\221\357\274\210\344\270\200\357\274\211Bean\346\263\250\345\205\245.md" similarity index 100% rename from "spring/spring\346\263\250\350\247\243\345\274\200\345\217\221\357\274\210\344\270\200\357\274\211Bean\346\263\250\345\205\245.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Spring/spring\346\263\250\350\247\243\345\274\200\345\217\221\357\274\210\344\270\200\357\274\211Bean\346\263\250\345\205\245.md" diff --git "a/spring/Spring\344\272\213\345\212\241\347\256\241\347\220\206\357\274\210\344\270\200\357\274\211\345\277\253\351\200\237\345\205\245\351\227\250.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Spring/\344\272\213\345\212\241\347\256\241\347\220\206/Spring\344\272\213\345\212\241\347\256\241\347\220\206\357\274\210\344\270\200\357\274\211\345\277\253\351\200\237\345\205\245\351\227\250.md" similarity index 100% rename from "spring/Spring\344\272\213\345\212\241\347\256\241\347\220\206\357\274\210\344\270\200\357\274\211\345\277\253\351\200\237\345\205\245\351\227\250.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Spring/\344\272\213\345\212\241\347\256\241\347\220\206/Spring\344\272\213\345\212\241\347\256\241\347\220\206\357\274\210\344\270\200\357\274\211\345\277\253\351\200\237\345\205\245\351\227\250.md" diff --git "a/spring/Spring\344\272\213\345\212\241\347\256\241\347\220\206\357\274\210\344\272\214\357\274\211\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241\347\256\241\347\220\206\344\271\213JTA\344\270\216\351\223\276\345\274\217\344\272\213\345\212\241.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Spring/\344\272\213\345\212\241\347\256\241\347\220\206/Spring\344\272\213\345\212\241\347\256\241\347\220\206\357\274\210\344\272\214\357\274\211\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241\347\256\241\347\220\206\344\271\213JTA\344\270\216\351\223\276\345\274\217\344\272\213\345\212\241.md" similarity index 100% rename from "spring/Spring\344\272\213\345\212\241\347\256\241\347\220\206\357\274\210\344\272\214\357\274\211\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241\347\256\241\347\220\206\344\271\213JTA\344\270\216\351\223\276\345\274\217\344\272\213\345\212\241.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/Spring/\344\272\213\345\212\241\347\256\241\347\220\206/Spring\344\272\213\345\212\241\347\256\241\347\220\206\357\274\210\344\272\214\357\274\211\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241\347\256\241\347\220\206\344\271\213JTA\344\270\216\351\223\276\345\274\217\344\272\213\345\212\241.md" diff --git "a/springboot/SpringBoot2\347\263\273\345\210\227\346\225\231\347\250\213\357\274\210\344\270\200\357\274\211\347\256\200\344\273\213\345\217\212HelloWorld\351\241\271\347\233\256.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/1. SpringBoot2\347\263\273\345\210\227\346\225\231\347\250\213-\347\256\200\344\273\213\345\217\212HelloWorld\351\241\271\347\233\256.md" similarity index 100% rename from "springboot/SpringBoot2\347\263\273\345\210\227\346\225\231\347\250\213\357\274\210\344\270\200\357\274\211\347\256\200\344\273\213\345\217\212HelloWorld\351\241\271\347\233\256.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/1. SpringBoot2\347\263\273\345\210\227\346\225\231\347\250\213-\347\256\200\344\273\213\345\217\212HelloWorld\351\241\271\347\233\256.md" diff --git "a/springboot/SpringBoot2\347\263\273\345\210\227\346\225\231\347\250\213\357\274\210\344\272\214\357\274\211maven\351\241\271\347\233\256\345\214\205-\357\274\210\347\211\271\345\210\253\345\256\214\346\225\264\357\274\201\357\274\211.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/2. SpringBoot2\347\263\273\345\210\227\346\225\231\347\250\213-maven\351\241\271\347\233\256.md" similarity index 100% rename from "springboot/SpringBoot2\347\263\273\345\210\227\346\225\231\347\250\213\357\274\210\344\272\214\357\274\211maven\351\241\271\347\233\256\345\214\205-\357\274\210\347\211\271\345\210\253\345\256\214\346\225\264\357\274\201\357\274\211.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/2. SpringBoot2\347\263\273\345\210\227\346\225\231\347\250\213-maven\351\241\271\347\233\256.md" diff --git "a/springboot/SpringBoot2\347\263\273\345\210\227\346\225\231\347\250\213\357\274\210\344\270\211\357\274\211DI\344\276\235\350\265\226\346\263\250\345\205\245.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/3. SpringBoot2\347\263\273\345\210\227\346\225\231\347\250\213-DI\344\276\235\350\265\226\346\263\250\345\205\245.md" similarity index 100% rename from "springboot/SpringBoot2\347\263\273\345\210\227\346\225\231\347\250\213\357\274\210\344\270\211\357\274\211DI\344\276\235\350\265\226\346\263\250\345\205\245.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/3. SpringBoot2\347\263\273\345\210\227\346\225\231\347\250\213-DI\344\276\235\350\265\226\346\263\250\345\205\245.md" diff --git "a/springboot/Springboot\351\241\271\347\233\256\344\270\213\350\275\275-\344\270\212\344\274\240\350\267\257\345\276\204\346\225\264\347\220\206.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/4. Springboot-\351\241\271\347\233\256\344\270\213\350\275\275\343\200\201\344\270\212\344\274\240\350\267\257\345\276\204\346\225\264\347\220\206.md" similarity index 100% rename from "springboot/Springboot\351\241\271\347\233\256\344\270\213\350\275\275-\344\270\212\344\274\240\350\267\257\345\276\204\346\225\264\347\220\206.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/4. Springboot-\351\241\271\347\233\256\344\270\213\350\275\275\343\200\201\344\270\212\344\274\240\350\267\257\345\276\204\346\225\264\347\220\206.md" diff --git "a/springboot/SpringBoot-\347\274\223\345\255\230&\350\265\204\346\272\220\344\274\230\345\214\226.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/5. SpringBoot-\347\274\223\345\255\230&\350\265\204\346\272\220\344\274\230\345\214\226.md" similarity index 100% rename from "springboot/SpringBoot-\347\274\223\345\255\230&\350\265\204\346\272\220\344\274\230\345\214\226.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/5. SpringBoot-\347\274\223\345\255\230&\350\265\204\346\272\220\344\274\230\345\214\226.md" diff --git "a/springboot/SpringBoot-\346\226\207\344\273\266\344\270\212\344\274\240-\344\270\213\350\275\275.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/6. SpringBoot-\346\226\207\344\273\266\344\270\212\344\274\240-\344\270\213\350\275\275.md" similarity index 100% rename from "springboot/SpringBoot-\346\226\207\344\273\266\344\270\212\344\274\240-\344\270\213\350\275\275.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/6. SpringBoot-\346\226\207\344\273\266\344\270\212\344\274\240-\344\270\213\350\275\275.md" diff --git "a/springboot/SpringBoot-\350\207\252\345\256\232\344\271\211\347\272\277\347\250\213\346\261\240.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/7. SpringBoot-\350\207\252\345\256\232\344\271\211\347\272\277\347\250\213\346\261\240.md" similarity index 100% rename from "springboot/SpringBoot-\350\207\252\345\256\232\344\271\211\347\272\277\347\250\213\346\261\240.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/7. SpringBoot-\350\207\252\345\256\232\344\271\211\347\272\277\347\250\213\346\261\240.md" diff --git "a/springboot/SpringBoot-\346\225\264\345\220\210\357\274\210\344\272\224\357\274\211Swagger2.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/8. SpringBoot-\346\225\264\345\220\210-Swagger2.md" similarity index 100% rename from "springboot/SpringBoot-\346\225\264\345\220\210\357\274\210\344\272\224\357\274\211Swagger2.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/8. SpringBoot-\346\225\264\345\220\210-Swagger2.md" diff --git "a/springboot/IDEA\344\270\255\346\211\276\344\270\215\345\210\260\347\250\213\345\272\217\345\214\205\345\222\214\346\211\276\344\270\215\345\210\260\347\254\246\345\217\267.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/IDEA\344\270\255\346\211\276\344\270\215\345\210\260\347\250\213\345\272\217\345\214\205\345\222\214\346\211\276\344\270\215\345\210\260\347\254\246\345\217\267.md" similarity index 100% rename from "springboot/IDEA\344\270\255\346\211\276\344\270\215\345\210\260\347\250\213\345\272\217\345\214\205\345\222\214\346\211\276\344\270\215\345\210\260\347\254\246\345\217\267.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/IDEA\344\270\255\346\211\276\344\270\215\345\210\260\347\250\213\345\272\217\345\214\205\345\222\214\346\211\276\344\270\215\345\210\260\347\254\246\345\217\267.md" diff --git "a/springboot/Json-\351\200\206\345\220\221\347\224\237\346\210\220-javaBean.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/Json-\351\200\206\345\220\221\347\224\237\346\210\220-javaBean.md" similarity index 100% rename from "springboot/Json-\351\200\206\345\220\221\347\224\237\346\210\220-javaBean.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/Json-\351\200\206\345\220\221\347\224\237\346\210\220-javaBean.md" diff --git "a/springboot/Spring-Boot-\343\200\201Spring-Data-JPA\343\200\201Hibernate\351\233\206\346\210\220.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/Spring-Boot-\343\200\201Spring-Data-JPA\343\200\201Hibernate\351\233\206\346\210\220.md" similarity index 100% rename from "springboot/Spring-Boot-\343\200\201Spring-Data-JPA\343\200\201Hibernate\351\233\206\346\210\220.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/Spring-Boot-\343\200\201Spring-Data-JPA\343\200\201Hibernate\351\233\206\346\210\220.md" diff --git "a/springboot/Spring-Boot-\345\246\202\344\275\225\346\211\223\345\214\205\346\210\220war\345\214\205.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/Spring-Boot-\345\246\202\344\275\225\346\211\223\345\214\205\346\210\220war\345\214\205.md" similarity index 100% rename from "springboot/Spring-Boot-\345\246\202\344\275\225\346\211\223\345\214\205\346\210\220war\345\214\205.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/Spring-Boot-\345\246\202\344\275\225\346\211\223\345\214\205\346\210\220war\345\214\205.md" diff --git "a/springboot/Spring-Boot-\351\241\271\347\233\256\345\256\236\347\216\260\347\203\255\351\203\250\347\275\262.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/Spring-Boot-\351\241\271\347\233\256\345\256\236\347\216\260\347\203\255\351\203\250\347\275\262.md" similarity index 100% rename from "springboot/Spring-Boot-\351\241\271\347\233\256\345\256\236\347\216\260\347\203\255\351\203\250\347\275\262.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/Spring-Boot-\351\241\271\347\233\256\345\256\236\347\216\260\347\203\255\351\203\250\347\275\262.md" diff --git "a/springboot/Intellij-IDEA-SpringBoot\351\241\271\347\233\256\347\203\255\351\203\250\347\275\262\350\247\243\345\206\263\346\226\271\346\241\210.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/SpringBoot-IDEA-\351\241\271\347\233\256\347\203\255\351\203\250\347\275\262\350\247\243\345\206\263\346\226\271\346\241\210.md" similarity index 100% rename from "springboot/Intellij-IDEA-SpringBoot\351\241\271\347\233\256\347\203\255\351\203\250\347\275\262\350\247\243\345\206\263\346\226\271\346\241\210.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/SpringBoot-IDEA-\351\241\271\347\233\256\347\203\255\351\203\250\347\275\262\350\247\243\345\206\263\346\226\271\346\241\210.md" diff --git "a/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/SpringBoot-Jpa-\344\275\277\347\224\250\351\227\256\351\242\230\350\247\243\345\206\263\346\225\264\345\220\210.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/SpringBoot-Jpa-\344\275\277\347\224\250\351\227\256\351\242\230\350\247\243\345\206\263\346\225\264\345\220\210.md" new file mode 100644 index 0000000..d8ad3c0 --- /dev/null +++ "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/SpringBoot-Jpa-\344\275\277\347\224\250\351\227\256\351\242\230\350\247\243\345\206\263\346\225\264\345\220\210.md" @@ -0,0 +1,5 @@ +##### 1. 启动项目报错 Not a managed type: class com.xzxx +这种问题一般出在多模块开发中的依赖传递导致的问题,该问题的原因是启动类找不到对应的bean。所以需要在启动类上添加注解`@EntityScan("com.xxxx`. + +##### 2. 启动项目报错 Cannot determine embedded database driver class for database type NONE +springboot启动时会自动注入数据源和配置jpa,但是web项目里不需要配置 数据库连接信息。所以需要在启动注解上修改`@SpringBootApplication(exclude={DataSourceAutoConfiguration.class,HibernateJpaAutoConfiguration.class})` diff --git "a/springboot/\344\275\277\347\224\250WireMock-\344\274\252\351\200\240-Rest-\346\234\215\345\212\241.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/SpringBoot-\344\275\277\347\224\250WireMock\344\274\252\351\200\240Rest\346\234\215\345\212\241.md" similarity index 100% rename from "springboot/\344\275\277\347\224\250WireMock-\344\274\252\351\200\240-Rest-\346\234\215\345\212\241.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/SpringBoot-\344\275\277\347\224\250WireMock\344\274\252\351\200\240Rest\346\234\215\345\212\241.md" diff --git "a/springboot/spring-boot\345\205\250\345\261\200\345\274\202\345\270\270\346\215\225\346\215\211.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/SpringBoot-\345\205\250\345\261\200\345\274\202\345\270\270\346\215\225\346\215\211.md" similarity index 100% rename from "springboot/spring-boot\345\205\250\345\261\200\345\274\202\345\270\270\346\215\225\346\215\211.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/SpringBoot-\345\205\250\345\261\200\345\274\202\345\270\270\346\215\225\346\215\211.md" diff --git "a/springboot/SpringBoot-\345\271\266\345\217\221\347\274\226\347\250\213\345\255\246\344\271\240\345\216\206\347\250\213(\347\273\235\345\257\271\347\232\204\345\271\262\350\264\247).md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/SpringBoot-\345\271\266\345\217\221\347\274\226\347\250\213\345\255\246\344\271\240\345\216\206\347\250\213(\347\273\235\345\257\271\347\232\204\345\271\262\350\264\247).md" similarity index 100% rename from "springboot/SpringBoot-\345\271\266\345\217\221\347\274\226\347\250\213\345\255\246\344\271\240\345\216\206\347\250\213(\347\273\235\345\257\271\347\232\204\345\271\262\350\264\247).md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/SpringBoot-\345\271\266\345\217\221\347\274\226\347\250\213\345\255\246\344\271\240\345\216\206\347\250\213(\347\273\235\345\257\271\347\232\204\345\271\262\350\264\247).md" diff --git "a/springboot/SpringBoot-\345\274\200\345\217\221\346\212\200\345\267\247-\345\217\212-\346\263\250\346\204\217\344\272\213\351\241\271.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/SpringBoot-\345\274\200\345\217\221\346\212\200\345\267\247-\345\217\212-\346\263\250\346\204\217\344\272\213\351\241\271.md" similarity index 100% rename from "springboot/SpringBoot-\345\274\200\345\217\221\346\212\200\345\267\247-\345\217\212-\346\263\250\346\204\217\344\272\213\351\241\271.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/SpringBoot-\345\274\200\345\217\221\346\212\200\345\267\247-\345\217\212-\346\263\250\346\204\217\344\272\213\351\241\271.md" diff --git "a/springboot/SpringBoot-\346\211\213\345\206\231\345\210\207\347\211\207-\351\235\242\345\220\221\345\210\207\351\235\242\347\274\226\347\250\213.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/SpringBoot-\346\211\213\345\206\231\345\210\207\347\211\207-\351\235\242\345\220\221\345\210\207\351\235\242\347\274\226\347\250\213.md" similarity index 100% rename from "springboot/SpringBoot-\346\211\213\345\206\231\345\210\207\347\211\207-\351\235\242\345\220\221\345\210\207\351\235\242\347\274\226\347\250\213.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/SpringBoot-\346\211\213\345\206\231\345\210\207\347\211\207-\351\235\242\345\220\221\345\210\207\351\235\242\347\274\226\347\250\213.md" diff --git "a/springboot/SpringBoot-\346\211\213\345\206\231\346\213\246\346\210\252\345\231\250.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/SpringBoot-\346\211\213\345\206\231\346\213\246\346\210\252\345\231\250.md" similarity index 100% rename from "springboot/SpringBoot-\346\211\213\345\206\231\346\213\246\346\210\252\345\231\250.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/SpringBoot-\346\211\213\345\206\231\346\213\246\346\210\252\345\231\250.md" diff --git "a/springboot/SpringBoot-\346\211\213\345\206\231\350\277\207\346\273\244\345\231\250&\345\212\240\350\275\275\347\254\254\344\270\211\346\226\271\350\277\207\346\273\244\345\231\250.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/SpringBoot-\346\211\213\345\206\231\350\277\207\346\273\244\345\231\250&\345\212\240\350\275\275\347\254\254\344\270\211\346\226\271\350\277\207\346\273\244\345\231\250.md" similarity index 100% rename from "springboot/SpringBoot-\346\211\213\345\206\231\350\277\207\346\273\244\345\231\250&\345\212\240\350\275\275\347\254\254\344\270\211\346\226\271\350\277\207\346\273\244\345\231\250.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/SpringBoot-\346\211\213\345\206\231\350\277\207\346\273\244\345\231\250&\345\212\240\350\275\275\347\254\254\344\270\211\346\226\271\350\277\207\346\273\244\345\231\250.md" diff --git "a/springboot/SpringBoot-\346\225\264\345\220\210-Dubbo&Zookeeper-\345\256\236\347\216\260\345\210\206\345\270\203\345\274\217.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/SpringBoot-\346\225\264\345\220\210-Dubbo&Zookeeper-\345\256\236\347\216\260\345\210\206\345\270\203\345\274\217.md" similarity index 100% rename from "springboot/SpringBoot-\346\225\264\345\220\210-Dubbo&Zookeeper-\345\256\236\347\216\260\345\210\206\345\270\203\345\274\217.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/SpringBoot-\346\225\264\345\220\210-Dubbo&Zookeeper-\345\256\236\347\216\260\345\210\206\345\270\203\345\274\217.md" diff --git "a/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/SpringBoot-\346\225\264\345\220\210-JPA-PageHelper-\347\232\204\345\210\206\351\241\265\346\234\200\347\256\200\345\256\236\347\216\260.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/SpringBoot-\346\225\264\345\220\210-JPA-PageHelper-\347\232\204\345\210\206\351\241\265\346\234\200\347\256\200\345\256\236\347\216\260.md" new file mode 100644 index 0000000..99f31f3 --- /dev/null +++ "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/SpringBoot-\346\225\264\345\220\210-JPA-PageHelper-\347\232\204\345\210\206\351\241\265\346\234\200\347\256\200\345\256\236\347\216\260.md" @@ -0,0 +1,73 @@ +>JPA又自己的Pageable来帮助我们实现分页,Mybatis有PageHelper帮我们实现分页,下面直接贴代码。 + + +### 1. 用JPA实现分页 + +##### 1.1 pom添加依赖 +``` + + org.springframework.boot + spring-boot-starter-data-jpa + + +``` +其实就是JPA的依赖。 +##### 1.2 核心实现 +``` + /** + * 查询全部 + */ + @Override + public ServerResponse selectAll(Integer page,Integer size) { + Pageable pageable = new PageRequest(page,size,Sort.Direction.DESC,"noticeId"); + Iterator all = noticeRepostory.findAll(pageable).iterator(); + List list = new ArrayList(); + while (all.hasNext()){ + list.add(all.next()); + } + if (all == null){ + return ServerResponse.createByErrorMessage("查询公告列表失败"); + } + return ServerResponse.createBySuccess(list); + } +``` +更多详细的请看[spring boot2 整合(二)JPA(特别完整!)](https://www.jianshu.com/p/3b31270a44b1)中分页那部分。 +### 2. 用Mybatis实现分页 + +##### 2.1 pom依赖 +``` + + com.github.pagehelper + pagehelper-spring-boot-starter + 1.0.0 + + +``` + +##### 2.2 核心代码 +``` + /** + * 查询消息列表 + * + * @param userId + */ + @Override + public ServerResponse selectNewsList(Integer userId,Integer pageNum,Integer pageSize) { + PageHelper.startPage(pageNum, pageSize); + List news = newsMapper.selectNewsList(userId); + PageInfo appsPageInfo = new PageInfo<>(news); + + if (StringUtils.isEmpty(news)){ + return ServerResponse.createByErrorMessage("查询消息列表失败"); + } + log.info("查询到的消息数目{}",appsPageInfo.getList()); + appsPageInfo.getList().forEach(p-> System.out.println(p.toString())); + return ServerResponse.createBySuccess(appsPageInfo.getList()); + } +``` +我们只需要PageHelper.startPage,然后紧跟着查询并返回一个list对象,然后用list对象创建一个PageInfo对象。 + +pageInfo有很多属性,比如当前页是多少,有没有下一页,数据一共多少等等,我这里调用它的getList方法来获取到news集合。 + +更多详细的请看[spring boot2 整合(一)Mybatis (特别完整!)](https://www.jianshu.com/p/c15094bd1965) +中分页那部分。 diff --git "a/springboot/SpringBoot-\346\225\264\345\220\210-Redis.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/SpringBoot-\346\225\264\345\220\210-Redis.md" similarity index 100% rename from "springboot/SpringBoot-\346\225\264\345\220\210-Redis.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/SpringBoot-\346\225\264\345\220\210-Redis.md" diff --git "a/springboot/springboot----2-0\347\211\210\346\234\254\350\207\252\345\256\232\344\271\211ReidsCacheManager\347\232\204\346\224\271\345\217\230.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/SpringBoot2-ReidsCacheManager\347\232\204\346\224\271\345\217\230.md" similarity index 100% rename from "springboot/springboot----2-0\347\211\210\346\234\254\350\207\252\345\256\232\344\271\211ReidsCacheManager\347\232\204\346\224\271\345\217\230.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/SpringBoot2-ReidsCacheManager\347\232\204\346\224\271\345\217\230.md" diff --git "a/springboot/spring-boot2-\346\225\264\345\220\210\357\274\210\344\270\211\357\274\211JOOQ\345\267\245\345\205\267.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/SpringBoot2-\346\225\264\345\220\210-JOOQ.md" similarity index 100% rename from "springboot/spring-boot2-\346\225\264\345\220\210\357\274\210\344\270\211\357\274\211JOOQ\345\267\245\345\205\267.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/SpringBoot2-\346\225\264\345\220\210-JOOQ.md" diff --git "a/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/SpringBoot2-\346\225\264\345\220\210-JPA.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/SpringBoot2-\346\225\264\345\220\210-JPA.md" new file mode 100644 index 0000000..6614b51 --- /dev/null +++ "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/SpringBoot2-\346\225\264\345\220\210-JPA.md" @@ -0,0 +1,385 @@ +>JPA全称Java Persistence API.JPA通过JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。 + +>JPA 的目标之一是制定一个可以由很多供应商实现的API,并且开发人员可以编码来实现该API,而不是使用私有供应商特有的API。 + +>JPA是需要Provider来实现其功能的,Hibernate就是JPA Provider中很强的一个,应该说无人能出其右。从功能上来说,JPA就是Hibernate功能的一个子集。 + +本教程大概流程: +1. 借助idea实现springboot 和 spring data jpa 整合 +2. 实现JpaRepository接口快捷开发 +3. 自定义Mapper查询接口方法 +4. MVC架构+分页功能实战 +5. QueryDSL工具与之的整合 +6. EntityManager的使用 +首先我的开发环境: +jdk1.8+maven3+IDEA + +####1. 完善pom文件 +``` + + + 4.0.0 + + springboot-jpa + springboot-jpa + 0.0.1-SNAPSHOT + jar + + springboot-jpa + Demo project for Spring Boot + + + org.springframework.boot + spring-boot-starter-parent + 2.0.0.RELEASE + + + + + UTF-8 + UTF-8 + 1.8 + + + + + org.springframework.boot + spring-boot-starter + + + + org.springframework.boot + spring-boot-starter-test + + + + org.springframework.boot + spring-boot-starter-web + + + mysql + mysql-connector-java + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + org.projectlombok + lombok + 1.16.18 + + + org.springframework.boot + spring-boot-test + 1.4.5.RELEASE + test + + + + com.querydsl + querydsl-jpa + + + com.querydsl + querydsl-apt + provided + + + + com.alibaba + druid + 1.0.26 + + + + com.alibaba + fastjson + 1.2.15 + + + com.alibaba + druid + 1.1.3 + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + com.mysema.maven + apt-maven-plugin + 1.1.3 + + + + target/generated-sources/java + com.querydsl.apt.jpa.JPAAnnotationProcessor + + + + + + + + + + +``` + +#### 2. 完善application.properties 文件 +``` +spring.datasource.url=jdbc:mysql://localhost:3306/user +spring.datasource.username=root +spring.datasource.password=root +spring.datasource.driver-class-name=com.mysql.jdbc.Driver +spring.datasource.type: com.alibaba.druid.pool.DruidDataSource +spring.datasource.filters:stat +spring.datasource.maxActive: 20 +spring.datasource.initialSize: 1 +spring.datasource.maxWait: 60000 +spring.datasource.minIdle: 1 +spring.datasource.timeBetweenEvictionRunsMillis: 60000 +spring.datasource.minEvictableIdleTimeMillis: 300000 +spring.datasource.validationQuery: select 'x' +spring.datasource.testWhileIdle: true +spring.datasource.testOnBorrow: false +spring.datasource.testOnReturn: false +spring.datasource.poolPreparedStatements: true +spring.datasource.maxOpenPreparedStatements: 20 + + +spring.jpa.properties.hibernate.hbm2ddl.auto=update +spring.jpa.show-sql=true +``` + +####3. 编写实体类 User.java +``` +package com.fantj.model; + + +import lombok.Data; + +import javax.persistence.*; +import java.util.Date; + +@Data +@Entity +@Table(name = "user") +public class User { + public User(){ + } + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Integer id; + + @Column(nullable = false) + private String username; + @Column(nullable = false) + private Date birthday; + @Column(nullable = false) + private String sex; + @Column(nullable = false) + private String address; +} +``` + +@Data注解是 lombok 依赖包下的注解,它可以自动帮我们生成set/getter方法,简化代码量。有兴趣的可以详细了解,这里不做多解释。 + +####4. 实现DAO层 + +``` +package com.fantj.repostory; + +/** + * Created by Fant.J. + */ +@Repository +public interface UserRepository extends JpaRepository { + + //自定义repository。手写sql + @Query(value = "update user set name=?1 where id=?4",nativeQuery = true) //占位符传值形式 + @Modifying + int updateById(String name,int id); + + @Query("from User u where u.username=:username") //SPEL表达式 + User findUser(@Param("username") String username);// 参数username 映射到数据库字段username +} + +``` + +注意:只有@Query 的注解下不能使用insert,我们需要在上面再添加个@Modify注解,我习惯都加,nativeQuery 是询问是否使用原生sql语句。多表查询也是在这里手写sql,不做演示。因为后面我们用更好的支持多表查询的工具框架 QueryDSL来帮助我们更简洁的实现它。 + +#### 5.实现Service层 +UserService .java +``` +package com.fantj.service; + +/** + * Created by Fant.J. + */ +public interface UserService { + /** 删除 */ + public void delete(int id); + /** 增加*/ + public void insert(User user); + /** 更新*/ + public int update(User user); + /** 查询单个*/ + public User selectById(int id); + /** 查询全部列表*/ + public Iterator selectAll(int pageNum, int pageSize); +} + +``` +UserServiceImpl.java +``` +package com.fantj.service.impl; + + +/** + * Created by Fant.J. + */ +@Service +public class UserServiceImpl implements UserService { + + @Autowired + private UserRepository userRepository; + + /** + * 删除 + * + * @param id + */ + @Override + public void delete(int id) { + userRepository.deleteById(id); + } + + /** + * 增加 + * + * @param user + */ + @Override + public void insert(User user) { + userRepository.save(user); + } + + /** + * 更新 + * + * @param user + */ + @Override + public int update(User user) { + userRepository.save(user); + return 1; + } + + /** + * 查询单个 + * + * @param id + */ + @Override + public User selectById(int id) { + Optional optional = userRepository.findById(id); + User user = optional.get(); + return user; + } + + /** + * 查询全部列表,并做分页 + * @param pageNum 开始页数 + * @param pageSize 每页显示的数据条数 + */ + @Override + public Iterator selectAll(int pageNum, int pageSize) { + //将参数传给这个方法就可以实现物理分页了,非常简单。 + Sort sort = new Sort(Sort.Direction.DESC, "id"); + Pageable pageable = new PageRequest(pageNum, pageSize, sort); + Page users = userRepository.findAll(pageable); + Iterator userIterator = users.iterator(); + return userIterator; + } +} + +``` +分页不止可以这样做,也可以在Controller层进行实例化和初始化然后将Pageable对象传给Service。 +当然也可以对分页进行封装,封装后的展示。 +``` + Page datas = userRepository.findAll(PageableTools.basicPage(1, 5, new SortDto("id"))); +``` +是不是很简洁。大家可以自己尝试一下。 + +#####6. 实现Controller +``` +package com.fantj.controller; + +/** + * Created by Fant.J. + */ +@RestController +@RequestMapping("/user") +public class UserController { + + @Autowired + private UserService userService; + + @RequestMapping(method = RequestMethod.GET,value = "/delete/{id}") + public void delete(@PathVariable("id")int id){ + userService.delete(id); + } + + @RequestMapping(method = RequestMethod.POST,value = "/insert") + public void insert(User user){ + userService.insert(user); + } + @RequestMapping(method = RequestMethod.POST,value = "/update/{id}") + public void update(@RequestParam User user){ + userService.update(user); + } + + @RequestMapping(method = RequestMethod.GET,value = "/{id}/select") + public User select(@PathVariable("id")int id){ + return userService.selectById(id); + } + + @RequestMapping(method = RequestMethod.GET,value = "/selectAll/{pageNum}/{pageSize}") + public List selectAll(@PathVariable("pageNum") int pageNum, @PathVariable("pageSize") int pageSize){ + Iterator userIterator = userService.selectAll(pageNum, pageSize); + List list = new ArrayList<>(); + while(userIterator.hasNext()){ + list.add(userIterator.next()); + } + return list; + } + +} + +``` + +![](https://upload-images.jianshu.io/upload_images/5786888-a880a5f69bca5442.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +### QueryDSL工具与上文的整合 + +可以参考恒宇少年的四篇文章: +* Maven环境下如何配置QueryDSL环境 https://www.jianshu.com/p/a22447c0897c +* 使用QueryDSL与SpringDataJPA实现单表普通条件查询 https://www.jianshu.com/p/4e9d8adaeeb2 +* 使用QueryDSL与SpringDataJPA完成Update&Delete https://www.jianshu.com/p/ac388c3c36c2 +* 使用QueryDSL与SpringDataJPA实现多表关联查询 https://www.jianshu.com/p/6199e76a5485 + +注意一点,目前springboot2.0 版本对JPA支持有误,如果你用springboot2 来配置querydsl,application启动类会运行不起来,正确的依赖包或者是配置类我还没有找到,希望有点子的朋友可以和我联系。 本人QQ:844072586 + +### 管理器EntityManager--执行数据库更新 +转载一篇博客: +https://blog.csdn.net/moridehuixiang/article/details/47005335 diff --git "a/springboot/spring-boot2-\346\225\264\345\220\210\357\274\210\345\233\233\357\274\211\345\256\232\346\227\266\344\273\273\345\212\241Scheduled----Quartz\345\271\266\346\214\201\344\271\205\345\214\226.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/SpringBoot2-\346\225\264\345\220\210-\345\256\232\346\227\266\344\273\273\345\212\241Scheduled-Quartz\345\271\266\346\214\201\344\271\205\345\214\226.md" similarity index 100% rename from "springboot/spring-boot2-\346\225\264\345\220\210\357\274\210\345\233\233\357\274\211\345\256\232\346\227\266\344\273\273\345\212\241Scheduled----Quartz\345\271\266\346\214\201\344\271\205\345\214\226.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/SpringBoot2-\346\225\264\345\220\210-\345\256\232\346\227\266\344\273\273\345\212\241Scheduled-Quartz\345\271\266\346\214\201\344\271\205\345\214\226.md" diff --git "a/springboot/SpringSecurity-\345\277\253\351\200\237\345\256\236\347\216\260\351\241\271\347\233\256.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/SpringSecurity-\345\277\253\351\200\237\345\256\236\347\216\260\351\241\271\347\233\256.md" similarity index 100% rename from "springboot/SpringSecurity-\345\277\253\351\200\237\345\256\236\347\216\260\351\241\271\347\233\256.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/SpringSecurity-\345\277\253\351\200\237\345\256\236\347\216\260\351\241\271\347\233\256.md" diff --git "a/springboot/Springboot----\347\224\250\346\233\264\344\274\230\351\233\205\347\232\204\346\226\271\345\274\217\345\217\221HTTP\350\257\267\346\261\202(RestTemplate\350\257\246\350\247\243).md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/Springboot-RestTemplate\350\257\246\350\247\243.md" similarity index 100% rename from "springboot/Springboot----\347\224\250\346\233\264\344\274\230\351\233\205\347\232\204\346\226\271\345\274\217\345\217\221HTTP\350\257\267\346\261\202(RestTemplate\350\257\246\350\247\243).md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/Springboot-RestTemplate\350\257\246\350\247\243.md" diff --git "a/springboot/Springboot-\351\241\271\347\233\256\344\270\255-Redis-\345\217\215\345\272\217\345\210\227\345\214\226\345\274\202\345\270\270.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/Springboot-\351\241\271\347\233\256\344\270\255-Redis-\345\217\215\345\272\217\345\210\227\345\214\226\345\274\202\345\270\270.md" similarity index 100% rename from "springboot/Springboot-\351\241\271\347\233\256\344\270\255-Redis-\345\217\215\345\272\217\345\210\227\345\214\226\345\274\202\345\270\270.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/Springboot-\351\241\271\347\233\256\344\270\255-Redis-\345\217\215\345\272\217\345\210\227\345\214\226\345\274\202\345\270\270.md" diff --git "a/springboot/spring-boot2-\346\225\264\345\220\210\357\274\210\344\270\200\347\273\255\357\274\211Druid\346\225\260\346\215\256\345\272\223\350\277\236\346\216\245\346\261\240\345\222\214FastJson.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/Springboot2-\346\225\264\345\220\210-Druid\346\225\260\346\215\256\345\272\223\350\277\236\346\216\245\346\261\240\345\222\214FastJson.md" similarity index 100% rename from "springboot/spring-boot2-\346\225\264\345\220\210\357\274\210\344\270\200\347\273\255\357\274\211Druid\346\225\260\346\215\256\345\272\223\350\277\236\346\216\245\346\261\240\345\222\214FastJson.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/Springboot2-\346\225\264\345\220\210-Druid\346\225\260\346\215\256\345\272\223\350\277\236\346\216\245\346\261\240\345\222\214FastJson.md" diff --git "a/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/Springboot2-\346\225\264\345\220\210-Mybatis.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/Springboot2-\346\225\264\345\220\210-Mybatis.md" new file mode 100644 index 0000000..6de6e92 --- /dev/null +++ "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/Springboot2-\346\225\264\345\220\210-Mybatis.md" @@ -0,0 +1,612 @@ +>整合Mybatis分为两种模式,一种是xml配置,一种是注解。(类似JPA) +我在这里重点放在xml配置上,因为如果想用注解的话,建议直接用jpa代替,因为Jpa有更成熟的CRUD接口更方便开发。我在后文中也会把注解方式说清楚。 + +大概介绍下流程: +1. 借助idea实现mybatis逆向工程 +2. 用xml配置实现整合 +3. 用cmd命令行实现mybatis逆向工程 +4. 用mapping.xml配置实现数据交互 +5. 用注解的方式实现数据交互 + + +首先我的开发环境: +jdk1.8+maven3+IDEA +###1. mybatis逆向攻城 +逆向工程方式很多,我目前接触到的就两种,一种是借助于ide开发工具,一种是在cmd中执行命令。(其实二者原理都一样,都是执行maven的generator命令,具体请看下文)。 +######1. 完善pom文件 +``` + + + 4.0.0 + + springboot-mybatis + springboot-mybatis + 0.0.1-SNAPSHOT + jar + + springboot-mybatis + Demo project for Spring Boot + + + springboot-integration + springboot-integration + 1.0-SNAPSHOT + + + + + + org.springframework.boot + spring-boot-starter-jdbc + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + 1.3.0 + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-starter-test + test + + + mysql + mysql-connector-java + 5.1.35 + + + + com.alibaba + druid + 1.0.11 + + + + com.alibaba + druid-spring-boot-starter + 1.1.0 + + + junit + junit + test + + + + com.github.pagehelper + pagehelper + 5.0.4 + + + + + + springboot-mybatis + + + + org.apache.maven.plugins + maven-surefire-plugin + + true + + + + org.mybatis.generator + mybatis-generator-maven-plugin + 1.3.2 + + true + true + + + + + + + +``` +######2. 逆向所需配置文件generatorConfig.xml +``` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
+
+``` +######3. 利用IDE创建逆向工程启动类 +![](https://upload-images.jianshu.io/upload_images/5786888-d1a0d44f736b173b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +######4. add一个Maven configuration +![](https://upload-images.jianshu.io/upload_images/5786888-69d75a2469eed01e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +![](https://upload-images.jianshu.io/upload_images/5786888-06dd1e5a1ecf042d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +######5. 我的数据库和表结构 +![](https://upload-images.jianshu.io/upload_images/5786888-df4403640a519a5e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +######6. application配置文件 + +``` +server: + port: 8080 + +spring: + datasource: + name: test + url: jdbc:mysql://127.0.0.1:3306/user + username: root + password: root + # druid 连接池 + type: com.alibaba.druid.pool.DruidDataSource + driver-class-name: com.mysql.jdbc.Driver + filters: stat + maxActive: 20 + initialSize: 1 + maxWait: 60000 + minIdle: 1 + timeBetweenEvictionRunsMillis: 60000 + minEvictableIdleTimeMillis: 300000 + validationQuery: select 'x' + testWhileIdle: true + testOnBorrow: false + testOnReturn: false + poolPreparedStatements: true + maxOpenPreparedStatements: 20 + +mybatis: + mapper-locations: classpath:mapping/*.xml + type-aliases-package: com.fant.model + +#pagehelper分页插件 +pagehelper: + helperDialect: mysql + reasonable: true + supportMethodsArguments: true + params: count=countSql +``` +**运行generator工程 自动生成代码** + +####生成的文件 +######User.java +``` +package com.fantj.model; + +import java.util.Date; + +public class User { + private Integer id; + + private String username; + + private Date birthday; + + private String sex; + + private String address; + + public Integer getId() { + return id; + } + + public void setId(Integer id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username == null ? null : username.trim(); + } + + public Date getBirthday() { + return birthday; + } + + public void setBirthday(Date birthday) { + this.birthday = birthday; + } + + public String getSex() { + return sex; + } + + public void setSex(String sex) { + this.sex = sex == null ? null : sex.trim(); + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address == null ? null : address.trim(); + } +} +``` +######UserMapper .java +``` +package com.fantj.mapper; + +import com.fantj.model.User; + +import java.util.List; + +public interface UserMapper { + int deleteByPrimaryKey(Integer id); + + int insert(User record); + + int insertSelective(User record); + + User selectByPrimaryKey(Integer id); + + int updateByPrimaryKeySelective(User record); + + int updateByPrimaryKey(User record); + + List selectAll(); +} +``` + +######UserMapper.xml +``` + + + + + + + + + + + + id, username, birthday, sex, address + + + + + delete from user + where id = #{id,jdbcType=INTEGER} + + + insert into user (id, username, birthday, + sex, address) + values (#{id,jdbcType=INTEGER}, #{username,jdbcType=VARCHAR}, #{birthday,jdbcType=DATE}, + #{sex,jdbcType=CHAR}, #{address,jdbcType=VARCHAR}) + + + insert into user + + + id, + + + username, + + + birthday, + + + sex, + + + address, + + + + + #{id,jdbcType=INTEGER}, + + + #{username,jdbcType=VARCHAR}, + + + #{birthday,jdbcType=DATE}, + + + #{sex,jdbcType=CHAR}, + + + #{address,jdbcType=VARCHAR}, + + + + + update user + + + username = #{username,jdbcType=VARCHAR}, + + + birthday = #{birthday,jdbcType=DATE}, + + + sex = #{sex,jdbcType=CHAR}, + + + address = #{address,jdbcType=VARCHAR}, + + + where id = #{id,jdbcType=INTEGER} + + + update user + set username = #{username,jdbcType=VARCHAR}, + birthday = #{birthday,jdbcType=DATE}, + sex = #{sex,jdbcType=CHAR}, + address = #{address,jdbcType=VARCHAR} + where id = #{id,jdbcType=INTEGER} + + +``` +### 修改启动类 + +逆向生成代码后,我们还需要在启动类上添加一个`@MapperScan("com.fantj.mapper")`注解,告诉我们的Mapper需要扫描的包,这样就不用每个Mapper上都添加@Mapper注解了。 + +``` +@SpringBootApplication +@MapperScan("com.fantj.mapper") +public class MybatisApplication { + + public static void main(String[] args) { + SpringApplication.run(MybatisApplication.class, args); + } + +} + +``` +### 完善controller和service +###### UserController.java +``` +@RestController +@RequestMapping("/user") +public class UserController { + + @Autowired + private UserService userService; + + @RequestMapping(method = RequestMethod.GET,value = "/delete/{id}") + public void delete(@PathVariable("id")int id){ + userService.delete(id); + } + + @RequestMapping(method = RequestMethod.POST,value = "/insert") + public void insert(User user){ + userService.insert(user); + } + @RequestMapping(method = RequestMethod.POST,value = "/update/{id}") + public void update(@RequestParam User user){ + userService.update(user); + } + + @RequestMapping(method = RequestMethod.GET,value = "/{id}/select") + public User select(@PathVariable("id")int id){ + return userService.selectById(id); + } + + @RequestMapping(method = RequestMethod.GET,value = "/selectAll/{pageNum}/{pageSize}") + public List selectAll(@PathVariable("pageNum") int pageNum, @PathVariable("pageSize") int pageSize){ + return userService.selectAll(pageNum,pageSize); + } + +} + +``` +###### UserService.java +``` +package com.fantj.service; + +import com.fantj.model.User; + +import java.util.List; + +public interface UserService { + /** 删除 */ + public void delete(int id); + /** 增加*/ + public void insert(User user); + /** 更新*/ + public int update(User user); + /** 查询单个*/ + public User selectById(int id); + /** 查询全部列表*/ + public List selectAll(int pageNum, int pageSize); +} + +``` +######UserServiceImpl .java +``` +@Service +public class UserServiceImpl implements UserService { + + @Autowired + private UserMapper userMapper; + + /** + * 删除 + * + * @param id + */ + @Override + public void delete(int id) { + userMapper.deleteByPrimaryKey(id); + } + + /** + * 增加 + * + * @param user + */ + @Override + public void insert(User user) { + userMapper.insert(user); + } + + /** + * 更新 + * + * @param user + */ + @Override + public int update(User user) { + return userMapper.updateByPrimaryKey(user); + } + + /** + * 查询单个 + * + * @param id + */ + @Override + public User selectById(int id) { + return userMapper.selectByPrimaryKey(id); + } + + /** + * 查询全部列表,并做分页 + * + * @param pageNum 开始页数 + * @param pageSize 每页显示的数据条数 + */ + @Override + public List selectAll(int pageNum, int pageSize) { + //将参数传给这个方法就可以实现物理分页了,非常简单。 + PageHelper.startPage(pageNum,pageSize); + return userMapper.selectAll(); + } +} + +``` +######浏览器访问127.0.0.1/user/selectAll/0/1 +![](https://upload-images.jianshu.io/upload_images/5786888-a76859c6557bcc09.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +说明我们成功了。 +######一个小甜点 +有人问,get方法可以直接从浏览器地址中的url来测试,那post请求怎么测试呢? +个人建议用postman工具,也可以写测试类用代码来完成测试。也可以使用idea的一个测试工具Test RESTful Web Service +![](https://upload-images.jianshu.io/upload_images/5786888-55cafea621c1eb87.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +![](https://upload-images.jianshu.io/upload_images/5786888-54b858f78cfd90a1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +#注解方式 +好了,配置方式我们介绍完了,我在这里稍微聊一聊注解开发方式,个人建议如果想用注解开发,直接用jpa,可以更方便自己的开发。 + +######mybatis java api :http://www.mybatis.org/mybatis-3/zh/java-api.html +######UserMapper.java +``` +package com.fantj.mapper; + +import com.fantj.model.User; +import org.apache.ibatis.annotations.*; + +import java.util.List; + +public interface UserMapper { + /*int deleteByPrimaryKey(Integer id); + + int insert(User record); + + int insertSelective(User record); + + User selectByPrimaryKey(Integer id); + + int updateByPrimaryKeySelective(User record); + + int updateByPrimaryKey(User record); + + List selectAll();*/ + //如果实例对象中的属性名和数据表中字段名不一致,用@Result注解进行说明映射关系,我在这里只是告诉你怎么写 + @Select("SELECT * FROM user") + @Results({ + @Result(property = "username", column = "username"), + @Result(property = "sex", column = "sex"), + @Result(property = "address",column = "address") + }) + List selectAll(); + + @Select("SELECT * FROM user WHERE id = #{id}") + @Results({ + @Result(property = "sex", column = "sex"), + @Result(property = "username", column = "username") + }) + User selectByPrimaryKey(int id); + + @Insert({"INSERT INTO user(username,birthday,sex,address}) VALUES(#{userName}, #{birthday}, #{sex},#{address})"}) + void insert(User user); + + @Update("UPDATE user SET userName=#{userName} WHERE id =#{id}") + int updateByPrimaryKey(User user); + + @Delete("DELETE FROM user WHERE id =#{id}") + int deleteByPrimaryKey(int id); +} +``` +######注解的一些解释 +* @Select 是查询类的注解,所有的查询均使用这个 +* @Result 修饰返回的结果集,关联实体类属性和数据库字段一一对应,如果实体类* 属性和数据库属性名保持一致,就不需要这个属性来修饰。 +* @Insert 插入数据库使用,直接传入实体类会自动解析属性到对应的值 +* @Update 负责修改,也可以直接传入对象 +* @delete 负责删除 + +######注意将application配置文件中的mybatis.mapper-locations:属性注视掉再启动项目。否则它会报错:Mapped Statements collection already contains value for com.fantj.mapper.UserMapper.insert 。意思是mapper中方法重复。 + +###最后一个甜点 +还有一个东西忘说了 -.-,就是用cmd来逆向生成代码。 +![](https://upload-images.jianshu.io/upload_images/5786888-6ce55ef0b8af8089.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +######声称语句 +`java -jar mybatis-generator-core-1.3.2.jar -configfile generator.xml -overwrite` +src目录请忽略,这是生成的目录。 +好了 干货都分享了,谢谢大家。0.0 diff --git a/springboot/springboot-&-springSecurity.md "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/springboot-&-springSecurity.md" similarity index 100% rename from springboot/springboot-&-springSecurity.md rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/springboot-&-springSecurity.md" diff --git "a/JVM/\346\237\245\347\234\213JVM\350\277\220\350\241\214\346\227\266\345\217\202\346\225\260.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/springboot-Redis.md" similarity index 100% rename from "JVM/\346\237\245\347\234\213JVM\350\277\220\350\241\214\346\227\266\345\217\202\346\225\260.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/springboot-Redis.md" diff --git "a/springboot/\345\246\202\344\275\225\344\275\277\347\224\250JPA\347\232\204UUID\344\270\273\351\224\256\347\224\237\346\210\220\347\255\226\347\225\245.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/\345\246\202\344\275\225\344\275\277\347\224\250JPA\347\232\204UUID\344\270\273\351\224\256\347\224\237\346\210\220\347\255\226\347\225\245.md" similarity index 100% rename from "springboot/\345\246\202\344\275\225\344\275\277\347\224\250JPA\347\232\204UUID\344\270\273\351\224\256\347\224\237\346\210\220\347\255\226\347\225\245.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/\345\246\202\344\275\225\344\275\277\347\224\250JPA\347\232\204UUID\344\270\273\351\224\256\347\224\237\346\210\220\347\255\226\347\225\245.md" diff --git "a/springboot/\345\246\202\344\275\225\347\224\250-SpringBoot-\344\274\230\351\233\205\347\232\204\345\206\231\344\273\243\347\240\201.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/\345\246\202\344\275\225\347\224\250-SpringBoot-\344\274\230\351\233\205\347\232\204\345\206\231\344\273\243\347\240\201.md" similarity index 100% rename from "springboot/\345\246\202\344\275\225\347\224\250-SpringBoot-\344\274\230\351\233\205\347\232\204\345\206\231\344\273\243\347\240\201.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/\345\246\202\344\275\225\347\224\250-SpringBoot-\344\274\230\351\233\205\347\232\204\345\206\231\344\273\243\347\240\201.md" diff --git "a/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/\346\211\213\346\234\272\351\202\256\347\256\261\346\255\243\345\210\231\350\277\207\346\273\244.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/\346\211\213\346\234\272\351\202\256\347\256\261\346\255\243\345\210\231\350\277\207\346\273\244.md" new file mode 100644 index 0000000..eb80e68 --- /dev/null +++ "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/\346\211\213\346\234\272\351\202\256\347\256\261\346\255\243\345\210\231\350\277\207\346\273\244.md" @@ -0,0 +1,24 @@ + +``` +/** + * Created by Fant.J. + */ +public class CheckFormat { + public static boolean isEmail(String email){ + String format = "^([a-z0-9A-Z]+[-|_|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$"; + Pattern regex = Pattern.compile(format); + Matcher matcher = regex.matcher(email); + return matcher.matches(); + } + + public static boolean isPhone(String phone){ + String format = "^(((13[0-9])|(14[579])|(15([0-3]|[5-9]))|(16[6])|(17[0135678])|(18[0-9])|(19[89]))\\d{8})$"; + Pattern regex = Pattern.compile(format); + Matcher matcher = regex.matcher(phone); + return matcher.matches(); + } +} +``` +**将format拿出来做成动态配置, 别在代码里写死** + +web -> catch -> db -> config -> format \ No newline at end of file diff --git "a/springboot/\350\267\250\345\237\237\351\227\256\351\242\230\350\247\243\345\206\263(\345\256\214\346\225\264).md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/\350\267\250\345\237\237\351\227\256\351\242\230\350\247\243\345\206\263(\345\256\214\346\225\264).md" similarity index 100% rename from "springboot/\350\267\250\345\237\237\351\227\256\351\242\230\350\247\243\345\206\263(\345\256\214\346\225\264).md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringBoot/\350\267\250\345\237\237\351\227\256\351\242\230\350\247\243\345\206\263(\345\256\214\346\225\264).md" diff --git "a/SpringCloud/\347\254\254\344\270\200\347\253\240\357\274\232SpringCloud\346\234\215\345\212\241\345\217\221\347\216\260\345\222\214\346\234\215\345\212\241\346\263\250\345\206\214.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringCloud/1. SpringCloud\346\234\215\345\212\241\345\217\221\347\216\260\345\222\214\346\234\215\345\212\241\346\263\250\345\206\214.md" similarity index 100% rename from "SpringCloud/\347\254\254\344\270\200\347\253\240\357\274\232SpringCloud\346\234\215\345\212\241\345\217\221\347\216\260\345\222\214\346\234\215\345\212\241\346\263\250\345\206\214.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringCloud/1. SpringCloud\346\234\215\345\212\241\345\217\221\347\216\260\345\222\214\346\234\215\345\212\241\346\263\250\345\206\214.md" diff --git "a/SpringCloud/\347\254\254\345\215\201\347\253\240\357\274\232SpringCloud-Zuul\350\267\257\347\224\261\345\231\250\345\222\214\350\277\207\346\273\244\345\231\250.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringCloud/10. SpringCloud-Zuul\350\267\257\347\224\261\345\231\250\345\222\214\350\277\207\346\273\244\345\231\250.md" similarity index 100% rename from "SpringCloud/\347\254\254\345\215\201\347\253\240\357\274\232SpringCloud-Zuul\350\267\257\347\224\261\345\231\250\345\222\214\350\277\207\346\273\244\345\231\250.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringCloud/10. SpringCloud-Zuul\350\267\257\347\224\261\345\231\250\345\222\214\350\277\207\346\273\244\345\231\250.md" diff --git "a/SpringCloud/\347\254\254\345\215\201\344\270\200\347\253\240\357\274\232SpringCloud-turbine-\345\205\210\347\251\272\347\235\200\357\274\214\345\220\216\351\235\242\350\241\245.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringCloud/11. SpringCloud-turbine-\345\205\210\347\251\272\347\235\200\357\274\214\345\220\216\351\235\242\350\241\245.md" similarity index 100% rename from "SpringCloud/\347\254\254\345\215\201\344\270\200\347\253\240\357\274\232SpringCloud-turbine-\345\205\210\347\251\272\347\235\200\357\274\214\345\220\216\351\235\242\350\241\245.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringCloud/11. SpringCloud-turbine-\345\205\210\347\251\272\347\235\200\357\274\214\345\220\216\351\235\242\350\241\245.md" diff --git "a/SpringCloud/\347\254\254\344\272\224\347\253\240\357\274\232SpringCloudRibbon&Hystrix\345\260\217\345\256\236\344\276\213.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringCloud/12. SpringCloudRibbon&Hystrix\345\260\217\345\256\236\344\276\213.md" similarity index 100% rename from "SpringCloud/\347\254\254\344\272\224\347\253\240\357\274\232SpringCloudRibbon&Hystrix\345\260\217\345\256\236\344\276\213.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringCloud/12. SpringCloudRibbon&Hystrix\345\260\217\345\256\236\344\276\213.md" diff --git "a/SpringCloud/\347\254\254\345\205\255\347\253\240\357\274\232SpringCloud-config-security-\357\274\210server-client\357\274\211.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringCloud/13. SpringCloud-config-security-\357\274\210server-client\357\274\211.md" similarity index 100% rename from "SpringCloud/\347\254\254\345\205\255\347\253\240\357\274\232SpringCloud-config-security-\357\274\210server-client\357\274\211.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringCloud/13. SpringCloud-config-security-\357\274\210server-client\357\274\211.md" diff --git "a/SpringCloud/\347\254\254\345\215\201\345\233\233\347\253\240\357\274\232SpringCloud-\344\276\235\350\265\226Git\344\273\223\345\272\223\345\256\236\347\216\260\351\205\215\347\275\256\345\257\271\347\247\260\345\212\240\345\257\206.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringCloud/14. SpringCloud-\344\276\235\350\265\226Git\344\273\223\345\272\223\345\256\236\347\216\260\351\205\215\347\275\256\345\257\271\347\247\260\345\212\240\345\257\206.md" similarity index 100% rename from "SpringCloud/\347\254\254\345\215\201\345\233\233\347\253\240\357\274\232SpringCloud-\344\276\235\350\265\226Git\344\273\223\345\272\223\345\256\236\347\216\260\351\205\215\347\275\256\345\257\271\347\247\260\345\212\240\345\257\206.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringCloud/14. SpringCloud-\344\276\235\350\265\226Git\344\273\223\345\272\223\345\256\236\347\216\260\351\205\215\347\275\256\345\257\271\347\247\260\345\212\240\345\257\206.md" diff --git "a/SpringCloud/\347\254\254\345\215\201\344\272\224\347\253\240\357\274\232SpringCloud-Config-\351\205\215\347\275\256\346\211\213\345\212\250\345\210\267\346\226\260\357\274\210\344\272\206\350\247\243\357\274\214\350\207\252\345\212\250\345\210\267\346\226\260\346\230\257\345\205\263\351\224\256\357\274\211.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringCloud/15. SpringCloud-Config-\351\205\215\347\275\256\346\211\213\345\212\250\345\210\267\346\226\260\357\274\210\344\272\206\350\247\243\357\274\214\350\207\252\345\212\250\345\210\267\346\226\260\346\230\257\345\205\263\351\224\256\357\274\211.md" similarity index 100% rename from "SpringCloud/\347\254\254\345\215\201\344\272\224\347\253\240\357\274\232SpringCloud-Config-\351\205\215\347\275\256\346\211\213\345\212\250\345\210\267\346\226\260\357\274\210\344\272\206\350\247\243\357\274\214\350\207\252\345\212\250\345\210\267\346\226\260\346\230\257\345\205\263\351\224\256\357\274\211.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringCloud/15. SpringCloud-Config-\351\205\215\347\275\256\346\211\213\345\212\250\345\210\267\346\226\260\357\274\210\344\272\206\350\247\243\357\274\214\350\207\252\345\212\250\345\210\267\346\226\260\346\230\257\345\205\263\351\224\256\357\274\211.md" diff --git "a/SpringCloud/\347\254\254\345\215\201\345\205\255\347\253\240\357\274\232SpringCloud-Config-\351\205\215\347\275\256\350\207\252\345\212\250\345\210\267\346\226\260.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringCloud/16. SpringCloud-Config-\351\205\215\347\275\256\350\207\252\345\212\250\345\210\267\346\226\260.md" similarity index 100% rename from "SpringCloud/\347\254\254\345\215\201\345\205\255\347\253\240\357\274\232SpringCloud-Config-\351\205\215\347\275\256\350\207\252\345\212\250\345\210\267\346\226\260.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringCloud/16. SpringCloud-Config-\351\205\215\347\275\256\350\207\252\345\212\250\345\210\267\346\226\260.md" diff --git "a/SpringCloud/\347\254\254\344\272\214\347\253\240\357\274\232SpringCloud-\345\260\206\345\276\256\346\234\215\345\212\241\346\263\250\345\206\214\350\207\263Eureka.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringCloud/2. SpringCloud-\345\260\206\345\276\256\346\234\215\345\212\241\346\263\250\345\206\214\350\207\263Eureka.md" similarity index 100% rename from "SpringCloud/\347\254\254\344\272\214\347\253\240\357\274\232SpringCloud-\345\260\206\345\276\256\346\234\215\345\212\241\346\263\250\345\206\214\350\207\263Eureka.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringCloud/2. SpringCloud-\345\260\206\345\276\256\346\234\215\345\212\241\346\263\250\345\206\214\350\207\263Eureka.md" diff --git "a/SpringCloud/\347\254\254\344\270\211\347\253\240\357\274\232Spring-Cloud-Config-Server-\347\232\204\351\205\215\347\275\256.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringCloud/3. Spring-Cloud-Config-Server-\347\232\204\351\205\215\347\275\256.md" similarity index 100% rename from "SpringCloud/\347\254\254\344\270\211\347\253\240\357\274\232Spring-Cloud-Config-Server-\347\232\204\351\205\215\347\275\256.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringCloud/3. Spring-Cloud-Config-Server-\347\232\204\351\205\215\347\275\256.md" diff --git "a/SpringCloud/\347\254\254\345\233\233\347\253\240\357\274\232SpringCloud-Config-Client\347\232\204\351\205\215\347\275\256.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringCloud/4. SpringCloud-Config-Client\347\232\204\351\205\215\347\275\256.md" similarity index 100% rename from "SpringCloud/\347\254\254\345\233\233\347\253\240\357\274\232SpringCloud-Config-Client\347\232\204\351\205\215\347\275\256.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringCloud/4. SpringCloud-Config-Client\347\232\204\351\205\215\347\275\256.md" diff --git "a/SpringCloud/\347\254\254\344\272\224\347\253\240\357\274\232SpringCloud\345\260\206config-server-and-client-\346\263\250\345\206\214\350\207\263-eureka.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringCloud/5. SpringCloud\345\260\206config-server-and-client-\346\263\250\345\206\214\350\207\263-eureka.md" similarity index 100% rename from "SpringCloud/\347\254\254\344\272\224\347\253\240\357\274\232SpringCloud\345\260\206config-server-and-client-\346\263\250\345\206\214\350\207\263-eureka.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringCloud/5. SpringCloud\345\260\206config-server-and-client-\346\263\250\345\206\214\350\207\263-eureka.md" diff --git "a/SpringCloud/\347\254\254\345\205\255\347\253\240\357\274\232SpringCloud-Ribbon-\350\207\252\345\256\232\344\271\211\346\234\215\345\212\241\347\253\257\345\217\243\350\256\277\351\227\256\347\255\226\347\225\245(Rule).md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringCloud/6. SpringCloud-Ribbon-\350\207\252\345\256\232\344\271\211\346\234\215\345\212\241\347\253\257\345\217\243\350\256\277\351\227\256\347\255\226\347\225\245(Rule).md" similarity index 100% rename from "SpringCloud/\347\254\254\345\205\255\347\253\240\357\274\232SpringCloud-Ribbon-\350\207\252\345\256\232\344\271\211\346\234\215\345\212\241\347\253\257\345\217\243\350\256\277\351\227\256\347\255\226\347\225\245(Rule).md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringCloud/6. SpringCloud-Ribbon-\350\207\252\345\256\232\344\271\211\346\234\215\345\212\241\347\253\257\345\217\243\350\256\277\351\227\256\347\255\226\347\225\245(Rule).md" diff --git "a/SpringCloud/\347\254\254\344\270\203\347\253\240\357\274\232SpringCloud-Feign\345\257\271hystrix\347\232\204\346\224\257\346\214\201.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringCloud/7. SpringCloud-Feign\345\257\271hystrix\347\232\204\346\224\257\346\214\201.md" similarity index 100% rename from "SpringCloud/\347\254\254\344\270\203\347\253\240\357\274\232SpringCloud-Feign\345\257\271hystrix\347\232\204\346\224\257\346\214\201.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringCloud/7. SpringCloud-Feign\345\257\271hystrix\347\232\204\346\224\257\346\214\201.md" diff --git "a/SpringCloud/\347\254\254\345\205\253\347\253\240\357\274\232SpringCloud-Feign\350\246\206\347\233\226\351\273\230\350\256\244\351\205\215\347\275\256\345\217\212\346\227\245\345\277\227\346\211\223\345\215\260.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringCloud/8. SpringCloud-Feign\350\246\206\347\233\226\351\273\230\350\256\244\351\205\215\347\275\256\345\217\212\346\227\245\345\277\227\346\211\223\345\215\260.md" similarity index 100% rename from "SpringCloud/\347\254\254\345\205\253\347\253\240\357\274\232SpringCloud-Feign\350\246\206\347\233\226\351\273\230\350\256\244\351\205\215\347\275\256\345\217\212\346\227\245\345\277\227\346\211\223\345\215\260.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringCloud/8. SpringCloud-Feign\350\246\206\347\233\226\351\273\230\350\256\244\351\205\215\347\275\256\345\217\212\346\227\245\345\277\227\346\211\223\345\215\260.md" diff --git "a/SpringCloud/\347\254\254\344\271\235\347\253\240\357\274\232SpringCloud-Feign\345\207\240\344\270\252\345\235\221.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringCloud/9. SpringCloud-Feign\345\207\240\344\270\252\345\235\221.md" similarity index 100% rename from "SpringCloud/\347\254\254\344\271\235\347\253\240\357\274\232SpringCloud-Feign\345\207\240\344\270\252\345\235\221.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringCloud/9. SpringCloud-Feign\345\207\240\344\270\252\345\235\221.md" diff --git "a/Spring-Security/SpringBoot-\346\225\264\345\220\210-Security\357\274\210\344\270\200\357\274\211\345\256\236\347\216\260\347\224\250\346\210\267\350\256\244\350\257\201\345\271\266\345\210\244\346\226\255\350\277\224\345\233\236json\350\277\230\346\230\257view.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringSecurity/1. SpringSecurity-\345\256\236\347\216\260\347\224\250\346\210\267\350\256\244\350\257\201\345\271\266\345\210\244\346\226\255\350\277\224\345\233\236json\350\277\230\346\230\257view.md" similarity index 100% rename from "Spring-Security/SpringBoot-\346\225\264\345\220\210-Security\357\274\210\344\270\200\357\274\211\345\256\236\347\216\260\347\224\250\346\210\267\350\256\244\350\257\201\345\271\266\345\210\244\346\226\255\350\277\224\345\233\236json\350\277\230\346\230\257view.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringSecurity/1. SpringSecurity-\345\256\236\347\216\260\347\224\250\346\210\267\350\256\244\350\257\201\345\271\266\345\210\244\346\226\255\350\277\224\345\233\236json\350\277\230\346\230\257view.md" diff --git "a/Spring-Security/SpringBoot-\346\225\264\345\220\210-Security\357\274\210\344\272\214\357\274\211\345\256\236\347\216\260\351\252\214\350\257\201\347\240\201\347\231\273\345\275\225.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringSecurity/2. SpringSecurity-\345\256\236\347\216\260\351\252\214\350\257\201\347\240\201\347\231\273\345\275\225.md" similarity index 100% rename from "Spring-Security/SpringBoot-\346\225\264\345\220\210-Security\357\274\210\344\272\214\357\274\211\345\256\236\347\216\260\351\252\214\350\257\201\347\240\201\347\231\273\345\275\225.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringSecurity/2. SpringSecurity-\345\256\236\347\216\260\351\252\214\350\257\201\347\240\201\347\231\273\345\275\225.md" diff --git "a/Spring-Security/SpringBoot-\346\225\264\345\220\210-oauth2\357\274\210\344\270\211\357\274\211\345\256\236\347\216\260-token-\350\256\244\350\257\201.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringSecurity/3. SpringSecurity-Oauth2\345\215\217\350\256\256\345\256\236\347\216\260token\350\256\244\350\257\201.md" similarity index 100% rename from "Spring-Security/SpringBoot-\346\225\264\345\220\210-oauth2\357\274\210\344\270\211\357\274\211\345\256\236\347\216\260-token-\350\256\244\350\257\201.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringSecurity/3. SpringSecurity-Oauth2\345\215\217\350\256\256\345\256\236\347\216\260token\350\256\244\350\257\201.md" diff --git "a/Spring-Security/SpringBoot-\346\225\264\345\220\210-oauth2\357\274\210\345\233\233\357\274\211\345\256\236\347\216\260-token-\346\214\201\344\271\205\345\214\226.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringSecurity/4. SpringSecurity-Oauth2\345\215\217\350\256\256\345\256\236\347\216\260token\346\214\201\344\271\205\345\214\226.md" similarity index 100% rename from "Spring-Security/SpringBoot-\346\225\264\345\220\210-oauth2\357\274\210\345\233\233\357\274\211\345\256\236\347\216\260-token-\346\214\201\344\271\205\345\214\226.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringSecurity/4. SpringSecurity-Oauth2\345\215\217\350\256\256\345\256\236\347\216\260token\346\214\201\344\271\205\345\214\226.md" diff --git "a/Spring-Security/SpringBoot-\346\225\264\345\220\210-oauth2\357\274\210\344\272\224\357\274\211\345\256\236\347\216\260-jwt-\345\217\212-\346\211\251\345\261\225.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringSecurity/5. SpringSecurity-Oauth2\345\215\217\350\256\256\345\256\236\347\216\260JWT\345\217\212\346\211\251\345\261\225.md" similarity index 100% rename from "Spring-Security/SpringBoot-\346\225\264\345\220\210-oauth2\357\274\210\344\272\224\357\274\211\345\256\236\347\216\260-jwt-\345\217\212-\346\211\251\345\261\225.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.1 \345\256\236\346\210\230\347\257\207/SpringSecurity/5. SpringSecurity-Oauth2\345\215\217\350\256\256\345\256\236\347\216\260JWT\345\217\212\346\211\251\345\261\225.md" diff --git "a/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.2 \346\272\220\347\240\201\347\257\207/Dubbo\346\272\220\347\240\201/Dubbo\346\272\220\347\240\201--SPI\346\211\251\345\261\225\346\234\272\345\210\266.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.2 \346\272\220\347\240\201\347\257\207/Dubbo\346\272\220\347\240\201/Dubbo\346\272\220\347\240\201--SPI\346\211\251\345\261\225\346\234\272\345\210\266.md" new file mode 100644 index 0000000..833c7d2 --- /dev/null +++ "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.2 \346\272\220\347\240\201\347\257\207/Dubbo\346\272\220\347\240\201/Dubbo\346\272\220\347\240\201--SPI\346\211\251\345\261\225\346\234\272\345\210\266.md" @@ -0,0 +1,413 @@ +>最好有是AOP、IOC、MVC框架基础和dubbo使用基础再阅读噢。 + +### 什么是SPI +>spi全称Service Provider Interface, 服务提供接口, 是Java提供的一套用来被第三方实现或者扩展的API。 + +没有使用过JDK SPI的可以百度一个例子自己跑下,这里只讲源码。 + +SPI的核心思想是解耦,基于接口、策略模式、配置实现实现类的动态扩展。 + +经验丰富的开发者肯定用过很多个`Driver`的实现类产品, 比如`oracle.jdbc.driver.OracleDriver`和`oracle.jdbc.OracleDriver`、还有ODBC(连接微软的那个数据库),以JDBC驱动为例,我们分析一下JDK是如何做到动态扩展的: + +在`mysql-connector-java-5.1.44.jar!/META-INF/services/java.sql.Driver`文件中: +``` +com.mysql.jdbc.Driver +com.mysql.fabric.jdbc.FabricMySQLDriver +``` +这两个都是`java.sql.Driver`的实现类的全限定名,我们看看哪里在加载这个`Driver.class`: +``` + private static void loadInitialDrivers() { + // 先从JVM启动参数里面获取到jdbc.drivers的值 (-Djdbc.drivers=xxx) + String drivers; + try { + drivers = AccessController.doPrivileged(new PrivilegedAction() { + public String run() { + return System.getProperty("jdbc.drivers"); + } + }); + } catch (Exception ex) { + drivers = null; + } + + AccessController.doPrivileged(new PrivilegedAction() { + public Void run() { + // JDK提供的加载SPI方式:ServiceLoader + ServiceLoader loadedDrivers = ServiceLoader.load(Driver.class); + Iterator driversIterator = loadedDrivers.iterator(); + try{ + while(driversIterator.hasNext()) { + driversIterator.next(); + } + } catch(Throwable t) { + // Do nothing + } + return null; + } + }); + + println("DriverManager.initialize: jdbc.drivers = " + drivers); + + if (drivers == null || drivers.equals("")) { + return; + } + String[] driversList = drivers.split(":"); + println("number of Drivers:" + driversList.length); + for (String aDriver : driversList) { + try { + println("DriverManager.Initialize: loading " + aDriver); + Class.forName(aDriver, true, + ClassLoader.getSystemClassLoader()); + } catch (Exception ex) { + println("DriverManager.Initialize: load failed: " + ex); + } + } + } +``` +我来解释下大概流程: +1. 加载JVM启动属性,拿到driverName +2. 用ServiceLoader加载Driver的所有实现类 +2. 根据jvm属性中的driver名字加载类: Class.forName(driverName) + +第一步和第三部大家应该都很熟悉。来探究下ServiceLoader的主干源码: +``` +#首先它实现了迭代类 +public final class ServiceLoader implements Iterable +所以从该对象中拿到的迭代器, 是定制的迭代器,我们再调用迭代器的hasNext方法,事实上调用的是: + + public boolean hasNext() { + if (acc == null) { + return hasNextService(); + } else { + PrivilegedAction action = new PrivilegedAction() { + public Boolean run() { return hasNextService(); } + }; + return AccessController.doPrivileged(action, acc); + } + } + private boolean hasNextService() { + if (nextName != null) { + return true; + } + if (configs == null) { + try { + // service就是我们传进来的接口类对象 + // PREFIX 是常量: private static final String PREFIX = "META-INF/services/"; + String fullName = PREFIX + service.getName(); + if (loader == null) + configs = ClassLoader.getSystemResources(fullName); + else + configs = loader.getResources(fullName); + } catch (IOException x) { + fail(service, "Error locating configuration files", x); + } + } + while ((pending == null) || !pending.hasNext()) { + if (!configs.hasMoreElements()) { + return false; + } + // 这里面会打开流去解析每一行, 把实现类全限定名加到names列表并返回其迭代器对象,用pending来接收 + pending = parse(service, configs.nextElement()); + } + nextName = pending.next(); + return true; + } +``` +由此我们可以逆向推出JDK的SPI使用方法: +1. SPI的配置文件路径:`META-INF/services/`,不能改变。 +2. 配置文件名以接口的全限定类名声明 +3. 配置文件中每一行是一个实现类的全限定类名 + +### Java SPI 的问题 +>Dubbo为什么不用java的SPI而选择自己再重写一套呢。 +1. JDK 标准的 SPI 会一次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源。 +2. 如果扩展点加载失败,连扩展点的名称都拿不到了。 +3. 不支持AOP和IOC。 + + +### Dubbo SPI +>Dubbo 的SPI 扩展从 JDK 标准的 SPI (Service Provider Interface) 扩展点发现机制加强而来。 改进了 JDK 标准的 SPI 的以上问题。 + +使用在这里不说了,没有用过的同学可以去官方看下:[http://dubbo.apache.org/zh-cn/docs/dev/SPI.html](http://dubbo.apache.org/zh-cn/docs/dev/SPI.html) + +``` + public static void main(String[] args) { + ExtensionLoader loader = ExtensionLoader.getExtensionLoader(Color.class); + Color yellow = loader.getExtension("yellow"); + System.out.println(yellow.getColor()); + } +``` +这是测试Dubbo SPI的一段代码,可以看到`getExtensionLoader`是dubbo SPI的入口,我们来看看dubbo是如何实现动态扩展的。 + +`Color yellow = loader.getExtension("yellow");`在这行代码中,经历了从配置的扩展类name到获得该类的实例化的过程,那我们从getExtension方法(类似spirng的getBean())看起。 + +``` + public T getExtension(String name) { + if (StringUtils.isEmpty(name)) { + throw new IllegalArgumentException("Extension name == null"); + } + if ("true".equals(name)) { + return getDefaultExtension(); + } + // 根据接口名获取它的持有类, 如果有从缓存中取, 没有就重新new + final Holder holder = getOrCreateHolder(name); + // 获取到实例 + Object instance = holder.get(); + // 双重检锁 获取和创建实例 + if (instance == null) { + synchronized (holder) { + instance = holder.get(); + if (instance == null) { + instance = createExtension(name); + holder.set(instance); + } + } + } + return (T) instance; + } +``` +假设现在什么都没有,则会到了createExtension(name);方法, 这个方法创建了扩展类的实例。 +``` + private T createExtension(String name) { + // 根据接口名获取所有实现类 + Class clazz = getExtensionClasses().get(name); + if (clazz == null) { + throw findException(name); + } + try { + // 从缓存中获取实例 + T instance = (T) EXTENSION_INSTANCES.get(clazz); + // 缓存(在时候就加载了)中没有, 调用反射创建 + if (instance == null) { + EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance()); + instance = (T) EXTENSION_INSTANCES.get(clazz); + } + // inject 依赖注入, 给实例注入需要的依赖对象 + injectExtension(instance); + // 从缓存拿接口的包装类(也是实现类) + Set> wrapperClasses = cachedWrapperClasses; + // 如果包装类不是空, 就遍历通过构造方法反射创建实例 + if (CollectionUtils.isNotEmpty(wrapperClasses)) { + for (Class wrapperClass : wrapperClasses) { + instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); + } + } + return instance; + } catch (Throwable t) { + throw new IllegalStateException("Extension instance (name: " + name + ", class: " + + type + ") couldn't be instantiated: " + t.getMessage(), t); + } + } +``` + +createExtension 方法包含了如下的步骤: +1. 通过 `getExtensionClasses `获取所有的实现类 +2. 通过反射`clazz.newInstance()`创建扩展对象 +3. `injectExtension(instance);`向拓展对象中注入依赖 +4. 实例化带接口参数的构造方法,将拓展对象包裹在相应的 Wrapper 对象中 + +从`wrapperClass.getConstructor(type).newInstance(instance);`代码中可以看到, Wrapper对象一定要有参数为接口类对象的构造方法。 + +那如何获取所有的扩展类的呢?我们探索一下`getExtensionClasses`方法: + +``` + private Map> getExtensionClasses() { + // 从缓存中拿map, 如果没有,就调用loadExtensionClasses + Map> classes = cachedClasses.get(); + if (classes == null) { + synchronized (cachedClasses) { + classes = cachedClasses.get(); + if (classes == null) { + classes = loadExtensionClasses(); + cachedClasses.set(classes); + } + } + } + return classes; + } +``` +这个方法很简单,从缓存中拿map, 如果没有,就调用loadExtensionClasses,看下loadExtensionClasses方法做了啥: +``` + private Map> loadExtensionClasses() { + cacheDefaultExtensionName(); + + Map> extensionClasses = new HashMap<>(); + loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName()); + loadDirectory(extensionClasses, DUBBO_INTERNAL_DIRECTORY, type.getName().replace("org.apache", "com.alibaba")); + loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName()); + loadDirectory(extensionClasses, DUBBO_DIRECTORY, type.getName().replace("org.apache", "com.alibaba")); + loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName()); + loadDirectory(extensionClasses, SERVICES_DIRECTORY, type.getName().replace("org.apache", "com.alibaba")); + return extensionClasses; + } +``` +1. 调用cacheDefaultExtensionName拿到默认实现类 +2. 调用loadDirectory从几个文件下寻找扩展类配置文件 + +先来看看cacheDefaultExtensionName是如何拿到默认实现类的: +``` + private void cacheDefaultExtensionName() { + // 获取SPI上的注解, 如果有设置value值,则取出, 这个值是默认实现类噢 + final SPI defaultAnnotation = type.getAnnotation(SPI.class); + if (defaultAnnotation != null) { + String value = defaultAnnotation.value(); + if ((value = value.trim()).length() > 0) { + String[] names = NAME_SEPARATOR.split(value); + if (names.length > 1) { + throw new IllegalStateException("More than 1 default extension name on extension " + type.getName() + + ": " + Arrays.toString(names)); + } + if (names.length == 1) { + cachedDefaultName = names[0]; + } + } + } + } +``` +这个方法很简单, 就是将SPI注解的value取出来来对应默认的实现类。 + +``` + private void loadDirectory(Map> extensionClasses, String dir, String type) { + String fileName = dir + type; + try { + Enumeration urls; + // 阿里封装的获取 classLoader 的方法 + ClassLoader classLoader = findClassLoader(); + if (classLoader != null) { + // jdk的方法, 与java spi一样的噢 + urls = classLoader.getResources(fileName); + } else { + urls = ClassLoader.getSystemResources(fileName); + } + // 拿到url后遍历调用loadResource方法 + if (urls != null) { + while (urls.hasMoreElements()) { + java.net.URL resourceURL = urls.nextElement(); + loadResource(extensionClasses, classLoader, resourceURL); + } + } + } catch (Throwable t) { + logger.error("Exception occurred when loading extension class (interface: " + + type + ", description file: " + fileName + ").", t); + } + } +``` +再具体下去大家自己看,我给描述下做了什么: +1. 用url获取到文件流 +2. 解析每一行, 将`=`号前面的赋值为name,后面的赋值为实现类的全限定路径 +3. 反射实例化每一行的实现类, 并调用loadClass方法实现对extensionClasses的最终赋值(它new了一个extensionClasses的map对象直到后面才进行赋值)。 + +``` + private void loadClass(Map> extensionClasses, java.net.URL resourceURL, Class clazz, String name) throws NoSuchMethodException { + if (!type.isAssignableFrom(clazz)) { + throw new IllegalStateException("Error occurred when loading extension class (interface: " + + type + ", class line: " + clazz.getName() + "), class " + + clazz.getName() + " is not subtype of interface."); + } + // 如果是Adaptive注解的类, 就把它放到cacheAdaptiveClass缓存中 + if (clazz.isAnnotationPresent(Adaptive.class)) { + cacheAdaptiveClass(clazz); + } else if (isWrapperClass(clazz)) { + cacheWrapperClass(clazz); + } else { + clazz.getConstructor(); + if (StringUtils.isEmpty(name)) { + name = findAnnotationName(clazz); + if (name.length() == 0) { + throw new IllegalStateException("No such extension name for the class " + clazz.getName() + " in the config " + resourceURL); + } + } + // 分割name, 因为配置文件中name可以有很多个,然后每个name对应赋值一个实现类对象(即使对象都相同) + String[] names = NAME_SEPARATOR.split(name); + if (ArrayUtils.isNotEmpty(names)) { + cacheActivateClass(clazz, names[0]); + for (String n : names) { + cacheName(clazz, n); + saveInExtensionClass(extensionClasses, clazz, n); + } + } + } + } +``` +1. 判断是否是Adaptive等注解的类, 就把它放到对应的cacheAdaptiveClass等缓存中 +2. 分割name, 因为配置文件中name可以有很多个,然后每个name对应赋值一个实现类对象(即使对象都相同) + +好了,到这里扩展类的加载就完成了, 接下来就到它的依赖注入(injectExtension方法): +``` + private T injectExtension(T instance) { + try { + // 你一定很好奇,如果objectFactory是空怎么办,其实再 + if (objectFactory != null) { + // 遍历实例所有的方法 + for (Method method : instance.getClass().getMethods()) { + // 如果是setter方法 + if (isSetter(method)) { + /** + * Check {@link DisableInject} to see if we need auto injection for this property + */ + if (method.getAnnotation(DisableInject.class) != null) { + continue; + } + Class pt = method.getParameterTypes()[0]; + if (ReflectUtils.isPrimitives(pt)) { + continue; + } + try { + // 获取setter方法属性 + String property = getSetterProperty(method); + // 根据第一个参数和类型 从List中获取实例 + Object object = objectFactory.getExtension(pt, property); + if (object != null) { + // 利用反射将获取的依赖注入到当前实例中 + method.invoke(instance, object); + } + } catch (Exception e) { + logger.error("Failed to inject via method " + method.getName() + + " of interface " + type.getName() + ": " + e.getMessage(), e); + } + } + } + } + } catch (Exception e) { + logger.error(e.getMessage(), e); + } + return instance; + } +``` +1. 如果objectFactory是空(证明还没有加载扩展类),则先加载扩展类到dubbo的IOC。 +2. 获取setter方法的参数和类型, 并从dubbo的IOC中拿到依赖对象的实例。 +3. 反射将依赖对象注入到当前实例中。 + +>objectFactory为什么一定不是空呢,因为再调用`ExtensionLoader.getExtensionLoader(Color.class);`方法的时候, dubbo已经对扩展类IOC进行初始化了, 详情看下面。 + +其实在调用getExtensionLoader的时候, 就把objectFactory实例化了, 而且初始化了IOC: +``` + public static ExtensionLoader getExtensionLoader(Class type) { + ... + if (loader == null) { + // 在这里new的对象 + EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader(type)); + loader = (ExtensionLoader) EXTENSION_LOADERS.get(type); + } + return loader; + } + + private ExtensionLoader(Class type) { + this.type = type; + objectFactory = (type == ExtensionFactory.class ? null : ExtensionLoader.getExtensionLoader(ExtensionFactory.class).getAdaptiveExtension()); + } +``` +它先后调用了这几个方法: +1. getAdaptiveExtension(从缓存里取,没有就调用下个方法) +2. createAdaptiveExtension(从缓存里取,没有就调用下个方法) +3. getAdaptiveExtensionClass 调用方法4, 然后调用方法5 +4. getExtensionClasses这个方法就和前面(`loader.getExtension("yellow");`)的一样了。 +5. createAdaptiveExtensionClass 方法,默认使用javassist生成代理字节码,然后用dubbo的Compiler解析成类并返回。 + +不知不觉中,dubbo的SPI还有自己独特的AOP和IOC源码就看完了。 +>你可能纳闷,我没看到AOP啊。 + +记得解析Wrapper那一块吗, dubbo用构造方法实例化Wrapper再将依赖注入,就完成了类似AOP的功能, 你可以再Wrapper里面定制你的逻辑。 + + +因为篇幅够长了,javassist部分足以拿出来重立一篇来讲,如果你读完感到很茫然,那就再读一遍,读源码,最重要的是主动接收的心态。多调试几遍,加油! + diff --git "a/Mybatis\346\272\220\347\240\201/\344\273\216Mybatis\346\272\220\347\240\201\345\210\206\346\236\220\345\205\266\345\267\245\344\275\234\345\216\237\347\220\206.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.2 \346\272\220\347\240\201\347\257\207/Mybatis\346\272\220\347\240\201/\344\273\216Mybatis\346\272\220\347\240\201\345\210\206\346\236\220\345\205\266\345\267\245\344\275\234\345\216\237\347\220\206.md" similarity index 92% rename from "Mybatis\346\272\220\347\240\201/\344\273\216Mybatis\346\272\220\347\240\201\345\210\206\346\236\220\345\205\266\345\267\245\344\275\234\345\216\237\347\220\206.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.2 \346\272\220\347\240\201\347\257\207/Mybatis\346\272\220\347\240\201/\344\273\216Mybatis\346\272\220\347\240\201\345\210\206\346\236\220\345\205\266\345\267\245\344\275\234\345\216\237\347\220\206.md" index 57f4916..aac3c2d 100644 --- "a/Mybatis\346\272\220\347\240\201/\344\273\216Mybatis\346\272\220\347\240\201\345\210\206\346\236\220\345\205\266\345\267\245\344\275\234\345\216\237\347\220\206.md" +++ "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.2 \346\272\220\347\240\201\347\257\207/Mybatis\346\272\220\347\240\201/\344\273\216Mybatis\346\272\220\347\240\201\345\210\206\346\236\220\345\205\266\345\267\245\344\275\234\345\216\237\347\220\206.md" @@ -1,6 +1,6 @@ ->Mybatis工作原理也是面试的一大考点,必须要对其非常清晰,这样才能怼回去。本文建立在Spring+SpringMVC+Mybatis整合的项目之上。 +>Mybatis将sql与代码分离、将查询的结果集与java对象自动映射, 大大方便了研发人员的开发和维护成本, 那它是如何做到的呢? -我将其工作原理分为六个部分: +我将其工作流程分为六个部分: 1. 读取核心配置文件并返回`InputStream`流对象。 2. 根据`InputStream`流对象解析出`Configuration`对象,然后创建`SqlSessionFactory`工厂对象 3. 根据一系列属性从`SqlSessionFactory`工厂中创建`SqlSession` @@ -8,6 +8,7 @@ 5. 对执行结果进行二次封装 6. 提交与事务 +接下来一步步跟进看下源码: 先给大家看看我的实体类: ``` @@ -117,7 +118,7 @@ public static InputStream getResourceAsStream(ClassLoader loader, String resourc } } ``` -获取到自身的ClassLoader对象,然后交给ClassLoader(lang包下的)来加载: +交给ClassLoader来加载, 如果classLoader为null, 则去"/" + resource 路径下装载流: ``` InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) { ClassLoader[] arr$ = classLoader; @@ -152,6 +153,10 @@ public SqlSessionFactory build(InputStream inputStream) { } ``` 这里要传入一个inputStream对象,就是将我们上一步获取到的InputStream对象传入。 + +>InputStream里面有什么数据呢? + +jdbc信息、和关联mapper.xml(BookMapper.xml) ``` public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { SqlSessionFactory var5; @@ -175,7 +180,7 @@ public SqlSessionFactory build(InputStream inputStream, String environment, Prop return var5; } ``` -如何解析的就大概说下,通过`Document`对象来解析,然后返回`InputStream`对象,然后交给`XMLConfigBuilder`构造成`org.apache.ibatis.session.Configuration`对象,然后交给build()方法构造程SqlSessionFactory: +可以看到, XMLConfigBuilder对象对输入流进行了解析, 如何解析的就大概说下,通过`Document`对象来解析,然后返回`InputStream`对象,然后交给`XMLConfigBuilder`构造成`org.apache.ibatis.session.Configuration`对象,然后交给build()方法构造程SqlSessionFactory: ``` public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config); @@ -356,6 +361,8 @@ public void setParameters(PreparedStatement ps) { ``` ### 5. 对查询结果二次封装 +>为什么要二次封装, 因为要将数据库返回数据转换成java对象类型。 + >在doUpdate方法中,解析生成完新的SQL后,然后执行var6 = handler.update(stmt);我们来看看它的源码。 ``` @@ -376,6 +383,8 @@ public int update(Statement statement) throws SQLException { 如果是query操作,返回的是一个ResultSet,mybatis将查询结果包装程`ResultSetWrapper`类型,然后一步步对应java类型赋值等...有兴趣的可以自己去看看。 ### 6. 提交与事务 +>mybatis的事务也是基于jdbc做的, 不了解jdbc事务可以去补一下这一块的知识。 + >最后,来看看commit()方法的源码。 @@ -427,6 +436,8 @@ public void commit(boolean required) throws SQLException { } ``` 最后调用JDBCTransaction的commit方法: +> 注意这里的this.connection, 保证了jdbc的同一个连接下的操作(同一次对话中完成事务)。这里面还是大有学问的, 有兴趣的可以继续钻研, 我的理解是开启连接依赖于java提供的javax.sql.DataSource对象, 然后后续是将这个connect对象一直持有着的直到销毁连接。 + ``` public void commit() throws SQLException { if (this.connection != null && !this.connection.getAutoCommit()) { diff --git "a/springboot\346\272\220\347\240\201/SpringBoot-starter\345\216\237\347\220\206\345\210\206\346\236\220.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.2 \346\272\220\347\240\201\347\257\207/SpringBoot\346\272\220\347\240\201/SpringBoot-starter\345\216\237\347\220\206\345\210\206\346\236\220.md" similarity index 100% rename from "springboot\346\272\220\347\240\201/SpringBoot-starter\345\216\237\347\220\206\345\210\206\346\236\220.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.2 \346\272\220\347\240\201\347\257\207/SpringBoot\346\272\220\347\240\201/SpringBoot-starter\345\216\237\347\220\206\345\210\206\346\236\220.md" diff --git "a/SpringMvc\346\272\220\347\240\201/\344\273\216SpringMvc\346\272\220\347\240\201\345\210\206\346\236\220\345\205\266\345\267\245\344\275\234\345\216\237\347\220\206.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.2 \346\272\220\347\240\201\347\257\207/SpringMVC\346\272\220\347\240\201/\344\273\216SpringMvc\346\272\220\347\240\201\345\210\206\346\236\220\345\205\266\345\267\245\344\275\234\345\216\237\347\220\206.md" similarity index 100% rename from "SpringMvc\346\272\220\347\240\201/\344\273\216SpringMvc\346\272\220\347\240\201\345\210\206\346\236\220\345\205\266\345\267\245\344\275\234\345\216\237\347\220\206.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.2 \346\272\220\347\240\201\347\257\207/SpringMVC\346\272\220\347\240\201/\344\273\216SpringMvc\346\272\220\347\240\201\345\210\206\346\236\220\345\205\266\345\267\245\344\275\234\345\216\237\347\220\206.md" diff --git "a/Spring\346\272\220\347\240\201/\345\275\273\345\272\225\346\220\236\346\207\202\344\276\235\350\265\226\346\263\250\345\205\245(\344\270\200)Bean\345\256\236\344\276\213\345\210\233\345\273\272\350\277\207\347\250\213.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.2 \346\272\220\347\240\201\347\257\207/Spring\346\272\220\347\240\201/1. Bean\345\256\236\344\276\213\345\210\233\345\273\272\350\277\207\347\250\213.md" similarity index 100% rename from "Spring\346\272\220\347\240\201/\345\275\273\345\272\225\346\220\236\346\207\202\344\276\235\350\265\226\346\263\250\345\205\245(\344\270\200)Bean\345\256\236\344\276\213\345\210\233\345\273\272\350\277\207\347\250\213.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.2 \346\272\220\347\240\201\347\257\207/Spring\346\272\220\347\240\201/1. Bean\345\256\236\344\276\213\345\210\233\345\273\272\350\277\207\347\250\213.md" diff --git "a/Spring\346\272\220\347\240\201/\345\275\273\345\272\225\346\220\236\346\207\202Bean\345\212\240\350\275\275.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.2 \346\272\220\347\240\201\347\257\207/Spring\346\272\220\347\240\201/2. Bean\345\212\240\350\275\275.md" similarity index 100% rename from "Spring\346\272\220\347\240\201/\345\275\273\345\272\225\346\220\236\346\207\202Bean\345\212\240\350\275\275.md" rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.2 \346\272\220\347\240\201\347\257\207/Spring\346\272\220\347\240\201/2. Bean\345\212\240\350\275\275.md" diff --git a/Java-NIO/2018-09-30.md "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.2 \346\272\220\347\240\201\347\257\207/Spring\346\272\220\347\240\201/3. \350\207\252\345\256\232\344\271\211\346\263\250\350\247\243\346\272\220\347\240\201\350\247\243\346\236\220.md" similarity index 100% rename from Java-NIO/2018-09-30.md rename to "3. \346\241\206\346\236\266\344\270\223\351\242\230/3.2 \346\272\220\347\240\201\347\257\207/Spring\346\272\220\347\240\201/3. \350\207\252\345\256\232\344\271\211\346\263\250\350\247\243\346\272\220\347\240\201\350\247\243\346\236\220.md" diff --git "a/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.2 \346\272\220\347\240\201\347\257\207/Spring\346\272\220\347\240\201/4. \344\270\244\347\247\215\345\212\250\346\200\201\344\273\243\347\220\206.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.2 \346\272\220\347\240\201\347\257\207/Spring\346\272\220\347\240\201/4. \344\270\244\347\247\215\345\212\250\346\200\201\344\273\243\347\220\206.md" new file mode 100644 index 0000000..a8c627d --- /dev/null +++ "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.2 \346\272\220\347\240\201\347\257\207/Spring\346\272\220\347\240\201/4. \344\270\244\347\247\215\345\212\250\346\200\201\344\273\243\347\220\206.md" @@ -0,0 +1,838 @@ +#### 什么是代理模式? +>如果用专业术语来解:为其他对象提供一种代理以控制对这个对象的访问。如果投影在生活中,它可以理解成`中介` `黄牛` `经纪人`等... + + + 解决的问题: + +>在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。`说白了就是在你代码前面插一段后面插一段`。 + +##### Java动态代理实现方式: + +1. JDK 自带的动态代理 +2. Cglib动态代理 + + +### 1. JDK 自带的动态代理 +>我以黄牛为例,黄牛刚开始了解该人需求,该人将信息(JAY演唱会门票)给予黄牛,黄牛给票。黄牛就是该买票人的代理。 +##### 1.1 People.java +>注意这必须是一个接口,原因往下看。 +``` +public interface People { + /** + * 交谈 + */ + void speak(); +} +``` +这个接口很简单,就是一个讲话的功能,但是它为什么必须是一个接口呢。因为在`HuangNiu`这个类中,`Proxy.newProxyInstance` 这个方法的实现需要接口,这一点我在`HuangNiu`类下解释的很清楚,往下看。 + + + +##### 1.2 HuangNiu.java +>黄牛代理类,获取到People信息后调用Proxy来生成一个新的代理类,它必须实现`InvocationHandler`接口,这个接口使得它可以通过`invoke`方法实现对真实角色(`People`)的代理访问。 +``` +public class HuangNiu implements InvocationHandler { + + private People people; + /** + * 获取被代理对象信息 + */ + public Object getInstance(People people){ + this.people = people; + Class clazz = people.getClass(); + System.out.println("没生成代理之前的class对象:"+clazz ); + return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this); + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + System.out.println("代理中..."); + method.invoke(people); + System.out.println("代理处理完毕,OK,请查收"); + return null; + } +} +``` + +在实例化`HuangNiu`这个对象的时候,我们调用了`Proxy`的`newProxyInstance`方法: + +``` +return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this); +``` +其中clazz是`People`的class对象。再来看看`newProxyInstance`的源码实现: +```java +public static Object newProxyInstance(ClassLoader loader, + Class[] interfaces, + InvocationHandler h){ + + final Class[] intfs = interfaces.clone(); + + //Look up or generate the designated proxy class. + + Class cl = getProxyClass0(loader, intfs); + + //获取代理类的构造函数对象 + //参数constructorParames为常量值:private static final Class[] constructorParams = { InvocationHandler.class }; + final Constructor cons = cl.getConstructor(constructorParames); + final InvocationHandler ih = h; + //根据代理类的构造函数对象来创建代理类对象 + return newInstance(cons, ih); +} +``` +`getProxyClass0`方法源码: +``` + private static Class getProxyClass0(ClassLoader loader, + Class... interfaces) { + if (interfaces.length > 65535) { + throw new IllegalArgumentException("interface limit exceeded"); + } + + // If the proxy class defined by the given loader implementing + // the given interfaces exists, this will simply return the cached copy; + // otherwise, it will create the proxy class via the ProxyClassFactory + return proxyClassCache.get(loader, interfaces); + } +``` +如果缓存中有该代理类,则取缓存,如果没有,则通过`ProxyClassFactory`来创建代理类。如果对如何生成代理类感兴趣则追踪下去即可。 + +我只取了核心代码和注释,可以看到JDK的动态代理实现是依据接口来重新生成一个新的代理类, + +**什么是新的代理类**? + +通俗点说就是综合和前后代理逻辑并重新生成一份`.class`文件来实现动态代理的类,下面也会具体说。 +##### 1.3 Me.java +>被代理对象,实现了`People`接口,给代理提供需要的信息来实现被代理。 +``` +public class Me implements People { + + private String name; + private String type; + + Me(String name, String type){ + this.name = name; + this.type = type; + } + @Override + public void speak() { + System.out.println("我叫"+name+", 我要一张"+type); + } +} +``` + +##### 1.4 Main.java +``` +public class Main { + public static void main(String[] args) { + People instance = (People)new HuangNiu().getInstance(new Me("Fantj", "JAY演唱会门票")); + instance.speak(); + System.out.println("生成代理对象后对象变成:"+instance.getClass()); + } +} +``` + +执行结果: +``` +没生成代理之前的class对象:class com.fantj.proxy.jdk.Me +代理中... +我叫Fantj, 我要一张JAY演唱会门票 +代理处理完毕,OK,请查收 +生成代理对象后对象变成:class com.sun.proxy.$Proxy0 +``` +为了证明事实上真的有代理类的产生,我在代理完成前和代理完成后分别打印出它的类信息,可以看出是不同的,可以猜想到代理中是有代理类产生的,这个代理类就是`$Proxy0`。 + +那既然知道了这个类的信息,我们就可以逆向生成这个`.class`文件来看看(在main方法后追加): +``` +/** + * 生成代码 + */ +try { + byte[] $Proxy0s = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{instance.getClass()}); + String path = Main.class.getResource("").toString(); +// System.out.println("get the path"+path); + FileOutputStream fileOutputStream = new FileOutputStream("$Proxy0.class"); + fileOutputStream.write($Proxy0s); + fileOutputStream.close(); +} catch (IOException e) { + e.printStackTrace(); +} +``` +>它默认生成在项目根目录下: +![](https://upload-images.jianshu.io/upload_images/5786888-f19a81f851c07945.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +我使用的IDEA工具会自动反编译.class文件为java代码,直接打开即刻看到源码,如果用别的工具的可以下载反编译工具来进行反编译。 +##### $Proxy0.class +``` +// +// Source code recreated from a .class file by IntelliJ IDEA +// (powered by Fernflower decompiler) +// + +import com.sun.proxy..Proxy0; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.lang.reflect.UndeclaredThrowableException; + +public final class $Proxy0 extends Proxy implements Proxy0 { + private static Method m1; + private static Method m5; + private static Method m2; + private static Method m4; + private static Method m11; + private static Method m13; + private static Method m0; + private static Method m10; + private static Method m12; + private static Method m6; + private static Method m9; + private static Method m3; + private static Method m7; + private static Method m8; + + public $Proxy0(InvocationHandler var1) throws { + super(var1); + } + + public final boolean equals(Object var1) throws { + try { + return (Boolean)super.h.invoke(this, m1, new Object[]{var1}); + } catch (RuntimeException | Error var3) { + throw var3; + } catch (Throwable var4) { + throw new UndeclaredThrowableException(var4); + } + } + + public final InvocationHandler getInvocationHandler(Object var1) throws IllegalArgumentException { + try { + return (InvocationHandler)super.h.invoke(this, m5, new Object[]{var1}); + } catch (RuntimeException | Error var3) { + throw var3; + } catch (Throwable var4) { + throw new UndeclaredThrowableException(var4); + } + } + + public final String toString() throws { + try { + return (String)super.h.invoke(this, m2, (Object[])null); + } catch (RuntimeException | Error var2) { + throw var2; + } catch (Throwable var3) { + throw new UndeclaredThrowableException(var3); + } + } + + public final Class getProxyClass(ClassLoader var1, Class[] var2) throws IllegalArgumentException { + try { + return (Class)super.h.invoke(this, m4, new Object[]{var1, var2}); + } catch (RuntimeException | Error var4) { + throw var4; + } catch (Throwable var5) { + throw new UndeclaredThrowableException(var5); + } + } + + public final Class getClass() throws { + try { + return (Class)super.h.invoke(this, m11, (Object[])null); + } catch (RuntimeException | Error var2) { + throw var2; + } catch (Throwable var3) { + throw new UndeclaredThrowableException(var3); + } + } + + public final void notifyAll() throws { + try { + super.h.invoke(this, m13, (Object[])null); + } catch (RuntimeException | Error var2) { + throw var2; + } catch (Throwable var3) { + throw new UndeclaredThrowableException(var3); + } + } + + public final int hashCode() throws { + try { + return (Integer)super.h.invoke(this, m0, (Object[])null); + } catch (RuntimeException | Error var2) { + throw var2; + } catch (Throwable var3) { + throw new UndeclaredThrowableException(var3); + } + } + + public final void wait() throws InterruptedException { + try { + super.h.invoke(this, m10, (Object[])null); + } catch (RuntimeException | InterruptedException | Error var2) { + throw var2; + } catch (Throwable var3) { + throw new UndeclaredThrowableException(var3); + } + } + + public final void notify() throws { + try { + super.h.invoke(this, m12, (Object[])null); + } catch (RuntimeException | Error var2) { + throw var2; + } catch (Throwable var3) { + throw new UndeclaredThrowableException(var3); + } + } + + public final Object newProxyInstance(ClassLoader var1, Class[] var2, InvocationHandler var3) throws IllegalArgumentException { + try { + return (Object)super.h.invoke(this, m6, new Object[]{var1, var2, var3}); + } catch (RuntimeException | Error var5) { + throw var5; + } catch (Throwable var6) { + throw new UndeclaredThrowableException(var6); + } + } + + public final void wait(long var1) throws InterruptedException { + try { + super.h.invoke(this, m9, new Object[]{var1}); + } catch (RuntimeException | InterruptedException | Error var4) { + throw var4; + } catch (Throwable var5) { + throw new UndeclaredThrowableException(var5); + } + } + + public final void speak() throws { + try { + super.h.invoke(this, m3, (Object[])null); + } catch (RuntimeException | Error var2) { + throw var2; + } catch (Throwable var3) { + throw new UndeclaredThrowableException(var3); + } + } + + public final boolean isProxyClass(Class var1) throws { + try { + return (Boolean)super.h.invoke(this, m7, new Object[]{var1}); + } catch (RuntimeException | Error var3) { + throw var3; + } catch (Throwable var4) { + throw new UndeclaredThrowableException(var4); + } + } + + public final void wait(long var1, int var3) throws InterruptedException { + try { + super.h.invoke(this, m8, new Object[]{var1, var3}); + } catch (RuntimeException | InterruptedException | Error var5) { + throw var5; + } catch (Throwable var6) { + throw new UndeclaredThrowableException(var6); + } + } + + static { + try { + m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); + m5 = Class.forName("com.sun.proxy.$Proxy0").getMethod("getInvocationHandler", Class.forName("java.lang.Object")); + m2 = Class.forName("java.lang.Object").getMethod("toString"); + m4 = Class.forName("com.sun.proxy.$Proxy0").getMethod("getProxyClass", Class.forName("java.lang.ClassLoader"), Class.forName("[Ljava.lang.Class;")); + m11 = Class.forName("com.sun.proxy.$Proxy0").getMethod("getClass"); + m13 = Class.forName("com.sun.proxy.$Proxy0").getMethod("notifyAll"); + m0 = Class.forName("java.lang.Object").getMethod("hashCode"); + m10 = Class.forName("com.sun.proxy.$Proxy0").getMethod("wait"); + m12 = Class.forName("com.sun.proxy.$Proxy0").getMethod("notify"); + m6 = Class.forName("com.sun.proxy.$Proxy0").getMethod("newProxyInstance", Class.forName("java.lang.ClassLoader"), Class.forName("[Ljava.lang.Class;"), Class.forName("java.lang.reflect.InvocationHandler")); + m9 = Class.forName("com.sun.proxy.$Proxy0").getMethod("wait", Long.TYPE); + m3 = Class.forName("com.sun.proxy.$Proxy0").getMethod("speak"); + m7 = Class.forName("com.sun.proxy.$Proxy0").getMethod("isProxyClass", Class.forName("java.lang.Class")); + m8 = Class.forName("com.sun.proxy.$Proxy0").getMethod("wait", Long.TYPE, Integer.TYPE); + } catch (NoSuchMethodException var2) { + throw new NoSuchMethodError(var2.getMessage()); + } catch (ClassNotFoundException var3) { + throw new NoClassDefFoundError(var3.getMessage()); + } + } +} +``` +里面大多是`Object`中的方法,还有我们定义的`speak()`方法,然后就是`静态代码块`(对变量进行初始化)。 + +当我们在Main中: +``` +People instance = (People)new HuangNiu().getInstance(new Me("Fantj", "JAY演唱会门票")); +instance.speak(); +``` +调用`instance.speak();`时,事实上就调用了`$Proxy0`中的`speak()`方法,然后在该方法中再调用父类`Proxy`的`invoke`方法: +``` +public final void speak() throws { + try { + super.h.invoke(this, m3, (Object[])null); + } catch (RuntimeException | Error var2) { + throw var2; + } catch (Throwable var3) { + throw new UndeclaredThrowableException(var3); + } +} +``` +其中,`super.h.invoke`的调用的是父类`Proxy`中的`InvocationHandler.invoke()`方法. + +#### 最后 +你一定也很好奇它的名字为什么是`$Proxy0`,也一定很好奇第二个代理类的命名。 + +`ProxyClassFactory`类中的代码中有体现: +``` +private static final class ProxyClassFactory + implements BiFunction[], Class> +{ + // prefix for all proxy class names + // 所有代理类的前缀 + private static final String proxyClassNamePrefix = "$Proxy"; + + // next number to use for generation of unique proxy class names + // 用java8新增的原子性AtomicLong类来做线程安全计数 + private static final AtomicLong nextUniqueNumber = new AtomicLong(); + + /* + * Choose a name for the proxy class to generate. + */ + // 拼接代理类 类名 + long num = nextUniqueNumber.getAndIncrement(); + String proxyName = proxyPkg + proxyClassNamePrefix + num; + ... + ... +``` + +### 2. Cglib实现动态代理 +>Cglib动态代理的实现原理和jdk基本一样,但是也有不同点。 + + +**不同点:** +1. jdk动态代理生成的代理类是继承自Proxy,实现你的被代理类所实现的接口,要求必须有接口。 +2. cglib动态代理生成的代理类是被代理者的子类,并且会重写父类的所有方法,要求该父类必须有空的构造方法,否则会报错:` Superclass has no null constructors but no arguments were given`,还有,private和final修饰的方法不会被子类重写。 + +**相同点:** +都是生成了新的代理类(字节码重组)。 + + +##### Me.java +``` +public class Me { + + private String name = "FantJ"; + private String type = "JAY演唱会门票"; + + Me() { + + } + + Me(String name, String type){ + this.name = name; + this.type = type; + } + public void speak() { + System.out.println("我叫"+name+", 我要一张"+type); + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } +} +``` + +##### HuangNiu.java +>该类要实现cglib包下的MethodInterceptor接口,从而实现对intercept的调用。 +``` +public class HuangNiu implements MethodInterceptor { + + /** + * 获取被代理对象信息 + * + */ + public Object getInstance(Object object){ + Enhancer enhancer = new Enhancer(); + enhancer.setSuperclass(object.getClass()); + System.out.println("生成代理对象前对象是:"+object.getClass()); + enhancer.setCallback(this); + return enhancer.create(); + } + + @Override + public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { + System.out.println("代理中..."); + methodProxy.invokeSuper(o, objects); +// methodProxy.invoke(o, objects); + System.out.println("代理处理完毕,OK,请查收"); + return null; + } +} +``` + +##### Main.java +``` +public class Main { + public static void main(String[] args) { + Me instance = (Me)new HuangNiu().getInstance(new Me("Fantj", "JAY演唱会门票")); +// Me instance = (Me)new HuangNiu().getInstance(new Me()); + instance.speak(); + System.out.println("生成代理对象后对象变成:"+instance.getClass()); + + /** + * 生成代码 + */ + try { + byte[] $Proxy0s = ProxyGenerator.generateProxyClass("Me$$EnhancerByCGLIB$$5d2d06a1", new Class[]{instance.getClass()}); + String path = com.fantj.proxy.jdk.Main.class.getResource("").toString(); +// System.out.println("get the path"+path); + FileOutputStream fileOutputStream = new FileOutputStream("Me$$EnhancerByCGLIB$$5d2d06a1.class"); + fileOutputStream.write($Proxy0s); + fileOutputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } +} +``` + +控制台结果: +``` +生成代理对象前对象是:class com.fantj.proxy.cglib.Me +代理中... +我叫FantJ, 我要一张JAY演唱会门票 +代理处理完毕,OK,请查收 +生成代理对象后对象变成:class com.fantj.proxy.cglib.Me$$EnhancerByCGLIB$$5d2d06a1 +``` + +同上,我们也把它生成的代理类反编译得到`Me$$EnhancerByCGLIB$$5d2d06a1.java`,原理和上面JDK代理类似,举一反三,这里不做介绍。 +##### Me$$EnhancerByCGLIB$$5d2d06a1.java +``` +// +// Source code recreated from a .class file by IntelliJ IDEA +// (powered by Fernflower decompiler) +// + +import com.fantj.proxy.cglib.Me..EnhancerByCGLIB..5d2d06a1; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.lang.reflect.UndeclaredThrowableException; +import net.sf.cglib.core.Signature; +import net.sf.cglib.proxy.Callback; +import net.sf.cglib.proxy.MethodProxy; + +public final class Me$$EnhancerByCGLIB$$5d2d06a1 extends Proxy implements 5d2d06a1 { + private static Method m1; + private static Method m6; + private static Method m7; + private static Method m2; + private static Method m15; + private static Method m21; + private static Method m23; + private static Method m0; + private static Method m20; + private static Method m13; + private static Method m12; + private static Method m3; + private static Method m10; + private static Method m22; + private static Method m8; + private static Method m11; + private static Method m14; + private static Method m4; + private static Method m19; + private static Method m9; + private static Method m18; + private static Method m16; + private static Method m17; + private static Method m5; + + public Me$$EnhancerByCGLIB$$5d2d06a1(InvocationHandler var1) throws { + super(var1); + } + + public final boolean equals(Object var1) throws { + try { + return (Boolean)super.h.invoke(this, m1, new Object[]{var1}); + } catch (RuntimeException | Error var3) { + throw var3; + } catch (Throwable var4) { + throw new UndeclaredThrowableException(var4); + } + } + + public final Object newInstance(Callback[] var1) throws { + try { + return (Object)super.h.invoke(this, m6, new Object[]{var1}); + } catch (RuntimeException | Error var3) { + throw var3; + } catch (Throwable var4) { + throw new UndeclaredThrowableException(var4); + } + } + + public final void setName(String var1) throws { + try { + super.h.invoke(this, m7, new Object[]{var1}); + } catch (RuntimeException | Error var3) { + throw var3; + } catch (Throwable var4) { + throw new UndeclaredThrowableException(var4); + } + } + + public final String toString() throws { + try { + return (String)super.h.invoke(this, m2, (Object[])null); + } catch (RuntimeException | Error var2) { + throw var2; + } catch (Throwable var3) { + throw new UndeclaredThrowableException(var3); + } + } + + public final Callback getCallback(int var1) throws { + try { + return (Callback)super.h.invoke(this, m15, new Object[]{var1}); + } catch (RuntimeException | Error var3) { + throw var3; + } catch (Throwable var4) { + throw new UndeclaredThrowableException(var4); + } + } + + public final Class getClass() throws { + try { + return (Class)super.h.invoke(this, m21, (Object[])null); + } catch (RuntimeException | Error var2) { + throw var2; + } catch (Throwable var3) { + throw new UndeclaredThrowableException(var3); + } + } + + public final void notifyAll() throws { + try { + super.h.invoke(this, m23, (Object[])null); + } catch (RuntimeException | Error var2) { + throw var2; + } catch (Throwable var3) { + throw new UndeclaredThrowableException(var3); + } + } + + public final int hashCode() throws { + try { + return (Integer)super.h.invoke(this, m0, (Object[])null); + } catch (RuntimeException | Error var2) { + throw var2; + } catch (Throwable var3) { + throw new UndeclaredThrowableException(var3); + } + } + + public final void wait() throws InterruptedException { + try { + super.h.invoke(this, m20, (Object[])null); + } catch (RuntimeException | InterruptedException | Error var2) { + throw var2; + } catch (Throwable var3) { + throw new UndeclaredThrowableException(var3); + } + } + + public final void CGLIB$SET_STATIC_CALLBACKS(Callback[] var1) throws { + try { + super.h.invoke(this, m13, new Object[]{var1}); + } catch (RuntimeException | Error var3) { + throw var3; + } catch (Throwable var4) { + throw new UndeclaredThrowableException(var4); + } + } + + public final void setCallbacks(Callback[] var1) throws { + try { + super.h.invoke(this, m12, new Object[]{var1}); + } catch (RuntimeException | Error var3) { + throw var3; + } catch (Throwable var4) { + throw new UndeclaredThrowableException(var4); + } + } + + public final String getName() throws { + try { + return (String)super.h.invoke(this, m3, (Object[])null); + } catch (RuntimeException | Error var2) { + throw var2; + } catch (Throwable var3) { + throw new UndeclaredThrowableException(var3); + } + } + + public final void setCallback(int var1, Callback var2) throws { + try { + super.h.invoke(this, m10, new Object[]{var1, var2}); + } catch (RuntimeException | Error var4) { + throw var4; + } catch (Throwable var5) { + throw new UndeclaredThrowableException(var5); + } + } + + public final void notify() throws { + try { + super.h.invoke(this, m22, (Object[])null); + } catch (RuntimeException | Error var2) { + throw var2; + } catch (Throwable var3) { + throw new UndeclaredThrowableException(var3); + } + } + + public final String getType() throws { + try { + return (String)super.h.invoke(this, m8, (Object[])null); + } catch (RuntimeException | Error var2) { + throw var2; + } catch (Throwable var3) { + throw new UndeclaredThrowableException(var3); + } + } + + public final void setType(String var1) throws { + try { + super.h.invoke(this, m11, new Object[]{var1}); + } catch (RuntimeException | Error var3) { + throw var3; + } catch (Throwable var4) { + throw new UndeclaredThrowableException(var4); + } + } + + public final void CGLIB$SET_THREAD_CALLBACKS(Callback[] var1) throws { + try { + super.h.invoke(this, m14, new Object[]{var1}); + } catch (RuntimeException | Error var3) { + throw var3; + } catch (Throwable var4) { + throw new UndeclaredThrowableException(var4); + } + } + + public final Object newInstance(Class[] var1, Object[] var2, Callback[] var3) throws { + try { + return (Object)super.h.invoke(this, m4, new Object[]{var1, var2, var3}); + } catch (RuntimeException | Error var5) { + throw var5; + } catch (Throwable var6) { + throw new UndeclaredThrowableException(var6); + } + } + + public final void wait(long var1) throws InterruptedException { + try { + super.h.invoke(this, m19, new Object[]{var1}); + } catch (RuntimeException | InterruptedException | Error var4) { + throw var4; + } catch (Throwable var5) { + throw new UndeclaredThrowableException(var5); + } + } + + public final void speak() throws { + try { + super.h.invoke(this, m9, (Object[])null); + } catch (RuntimeException | Error var2) { + throw var2; + } catch (Throwable var3) { + throw new UndeclaredThrowableException(var3); + } + } + + public final void wait(long var1, int var3) throws InterruptedException { + try { + super.h.invoke(this, m18, new Object[]{var1, var3}); + } catch (RuntimeException | InterruptedException | Error var5) { + throw var5; + } catch (Throwable var6) { + throw new UndeclaredThrowableException(var6); + } + } + + public final Callback[] getCallbacks() throws { + try { + return (Callback[])super.h.invoke(this, m16, (Object[])null); + } catch (RuntimeException | Error var2) { + throw var2; + } catch (Throwable var3) { + throw new UndeclaredThrowableException(var3); + } + } + + public final MethodProxy CGLIB$findMethodProxy(Signature var1) throws { + try { + return (MethodProxy)super.h.invoke(this, m17, new Object[]{var1}); + } catch (RuntimeException | Error var3) { + throw var3; + } catch (Throwable var4) { + throw new UndeclaredThrowableException(var4); + } + } + + public final Object newInstance(Callback var1) throws { + try { + return (Object)super.h.invoke(this, m5, new Object[]{var1}); + } catch (RuntimeException | Error var3) { + throw var3; + } catch (Throwable var4) { + throw new UndeclaredThrowableException(var4); + } + } + + static { + try { + m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); + m6 = Class.forName("com.fantj.proxy.cglib.Me$$EnhancerByCGLIB$$5d2d06a1").getMethod("newInstance", Class.forName("[Lnet.sf.cglib.proxy.Callback;")); + m7 = Class.forName("com.fantj.proxy.cglib.Me$$EnhancerByCGLIB$$5d2d06a1").getMethod("setName", Class.forName("java.lang.String")); + m2 = Class.forName("java.lang.Object").getMethod("toString"); + m15 = Class.forName("com.fantj.proxy.cglib.Me$$EnhancerByCGLIB$$5d2d06a1").getMethod("getCallback", Integer.TYPE); + m21 = Class.forName("com.fantj.proxy.cglib.Me$$EnhancerByCGLIB$$5d2d06a1").getMethod("getClass"); + m23 = Class.forName("com.fantj.proxy.cglib.Me$$EnhancerByCGLIB$$5d2d06a1").getMethod("notifyAll"); + m0 = Class.forName("java.lang.Object").getMethod("hashCode"); + m20 = Class.forName("com.fantj.proxy.cglib.Me$$EnhancerByCGLIB$$5d2d06a1").getMethod("wait"); + m13 = Class.forName("com.fantj.proxy.cglib.Me$$EnhancerByCGLIB$$5d2d06a1").getMethod("CGLIB$SET_STATIC_CALLBACKS", Class.forName("[Lnet.sf.cglib.proxy.Callback;")); + m12 = Class.forName("com.fantj.proxy.cglib.Me$$EnhancerByCGLIB$$5d2d06a1").getMethod("setCallbacks", Class.forName("[Lnet.sf.cglib.proxy.Callback;")); + m3 = Class.forName("com.fantj.proxy.cglib.Me$$EnhancerByCGLIB$$5d2d06a1").getMethod("getName"); + m10 = Class.forName("com.fantj.proxy.cglib.Me$$EnhancerByCGLIB$$5d2d06a1").getMethod("setCallback", Integer.TYPE, Class.forName("net.sf.cglib.proxy.Callback")); + m22 = Class.forName("com.fantj.proxy.cglib.Me$$EnhancerByCGLIB$$5d2d06a1").getMethod("notify"); + m8 = Class.forName("com.fantj.proxy.cglib.Me$$EnhancerByCGLIB$$5d2d06a1").getMethod("getType"); + m11 = Class.forName("com.fantj.proxy.cglib.Me$$EnhancerByCGLIB$$5d2d06a1").getMethod("setType", Class.forName("java.lang.String")); + m14 = Class.forName("com.fantj.proxy.cglib.Me$$EnhancerByCGLIB$$5d2d06a1").getMethod("CGLIB$SET_THREAD_CALLBACKS", Class.forName("[Lnet.sf.cglib.proxy.Callback;")); + m4 = Class.forName("com.fantj.proxy.cglib.Me$$EnhancerByCGLIB$$5d2d06a1").getMethod("newInstance", Class.forName("[Ljava.lang.Class;"), Class.forName("[Ljava.lang.Object;"), Class.forName("[Lnet.sf.cglib.proxy.Callback;")); + m19 = Class.forName("com.fantj.proxy.cglib.Me$$EnhancerByCGLIB$$5d2d06a1").getMethod("wait", Long.TYPE); + m9 = Class.forName("com.fantj.proxy.cglib.Me$$EnhancerByCGLIB$$5d2d06a1").getMethod("speak"); + m18 = Class.forName("com.fantj.proxy.cglib.Me$$EnhancerByCGLIB$$5d2d06a1").getMethod("wait", Long.TYPE, Integer.TYPE); + m16 = Class.forName("com.fantj.proxy.cglib.Me$$EnhancerByCGLIB$$5d2d06a1").getMethod("getCallbacks"); + m17 = Class.forName("com.fantj.proxy.cglib.Me$$EnhancerByCGLIB$$5d2d06a1").getMethod("CGLIB$findMethodProxy", Class.forName("net.sf.cglib.core.Signature")); + m5 = Class.forName("com.fantj.proxy.cglib.Me$$EnhancerByCGLIB$$5d2d06a1").getMethod("newInstance", Class.forName("net.sf.cglib.proxy.Callback")); + } catch (NoSuchMethodException var2) { + throw new NoSuchMethodError(var2.getMessage()); + } catch (ClassNotFoundException var3) { + throw new NoClassDefFoundError(var3.getMessage()); + } + } +} +``` diff --git "a/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.4 \346\211\213\345\206\231\345\256\236\347\216\260\347\257\207/\346\211\213\345\206\231\344\270\200\344\270\252Jedis\344\273\245\345\217\212JedisPool.md" "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.4 \346\211\213\345\206\231\345\256\236\347\216\260\347\257\207/\346\211\213\345\206\231\344\270\200\344\270\252Jedis\344\273\245\345\217\212JedisPool.md" new file mode 100644 index 0000000..0be81f7 --- /dev/null +++ "b/3. \346\241\206\346\236\266\344\270\223\351\242\230/3.4 \346\211\213\345\206\231\345\256\236\347\216\260\347\257\207/\346\211\213\345\206\231\344\270\200\344\270\252Jedis\344\273\245\345\217\212JedisPool.md" @@ -0,0 +1,469 @@ +Redis、Jedis的用途就不介绍了,不了解的可以先去官网:https://www.redis.net.cn/tutorial/3501.html 学习和使用。本文章着重讲解如何手动实现一个类似jedis的工具。 + +### 1. 源码探索 +经过源码的研究,可以发现Jedis的实现是基于Socket,可以从Jedis的set(key,value)方法开始追溯: +``` +public void set(String key, String value) { + this.set(SafeEncoder.encode(key), SafeEncoder.encode(value)); +} +public void set(byte[] key, byte[] value) { + this.sendCommand(Command.SET, new byte[][]{key, value}); +} +public static void sendCommand(RedisOutputStream os, Protocol.Command command, byte[]... args) { + sendCommand(os, command.raw, args); +} + +private static void sendCommand(RedisOutputStream os, byte[] command, byte[]... args) { + try { + os.write((byte)42); + os.writeIntCrLf(args.length + 1); + os.write((byte)36); + os.writeIntCrLf(command.length); + os.write(command); + os.writeCrLf(); + byte[][] var3 = args; + int var4 = args.length; + + for(int var5 = 0; var5 < var4; ++var5) { + byte[] arg = var3[var5]; + os.write((byte)36); + os.writeIntCrLf(arg.length); + os.write(arg); + os.writeCrLf(); + } + + } catch (IOException var7) { + throw new JedisConnectionException(var7); + } +} +``` +``` +System.out.println((char)42); +System.out.println(((char)36)); + +控制台: +* +$ +``` +可以大体的看出他得实现过程。把`sendCommand`方法翻译一下就是: +``` +os.write("*".getBytes()); +os.write(//数组长度\r\n); +os.write("$".getBytes()); +os.write(//命令长度\r\n) +os.write(命令\r\n); +然后进入循环写入: + $+参数长度\r\n + 参数\r\n +``` +整理一下:就是Redis的通信协议,官方文档:https://redis.io/topics/protocol 。 + +总结一下就是以下几点: +``` +*3 // 数据一共有三个数组 +//数组1 +$6 //下行为6个长度的字符串 +APPEND +//数组2 +$5 // 下行为5个长度的字符串 +fantj +//数组3 +$3 // 下行为3个长度的字符串 +666 +``` + +### 手写Jedis +通过源码可以看到,它在`Connection`类中进行sendCommend,那我们也一样: + +#### Connection.java +>负责与Redis的Server端建立连接并获取反馈信息。 +``` +package com.fantj.jedis.connect; + +import com.fantj.jedis.protocol.Protocol; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; + +/** + * 连接类 + * 在这里进行创建连接并处理IO请求,用inputStream进行数据回显, + * 提供OutputStream给协议层,以便让其给服务端发送命令 + */ +public class Connection { + private String host = "localhost"; + private int port = 6379; + private Socket socket; + private OutputStream outputStream; + private InputStream inputStream; + + public Connection() { + } + + public Connection(String host) { + this.host = host; + } + + public Connection(String host, int port) { + this.host = host; + this.port = port; + } + public Connection sendCommand(Protocol.Command cmd, byte[]... args) { + connect(); + Protocol.sendCommand(outputStream, cmd, args); + return this; + } + + private void connect() { + + try { + if (socket == null) { //IO复用 + socket = new Socket(host, port); + inputStream = socket.getInputStream(); + outputStream = socket.getOutputStream(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + /** + * 操作状态的返回 + * 比如:SET 操作成功返回 +OK + */ + public String getStatus() { + byte[] bytes = new byte[1024]; + try { + socket.getInputStream().read(bytes); + } catch (IOException e) { + e.printStackTrace(); + } + return new String(bytes); + } +} +``` +而真正的`sendCommend`实现是在`Protocol`类中实现(Jedis源码也是这样): + +#### Protocol.java +>负责提供RESP协议支持和拼接。 +``` +package com.fantj.jedis.protocol; + +import java.io.IOException; +import java.io.OutputStream; + +/** + * RESP协议 + * 详见; https://redis.io/topics/protocol + */ +public class Protocol { + // jedis后来将这些常量优化为byte,在os进行写出的时候对其进行char转型 + private static final String DOLLAR_BYTE = "$"; + private static final String ASTERISK_BYTE = "*"; + public static final byte PLUS_BYTE = 43; + public static final byte MINUS_BYTE = 45; + public static final byte COLON_BYTE = 58; + private static final String BLANK_BYTE = "\r\n"; + + /** + * 拼接RESP 并 发送write + */ + public static void sendCommand(OutputStream os,Protocol.Command cmd, byte[] ... args){ + // 1. 生成协议 *3 $3 SET $3 key $5 value + StringBuffer stringBuffer = new StringBuffer(); + // 1.1 数组长度 *3 + stringBuffer.append(ASTERISK_BYTE).append(args.length+1).append(BLANK_BYTE); + // 1.2 命令长度 $3 + stringBuffer.append(DOLLAR_BYTE).append(cmd.name().length()).append(BLANK_BYTE); + // 1.3 命令 SET / GET + stringBuffer.append(cmd).append(BLANK_BYTE); + for (byte[] arg: args){ + // 1.4 key/value 长度 + stringBuffer.append(DOLLAR_BYTE).append(arg.length).append(BLANK_BYTE); + // 1.5 key/value + stringBuffer.append(new String(arg)).append(BLANK_BYTE); + } + // 写出到服务端 + try { + os.write(stringBuffer.toString().getBytes()); + } catch (IOException e) { + e.printStackTrace(); + } + + } + + + /** + * 定义一个枚举类 存放命令 + */ + public static enum Command{ + SET , GET , KEYS, APPEND + }} +``` + +#### Client.java +>对外提供API。 +``` +package com.fantj.jedis.client; + +import com.fantj.jedis.connect.Connection; +import com.fantj.jedis.protocol.Protocol; + +/** + * 客户端 + * 给开发人员使用提供API + */ +public class Client { + private Connection connection; + public Client(String host, int port){ + connection = new Connection(host,port); + } + + public String set(String key, String value) { + connection.sendCommand(Protocol.Command.SET, key.getBytes(), value.getBytes()); + return connection.getStatus(); + } + + public String get(String key) { + connection.sendCommand(Protocol.Command.GET, key.getBytes()); + return connection.getStatus(); + } + + + public void set(String key, String value, String nx, String ex, int i) { + connection.sendCommand(Protocol.Command.SET, key.getBytes(), value.getBytes(), nx.getBytes(), ex.getBytes(), String.valueOf(i).getBytes()); + } + public void append(String key, String value){ + connection.sendCommand(Protocol.Command.APPEND, key.getBytes(),value.getBytes()); + } +} +``` + +#### Jedis.java +>对Client类的进一层封装,留给开发人员使用。 + +``` +package com.fantj.jedis.client; + +public class Jedis extends Client{ + + public Jedis(String host, int port) { + super(host, port); + } + + @Override + public String set(String key, String value) { + return super.set(key, value); + } + + @Override + public String get(String key) { + return super.get(key); + } + + @Override + public void set(String key, String value, String nx, String ex, int i) { + super.set(key, value, nx, ex, i); + } + + @Override + public void append(String key, String value) { + super.append(key, value); + } +} +``` + + +### 测试 +``` +/** + * 测试我们自己写的客户端 + */ +public class Main { + private Jedis client = new Jedis("www.xxx.top",6380); + @Test + public void set(){ + client.set("fantj","fantj"); + String result = client.get("fantj"); + System.out.println(result); + } + @Test + public void setNx(){ + client.set("fantj","fantj","NX","EX",10000); + String result = client.get("fantj"); + System.out.println(result); + } + @Test + public void append(){ +// client.append("fantj","-2019"); + String fantj = client.get("fantj"); + System.out.println(fantj); + } + @Test + public void testChar(){ + System.out.println((char)42); + System.out.println(((char)36)); + } +} +``` +测试都可以通过。 + +### 总结一下原理 + +假设我执行`append("fantj","666")`这个命令,那客户端的操作过程: + +1. RESP协议对命令的分析 +``` +*3 // 数据一共有三个数组 +//数组1 +$6 //下行为6个长度的字符串 +APPEND +//数组2 +$5 // 下行为5个长度的字符串 +fantj +//数组3 +$3 // 下行为3个长度的字符串 +666 +``` +2. 然后我们进行拼接 +``` +StringBuffer sb = new StringBuffer(); +// 注意每个类型表示完后都进行换行 +sb.append("*").append("3").append("\r\n"); +sb.append("$").append("6").append("\r\n"); +sb.append("APPEND").append("\r\n"); +sb.append("$").append("5").append("\r\n"); +sb.append("fantj").append("\r\n"); +sb.append("$").append("3").append("\r\n"); +sb.append("666").append("\r\n"); +``` +3. 然后用os进行写出 +``` +os.write(sb.toString().getBytes()); +``` + +### 连接池的实现 + +#### Pool.java +``` +/** + * 连接池契约 + */ +public interface Pool { + /** + * 初始化连接池 + * @param maxTotal 最大连接数 + * @param maxWaitMillis 最大等待时间 + */ + public void init(int maxTotal, long maxWaitMillis); + + /** + * 获取连接 + * @return 返回jedis对象 + */ + public Jedis getResource() throws Exception; + + /** + * 释放连接 + */ + public void release(T t); +} +``` + +#### JedisPool.java +>实现方式与ExcutorThreadPool工作流程类似,注释写的挺全的就不做详细解读了。 + +``` +package com.fantj.jedis.pool; + +import com.fantj.jedis.client.Jedis; + +import java.util.Queue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +public class JedisPool implements Pool{ + + public JedisPool(String url, int port) { + this.url = url; + this.port = port; + init(maxTotal,maxWaitMillis); + } + + public JedisPool(String url, int port, int maxTotal, long maxWaitMillis) { + this.url = url; + this.port = port; + this.maxTotal = maxTotal; + this.maxWaitMillis = maxWaitMillis; + } + + private String url; + private int port; + private int maxTotal = 20; + private long maxWaitMillis = 1000; + // 空闲的连接queue + private LinkedBlockingQueue idleWorkQueue = null; + private Queue activeWorkQueue = null; + // 当前连接数量 + private AtomicInteger count = new AtomicInteger(0); + @Override + public void init(int maxTotal, long maxWaitMillis) { + maxTotal = maxTotal; + maxWaitMillis = maxWaitMillis; + idleWorkQueue = new LinkedBlockingQueue<>(maxTotal); + activeWorkQueue = new LinkedBlockingQueue<>(maxTotal); + } + + @Override + public Jedis getResource() throws Exception { + Jedis jedis = null; + // 1. 记录开始时间,检测超时 + long startTime = System.currentTimeMillis(); + while (true){ + // 2. 从空闲队列中获取连接,如果拿到,一式两份存放到活动队列 + jedis = idleWorkQueue.poll(); + if (jedis != null){ + activeWorkQueue.offer(jedis); + return jedis; + } + // 3. 如果失败,判断池是否满,没满则创建 + if (count.get() < maxTotal){ + if (count.incrementAndGet() <= maxTotal){ + jedis = new Jedis(url,port); + activeWorkQueue.offer(jedis); + System.out.printf("创建了一个新的连接: %s \r\n", jedis.toString()); + return jedis; + }else { + count.decrementAndGet(); + } + } + // 4. 如果连接池满了,则在超时时间内进行等待 + try { + jedis = idleWorkQueue.poll(maxWaitMillis-(System.currentTimeMillis()-startTime), TimeUnit.MILLISECONDS); + if (jedis != null){ + activeWorkQueue.offer(jedis); + return jedis; + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + // 5. poll可能被中断,所以在这里再进行超时判断 + if (maxWaitMillis < (System.currentTimeMillis()-startTime)){ + throw new RuntimeException("JedisPool: jedis connect timeout"); + } + } + } + + @Override + public void release(Jedis jedis) { + if (activeWorkQueue.remove(jedis)){ + idleWorkQueue.offer(jedis); + } + } +} +``` + + +GitHub地址: https://github.com/fantj2016/easy-jedis \ No newline at end of file diff --git "a/\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241/Spring\344\272\213\345\212\241\347\256\241\347\220\206\357\274\210\344\270\200\357\274\211\345\277\253\351\200\237\345\205\245\351\227\250.md" "b/4. \345\210\206\345\270\203\345\274\217\344\270\223\351\242\230/4.1 \345\210\206\345\270\203\345\274\217\344\272\213\345\212\241/1. Spring\344\272\213\345\212\241\347\256\241\347\220\206-\345\277\253\351\200\237\345\205\245\351\227\250.md" similarity index 100% rename from "\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241/Spring\344\272\213\345\212\241\347\256\241\347\220\206\357\274\210\344\270\200\357\274\211\345\277\253\351\200\237\345\205\245\351\227\250.md" rename to "4. \345\210\206\345\270\203\345\274\217\344\270\223\351\242\230/4.1 \345\210\206\345\270\203\345\274\217\344\272\213\345\212\241/1. Spring\344\272\213\345\212\241\347\256\241\347\220\206-\345\277\253\351\200\237\345\205\245\351\227\250.md" diff --git "a/\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241/Spring\344\272\213\345\212\241\347\256\241\347\220\206\357\274\210\344\272\214\357\274\211\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241\347\256\241\347\220\206\344\271\213JTA\344\270\216\351\223\276\345\274\217\344\272\213\345\212\241.md" "b/4. \345\210\206\345\270\203\345\274\217\344\270\223\351\242\230/4.1 \345\210\206\345\270\203\345\274\217\344\272\213\345\212\241/2. Spring\344\272\213\345\212\241\347\256\241\347\220\206-\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241\347\256\241\347\220\206\344\271\213JTA\344\270\216\351\223\276\345\274\217\344\272\213\345\212\241.md" similarity index 100% rename from "\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241/Spring\344\272\213\345\212\241\347\256\241\347\220\206\357\274\210\344\272\214\357\274\211\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241\347\256\241\347\220\206\344\271\213JTA\344\270\216\351\223\276\345\274\217\344\272\213\345\212\241.md" rename to "4. \345\210\206\345\270\203\345\274\217\344\270\223\351\242\230/4.1 \345\210\206\345\270\203\345\274\217\344\272\213\345\212\241/2. Spring\344\272\213\345\212\241\347\256\241\347\220\206-\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241\347\256\241\347\220\206\344\271\213JTA\344\270\216\351\223\276\345\274\217\344\272\213\345\212\241.md" diff --git "a/4. \345\210\206\345\270\203\345\274\217\344\270\223\351\242\230/4.1 \345\210\206\345\270\203\345\274\217\344\272\213\345\212\241/\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241\345\216\237\347\220\206\347\256\200\344\273\213" "b/4. \345\210\206\345\270\203\345\274\217\344\270\223\351\242\230/4.1 \345\210\206\345\270\203\345\274\217\344\272\213\345\212\241/\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241\345\216\237\347\220\206\347\256\200\344\273\213" new file mode 100644 index 0000000..44d2e34 --- /dev/null +++ "b/4. \345\210\206\345\270\203\345\274\217\344\270\223\351\242\230/4.1 \345\210\206\345\270\203\345\274\217\344\272\213\345\212\241/\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241\345\216\237\347\220\206\347\256\200\344\273\213" @@ -0,0 +1,293 @@ +# 1. 单数据源事务实现 + +工作上,对于只有一个数据源的操作中,我们仅仅用@Transaction 注解即可实现多个操作的事务控制。这个注解是spring提供的,与此同时,spring提供了多种事务管理器,有相应的依赖就会自动完成配置,我们使用的时候,只需要将我们想要的事务实现对象注入到 PlatformTransactionManager 里,就可以实现多种事务控制,也可以自己遵循spring的事务管理接口规范,自己写一个事务实现并注入: + +![image.png](https://upload-images.jianshu.io/upload_images/5786888-d4c40b7d02d3670e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +spring是如何自动识别并装配事务呢。以jdbc事务为例: +#### 扩展:jdbc事务自动配置 +``` +@Configuration +@ConditionalOnClass({ JdbcTemplate.class, PlatformTransactionManager.class }) +@AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE) +@EnableConfigurationProperties(DataSourceProperties.class) +public class DataSourceTransactionManagerAutoConfiguration { + + @Configuration + @ConditionalOnSingleCandidate(DataSource.class) + static class DataSourceTransactionManagerConfiguration { + + private final DataSource dataSource; + + private final TransactionManagerCustomizers transactionManagerCustomizers; + + DataSourceTransactionManagerConfiguration(DataSource dataSource, + ObjectProvider transactionManagerCustomizers) { + this.dataSource = dataSource; + this.transactionManagerCustomizers = transactionManagerCustomizers + .getIfAvailable(); + } + + @Bean + @ConditionalOnMissingBean(PlatformTransactionManager.class) + public DataSourceTransactionManager transactionManager( + DataSourceProperties properties) { + DataSourceTransactionManager transactionManager = new DataSourceTransactionManager( + this.dataSource); + if (this.transactionManagerCustomizers != null) { + this.transactionManagerCustomizers.customize(transactionManager); + } + return transactionManager; + } + + } + +} +``` +@AutoConfigureOrder这个注解是springboot的新特性, 它修饰的类可以在容器启动的时候被spring扫描并配置注入。再加上@Conditional 家族的组合拳,使得jdbc的DataSourceTransactionManager 事务管理可以注入到IOC容器。其他的数据源也类似。 + + +#### 1.1 spring单机事务实战 + +声明式事务:@Transactional 一个注解就好了, spring通过切面来实现事务控制。 + +编程式事务: + +``` +public void transferAccount() { + Connection conn = null; + Statement stmt = null; + try{ + conn = getDataSource().getConnection(); + // 将自动提交设置为 false, + //若设置为 true 则数据库将会把每一次数据更新认定为一个事务并自动提交 + conn.setAutoCommit(false); + + stmt = conn.createStatement(); + // 将 A 账户中的金额减少 500 + stmt.execute("\ + update t_account set amount = amount - 500 where account_id = 'A'"); + // 将 B 账户中的金额增加 500 + stmt.execute("\ + update t_account set amount = amount + 500 where account_id = 'B'"); + + // 提交事务 + conn.commit(); + // 事务提交:转账的两步操作同时成功 + } catch(SQLException sqle){ + try{ + // 发生异常,回滚在本事务中的操作 + conn.rollback(); + // 事务回滚:转账的两步操作完全撤销 + stmt.close(); + conn.close(); + }catch(Exception ignore){ + + } + sqle.printStackTrace(); + } + } +``` +上面的两种方式,大家应该是再熟悉不过了,编程式事务就是声明式事务的源码。在编程式事务中可以看到,`conn = getDataSource().getConnection(); `会在执行sql前调用,这个就是用来获取jdbc链接的,思考一下如果我开多个线程去调用它内存地址会是一样的吗,大家可以在代码里面试试。那为什么会不一样呢,结合mysql事务机制仔细想一下,相信你一定题录灌顶。 + + +#### 思考:单数据源事务的关键是同一连接,spring是如何保证的 + +> 我们知道,mysql不能用两个黑框连接来实现同一个事务,假如有多个sql操作,分别操作同库中不同的表,要完成这个事务,必须要保证这些操作在同一个sql连接中,spring是如何做的呢。 + +以JDBC驱动为例,JDBC的dataSource连接是通过org.springframework.jdbc.datasource.DataSourceUtils来获取,在DataSourceUtils获取连接的时候,会将Connection和当前线程做绑定(调用TransactionSynchronizationManager的bindResource(dataSource, holderToUse);方法),在获取连接时,从ThreadLocal中拿到对应的Connection(TransactionSynchronizationManager中提供 getResource方法),然后在同一连接下继续执行事务。 + + +#### 哪些场景不应该用普通事务 + +> 当容器内存在多个数据源时,如果一个应用容器中存在多个不同的数据源,通常我们会创建多个数据源的事务管理器. + +比如一个Java应用要连数据库,同时也要连接MQ,这种情况下进行事务管理,我们就会创建一个DataSourceTransactionManager和一个JMSTransactionManager分别来对数据库事务和MQ事务进行管理. + +在一次请求服务的过程中使用到了2个数据源,那么事务的处理可能是这样的流程: + +``` +// 进行如下操作 +1. start message transaction +2. receive message +3. start database transaction +4. update database +5. commit database transaction +// 我出错了 +6. commit messaging transaction +``` + +在第5步和第6步之间如果出现了异常,那么由于第5步已经提交了,不能进行回滚操作,数据库已经插入了,但是消息还没有被消费掉,这样就无法满足事务的一致性了.所以就需要用分布式事务来保证其一致性。 + + +# 2. 分布式事务 + +> 跨数据源的事务。 + +### 2.1 XA协议简介 + +XA是由X/Open组织提出的分布式事务的架构(或者叫协议),是一种通用的规范。XA规范的基础是两阶段提交协议(2PC)。Oracle, Sybase, DB2, SQL Server等大型数据库支持XA。 + +XA协议内容是对底层事务资源的抽象,定义了分布式事务处理过程中事务管理器和资源管理器之间的协议,各事务资源提供商(如 JDBC 驱动,JMS)将提供此接口的实现。使用此接口,开发人员可以通过自己的编程实现分布式事务处理,但这些通常都是由应用服务器实现的(服务器自带实现更加高效,稳定) + +Mysql也有XA的实现,获取了一个普通的链接Connection之后,封装成了MysqlXAConnection + +#### XA常见问题 +##### XA二阶段提交 + +1. 性能问题 +XA协议遵循强一致性。在事务执行过程中,各个节点占用着数据库资源,只有当所有节点准备完毕,事务协调者才会通知提交,参与者提交后释放资源。这样的过程有着非常明显的性能问题。 + +2. 协调者单点故障问题 +事务协调者是整个XA模型的核心,一旦事务协调者节点挂掉,参与者收不到提交或是回滚通知,参与者会一直处于中间状态无法完成事务。 + +3. 丢失消息导致的不一致问题。 +在XA协议的第二个阶段,如果发生局部网络问题,一部分事务参与者收到了提交消息,另一部分事务参与者没收到提交消息,那么就导致了节点之间数据的不一致。 + +##### XA三阶段提交 +XA三阶段提交在两阶段提交的基础上增加了CanCommit阶段,并且引入了超时机制。一旦事物参与者迟迟没有接到协调者的commit请求,会自动进行本地commit。这样有效解决了协调者单点故障的问题。但是性能问题和不一致的问题仍然没有根本解决。 + +### 2.2 JTA事务介绍 + +Java 事务编程接口(JTA:Java Transaction API),是J2EE的编程接口规范,它是XA协议的JAVA实现。 + +``` +public void transferAccount() { + UserTransaction userTx = null; + try{ + // 获得 Transaction 管理对象 + userTx = (UserTransaction)getContext().lookup("\ + java:comp/UserTransaction"); + // 启动事务 + userTx.begin(); + // 将 A 账户中的金额减少 500 + stmtA.execute(sql1); + // 将 B 账户中的金额增加 500 + stmtB.execute(sql2); + // 提交事务 + userTx.commit(); + // 事务提交:转账的两步操作同时成功(数据库 A 和数据库 B 中的数据被同时更新) + } catch(SQLException sqle){ + // 发生异常,回滚在本事务中的操纵 + userTx.rollback(); + // 事务回滚:转账的两步操作完全撤销 + //( 数据库 A 和数据库 B 中的数据更新被同时撤销) + } catch(Exception ne){ + e.printStackTrace(); + } + } +``` + +面向开发人员的接口为 UserTransaction: + + +* begin() – 开始一个分布式事务,(在后台 TransactionManager 会创建一个 Transaction 事务对象并把此对象通过 ThreadLocale 关联到当前线程上 ) +* commit() – 提交事务(在后台 TransactionManager 会从当前线程下取出事务对象并把此对象所代表的事务提交) +* rollback() – 回滚事务(在后台 TransactionManager 会从当前线程下取出事务对象并把此对象所代表的事务回滚) +* getStatus() – 返回关联到当前线程的分布式事务的状态 (Status 对象里边定义了所有的事务状态,感兴趣的读者可以参考 API 文档 ) +* setRollbackOnly() – 标识关联到当前线程的分布式事务将被回滚 + + +UserTransaction是对Transaction接口的扩展,上面的这些操作最终都会落到真正的Transaction上来实现commit和rollback,这里的Transaction 操作的就是实现了XA协议的数据源。 + +不同的是,JTA的连接管理是通过TransactionManager 来实现,调用 UserTransaction.begin() 方法时 TransactionManager 会创建一个 Transaction 事务对象并把此对象通过 ThreadLocale 关联到当前线程上 + +#### 思考:如何记录同一个切面下 不同事务 属于同一个分布式事务呢? + +支持事务的数据源与普通的数据源是不同的,它实现了额外的 XADataSource 接口。 + +![image.png](https://upload-images.jianshu.io/upload_images/5786888-d2bb5fa2960a8404.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +Xid 类用来标识事务,可以把 Xid 想象成事务的一个标志符,每次在新事务创建是都会为事务分配一个 Xid,Xid 包含三个元素:formatID、gtrid(全局事务标识符)和 bqual(分支修饰词标识符)。 formatID 通常是零,这意味着你将使用 OSI CCR(Open Systems Interconnection Commitment, Concurrency 和 Recovery 标准)来命名;如果你要使用另外一种格式,那么 formatID 应该大于零,-1 值意味着 Xid 为无效。gtrid 和 bqual 分别包含 64 个字节二进制码来分别标识全局事务和分支事务, gtrid 和 bqual 必须是全局唯一的。 + +在事务被提交时,Transaction 对象会收集所有被当前事务包含的 XAResource 资源,然后调用资源的提交方法,如果有失败就全部rollback。 + +JTA一般和Atomikos框架配合使用 + +### 2.3 TCC事务介绍 + +> 是基于补偿型事务的AP系统的一种实现, 具有最终一致性。原理是2PC两阶段提交。 + +TCC(Try/Confirm/Cancel)事务机制相对于传统事务机制(X/Open XA),其特征在于它不依赖资源管理器(RM)对XA的支持,而是通过对(由业务系统提供的)业务逻辑的调度来实现分布式事务。 + +国内开源的TCC框架:ByteTCC、Himly、TCC-transaction + +##### TCC原理图: + +![image.png](https://upload-images.jianshu.io/upload_images/5786888-7ef17ab1538782a0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +TCC是2PC的思想在应用层面的落地实现,使用TCC实现事务,需要每个接口中都有try、Confirm、Cancel接口,并保证这三个接口的幂等性。需要开发很大量的代码,花费很高的成本,一般不会使用它。 + +##### TCC优缺点 +优点:可以让应用自己定义数据操作的粒度,使得降低锁冲突、提高吞吐量成为可能。 +缺点:对应用的侵入性非常强,业务逻辑的每个分支都需要实现try、confirm、cancel三个操作。此外,其实现难度也比较大,需要按照网络状态、系统故障等不同的失败原因实现不同的回滚策略。Confirm/Cancel业务可能会被多次调用,就需要保障其幂等性。 + +TCC故障处理:[https://blog.csdn.net/dm_vincent/article/details/92432059](https://blog.csdn.net/dm_vincent/article/details/92432059) + +### 2.4 LCN事务框架 + +> LCN分布式事务框架其本身并不创建事务,而是基于对本地事务的协调从而达到事务一致性的效果。 + +LCN有三种模式:LCN模式,TCC模式,TXC模式。TCC模式上面有说,这里介绍LCN模式。 + +##### 使用方式: +多数据源配置 + 多数据源实例注入到LCN事务管理器中,后续即可通过@LcnTransaction 注解来实现。 + +事务流程图: + +![image.png](https://upload-images.jianshu.io/upload_images/5786888-a16cb14618965f66.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +TxManager本身是一个Socket服务,来处理每个事务传递过来的指令。同一个事物组中都成功后会返回成功,有一个事务失败后会返回给所有的事务rollback。 + +整个事务在一个切面里面,切面里面会生成一个事务组id,将各个事务绑定,并上传各个事务的结果:commit 还是rollback。 + +##### 事务管理如何做到等待全部事务的结果呢? + +每个事务提交会附带一个锁对象,事务管理新开辟线程来锁住该对象,等本地事务状态达到终态后将锁进行释放,事务管理就会收到事务的结果。 + +LCN事务本地事务是一定要保持在同一个线程内完成,如何保证的呢?ThreadLocal,在获取dataSource连接&创建事务的时候,在threadLocal中存放一份事务对象 + +##### 优缺点 + +优点:性能优秀;对代码的嵌入性低;该模式仅限于本地存在连接对象且可通过连接对象控制事务的模块。 + +缺点:需额外部署 tx-manager 服务节点;代理的连接需要随事务发起方一共释放连接,增加了连接占用的时间; 服务超时时,会造成其他服务的资源被锁住,比如支付服务超时过程中,相关商品库存会一直无法操作; + +### seata框架(GTS开源版本) + +官网:http://seata.io/zh-cn/ +文档:http://seata.io/zh-cn/docs/overview/what-is-seata.html + +GTS文档:https://www.jianshu.com/p/65b7fd061a33 + +### 2.5 消息驱动事务 + +> 基于消息中间件的最终一致性事务方案是互联网公司在高并发场景中探索出的一种创新型应用模式。 + +消息一致性方案是通过消息中间件保证上、下游应用数据操作的一致性。基本思路是将本地操作和发送消息放在一个事务中,保证本地操作和消息发送要么两者都成功或者都失败。下游应用向消息系统订阅该消息,收到消息后执行相应操作。 + +![image.png](https://upload-images.jianshu.io/upload_images/5786888-4f13ec2482fa7ae1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +消息方案从本质上讲是将分布式事务转换为两个本地事务,然后依靠下游业务的重试机制达到最终一致性。基于消息的最终一致性方案对应用侵入性也很高,应用需要进行大量业务改造,成本较高。由于会出现网络延迟的问题,消息重复、消息顺序无法保证的情况也会出现。需要业务机制进行补偿。 + +#### 扩展:dataSource连接 Connection 接口会有commit和rollback方法,它如何实现提交和回滚操作的? + +通过sockcet和mysql进行的指令交互,真正实现靠mysql的事务管理。undo、redo日志实现。[https://www.cnblogs.com/wyc1994666/p/11367051.html](https://www.cnblogs.com/wyc1994666/p/11367051.html) + + +### 事务的适用场景 +>分布式事务,按照控制力度可以分为:不控制、部分控制和完全控制。 + +* 不控制就是不引入分布式事务。 +* 部分控制就是引入了分布式事务,但数据不是强一致性,比如TCC和消息驱动事务,它的优点是并发量和性能很好。适用于一些不要求时效性和一致性的场景。比如:交易成功发短息、推送消息等 +* 完全控制保证了数据的强一致性,比如LCN、GTS,这种事务牺牲了性能,保障了数据的一致性。适用于要求时效性和一致性的场景,比如金融支付,共享出行等。 + + +### 工作上能到用哪些 +目前我们大多数场景使用的是消息驱动的事务模型,确保数据最终一致性即可,金融、库存服务对数据强一致性要求较高,所以对事务的强一致性要求也较高,不适合用消息驱动的事务模型。这些不同的思想的学习,可以为我们后续的工作添加一份思路。 + +1. 学习spring事务管理接口定义的设计模式,在需求开发中提高功能的可扩展性。 +2. 对需要同一线程处理的资源,可以模拟TransactionSynchronizationManager 的思路,用ThreadLocal实现。使用时注意ThreadLocal的内存泄漏问题。 diff --git "a/Redis\351\235\242\350\257\225/redis.md" "b/5. \344\270\255\351\227\264\344\273\266\344\270\223\351\242\230/0. \351\235\242\350\257\225\346\261\207\346\200\273/Redis\351\235\242\350\257\225.md" similarity index 100% rename from "Redis\351\235\242\350\257\225/redis.md" rename to "5. \344\270\255\351\227\264\344\273\266\344\270\223\351\242\230/0. \351\235\242\350\257\225\346\261\207\346\200\273/Redis\351\235\242\350\257\225.md" diff --git "a/nginx/Nginx&Keepalived-\345\256\236\347\216\260\351\253\230\345\217\257\347\224\250.md" "b/5. \344\270\255\351\227\264\344\273\266\344\270\223\351\242\230/5.1 \345\256\236\346\210\230\347\257\207/1. Nginx/Nginx&Keepalived-\345\256\236\347\216\260\351\253\230\345\217\257\347\224\250.md" similarity index 100% rename from "nginx/Nginx&Keepalived-\345\256\236\347\216\260\351\253\230\345\217\257\347\224\250.md" rename to "5. \344\270\255\351\227\264\344\273\266\344\270\223\351\242\230/5.1 \345\256\236\346\210\230\347\257\207/1. Nginx/Nginx&Keepalived-\345\256\236\347\216\260\351\253\230\345\217\257\347\224\250.md" diff --git "a/nginx/Nginx\344\273\243\347\220\206\350\256\277\351\227\256\346\217\220\347\244\272ERR_CONTENT_LENGTH_MISMATCH.md" "b/5. \344\270\255\351\227\264\344\273\266\344\270\223\351\242\230/5.1 \345\256\236\346\210\230\347\257\207/1. Nginx/Nginx\344\273\243\347\220\206\350\256\277\351\227\256\346\217\220\347\244\272ERR_CONTENT_LENGTH_MISMATCH.md" similarity index 100% rename from "nginx/Nginx\344\273\243\347\220\206\350\256\277\351\227\256\346\217\220\347\244\272ERR_CONTENT_LENGTH_MISMATCH.md" rename to "5. \344\270\255\351\227\264\344\273\266\344\270\223\351\242\230/5.1 \345\256\236\346\210\230\347\257\207/1. Nginx/Nginx\344\273\243\347\220\206\350\256\277\351\227\256\346\217\220\347\244\272ERR_CONTENT_LENGTH_MISMATCH.md" diff --git "a/redis/\347\254\254\344\270\200\347\253\240\357\274\232Redis\345\210\235\350\257\206\345\217\212\345\256\211\350\243\205.md" "b/5. \344\270\255\351\227\264\344\273\266\344\270\223\351\242\230/5.1 \345\256\236\346\210\230\347\257\207/2. Redis/1. Redis\345\210\235\350\257\206\345\217\212\345\256\211\350\243\205.md" similarity index 100% rename from "redis/\347\254\254\344\270\200\347\253\240\357\274\232Redis\345\210\235\350\257\206\345\217\212\345\256\211\350\243\205.md" rename to "5. \344\270\255\351\227\264\344\273\266\344\270\223\351\242\230/5.1 \345\256\236\346\210\230\347\257\207/2. Redis/1. Redis\345\210\235\350\257\206\345\217\212\345\256\211\350\243\205.md" diff --git "a/zk/ZooKeeper\345\277\253\351\200\237\345\205\245\351\227\250.md" "b/5. \344\270\255\351\227\264\344\273\266\344\270\223\351\242\230/5.1 \345\256\236\346\210\230\347\257\207/3. Zookeeper/ZooKeeper\345\277\253\351\200\237\345\205\245\351\227\250.md" similarity index 100% rename from "zk/ZooKeeper\345\277\253\351\200\237\345\205\245\351\227\250.md" rename to "5. \344\270\255\351\227\264\344\273\266\344\270\223\351\242\230/5.1 \345\256\236\346\210\230\347\257\207/3. Zookeeper/ZooKeeper\345\277\253\351\200\237\345\205\245\351\227\250.md" diff --git "a/mq/SpringBoot\346\225\264\345\220\210RabbitMQ\357\274\210\344\270\200\357\274\211\345\277\253\351\200\237\345\205\245\351\227\250.md" "b/5. \344\270\255\351\227\264\344\273\266\344\270\223\351\242\230/5.1 \345\256\236\346\210\230\347\257\207/4. MQ/1. SpringBoot\346\225\264\345\220\210RabbitMQ-\345\277\253\351\200\237\345\205\245\351\227\250.md" similarity index 100% rename from "mq/SpringBoot\346\225\264\345\220\210RabbitMQ\357\274\210\344\270\200\357\274\211\345\277\253\351\200\237\345\205\245\351\227\250.md" rename to "5. \344\270\255\351\227\264\344\273\266\344\270\223\351\242\230/5.1 \345\256\236\346\210\230\347\257\207/4. MQ/1. SpringBoot\346\225\264\345\220\210RabbitMQ-\345\277\253\351\200\237\345\205\245\351\227\250.md" diff --git "a/mq/SpringBoot\346\225\264\345\220\210RabbitMQ(\344\272\214)\345\215\225\346\234\272\346\212\242\347\245\250\347\263\273\347\273\237.md" "b/5. \344\270\255\351\227\264\344\273\266\344\270\223\351\242\230/5.1 \345\256\236\346\210\230\347\257\207/4. MQ/2. SpringBoot\346\225\264\345\220\210RabbitMQ-\345\215\225\346\234\272\346\212\242\347\245\250\347\263\273\347\273\237.md" similarity index 100% rename from "mq/SpringBoot\346\225\264\345\220\210RabbitMQ(\344\272\214)\345\215\225\346\234\272\346\212\242\347\245\250\347\263\273\347\273\237.md" rename to "5. \344\270\255\351\227\264\344\273\266\344\270\223\351\242\230/5.1 \345\256\236\346\210\230\347\257\207/4. MQ/2. SpringBoot\346\225\264\345\220\210RabbitMQ-\345\215\225\346\234\272\346\212\242\347\245\250\347\263\273\347\273\237.md" diff --git "a/5. \344\270\255\351\227\264\344\273\266\344\270\223\351\242\230/5.2 \346\272\220\347\240\201\347\257\207/Redis\346\272\220\347\240\201/Redis\345\257\271\350\261\241\345\272\225\345\261\202\345\256\236\347\216\260.md" "b/5. \344\270\255\351\227\264\344\273\266\344\270\223\351\242\230/5.2 \346\272\220\347\240\201\347\257\207/Redis\346\272\220\347\240\201/Redis\345\257\271\350\261\241\345\272\225\345\261\202\345\256\236\347\216\260.md" new file mode 100644 index 0000000..e092b5f --- /dev/null +++ "b/5. \344\270\255\351\227\264\344\273\266\344\270\223\351\242\230/5.2 \346\272\220\347\240\201\347\257\207/Redis\346\272\220\347\240\201/Redis\345\257\271\350\261\241\345\272\225\345\261\202\345\256\236\347\216\260.md" @@ -0,0 +1,275 @@ +>上一章我们讲了Redis的底层数据结构,不了解的人可能会有疑问:这个和平时用的五大对象有啥关系呢?这一章我们就主要解释他们所建立的联系。 + +看这个文件之前,如果对ziplist、skiplist、intset等数据结构不熟悉的话,建议先回顾一下上一章节:xxxxx + +### 0. 五类对象分别是什么 +>五类对象就是我们常用的string、list、set、zset、hash + + +### 1. 为什么要有对象 +>我们平时主要是通过操作对象的api来操作redis,而不是通过它的调用它底层数据结构来完成(外观模式)。但我们还需要了解其底层,只有这样才能写最优化高效的代码。 + + +1. 跟java一样,对象使开发更方便简洁,降低开发门槛。开发者不需要了解其复杂的底层API,直接调用高层接口即可实现开发。 +2. Redis根据对象类型来判断命令是否违法,如果你set key value1 value2就报错。 +3. 对象下可以包含多种数据结构,使数据存储更加多态化。(下面主讲) +4. Reids基于对象做了垃圾回收(引用计数法)。 +5. 对象带有更丰富的属性,来帮助redis实现更高级的功能。(比如对象的闲置时间)。 + +### 2. Redis对象(RedisObject)源码分析 + +``` +typedef struct redisObject { + + // 类型 + unsigned type:4; + + // 编码 + unsigned encoding:4; + + // 指向底层实现数据结构的指针 + void *ptr; + + // ... + +} robj; +``` + +#### type字段 +>记录对象类型。 + +我们平时用的命令`type `,其实就是返回这个字段的属性。 +``` +127.0.0.1:6379> set hello world +OK +127.0.0.1:6379> type hello +string +127.0.0.1:6379> rpush list 1 2 3 +(integer) 3 +127.0.0.1:6379> type list +list +... +``` +那type有多少中类型呢?看下面这个表: + +对象|type 字段| TYPE命令的输出 +---|---|--- +字符串对象| REDIS_STRING |"string"| +|列表对象| REDIS_LIST |"list"| +|哈希对象| REDIS_HASH |"hash"| +|集合对象| REDIS_SET |"set"| +|有序集合对象| REDIS_ZSET |"zset"| + + +#### encoding字段 +>记录对象使用的编码(数据结构),Reids中称数据结构为encoding。 + + +我们可以这样查看我们redis对象中的encoding: +``` +127.0.0.1:6379> object encoding hello +"embstr" +127.0.0.1:6379> object encoding list +"quicklist" +... +``` + +既然它是标明该`redisObject`是使用的什么数据结构,那肯定也有个对应的表: + +类型| 编码| 对象 +----|---|--- +REDIS_STRING| REDIS_ENCODING_INT| 使用整数值实现的字符串对象。 +REDIS_STRING| REDIS_ENCODING_EMBSTR| 使用 embstr 编码的简单动态字符串实现的字符串对象。 +REDIS_STRING| REDIS_ENCODING_RAW| 使用简单动态字符串实现的字符串对象。 +REDIS_LIST| REDIS_ENCODING_ZIPLIST| 使用压缩列表实现的列表对象。 +REDIS_LIST| REDIS_ENCODING_LINKEDLIST| 使用双端链表实现的列表对象。 +REDIS_HASH| REDIS_ENCODING_ZIPLIST| 使用压缩列表实现的哈希对象。 +REDIS_HASH| REDIS_ENCODING_HT| 使用字典实现的哈希对象。 +REDIS_SET| REDIS_ENCODING_INTSET| 使用整数集合实现的集合对象。 +REDIS_SET| REDIS_ENCODING_HT| 使用字典实现的集合对象。 +REDIS_ZSET| REDIS_ENCODING_ZIPLIST| 使用压缩列表实现的有序集合对象。 +REDIS_ZSET| REDIS_ENCODING_SKIPLIST| 使用跳跃表和字典实现的有序集合对象。 + +我们可以看到,Redis对对象的底层encoding分的很细,String类型就有三个,其它四个对象都分别有两种不同的底层数据结构的实现。他们有一规律,就是用`ziplist`、`intset`、`embstr`来实现少量的数据,数据量一旦庞大,就会升级到`skiplist`、`raw`、`linkedlist`、`ht`来实现,后面我会仔细讲解。 + + +### 3. 分别分析各个对象的底层编码实现(数据结构) + +#### 3.1 字符串(string) +>字符串编码有三个:int、raw、embstr。 + +##### 3.1.1 int +>当string对象的值全部是数字,就会使用int编码。 + +``` +127.0.0.1:6379> set number 123455 +OK +127.0.0.1:6379> object encoding number +"int" +``` +##### 3.1.2 embstr +>字符串或浮点数长度小于等于39字节,就会使用embstr编码方式来存储,embstr存储内存一般很小,所以redis一次性分配且内存连续(效率高)。 + +``` +127.0.0.1:6379> set shortStr "suwe suwe suwe" +OK +127.0.0.1:6379> object encoding shortStr +"embstr" +``` +##### 3.1.2 raw +>当一个字符串或浮点数长度大于39字节,就使用SDS来保存,编码为raw,由于不确定值的字节大小,所以键和值各分配各的,所以就分配两次内存(回收也是两次),同理它一定不是内存连续的。 + +``` +127.0.0.1:6379> set longStr "hello everyone, we dont need to sleep around to go aheard! do you think?" +OK +127.0.0.1:6379> object encoding longStr +"raw" +``` + +##### 3.1.3 编码转换 +>前面说过,Redis会自动对编码进行转换来适应和优化数据的存储。 + +int->raw +>条件:数字对象进行append字母,就会发生转换。 +``` +127.0.0.1:6379> object encoding number +"int" +127.0.0.1:6379> append number " is a lucky number" +(integer) 24 +127.0.0.1:6379> object encoding number +"raw" +``` +embstr->raw +>条件:对embstr进行修改,redis会先将其转换成raw,然后才进行修改。所以embstr实际上是只读性质的。 +``` +127.0.0.1:6379> object encoding shortStr +"embstr" +127.0.0.1:6379> append shortStr "(hhh" +(integer) 18 +127.0.0.1:6379> object encoding shortStr +"raw" +``` + + +#### 3.2 列表(list) +>列表对象编码可以是:ziplist或linkedlist。 + +1. `ziplist`压缩列表不知道大家还记得不,就是`zlbytes zltail zllen entry1 entry2 ..end`结构,`entry节点`里有`pre-length、encoding、content`属性,忘记的可以返回去看下。 + +2. `linkedlist`,类似双向链表,也是上一章的知识。 + +##### 3.2.1 编码转换 + +ziplist->linkedlist +>条件:列表对象的所有字符串元素的长度大于等于64字节 & 列表元素数大于等于512. 反之,小于64和小于512会使用ziplist而不是用linkedlist。 + +>这个阈值是可以修改的,修改选项:`list-max-ziplist-value`和`list-max-ziplist-entriess` + + +#### 3.3 哈希(hash) +>哈希对象的编码有:ziplist和hashtable + + +##### 3.3.1 编码转换 +ziplist->hashtable +>条件:哈希对象所有键和值字符串长度大于等于64字节 & 键值对数量大于等于512 + +>这个阈值也是可以修改的,修改选项:`hash-max-ziplist-value`和`hash-max-ziplist-entriess` + + + +#### 3.4. 集合(set) +>集合对象的编码有:intset和hashtable + +##### 3.4.1 intset + +1. 集合对象所有元素都是整数 +2. 集合对象元素数不超过512个 + +##### 3.4.2 编码转换 +intset->hashtable +>条件:元素不都是整数 & 元素数大于等于512 + +#### 3.5. 有序集合(zset) +>有序集合用到的编码:ziplist和skiplist + +大家可能很好奇阿,ziplist的entry中只有属性content可以存放数据,集合也是`key-value`形式,那怎么存储呢? +>第一个节点保存key、第二个节点保存value 以此类推... + +##### 3.5.1 为什么要用这两个编码 +1. 如果只用ziplist来实现,无法做到元素的排序,不支持范围查找,能做到元素的快速查找。 +2. 如果只用skiplist来实现,无法做到快速查找,但能做到元素排序、范围操作。 + + +##### 3.5.2 编码转换 + +ziplist->skiplist +>条件:有序集合元素数 >= 128 & 含有元素的长度 >= 64 + +>这个阈值也是可以修改的,修改选项:`zset-max-ziplist-value`和`zset-max-ziplist-entriess` + +### 4. 垃圾回收 +>为什么要说内存回收呢,因为redisObject有一个字段: +``` +typedef struct redisObject { + + // ... + + // 引用计数 + int refcount; + + // ... + +} robj; +``` +redis的垃圾回收采用引用计数法(和jvm一样),底层采用一个变量对对象的使用行为进行计数。 +* 初始化为1 +* 对象被引用,+1 +* 对象引用消除,-1 +* 计数器==0, 回收对象 + + + +### 5. 对象共享 + + +#### 5.1 对象共享的体现 +1. redis中,值是整数值且相等的两个对象,redis会将该对象进行共享,且引用计数+1 +2. redis启动会自动生成0-9999的整数值放到内存中来共享。 + +#### 5.2 为什么要对象共享 +节约内存 + +#### 5.3 为什么不对字符串进行共享 +>成本太高。 + +验证整数相等只需要O(1)的时间复杂度,而验证字符串要O(n). + + +### 6. 对象的空闲时长 +>最后,redisObject还有一个字段,记录了对象最后一次被访问的时间: +``` +typedef struct redisObject { + + // ... + + unsigned lru:22; + + // ... + +} robj; +``` + +因为这个字段记录对象最后一次被访问的时间,所以它可以用来查看该对象多久未使用,即:用当前时间-lru + +``` +127.0.0.1:6379> object idletime hello +(integer) 5110 +``` + +它还关系到redis的热点数据实现,如果我们选择lr算法,当内存超出阈值后会对空闲时长较高的对象进行释放,回收内存。 + + +参考文献: +1. 《Redis设计与实现》黄健宏著 +2. http://redisbook.com/index.html diff --git "a/5. \344\270\255\351\227\264\344\273\266\344\270\223\351\242\230/5.2 \346\272\220\347\240\201\347\257\207/Redis\346\272\220\347\240\201/Redis\346\214\201\344\271\205\345\214\226\346\267\261\345\205\245\347\220\206\350\247\243.md" "b/5. \344\270\255\351\227\264\344\273\266\344\270\223\351\242\230/5.2 \346\272\220\347\240\201\347\257\207/Redis\346\272\220\347\240\201/Redis\346\214\201\344\271\205\345\214\226\346\267\261\345\205\245\347\220\206\350\247\243.md" new file mode 100644 index 0000000..0fef63d --- /dev/null +++ "b/5. \344\270\255\351\227\264\344\273\266\344\270\223\351\242\230/5.2 \346\272\220\347\240\201\347\257\207/Redis\346\272\220\347\240\201/Redis\346\214\201\344\271\205\345\214\226\346\267\261\345\205\245\347\220\206\350\247\243.md" @@ -0,0 +1,198 @@ +用过Redis的都知道,Redis有两种持久化方式:RDB和AOF,他们的区别大家应该都清楚,所以今天主要想分享一下这两种持久化方式的底层原理以及实现。 + +如果让你手写一个持久化(架构级)的功能,你没有思路的话,那希望这个文章可以给你灵感。 + + +### 1. RDB持久化 + +#### 1.1 创建 +>简单回顾下RDB文件的创建。 + +有两种创建方式: +1. save.阻塞进程去处理(期间不处理别的请求) +2. bgsave.派生一个子进程去处理 + +#### 1.2 载入 +>在redis服务启动时,如果检测到RDB文件,会进行自动载入。 + +##### 如果RDB文件和AOF都存在,优先载入谁? +如果开启了AOF,则会优先AOF + +#### 1.3 save的底层实现 + +``` +save 900 1 +save 300 10 +save 60 10000 +``` +这是`redis.conf`配置文件中关于RDB save时机的配置,它映射在`redisServer`结构体的`saveparams`字段中: +``` +struct redisServer{ + .... + // 保存了redis.conf配置的属性 + struct saveparam *saveparams; + + // 记录上一次save的时间 + time_t lastsave; + + // 修改计数器 + long long dirty; + ... +}; +``` +那来看看它怎么保存的: +``` +struct saveparam { + // 秒数 + time_t seconds; + // 修改次数 + int changes; +}; +``` + +redis自己有一个定时任务每100毫秒执行一次,其中有一个任务就是检查save条件是否满足,如何判断的呢?就是用`lastsave`与`saveparam.seconds`比较时间是否满足,`dirty`与`changes`比较修改次数是否满足。 + +那bgsave如何实现呢,new一个子线程,然后拷贝个数据副本,然后和save一样处理。 + +好了,到这里,用Java写一个这应该是没问题了,那RDB的文件结构如何设计呢? +我们来看看redis的设计。 +#### 1.4 RDB文件结构 +>REDIS+数据库版本号+数据类型+数据+EOF(表示数据结束)(377)+检验和 + +我们知道java中Class文件结构很复杂,因为它包含了常量、接口、类、父类、字段等面向对象的信息,而RDB的就比较简单了,因为它只需要存放数据即可。 + +和class结构一样,它的开头也是文件标识`REDIS`+版本号标识. + +``` +[root@izuf6i2jk9azj2te13kjx8z redis-4.0.9]# od -c dump.rdb +0000000 R E D I S 0 0 0 8 372 \t r e d i s +0000020 - v e r 005 4 . 0 . 9 372 \n r e d i +0000040 s - b i t s 300 @ 372 005 c t i m e 302 +0000060 231 ; 017 ] 372 \b u s e d - m e m 302 310 +0000100 p \r \0 372 \f a o f - p r e a m b l +0000120 e 300 \0 376 \0 373 ( \0 \0 006 k - 7 5 9 9 +0000140 006 v - 7 5 9 9 \0 022 c p t : 254 355 \0 +0000160 005 t \0 \a g e t O n e 4 303 L 220 ^ 303 +0000200 037 254 355 \0 005 s r \0 % c o m . f a n +0000220 t . c o r e . r e s p o n s e . +0000240 S 005 e r v e r R 240 016 030 222 224 e 250 : +0000260 035 323 ? 002 \0 003 I \0 006 s t a t u s L +0000300 \0 004 d a \t 031 \0 022 L j a v a / l +0000320 a n g / O b j e c t ; L \0 003 m s +0000340 g 340 005 032 \f S t r i n g ; x p \0 \0 +0000360 \0 310 y \0 036 340 005 y 037 p o j o . C +0000400 o m p e t i t i o n Z 276 231 334 b 025 + +... +0140540 \a 004 j a v a 377 \v 006 n u m b e r 024 +0140560 002 \0 \0 \0 006 \0 \0 \0 001 \0 002 \0 003 \0 004 \0 +0140600 005 \0 006 \0 016 004 l i s t 001 027 027 \0 \0 \0 +0140620 024 \0 \0 \0 006 \0 \0 362 002 363 002 364 002 365 002 366 +0140640 002 367 377 377 - 022 036 ] 367 332 257 _ + +``` +分析: +``` +R E D I S:RDB文件标志 +0 0 0 8:版本号 +372:结束符 +r e d i s +0000020 - v e r 005 4 . 0 . 9:redis-version4.0.9 +r e d i +0000040 s - b i t s 300 @:redis的位数64或32 +c t i m e 302 0000060 231 ; 017 ]:时间戳 +u s e d - m e m 302 310 0000100 p \r \0:redis使用内存的大小 +374:RDB_OPCODE_EXPIRETIME_MS(带有过期时间标识) +\0: 表示字符串 +最后8字节为校验和 +``` +更详细的可以查看http://redisbook.com/preview/rdb/rdb_struct.html + + +手写过Jedis的朋友都熟悉RESP协议,RDB的数据段和它的排版方式很相似。 +比如:`\0 \0 003 m s g 005 h e l l o 377`就表示键值对:`msg(3个长度):hello(5个长度)` + + +### AOF +>AOF以拼接和重写命令的方式来实现。 + +``` +# 是否开启aof +appendonly yes + +# 文件名称 +appendfilename "appendonly.aof" + +# 同步方式 +##每次收到写命令就立即强制写入磁盘,最慢的,但是保证完全的持久化,不推荐使用 +# appendfsync always +##每秒钟强制写入磁盘一次,在性能和持久化方面做了很好的折中,系统默认 +appendfsync everysec +##完全依赖os,性能最好,持久化没保证 +# appendfsync no + +# aof重写期间是否同步 +no-appendfsync-on-rewrite no + +# 重写触发配置 +auto-aof-rewrite-percentage 100 +auto-aof-rewrite-min-size 64mb + +# 加载aof时如果有错如何处理 +aof-load-truncated yes + +# 文件重写策略 +aof-rewrite-incremental-fsync yes +``` + +这一段配置中,大家着重理解同步方式的配置。redis默认采用的每秒一次写入AOF文件的策略。 +#### 实现原理 +``` +struct redisServer { + + // ... + // 存放AOF缓冲 + sds aof_buf; + + // ... +}; +``` +当有新的命令进来,redis就会将其(协议化后)追加到`aof_buf`的末尾。 + +同理,redis的事件循环也会监听AOF的配置,如果满足配置文件中的同步方式`appendfsync everysec等`,就会将`aof_buf`中的内容保存到AOF文件里。 + + +#### 为什么要进行AOF重写 +>我们知道,redis对AOF有重写机制,用来控制AOF文件的大小。 + +1. AOF体积过大不利于存储。 +2. AOF体积过大,使用AOF数据还原的时间更长。 + +#### AOF重写多个键值对的数据一定是使用一条数据完成吗 +>发生在重写列表、哈希表、集合、有序集合可能会带有多个元素的键时。 + +不是,如果它的值超过64项,则会用多条命令来完成。(避免客户端输入缓冲区溢出) + +#### AOF谁来执行 + +Redis不希望AOF重写造成服务器阻塞,所以用子进程(带有数据副本)去处理。 + +#### AOF期间有新的数据进来会导致AOF文件与当前数据不一致吗 +不会。为了解决这个问题,Reids设置了AOF重写缓冲区(创建子进程后开启),当Redis执行命令时,redis会同时将这个信息发送给`aof_buf`和AOF重写缓冲区。 + +### 扩展 + +#### 过期键的删除策略 + +1. 定时删除。过期键较多的情况下,大量的CPU用于删除键而影响了客户端的请求。 +2. 惰性删除。只有过期键被访问才删除,可能会导致过期键过多,造成内存浪费和溢出。 +3. 定期删除。限制时长和频率对过期键进行删除,难点在于时长和频率难以确定。 + +#### Redis的过期键删除策略 +Redis采用的是惰性删除和定期删除,配合这两种策略来取得CPU和内存的平衡。 + + +#### RDB和AOF文件中会包含过期键吗 +不包含。 + +在生成RDB和AOF文件时,程序会对键进行检查,已过期的键不保存到文件中。 \ No newline at end of file diff --git "a/5. \344\270\255\351\227\264\344\273\266\344\270\223\351\242\230/5.2 \346\272\220\347\240\201\347\257\207/Redis\346\272\220\347\240\201/Redis\346\225\260\346\215\256\347\273\223\346\236\204\345\256\236\347\216\260\345\210\206\346\236\220.md" "b/5. \344\270\255\351\227\264\344\273\266\344\270\223\351\242\230/5.2 \346\272\220\347\240\201\347\257\207/Redis\346\272\220\347\240\201/Redis\346\225\260\346\215\256\347\273\223\346\236\204\345\256\236\347\216\260\345\210\206\346\236\220.md" new file mode 100644 index 0000000..fc70522 --- /dev/null +++ "b/5. \344\270\255\351\227\264\344\273\266\344\270\223\351\242\230/5.2 \346\272\220\347\240\201\347\257\207/Redis\346\272\220\347\240\201/Redis\346\225\260\346\215\256\347\273\223\346\236\204\345\256\236\347\216\260\345\210\206\346\236\220.md" @@ -0,0 +1,440 @@ +面试中,redis也是很受面试官亲睐的一部分。我向在这里讲的是redis的底层数据结构,而不是你理解的五大数据结构。你有没有想过redis底层是怎样的数据结构呢,他们和我们java中的HashMap、List、等使用的数据结构有什么区别呢。 + + +### 1. 字符串处理(string) +我们都知道redis是用C语言写,但是C语言处理字符串和数组的成本是很高的,下面我分别说几个例子。 + +#### 没有数据结构支撑的几个问题 +1. 及其容易造成缓冲区溢出问题,比如用`strcat()`,在用这个函数之前必须要先给目标变量分配足够的空间,否则就会溢出。 +2. 如果要获取字符串的长度,没有数据结构的支撑,可能就需要遍历,它的复杂度是O(N) +3. 内存重分配。C字符串的每次变更(曾长或缩短)都会对数组作内存重分配。同样,如果是缩短,没有处理好多余的空间,也会造成内存泄漏。 + +好了,Redis自己构建了一种名叫`Simple dynamic string(SDS)`的数据结构,他分别对这几个问题作了处理。我们先来看看它的结构源码: +``` +struct sdshdr{ + //记录buf数组中已使用字节的数量 + //等于 SDS 保存字符串的长度 + int len; + //记录 buf 数组中未使用字节的数量 + int free; + //字节数组,用于保存字符串 + char buf[]; +} +``` +再来说说它的优点: +1. 开发者不用担心字符串变更造成的内存溢出问题。 +2. 常数时间复杂度获取字符串长度`len字段`。 +3. 空间预分配`free字段`,会默认留够一定的空间防止多次重分配内存。 + +更多了解:https://redis.io/topics/internals-sds + +这就是string的底层实现,更是redis对所有字符串数据的处理方式(SDS会被嵌套到别的数据结构里使用)。 + +### 2. 链表 +>Redis的链表在双向链表上扩展了头、尾节点、元素数等属性。 + + +![](https://upload-images.jianshu.io/upload_images/5786888-f6280610218dac01.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +#### 2.1 源码 + +ListNode节点数据结构: +``` +typedef struct listNode{ + //前置节点 + struct listNode *prev; + //后置节点 + struct listNode *next; + //节点的值 + void *value; +}listNode +``` +链表数据结构: +``` +typedef struct list{ + //表头节点 + listNode *head; + //表尾节点 + listNode *tail; + //链表所包含的节点数量 + unsigned long len; + //节点值复制函数 + void (*free) (void *ptr); + //节点值释放函数 + void (*free) (void *ptr); + //节点值对比函数 + int (*match) (void *ptr,void *key); +}list; +``` +从上面可以看到,Redis的链表有这几个特点: +1. 可以直接获得头、尾节点。 +2. 常数时间复杂度得到链表长度。 +3. 是双向链表。 + + +### 3. 字典(Hash) +>Redis的Hash,就是在`数组+链表`的基础上,进行了一些rehash优化等。 + +![](https://upload-images.jianshu.io/upload_images/5786888-e40d385f15f6c461.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +#### 3.1 数据结构源码 + +哈希表: +``` +typedef struct dictht { + // 哈希表数组 + dictEntry **table; + // 哈希表大小 + unsigned long size; + // 哈希表大小掩码,用于计算索引值 + // 总是等于 size - 1 + unsigned long sizemask; + // 该哈希表已有节点的数量 + unsigned long used; +} dictht; +``` +Hash表节点: +``` +typedef struct dictEntry { + // 键 + void *key; + // 值 + union { + void *val; + uint64_t u64; + int64_t s64; + } v; + // 指向下个哈希表节点,形成链表 + struct dictEntry *next; // 单链表结构 +} dictEntry; +``` + +字典: +``` +typedef struct dict { + // 类型特定函数 + dictType *type; + // 私有数据 + void *privdata; + // 哈希表 + dictht ht[2]; + // rehash 索引 + // 当 rehash 不在进行时,值为 -1 + int rehashidx; /* rehashing not in progress if rehashidx == -1 */ +} dict; +``` + +可以看出: +1. Reids的Hash采用链地址法来处理冲突,然后它没有使用红黑树优化。 +2. 哈希表节点采用单链表结构。 +3. rehash优化。 + +下面我们讲一下它的rehash优化。 + +#### 3.2 rehash +>当哈希表的键对泰国或者太少,就需要对哈希表的大小进行调整,redis是如何调整的呢? + +1. 我们仔细可以看到`dict`结构里有个字段`dictht ht[2]`代表有两个dictht数组。第一步就是为ht[1]哈希表分配空间,大小取决于ht[0]当前使用的情况。 +2. 将保存在ht[0]中的数据rehash(重新计算哈希值)到ht[1]上。 +3. 当ht[0]中所有键值对都迁移到ht[1]后,释放ht[0],将ht[1]设置为ht[0],并ht[1]初始化,为下一次rehash做准备。 + +#### 3.3 渐进式rehash +>我们在3.2中看到,redis处理rehash的流程,但是更细一点的讲,它如何进行数据迁的呢? + +这就涉及到了渐进式rehash,redis考虑到大量数据迁移带来的cpu繁忙(可能导致一段时间内停止服务),所以采用了渐进式rehash的方案。步骤如下: +1. 为ht[1]分配空间,同时持有两个哈希表(一个空表、一个有数据)。 +2. 维持一个技术器rehashidx,初始值0。 +3. 每次对字典增删改查,会顺带将ht[0]中的数据迁移到ht[1],`rehashidx++`(注意:ht[0]中的数据是只减不增的)。 +4. 直到rehash操作完成,rehashidx值设为-1。 + +它的好处:采用分而治之的思想,将庞大的迁移工作量划分到每一次CURD中,避免了服务繁忙。 + + +### 4. 跳跃表 +>这个数据结构是我面试中见过最多的,它其实特别简单。学过的人可能都知道,它和平衡树性能很相似,但为什么不用平衡树而用skipList呢? + +![](https://upload-images.jianshu.io/upload_images/5786888-173930379a3690fc.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +#### 4.1 skipList & AVL 之间的选择 +1. 从算法实现难度上来比较,skiplist比平衡树要简单得多。 +2. 平衡树的插入和删除操作可能引发子树的调整,逻辑复杂,而skiplist的插入和删除只需要修改相邻节点的指针,操作简单又快速。 +3. 查找单个key,skiplist和平衡树的时间复杂度都为O(log n),大体相当。 +4. 在做范围查找的时候,平衡树比skiplist操作要复杂。 +5. skiplist和各种平衡树(如AVL、红黑树等)的元素是有序排列的。 + +可以看到,skipList中的元素是有序的,所以跳跃表在redis中用在**有序集合键、集群节点内部数据结构** + +#### 4.2 源码 + +跳跃表节点: +``` +typedef struct zskiplistNode { + + // 后退指针 + struct zskiplistNode *backward; + + // 分值 + double score; + + // 成员对象 + robj *obj; + + // 层 + struct zskiplistLevel { + + // 前进指针 + struct zskiplistNode *forward; + + // 跨度 + unsigned int span; + + } level[]; + +} zskiplistNode; +``` +跳跃表: +``` +typedef struct zskiplist { + + // 表头节点和表尾节点 + struct zskiplistNode *header, *tail; + + // 表中节点的数量 + unsigned long length; + + // 表中层数最大的节点的层数 + int level; + +} zskiplist; +``` + +它有几个概念: +##### 4.2.1 层(level[]) +层,也就是`level[]`字段,层的数量越多,访问节点速度越快。(因为它相当于是索引,层数越多,它索引就越细,就能很快找到索引值) + +##### 4.2.2 前进指针(forward) +层中有一个`forward`字段,用于从表头向表尾方向访问。 + +##### 4.2.3 跨度(span) +用于记录两个节点之间的距离 + +##### 4.2.4 后退指针(backward) +用于从表尾向表头方向访问。 + + +### 案例 +``` +level0 1---------->5 +level1 1---->3---->5 +level2 1->2->3->4->5->6->7->8 +``` +比如我要找键为6的元素,在level0中直接定位到5,然后再往后走一个元素就找到了。 + + +### 5. 整数集合(intset) +>Reids对整数存储专门作了优化,intset就是redis用于保存整数值的集合数据结构。当一个结合中只包含整数元素,redis就会用这个来存储。 + +``` +127.0.0.1:6379[2]> sadd number 1 2 3 4 5 6 +(integer) 6 +127.0.0.1:6379[2]> object encoding number +"intset" +``` + + +#### 源码 + +intset数据结构: +``` +typedef struct intset { + + // 编码方式 + uint32_t encoding; + + // 集合包含的元素数量 + uint32_t length; + + // 保存元素的数组 + int8_t contents[]; + +} intset; +``` + +你肯定很好奇编码方式(encoding)字段是干嘛用的呢? + +* 如果 encoding 属性的值为 INTSET_ENC_INT16 , 那么 contents 就是一个 int16_t 类型的数组, 数组里的每个项都是一个 int16_t 类型的整数值 (最小值为 -32,768 ,最大值为 32,767 )。 +* 如果 encoding 属性的值为 INTSET_ENC_INT32 , 那么 contents 就是一个 int32_t 类型的数组, 数组里的每个项都是一个 int32_t 类型的整数值 (最小值为 -2,147,483,648 ,最大值为 2,147,483,647 )。 +* 如果 encoding 属性的值为 INTSET_ENC_INT64 , 那么 contents 就是一个 int64_t 类型的数组, 数组里的每个项都是一个 int64_t 类型的整数值 (最小值为 -9,223,372,036,854,775,808 ,最大值为 9,223,372,036,854,775,807 )。 + +说白了就是根据contents字段来判断用哪个int类型更好,也就是对int存储作了优化。 + +说到优化,那redis如何作的呢?就涉及到了升级。 + +##### 5.1 encoding升级 +如果我们有个Int16类型的整数集合,现在要将65535(int32)加进这个集合,int16是存储不下的,所以就要对整数集合进行升级。 + +###### 它是怎么升级的呢(过程)? + +假如现在有2个int16的元素:1和2,新加入1个int32位的元素65535。 + +1. 内存重分配,新加入后应该是3个元素,所以分配3*32-1=95位。 +2. 选择最大的数65535, 放到(95-32+1, 95)位这个内存段中,然后2放到(95-32-32+1+1, 95-32)位...依次类推。 + +###### 升级的好处是什么呢? +1. 提高了整数集合的灵活性。 +2. 尽可能节约内存(能用小的就不用大的)。 + +##### 5.2 不支持降级 +按照上面的例子,如果我把65535又删掉,encoding会不会又回到Int16呢,答案是不会的。官方没有给出理由,我觉得应该是降低性能消耗吧,毕竟调整一次是O(N)的时间复杂度。 + + +### 6. 压缩列表(ziplist) +>ziplist是redis为了节约内存而开发的顺序型数据结构。它被用在列表键和哈希键中。一般用于小数据存储。 + +引用https://segmentfault.com/a/1190000016901154中的两个图: + +![](https://upload-images.jianshu.io/upload_images/5786888-336a0927247c66c9.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +![](https://upload-images.jianshu.io/upload_images/5786888-560c7b73056a190d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +#### 6.1 源码 +>ziplist没有明确定义结构体,这里只作大概的演示。 + +``` +typedef struct entry { + /*前一个元素长度需要空间和前一个元素长度*/ + unsigned int prevlengh; + /*元素内容编码*/ + unsigned char encoding; + /*元素实际内容*/ + unsigned char *data; +}zlentry; +``` + +``` +typedef struct ziplist{ + /*ziplist分配的内存大小*/ + uint32_t zlbytes; + /*达到尾部的偏移量*/ + uint32_t zltail; + /*存储元素实体个数*/ + uint16_t zllen; + /*存储内容实体元素*/ + unsigned char* entry[]; + /*尾部标识*/ + unsigned char zlend; +}ziplist; +``` + + +第一次看可能会特别蒙蔽,你细细的把我这段话看完就一定能懂。 + +##### Entry的分析 +entry结构体里面有三个重要的字段: +1. previous_entry_length: 这个字段记录了ziplist中前一个节点的长度,什么意思?就是说通过该属性可以进行指针运算达到表尾向表头遍历,这个字段还有一个大问题下面会讲。 +2. encoding:记录了数据类型(int16? string?)和长度。 +3. data/content: 记录数据。 + +##### 连锁更新 + +###### previous_entry_length字段的分析 +上面有说到,previous_entry_length这个字段存放上个节点的长度,那默认长度给分配多少呢?redis是这样分的,如果前节点长度小于254,就分配1字节,大于的话分配5字节,那问题就来了。 + +**如果前一个节点的长度刚开始小于254字节,后来大于254,那不就存放不下了吗?** +这就涉及到previous_entry_length的更新,但是改一个肯定不行阿,后面的节点内存信息都需要改。所以就需要重新分配内存,然后连锁更新包括该受影响节点后面的所有节点。 + +除了增加新节点会引发连锁更新、删除节点也会触发。 + + +### 7. 快速列表(quicklist) +>一个由ziplist组成的双向链表。但是一个quicklist可以有多个quicklist节点,它很像B树的存储方式。是在redis3.2版本中新加的数据结构,用在列表的底层实现。 + +![](https://upload-images.jianshu.io/upload_images/5786888-5c5d06777182c89a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +##### 结构体源码 +表头结构: +``` +typedef struct quicklist { + //指向头部(最左边)quicklist节点的指针 + quicklistNode *head; + + //指向尾部(最右边)quicklist节点的指针 + quicklistNode *tail; + + //ziplist中的entry节点计数器 + unsigned long count; /* total count of all entries in all ziplists */ + + //quicklist的quicklistNode节点计数器 + unsigned int len; /* number of quicklistNodes */ + + //保存ziplist的大小,配置文件设定,占16bits + int fill : 16; /* fill factor for individual nodes */ + + //保存压缩程度值,配置文件设定,占16bits,0表示不压缩 + unsigned int compress : 16; /* depth of end nodes not to compress;0=off */ +} quicklist; +``` +quicklist节点结构: +``` +typedef struct quicklistNode { + struct quicklistNode *prev; //前驱节点指针 + struct quicklistNode *next; //后继节点指针 + + //不设置压缩数据参数recompress时指向一个ziplist结构 + //设置压缩数据参数recompress指向quicklistLZF结构 + unsigned char *zl; + + //压缩列表ziplist的总长度 + unsigned int sz; /* ziplist size in bytes */ + + //ziplist中包的节点数,占16 bits长度 + unsigned int count : 16; /* count of items in ziplist */ + + //表示是否采用了LZF压缩算法压缩quicklist节点,1表示压缩过,2表示没压缩,占2 bits长度 + unsigned int encoding : 2; /* RAW==1 or LZF==2 */ + + //表示一个quicklistNode节点是否采用ziplist结构保存数据,2表示压缩了,1表示没压缩,默认是2,占2bits长度 + unsigned int container : 2; /* NONE==1 or ZIPLIST==2 */ + + //标记quicklist节点的ziplist之前是否被解压缩过,占1bit长度 + //如果recompress为1,则等待被再次压缩 + unsigned int recompress : 1; /* was this node previous compressed? */ + + //测试时使用 + unsigned int attempted_compress : 1; /* node can't compress; too small */ + + //额外扩展位,占10bits长度 + unsigned int extra : 10; /* more bits to steal for future usage */ +} quicklistNode; +``` + +##### 相关配置 +在redis.conf中的ADVANCED CONFIG部分: +``` +list-max-ziplist-size -2 +list-compress-depth 0 +``` +###### list-max-ziplist-size参数 +我们来详细解释一下`list-max-ziplist-size`这个参数的含义。它可以取正值,也可以取负值。 + +当取正值的时候,表示按照数据项个数来限定每个quicklist节点上的ziplist长度。比如,当这个参数配置成5的时候,表示每个quicklist节点的ziplist最多包含5个数据项。 + +当取负值的时候,表示按照占用字节数来限定每个quicklist节点上的ziplist长度。这时,它只能取-1到-5这五个值,每个值含义如下: + +-5: 每个quicklist节点上的ziplist大小不能超过64 Kb。(注:1kb => 1024 bytes) + +-4: 每个quicklist节点上的ziplist大小不能超过32 Kb。 + +-3: 每个quicklist节点上的ziplist大小不能超过16 Kb。 + +-2: 每个quicklist节点上的ziplist大小不能超过8 Kb。(-2是Redis给出的默认值) + +###### list-compress-depth参数 +这个参数表示一个quicklist两端不被压缩的节点个数。注:这里的节点个数是指quicklist双向链表的节点个数,而不是指ziplist里面的数据项个数。实际上,一个quicklist节点上的ziplist,如果被压缩,就是整体被压缩的。 + +参数list-compress-depth的取值含义如下: + +0: 是个特殊值,表示都不压缩。这是Redis的默认值。 +1: 表示quicklist两端各有1个节点不压缩,中间的节点压缩。 +2: 表示quicklist两端各有2个节点不压缩,中间的节点压缩。 +3: 表示quicklist两端各有3个节点不压缩,中间的节点压缩。 +依此类推… + + +Redis对于quicklist内部节点的压缩算法,采用的LZF——一种无损压缩算法。 \ No newline at end of file diff --git "a/Git/Git\346\225\231\347\250\213--\344\270\212\344\274\240\351\241\271\347\233\256\345\210\260\350\277\234\347\250\213\344\273\223\345\272\223.md" "b/6. \351\253\230\346\225\210\345\274\200\345\217\221/6.1 Git/Git-\344\270\212\344\274\240\351\241\271\347\233\256\345\210\260\350\277\234\347\250\213\344\273\223\345\272\223.md" similarity index 100% rename from "Git/Git\346\225\231\347\250\213--\344\270\212\344\274\240\351\241\271\347\233\256\345\210\260\350\277\234\347\250\213\344\273\223\345\272\223.md" rename to "6. \351\253\230\346\225\210\345\274\200\345\217\221/6.1 Git/Git-\344\270\212\344\274\240\351\241\271\347\233\256\345\210\260\350\277\234\347\250\213\344\273\223\345\272\223.md" diff --git "a/Git/git\344\270\215\346\267\273\345\212\240-idea\347\255\211IDE\351\205\215\347\275\256\346\226\207\344\273\266\345\244\271.md" "b/6. \351\253\230\346\225\210\345\274\200\345\217\221/6.1 Git/Git-\345\246\202\344\275\225\345\277\275\347\225\245IDEA\351\205\215\347\275\256\346\226\207\344\273\266\345\244\271.md" similarity index 100% rename from "Git/git\344\270\215\346\267\273\345\212\240-idea\347\255\211IDE\351\205\215\347\275\256\346\226\207\344\273\266\345\244\271.md" rename to "6. \351\253\230\346\225\210\345\274\200\345\217\221/6.1 Git/Git-\345\246\202\344\275\225\345\277\275\347\225\245IDEA\351\205\215\347\275\256\346\226\207\344\273\266\345\244\271.md" diff --git "a/Maven/Maven-Profile\346\214\211\347\216\257\345\242\203\346\211\223\345\214\205.md" "b/6. \351\253\230\346\225\210\345\274\200\345\217\221/6.2 Maven/1. Maven-Profile\346\214\211\347\216\257\345\242\203\346\211\223\345\214\205.md" similarity index 100% rename from "Maven/Maven-Profile\346\214\211\347\216\257\345\242\203\346\211\223\345\214\205.md" rename to "6. \351\253\230\346\225\210\345\274\200\345\217\221/6.2 Maven/1. Maven-Profile\346\214\211\347\216\257\345\242\203\346\211\223\345\214\205.md" diff --git "a/Maven/Maven-\350\207\252\345\256\232\344\271\211archeType.md" "b/6. \351\253\230\346\225\210\345\274\200\345\217\221/6.2 Maven/2. Maven-\350\207\252\345\256\232\344\271\211archeType.md" similarity index 100% rename from "Maven/Maven-\350\207\252\345\256\232\344\271\211archeType.md" rename to "6. \351\253\230\346\225\210\345\274\200\345\217\221/6.2 Maven/2. Maven-\350\207\252\345\256\232\344\271\211archeType.md" diff --git "a/Maven/Maven-\350\207\252\345\256\232\344\271\211\346\217\222\344\273\266.md" "b/6. \351\253\230\346\225\210\345\274\200\345\217\221/6.2 Maven/3. Maven-\350\207\252\345\256\232\344\271\211\346\217\222\344\273\266.md" similarity index 100% rename from "Maven/Maven-\350\207\252\345\256\232\344\271\211\346\217\222\344\273\266.md" rename to "6. \351\253\230\346\225\210\345\274\200\345\217\221/6.2 Maven/3. Maven-\350\207\252\345\256\232\344\271\211\346\217\222\344\273\266.md" diff --git "a/springboot/Jenkins-\346\225\231\347\250\213\357\274\210\344\270\200\357\274\211\345\256\236\347\216\260\350\207\252\345\212\250\345\214\226\346\211\223\345\214\205\345\217\212\351\202\256\344\273\266\351\200\232\347\237\245.md" "b/6. \351\253\230\346\225\210\345\274\200\345\217\221/6.3 Jenkins/1. Jenkins-\345\256\236\347\216\260\350\207\252\345\212\250\345\214\226\346\211\223\345\214\205\345\217\212\351\202\256\344\273\266\351\200\232\347\237\245.md" similarity index 100% rename from "springboot/Jenkins-\346\225\231\347\250\213\357\274\210\344\270\200\357\274\211\345\256\236\347\216\260\350\207\252\345\212\250\345\214\226\346\211\223\345\214\205\345\217\212\351\202\256\344\273\266\351\200\232\347\237\245.md" rename to "6. \351\253\230\346\225\210\345\274\200\345\217\221/6.3 Jenkins/1. Jenkins-\345\256\236\347\216\260\350\207\252\345\212\250\345\214\226\346\211\223\345\214\205\345\217\212\351\202\256\344\273\266\351\200\232\347\237\245.md" diff --git "a/springboot/Jenkins-\346\225\231\347\250\213\357\274\210\344\272\214\357\274\211\345\256\236\347\216\260\346\234\254\345\234\260\350\265\204\346\272\220ssh\344\270\212\344\274\240.md" "b/6. \351\253\230\346\225\210\345\274\200\345\217\221/6.3 Jenkins/2. Jenkins-\345\256\236\347\216\260\346\234\254\345\234\260\350\265\204\346\272\220ssh\344\270\212\344\274\240.md" similarity index 100% rename from "springboot/Jenkins-\346\225\231\347\250\213\357\274\210\344\272\214\357\274\211\345\256\236\347\216\260\346\234\254\345\234\260\350\265\204\346\272\220ssh\344\270\212\344\274\240.md" rename to "6. \351\253\230\346\225\210\345\274\200\345\217\221/6.3 Jenkins/2. Jenkins-\345\256\236\347\216\260\346\234\254\345\234\260\350\265\204\346\272\220ssh\344\270\212\344\274\240.md" diff --git "a/6. \351\253\230\346\225\210\345\274\200\345\217\221/6.4 IDEA/SpringBoot-IDEA-\351\241\271\347\233\256\347\203\255\351\203\250\347\275\262\350\247\243\345\206\263\346\226\271\346\241\210.md" "b/6. \351\253\230\346\225\210\345\274\200\345\217\221/6.4 IDEA/SpringBoot-IDEA-\351\241\271\347\233\256\347\203\255\351\203\250\347\275\262\350\247\243\345\206\263\346\226\271\346\241\210.md" new file mode 100644 index 0000000..0cd2443 --- /dev/null +++ "b/6. \351\253\230\346\225\210\345\274\200\345\217\221/6.4 IDEA/SpringBoot-IDEA-\351\241\271\347\233\256\347\203\255\351\203\250\347\275\262\350\247\243\345\206\263\346\226\271\346\241\210.md" @@ -0,0 +1,13 @@ +第一步:在项目pom文件中导入依赖 +``` + + + org.springframework.boot + spring-boot-devtools + 1.4.3.RELEASE + +``` +第二步:修改Intellij IDEA的配置 +`CTRL +SHIFT +A` 查找 `make project automatically` 并选中 +`CTRL +SHIFT+A `查找`Registry` 找到选项`compile.automake.allow.when.app.running` +重启IDEA后启动项目就可以在IDEA中修改代码和静态页面模板,无需再重启SpringBoot项目了 diff --git "a/RedHat-CentOS/CentOS-mini\347\211\210\346\234\254\347\275\221\347\273\234\351\205\215\347\275\256+SSH\345\267\245\345\205\267\350\277\236\346\216\245.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/CentOS-mini\347\211\210\346\234\254\347\275\221\347\273\234\351\205\215\347\275\256+SSH\345\267\245\345\205\267\350\277\236\346\216\245.md" similarity index 100% rename from "RedHat-CentOS/CentOS-mini\347\211\210\346\234\254\347\275\221\347\273\234\351\205\215\347\275\256+SSH\345\267\245\345\205\267\350\277\236\346\216\245.md" rename to "7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/CentOS-mini\347\211\210\346\234\254\347\275\221\347\273\234\351\205\215\347\275\256+SSH\345\267\245\345\205\267\350\277\236\346\216\245.md" diff --git "a/RedHat-CentOS/Fedora-CentOS-Redhat\347\263\273\345\210\227\346\241\214\351\235\242\347\272\247\345\267\245\345\205\267\346\225\264\345\220\210.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/Fedora-CentOS-Redhat\347\263\273\345\210\227\346\241\214\351\235\242\347\272\247\345\267\245\345\205\267\346\225\264\345\220\210.md" similarity index 100% rename from "RedHat-CentOS/Fedora-CentOS-Redhat\347\263\273\345\210\227\346\241\214\351\235\242\347\272\247\345\267\245\345\205\267\346\225\264\345\220\210.md" rename to "7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/Fedora-CentOS-Redhat\347\263\273\345\210\227\346\241\214\351\235\242\347\272\247\345\267\245\345\205\267\346\225\264\345\220\210.md" diff --git "a/Centos\345\256\211\350\243\205\345\267\245\345\205\267\346\226\207\351\233\206/fedora-U\347\233\230\344\277\256\345\244\215grub\345\274\225\345\257\274.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/Fedora-U\347\233\230\344\277\256\345\244\215grub\345\274\225\345\257\274.md" similarity index 100% rename from "Centos\345\256\211\350\243\205\345\267\245\345\205\267\346\226\207\351\233\206/fedora-U\347\233\230\344\277\256\345\244\215grub\345\274\225\345\257\274.md" rename to "7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/Fedora-U\347\233\230\344\277\256\345\244\215grub\345\274\225\345\257\274.md" diff --git "a/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/ACL.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/ACL.md" new file mode 100644 index 0000000..3f4f44b --- /dev/null +++ "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/ACL.md" @@ -0,0 +1,88 @@ +>acl全称是访问控制列表,如果涉及到对某个用户的权限设置,则用ACL更方便快捷。 + +注意:针对某个用户设置的权限是覆盖other用户权限的。 + +比如你给jiao用户设置了---权限,系统默认other权限是rw-,但是该文件对jiao用户所执行的权限是---。 + +#### 查看文件权限 +`getfacl xxx` + +``` + +``` + + +#### 设置文件/文件夹权限 + +对用户设置: + +`setfacl -m u:jiao:rw- fantj.file ` +``` +-m modify修改文件用户权限 +-x 取消用户权限 +``` +``` +[root@localhost fantj]# getfacl fantj.file +# file: fantj.file +# owner: root +# group: root +user::rw- +user:jiao:rw- +group::r-- +mask::rw- +other::r-- +``` + + + +对组设置: + +与对用户设置一样`setfacl -m g:jiao:rw- fantj.file` +就是把u改成g + +#### 取消文件权限 +`setfacl -x u:jiao fantj.file ` + +``` +[root@localhost fantj]# setfacl -x u:jiao fantj.file +[root@localhost fantj]# getfacl fantj.file +# file: fantj.file +# owner: root +# group: root +user::rw- +group::r-- +mask::r-- +other::r-- +``` + +对组设置: +与对用户设置一样,将u变成g即可。 + + +#### 设置文件/目录对用户的默认权限 +>有的时候,我们需要对某个目录下的所有新建东西给某个用户权限,那我们就需要用这个命令。 +`setfacl -m d:u:jiao:rwx /fantj` + +d的意思是default,不管哪个用户在fantj里新创建的文件或者目录,对jiao都是rwx权限 + +``` +[root@localhost fantj]# setfacl -m d:u:jiao:rwx /home/fantj/ + +[root@localhost fantj]# touch newfile.txt +[root@localhost fantj]# getfacl newfile.txt +# file: newfile.txt +# owner: root +# group: root +user::rw- +user:jiao:rwx #effective:rw- +group::r-x #effective:r-- +mask::rw- +other::r-- +``` +可以看到,user里jiao有rwx权限。 + +#### 取消文件/目录对用户的默认权限 + +`setfacl -x d:u:jiao /home/fantj` + +注意:该操作不会对已经设置权限的文件有影响,只会对新的文件有影响。 \ No newline at end of file diff --git "a/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/DNS.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/DNS.md" new file mode 100644 index 0000000..948b2f7 --- /dev/null +++ "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/DNS.md" @@ -0,0 +1,187 @@ +>在以前的版本,我们都用bind来设置DNS,在Rhel7中,我们用unbound来配置。 + +### DHCP&MAC地址绑定 +>为客户端分配必要的网络信息。 + + +###### 工作流程 +>客户端新生成后会在局域网发广播,DHCP收到后从地址池里拿出ip作回应。然后客户端再发广播拿到的ip地址,然后DHCP回应放出ip。 + + +##### 安装DHCP服务器 + +1. `yum install dhcp -y` +2. 配置文件`/etc/dhcp/dhcpd.conf`,发现里面是空的,所以找个模版`rpm -ql dhcp |grep example` +``` +[root@fantj dhcp]# rpm -ql dhcp |grep example +/usr/share/doc/dhcp-4.2.5/dhcpd.conf.example +/usr/share/doc/dhcp-4.2.5/dhcpd6.conf.example +``` + +复制内容:`cat /usr/share/doc/dhcp-4.2.5/dhcpd.conf.example > /etc/dhcp/dhcpd.conf` + +dhcpd.conf文件配置解析 + +``` +# dhcpd.conf +option domain-name "fantj.cc"; 域名 +option domain-name-servers 192.168.27.3; 编址服务器 + +default-lease-time 600; 租约:租期 +max-lease-time 7200; 租约:最大续期 + +#ddns-update-style none; 根据客户端ip地址的改变来动态dns +log-facility local7; +subnet 192.168.27.0(作用域) netmask 255.255.255.0 { + range 192.168.27.169 192.168.27.250;定义地址池 + option domain-name "internal.example.org"; + option routers 192.168.27.2;网关地址 + option broadcast-address 192.168.27.255;广播地址 +} subnet配置网络信息,一个网段一个作用域, + + +绑定mac地址 + +:服务器在运行时,如果没有给服务器配静态地址, +但是我们希望每次获取的地址是一样的, +就需要我们对mac地址做绑定。 +host fantasia { + hardware ethernet 08:00:07:26:c0:a5; mac地址(可通过ip a 查看) + fixed-address 192.168.27.88;分配的地址不一定要在上面的地址池里。 +} + +``` + +3. 开启服务:`systemctl start dhcpd` +4. 开启防火墙:`firewall-cmd --add-service=dhcp`允许dhcp服务。 + + +### DNS +> + + +`www.baidu.com `是主机名 + +`baidu.com ` 是域名 + +`mail.baidu.com` 是`baidu.com`域中的主机 + + +###### DNS解析路径追踪` + +`dig +trace www.baidu.com` + +``` +[root@fantj ~]# dig +trace www.baidu.com + +; <<>> DiG 9.9.4-RedHat-9.9.4-14.el7 <<>> +trace www.baidu.com +;; global options: +cmd +. 221744 IN NS m.root-servers.net. +. 221744 IN NS b.root-servers.net. +. 221744 IN NS c.root-servers.net. +. 221744 IN NS d.root-servers.net. +. 221744 IN NS e.root-servers.net. +. 221744 IN NS f.root-servers.net. +. 221744 IN NS g.root-servers.net. +. 221744 IN NS h.root-servers.net. +. 221744 IN NS i.root-servers.net. +. 221744 IN NS j.root-servers.net. +. 221744 IN NS a.root-servers.net. +. 221744 IN NS k.root-servers.net. +. 221744 IN NS l.root-servers.net. + +上面是13个.域服务器 + + +com. 172800 IN NS a.gtld-servers.net. +com. 172800 IN NS b.gtld-servers.net. +com. 172800 IN NS c.gtld-servers.net. +com. 172800 IN NS d.gtld-servers.net. +com. 172800 IN NS e.gtld-servers.net. +com. 172800 IN NS f.gtld-servers.net. +com. 172800 IN NS g.gtld-servers.net. +com. 172800 IN NS h.gtld-servers.net. +com. 172800 IN NS i.gtld-servers.net. +com. 172800 IN NS j.gtld-servers.net. +com. 172800 IN NS k.gtld-servers.net. +com. 172800 IN NS l.gtld-servers.net. +com. 172800 IN NS m.gtld-servers.net. + + +com域服务器 + + +baidu.com. 172800 IN NS dns.baidu.com. +baidu.com. 172800 IN NS ns2.baidu.com. +baidu.com. 172800 IN NS ns3.baidu.com. +baidu.com. 172800 IN NS ns4.baidu.com. +baidu.com. 172800 IN NS ns7.baidu.com. + + +上面的是百度域服务器 + + +www.baidu.com. 1200 IN CNAME www.a.shifen.com. +a.shifen.com. 1200 IN NS ns2.a.shifen.com. +a.shifen.com. 1200 IN NS ns3.a.shifen.com. +a.shifen.com. 1200 IN NS ns1.a.shifen.com. +a.shifen.com. 1200 IN NS ns4.a.shifen.com. +a.shifen.com. 1200 IN NS ns5.a.shifen.com. + + +上面是baidu.com别名以后a.shifen.com的dns服务器 +``` + + +###### 1. 安装unbound + +`yum install unbound -y` + + +###### 2. 开启dns服务 +>默认53端口,支持tcp协议和udp协议。 + +`firewall-cmd --add-service=dns` + +``` +[root@fantj ~]# firewall-cmd --add-service=dns +success +``` + + +###### 3. 配置DNS + +主配置文件: +`/etc/unbound/unbound.conf` + +``` +interface 0.0.0.0 监听接口 +# 访问控制 +access-control: 127.0.0.0/8 allow +access-control: 192.168.0.0/24 allow +# 区域配置,本地区域配置主要分为local-zone、local-data 和 local-data-ptr 。这里假设配置一个域名为rhce.com 的域名,其A记录、MX记录配置结果如下 + +local-zone: "rhce.com" static +local-data: "www.rhce.com. IN A 192.168.0.103" +local-data-ptr: "192.168.0.103 www.rhce.com" +local-data: "rhce.com. IN MX 5 mail.rhce.com" +local-data: "mail.rhce.com. IN A 192.168.0.103" + +#转发配置,指定上一级DNS转发 +forward-zone: + name: . + forward-addr: 114.114.114.114 + forward-addr: 8.8.8.8 + +``` +###### 4. 重启服务 +`systemctl restart unbound` + +`systemctl enable unbound` +###### 5. 防火墙配置 + +`firewall-cmd --permanent --add-service=dns` + + + + diff --git "a/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/LDAP.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/LDAP.md" new file mode 100644 index 0000000..e69de29 diff --git "a/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/LVM.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/LVM.md" new file mode 100644 index 0000000..14d2fdd --- /dev/null +++ "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/LVM.md" @@ -0,0 +1,309 @@ +>逻辑卷优点?可以动态扩展内存空间。 + +传统分区缺点:如果需要更改分区大小需要格式化分区然后重新分区。 + +VG:卷组 + +LV:逻辑卷 + +PV:物理卷 + +关系:物理卷组成卷组,然后在卷组上划分逻辑卷(PV->VG->LV) + +#### 1. 将物理分区初始化为物理卷 + +1. `pvscan或者pvs` 查看物理卷 + +2. 开始转换分区类型 + +注意:不能直接更改扩展分区类型为8e(Lvm分区id) +``` +You cannot change a partition into an extended one or vice versa. +Delete it first. +``` +所以我们如果想将扩展分区改为LVM分区则先要删除扩展分区。 + +``` +命令(输入 m 获取帮助):d +分区号 (1-6,默认 6):4 +分区 4 已删除 + +命令(输入 m 获取帮助):p + +磁盘 /dev/sda:21.5 GB, 21474836480 字节,41943040 个扇区 +Units = 扇区 of 1 * 512 = 512 bytes +扇区大小(逻辑/物理):512 字节 / 512 字节 +I/O 大小(最小/最佳):512 字节 / 512 字节 +磁盘标签类型:dos +磁盘标识符:0x00092946 + + 设备 Boot Start End Blocks Id System +/dev/sda1 * 2048 1026047 512000 83 Linux +/dev/sda2 1026048 2050047 512000 83 Linux +/dev/sda3 2050048 3074047 512000 83 Linux + +命令(输入 m 获取帮助):n +Partition type: + p primary (3 primary, 0 extended, 1 free) + e extended +Select (default e): p +已选择分区 4 +起始 扇区 (3074048-41943039,默认为 3074048): +将使用默认值 3074048 +Last 扇区, +扇区 or +size{K,M,G} (3074048-41943039,默认为 41943039): +将使用默认值 41943039 +分区 4 已设置为 Linux 类型,大小设为 18.5 GiB + +命令(输入 m 获取帮助):t +分区号 (1-4,默认 4):3 +Hex 代码(输入 L 列出所有代码):8e +已将分区“Linux”的类型更改为“Linux LVM” +``` +同理,将sda4也做类型转换 +``` +命令(输入 m 获取帮助):p + +磁盘 /dev/sda:21.5 GB, 21474836480 字节,41943040 个扇区 +Units = 扇区 of 1 * 512 = 512 bytes +扇区大小(逻辑/物理):512 字节 / 512 字节 +I/O 大小(最小/最佳):512 字节 / 512 字节 +磁盘标签类型:dos +磁盘标识符:0x00092946 + + 设备 Boot Start End Blocks Id System +/dev/sda1 * 2048 1026047 512000 83 Linux +/dev/sda2 1026048 2050047 512000 83 Linux +/dev/sda3 2050048 3074047 512000 8e Linux LVM +/dev/sda4 3074048 41943039 19434496 8e Linux LVM +``` + +然后执行`pvcreate /dev/sda{3,4}`即可创建物理卷,然后`pvs`查看。 + +``` +[root@localhost home]# pvcreate /dev/sda{5,6} + Physical volume "/dev/sda5" successfully created + Physical volume "/dev/sda6" successfully created +[root@localhost home]# pvs + PV VG Fmt Attr PSize PFree + /dev/sda5 lvm2 a-- 1.00g 1.00g + /dev/sda6 lvm2 a-- 2.00g 2.00g +``` + + +#### 2. 将物理卷创建为卷组 +`vgcreate vg0 /dev/sda{3,4}`然后`vgscan`查看卷组,然后再执行`pvs`可以看到VG列有参数了,那就是卷组名vg0. + +``` +[root@localhost home]# vgcreate vg0 /dev/sda{5,6} + Volume group "vg0" successfully created +``` +``` +[root@localhost home]# vgs + VG #PV #LV #SN Attr VSize VFree + vg0 2 0 0 wz--n- 2.99g 2.99g +``` +``` +[root@localhost home]# pvs + PV VG Fmt Attr PSize PFree + /dev/sda5 vg0 lvm2 a-- 1020.00m 1020.00m + /dev/sda6 vg0 lvm2 a-- 2.00g 2.00g +``` +###### 查看所有卷组信息 +`vgdisplay` +``` +[root@localhost home]# vgdisplay + --- Volume group --- + VG Name vg0 + System ID + Format lvm2 + Metadata Areas 2 + Metadata Sequence No 1 + VG Access read/write + VG Status resizable + MAX LV 0 + Cur LV 0 + Open LV 0 + Max PV 0 + Cur PV 2 + Act PV 2 + VG Size 2.99 GiB + PE Size 4.00 MiB + Total PE 766 + Alloc PE / Size 0 / 0 + Free PE / Size 766 / 2.99 GiB + VG UUID FGEHCq-RI5V-DXOX-t9Io-b0v5-gEmx-1r08c3 + +``` +其中,PE(物理扩展):组成卷组的最小单位。默认是4M +###### 创建PE大小为8的卷组 +>什么是PE,PE是卷组的最小单位,可以理解成组成卷组的元素大小。默认是4M.可以通过`vgdisplay`命令查看 + +修改PE大小需要使用命令 +`vgcreate -s 8 vg0 /dev/sda{3,4}` + +其中,-s 8 的意思指定是PE为8MB + +###### 查看某个卷组信息 +`vgdisplay vgname` + +###### 扩展卷组 +`vgextend vg0 /dev/sda5`将sda5扩展到vg0卷组,然后在`vgdisplay vg0`查看vg size,会发现,变大了。同理,`pvs`命令结果也会同步的加上sda5的vg所属组。 + +###### 减小卷组 +`vgreduce vg0 /dev/sda5`从vg0卷组中将sda5移除 + + +###### vg重命名 +`vgrename old new` + +###### 删除卷组 +`vgremove vgname` + +###### 导入/导出卷组 +>当我们需要将硬盘组成的LVM转移到另一台机器,并且希望这个LVM还照旧的话,就用此命令。 + +`vgexport`和`vgimport`分别为导出和导入命令 + + + +### 3. 将卷组创建为逻辑卷 +>创建逻辑卷必须要在卷组上面创建。 + +###### 创建逻辑卷 +1. 创建方式一: + +`lvcreate -n lv0 -L 99M vg0` +``` +[root@localhost home]# lvcreate -n lv0 -L 99M vg0 + Rounding up size to full physical extent 100.00 MiB + Logical volume "lv0" created +``` + +-n name名字 + +-L length大小 + +执行该命令后提示创建了一个100M的lv0.为什么呢?因为PE默认为4M,100是99最接近的4的整数倍。也可以用`lvscan`来确认lv0的大小是100. + +2. 创建方式二: + +`lvcreate -n lv1 -l 25 vg0` + +-l 是指多少个PE,25个PE就是100M + +然后`lvscan`查看结果。 +``` +[root@localhost home]# lvcreate -n lv1 -l 25 vg0 + Logical volume "lv1" created +[root@localhost home]# lvs + LV VG Attr LSize Pool Origin Data% Move Log Cpy%Sync Convert + lv0 vg0 -wi-a----- 100.00m + lv1 vg0 -wi-a----- 100.00m +``` +3. 创建方式三: + +`lvcreate -n lv2 -l 100%free vg0` + +将vg0卷组中剩余的空间容量都分给lv2。当然,百分比可以是其他数值。 +###### 扩展逻辑卷 +注意三点: +1. 扩展lv时不需要卸载,即在线扩展 +2. lv可以减小,但是变成xfs(文件系统)不能减小。 +3. ext4是可以减小的。(ext4操作在后面展示) + +如果一开始用的xfs文件系统的话,一开始就需要规划好。 + +操作步骤: + +1. lv0扩展200M + +`lvextend -L +200M /dev/vg0/lv0` + +此时这200M只是空间,没有文件系统。所以在`df -hT`查看lv0大小时,还是显示100M,因为这200M还不是文件系统。 +``` +[root@localhost home]# lvextend -L +200M /dev/vg0/lv0 + Extending logical volume lv0 to 500.00 MiB + Logical volume lv0 successfully resized +[root@localhost home]# df -hT +文件系统 类型 容量 已用 可用 已用% 挂载点 +/dev/sda2 xfs 15G 3.4G 12G 23% / +devtmpfs devtmpfs 906M 0 906M 0% /dev +tmpfs tmpfs 914M 136K 914M 1% /dev/shm +tmpfs tmpfs 914M 8.9M 905M 1% /run +tmpfs tmpfs 914M 0 914M 0% /sys/fs/cgroup +/dev/sda1 xfs 197M 101M 97M 52% /boot +/dev/mapper/vg0-lv0 xfs 297M 16M 282M 6% /home/html +``` +2. 扩展文件系统:执行`xfs_growfs /dev/vg0/lv0`,然后在`df -hT`查看lv0大小,此时就变成了300M + +``` +[root@localhost home]# xfs_growfs /dev/vg0/lv0 +meta-data=/dev/mapper/vg0-lv0 isize=256 agcount=4, agsize=19200 blks + = sectsz=512 attr=2, projid32bit=1 + = crc=0 +data = bsize=4096 blocks=76800, imaxpct=25 + = sunit=0 swidth=0 blks +naming =version 2 bsize=4096 ascii-ci=0 ftype=0 +log =internal bsize=4096 blocks=853, version=2 + = sectsz=512 sunit=0 blks, lazy-count=1 +realtime =none extsz=4096 blocks=0, rtextents=0 +data blocks changed from 76800 to 128000 +[root@localhost home]# df -hT +文件系统 类型 容量 已用 可用 已用% 挂载点 +/dev/sda2 xfs 15G 3.4G 12G 23% / +devtmpfs devtmpfs 906M 0 906M 0% /dev +tmpfs tmpfs 914M 136K 914M 1% /dev/shm +tmpfs tmpfs 914M 8.9M 905M 1% /run +tmpfs tmpfs 914M 0 914M 0% /sys/fs/cgroup +/dev/sda1 xfs 197M 101M 97M 52% /boot +/dev/mapper/vg0-lv0 xfs 497M 16M 482M 4% /home/html +``` + + +###### 删除逻辑卷 +`lvremoce -f /dev/vg0/lv2` + +-f 强制删除 + + +### 4. 挂载逻辑卷 + +`mount /dev/vg0/lv0 /home/fantj` + +在挂载的时候可能会报错: +``` +[root@localhost home]# mount /dev/vg0/lv0 /home/html/ +mount: /dev/mapper/vg0-lv0 写保护,将以只读方式挂载 +mount: 未知的文件系统类型“(null)” +``` +此时需要对lv进行文件系统类型格式化`mkfs.xfs /dev/vg0/lv0`: +``` +[root@localhost home]# mkfs.xfs /dev/vg0/lv0 +meta-data=/dev/vg0/lv0 isize=256 agcount=4, agsize=19200 blks + = sectsz=512 attr=2, projid32bit=1 + = crc=0 +data = bsize=4096 blocks=76800, imaxpct=25 + = sunit=0 swidth=0 blks +naming =version 2 bsize=4096 ascii-ci=0 ftype=0 +log =internal log bsize=4096 blocks=853, version=2 + = sectsz=512 sunit=0 blks, lazy-count=1 +realtime =none extsz=4096 blocks=0, rtextents=0 +``` +然后执行挂载: +``` +[root@localhost home]# mount /dev/vg0/lv0 /home/html/ +[root@localhost home]# df -hT +文件系统 类型 容量 已用 可用 已用% 挂载点 +/dev/sda2 xfs 15G 3.4G 12G 23% / +devtmpfs devtmpfs 906M 0 906M 0% /dev +tmpfs tmpfs 914M 136K 914M 1% /dev/shm +tmpfs tmpfs 914M 8.9M 905M 1% /run +tmpfs tmpfs 914M 0 914M 0% /sys/fs/cgroup +/dev/sda1 xfs 197M 101M 97M 52% /boot +/dev/mapper/vg0-lv0 xfs 297M 16M 282M 6% /home/html +``` + + + +### ext4文件系统的lvm设置 + diff --git "a/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/MariaDB.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/MariaDB.md" new file mode 100644 index 0000000..6382490 --- /dev/null +++ "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/MariaDB.md" @@ -0,0 +1,159 @@ + + +### 1. 安装 +`yum install mariadb\* -y` + +##### 1.1 启动服务 +>默认3306端口 + +`systemctl restart mariadb` + +`systemctl enable mariadb` + +##### 1.2 登录 + +`mysql` + +查看mysql用户列表: +``` +MariaDB [mysql]> select host,user,password from user; ++-----------+------+----------+ +| host | user | password | ++-----------+------+----------+ +| localhost | root | | +| s168 | root | | +| 127.0.0.1 | root | | +| ::1 | root | | +| localhost | | | +| s168 | | | ++-----------+------+----------+ +6 rows in set (0.00 sec) +``` + +##### 1.3 设置密码 + +###### 方法一: +`mysqladmin -u root -p password 'redhat'` + +没有原密码的话直接回车。此时redhat就是新密码。 + +###### 方法二: + +`mysql_secure_installation` +``` +Set root password? [Y/n] 这里直接回车 +New password: 填写密码 +Re-enter new password: 重复密码 +Password updated successfully! +然后一路回车,一路默认就行 +``` +###### 方法三: +1. `mysql `无登录用户进入cli + +2. 然后`set password=password('redhat');` + +3. 最后`flush privileges;`刷新配置。 + + +### 2. 配置 + + +##### 2.1 主配置文件 +`/etc/my.cnf` + +###### 拒绝远程访问配置 +>对[mysqld]配置块做修改。 +``` +单机运行MySQL使用skip-networking关闭MySQL的TCP/IP连接方式 + +skip-networking + +开启该选项后就不能远程访问MySQL +``` +###### 优化连接速度 +>对[mysqld]配置块做修改。 +``` +使用skip-name-resolve增加远程连接速度 + +skip-name-resolve + +该选项表示禁用DNS解析,属于官方一个系统上的特殊设定不管,链接的的方式是经过hosts或是IP的模式,他都会对DNS做反查,由于反查解析过慢,就会无法应付过量的查询。 +``` + +###### 指定ip访问 +>对[mysqld]配置块做修改。 + +``` +为安全考虑希望指定的IP访问MySQL,可以在配置文件中增加bind-address=IP,前提是关闭skip-networking + +bind-address=192.168.1.100 + +MySQL优化应该按实际情况配置。 +``` + + + +### 强行修改密码 + +##### 修改配置文件 + +1. `/etc/my.cnf`在mysqld下面加`skip-gran-tables`(或者执行命令`mysqld_safe --skip-grant-tables` +两者道理一样。) +2. 重启服务 +3. `mysql`直接回车遍可以进入安全模式。 +4. `update mysql.user set password=password('redhat') where user='root' and host='localhost'` +5. `flush privileges;` +6. 去掉1中添加的配置。 +7. + +# Mysqldump +> + + +### 1. 做备份 + + +语法: +`mysqldump -u root-p 库 表 > /path/file` + +``` +[root@s168 ~]# mysqldump -uroot -p mysql user > /home/fantj/sql.bak; +Enter password: +[root@s168 ~]# cd /home/fantj/ +[root@s168 fantj]# ls +download hadoop jdk keepalived scala spark sql.bak + +[root@s168 fantj]# cat sql.bak +-- MySQL dump 10.14 Distrib 5.5.60-MariaDB, for Linux (x86_64) +-- +-- Host: localhost Database: mysql +-- ------------------------------------------------------ +-- Server version 5.5.60-MariaDB +``` + +###### 备份库中所有表 + +省略表参数就行:`mysqldump -uroot -p 库 > /path/file` + +如果希望sql中附带创建数据库,则需要在库名前外加`-B `属性。 + +###### 备份表数据(不带数据库语句) +>在mysql cli中完成。 + +`select * from user into outfile '/home/fantj/sql.data'`也可以给他加分隔符,在该命令后追加`fields terminated by ','`则会以逗号分隔保存。 + +当前,前提是mysql用户对该目录有写权限:`setfacl -m u:mysql:rwx /home/fantj` + + +相反,将数据文件导入user表则是:`load data infile '/hoem/fantj/sql.data into table user'`,如果有分隔符,则需要在命令后追加`fields terminated by ','` +### 2. 做还原 +>需要进入mysql cli中进行操作。 + +语法:`source /home/fantj/sql.bak` + +``` +MariaDB [(none)]> source /home/fantj/sql.bak +Query OK, 0 rows affected (0.00 sec) + +Query OK, 0 rows affected (0.00 sec) +``` \ No newline at end of file diff --git "a/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/NFS.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/NFS.md" new file mode 100644 index 0000000..dc41761 --- /dev/null +++ "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/NFS.md" @@ -0,0 +1,147 @@ +>两个linux系统可以通过NFS系统实现文件共享。 + +其中,`showmount`是连接linux系统中的nfs。如果想连接windows上的共享文件,需要用`smbclient`工具来实现。 + + +### 1. 搭建nfs服务 +>我在ip地址为192.168.27.100上部署NFS服务。 + +默认端口2049 + +##### 1.1 修改配置文件 +>该文件是共享目录设置,格式为 共享目录+参数(在尾巴详解) + + +`vim /etc/exports` +``` +添加一行 + +home/fantj *(rw,sync,no_root_squash) +``` +###### 刷新文件 +`exports -r` +###### 全部参数介绍: +``` +rw 可读写的权限  +ro 只读的权限  +no_root_squash 登入NFS主机,使用该共享目录时相当于该目录的拥有者,如果是root的话,那么对于这个共享的目录来说,他就具有root的权  +               限,这个参数『极不安全』,不建议使用 + +root_squash 登入NFS主机,使用该共享目录时相当于该目录的拥有者。但是如果是以root身份使用这个共享目录的时候,那么这个使用者(root) +             的权限将被压缩成为匿名使用者,即通常他的UID与GID都会变成nobody那个身份 + +all_squash 不论登入NFS的使用者身份为何,他的身份都会被压缩成为匿名使用者,通常也就是nobody +anonuid 可以自行设定这个UID的值,这个UID必需要存在于你的/etc/passwd当中 +anongid 同anonuid,但是变成groupID就是了  +sync 资料同步写入到内存与硬盘当中  +async 资料会先暂存于内存当中,而非直接写入硬盘  +insecure 允许从这台机器过来的非授权访问 +sec 加密 如:sec=krb5p +``` + +##### 1.2 启动nfs +`systemctl start nfs` + +`systmctl status nfs` + +``` +[root@localhost home]# systemctl start nfs +[root@localhost home]# systemctl status nfs +nfs-server.service - NFS Server + Loaded: loaded (/usr/lib/systemd/system/nfs-server.service; disabled) + Active: active (exited) since 二 2018-09-18 13:54:01 CST; 5s ago + Process: 46909 ExecStart=/usr/sbin/rpc.nfsd $RPCNFSDARGS $RPCNFSDCOUNT (code=exited, status=0/SUCCESS) + Process: 46905 ExecStartPre=/usr/sbin/exportfs -r (code=exited, status=0/SUCCESS) + Process: 46903 ExecStartPre=/usr/libexec/nfs-utils/scripts/nfs-server.preconfig (code=exited, status=0/SUCCESS) + Main PID: 46909 (code=exited, status=0/SUCCESS) + CGroup: /system.slice/nfs-server.service + +9月 18 13:54:01 localhost.localdomain systemd[1]: Starting NFS Server... +9月 18 13:54:01 localhost.localdomain systemd[1]: Started NFS Server. +``` + + + +### 2. 客户端连接nfs +>我在ip地址为192.168.27.169上部署客户端。 + +###### showmount命令 +``` +语法:showmount [-aed] [hostname] +-a:显示目前以及连上主机的client机器的使用目录的状态 +-e:显示hostname的/etc/exports里面共享的目录 +-d:只显示被client机器挂载的目录 +``` +--------------------- + + +###### 2.1 测试连通性 +`showmount -e xx.xx.xx.xx` +如果没有showmount命令的话 先yum安装。 +``` +[root@s169 ~]# showmount -e 192.168.27.100 +Export list for 192.168.27.100: +/home/fantj * +``` +如果这一步出现报错,则需要设置服务端的防火墙规则,不正规的做法: `iptables -F`,清除所有防火墙规则。 + +##### 2.2 挂载 +>使用网络设备的时候,也需要挂载才可以使用。 + + +挂载命令:`mount 192.168.27.100:/home/fantj /home/nfs` + +``` +[root@s169 home]# mount 192.168.27.100:/home/fantj /home/nfs +[root@s169 home]# df -h +Filesystem Size Used Avail Use% Mounted on +/dev/mapper/centos-root 13G 2.4G 11G 19% / +devtmpfs 476M 0 476M 0% /dev +tmpfs 488M 0 488M 0% /dev/shm +tmpfs 488M 7.7M 480M 2% /run +tmpfs 488M 0 488M 0% /sys/fs/cgroup +/dev/sda1 1014M 159M 856M 16% /boot +tmpfs 98M 0 98M 0% /run/user/0 +192.168.27.100:/home/fantj 15G 3.4G 12G 23% /home/nfs +``` +可以看到,已经成功挂载(自动识别文件系统)。 + + +然后写入`/etc/fstab`文件中。切记文件系统的格式是nfs。 + +那如果nfs文件是加密的呢?👇 +###### 带加密挂载到fstab +>如果nfs经过krb5加密,则需要在挂载的时候声明它的参数。 + +`192.168.27.100:home/fantj /home/nfs defaults,v4.2,sec=krb5p 0 0` + +##### 2.3 测试写入读取 + +``` +[root@s169 home]# cd /home/nfs/ +[root@s169 nfs]# touch test +[root@s169 nfs]# ls +test +``` + + +### 3. linux连接windows共享文件 + +##### 3.1 建立连接 +`smbclient -L //192.168.0 +1 -U username%passwd` + +username是账号,passwd是密码。 + +-L 是登录 + +##### 3.2 挂载 + +直接使用mount挂载的话会报错显示文件系统类型错误。 + +此时我们需要安装`samba、*和cifs\*`(使其支持cifs文件格式) + +然后再挂载`mount -o username=xxx,password=xxx //192.168.0.1/D /windisk`。文件类型是cifs。 + + +然后 diff --git "a/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/Network-Management.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/Network-Management.md" new file mode 100644 index 0000000..f9a5f65 --- /dev/null +++ "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/Network-Management.md" @@ -0,0 +1,163 @@ +LDAP:轻量型目录访问协议 + +RHDS:红帽目录服务,客户端工具 +IPA: + +>使用nmcli命令会同步写到配置文件中。 +##### 查看链接信息 + +``` +[root@localhost ~]# nmcli connection +名称 UUID 类型 设备 +ens33 13756690-ac77-b776-4fc1-f5535cee6f16 802-3-ethernet eno16777736 +``` +##### 查看活跃网络信息 +``` +[root@localhost ~]# nmcli connection show --active +名称 UUID 类型 设备 +ens33 13756690-ac77-b776-4fc1-f5535cee6f16 802-3-ethernet eno16777736 +``` + +### 添加新网卡 + +在vm中设置>网络适配器>添加>网络适配器>NET>确定 + +##### 方法一:复制配置文件 + +复制修改`/etc/sysconfig/network-scripts/ifcfg-enxxxx` +##### 方法二:命令行添加 + + +显示网络信息: +`nmcli connection show` + +添加网络信息: + +`nmcli connection add con-name eth1 type ethernet ifname eth1` + +con-name 是指定名字 + +ifname 是接口名 + +tpye 因特网 + +``` +[root@localhost ~]# nmcli connection add con-name eth1 type ethernet ifname eth1 +Connection 'eth1' (bd5b981e-3518-4dee-8aad-0b756622ab55) successfully added. +``` + +检查结果: +``` +[root@localhost ~]# ip a +1: lo: mtu 65536 qdisc noqueue state UNKNOWN + link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 + inet 127.0.0.1/8 scope host lo + valid_lft forever preferred_lft forever + inet6 ::1/128 scope host + valid_lft forever preferred_lft forever +2: eno16777736: mtu 1500 qdisc pfifo_fast state UP qlen 1000 + link/ether 00:0c:29:f3:47:3c brd ff:ff:ff:ff:ff:ff + inet 192.168.27.100/24 brd 192.168.27.255 scope global eno16777736 + valid_lft forever preferred_lft forever + inet6 fe80::20c:29ff:fef3:473c/64 scope link + valid_lft forever preferred_lft forever +3: eno33554992: mtu 1500 qdisc pfifo_fast state UP qlen 1000 + link/ether 00:0c:29:f3:47:46 brd ff:ff:ff:ff:ff:ff +``` +``` +[root@localhost ~]# nmcli connection show +名称 UUID 类型 设备 +eth1 bd5b981e-3518-4dee-8aad-0b756622ab55 802-3-ethernet -- +ens33 13756690-ac77-b776-4fc1-f5535cee6f16 802-3-ethernet eno16777736 +``` + +### 配置网络 + +##### 1. 先查看是否活跃 +``` +[root@localhost ~]# nmcli connection show --active +名称 UUID 类型 设备 +ens33 13756690-ac77-b776-4fc1-f5535cee6f16 802-3-ethernet eno16777736 +``` +可以看到eth1并没有激活。 +所以我们需要先激活。 + +激活网络连接:`nmcli connection up eth1` + +关闭网络连接:`nmcli connection down eth1` + +去掉网络连接(拔掉网线):`nmcli device disconnect eth1` + +接上网络连接(插入网线):`nmcli device connect eth1` + +查看eth1所有参数:`nmcli connection show eth1` + +修改ip地址:`nmcli connection modify eth1 ipv4.addresses 192.168.27.110`,改完重启NetworkManager服务,不然不能及时生效。 + +修改ip获取方式:`nmcli connection eth1 ipv4.method manua` 修改为手动获取。 + + + + + + + + + +### 设置主机名 + +##### 传统方式: + +修改/etc/hostname + +或者临时修改`hostname xxx` + +##### rhel7方式: + +查看hostname信息:`hostnamectl status` +``` +[root@localhost ~]# hostnamectl status + Static hostname: localhost.localdomain +Transient hostname: xxx + Icon name: computer + Chassis: n/a + Machine ID: dace283296e141d6b6eaa41d03184eeb + Boot ID: 0c39622521a64fcf86c5d9297f1e8a17 + Virtualization: vmware + Operating System: Red Hat Enterprise Linux Server 7.0 (Maipo) + CPE OS Name: cpe:/o:redhat:enterprise_linux:7.0:GA:server + Kernel: Linux 3.10.0-123.el7.x86_64 + Architecture: x86_64 +``` +命令永久修改hostname:`hostnamectl set-hostname fantj` + +``` +[root@localhost ~]# hostnamectl set-hostname fantj +[root@localhost ~]# cat /etc/hostname +fantj +``` + + +### ipv6设置 +>ipv6地址举例:(128比特,16进制) 2001:250:c01:6188:655b:1bd0:b59a:9ef + +ipv6的公网地址:2000::1/64 + +ipv6的私有地址:fec0::1/64 + +回还地址: ::1 + +fe80:: 一般表示没有获取ipv6地址。 + +查看ipv6地址信息: +`nmcli connection show eth1 |grep ipv6` + + +设置ipv6地址:`nmcli connection modify eth1 ipv6.addresses "fec0::2/64 fec0::1"`前者是地址,后者是网关。注意修改获取方式为手工manual.和网段/64保持一致(掩码保持一致)。 + +ping ipv6的方法: +ping6 200exxx + + + + diff --git "a/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/SMB.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/SMB.md" new file mode 100644 index 0000000..898aaf7 --- /dev/null +++ "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/SMB.md" @@ -0,0 +1,100 @@ +>Samba是一种在局域网上共享文件和打印机的一种通信协议,它为局域网内的不同计算机之间提供文件及打印机等资源的共享服务。 + +如果是windows的文件共享,需要安装cifs包来支持此文件系统。 + + + +### 1. 安装 +`yum install samba -y` + +### 2. 配置 + +###### 配置文件目录 +`/etc/samba/` + + +###### 修改文件安全上下文 +>chcon命令是修改对象(文件)的安全上下文,比如:用户、角色、类型、安全级别。也就是将每个文件的安全环境变更至指定环境。 + + +``` +chcon [选项]... 环境 文件... +chcon [选项]... [-u 用户] [-r 角色] [-l 范围] [-t 类型] 文件... +chcon [选项]... --reference=参考文件 文件... + + +-h, --no-dereference:影响符号连接而非引用的文件。 + --reference=参考文件:使用指定参考文件的安全环境,而非指定值。 +-R, --recursive:递归处理所有的文件及子目录。 +-v, --verbose:为处理的所有文件显示诊断信息。 +-u, --user=用户:设置指定用户的目标安全环境。 +-r, --role=角色:设置指定角色的目标安全环境。 +-t, --type=类型:设置指定类型的目标安全环境。 +-l, --range=范围:设置指定范围的目标安全环境。 + + +以下选项是在指定了-R选项时被用于设置如何穿越目录结构体系。如果您指定了多于一个选项,那么只有最后一个会生效。 + +-H:如果命令行参数是一个通到目录的符号链接,则遍历符号链接。 +-L:遍历每一个遇到的通到目录的符号链接。 +-P:不遍历任何符号链接(默认)。 +--help:显示此帮助信息并退出。 +--version:显示版本信息并退出。 +``` + +如果你希望将samba目录共享给其他用户,你需要设置:`chcon -R -t samba_sharee_t /common` + +如果你想把这个ftp共享给匿名用户的话,需要开启以下:`chcon -R -t public_content_t /var/ftp` + +如果你想让你设置的FTP目录可以上传文件的话,SELINUX需要设置:`chcon -t public_content_rw_t /var/ftp/incoming` + +###### 主配置文件`smb.conf` +``` +workgroup = SMBGROUP #设置工作组 +[common] #与文件路径同名 + comment = linhut #描述 + path = /common #共享资源名 + #read list= natasha #读权限用户,添加这个名单就只有里面的可以读 + browseable = yes #共享资源是否允许用户浏览 + hosts allow = 172.25.12. #允许访问的(这里题目DNS域解析为这个段) +[miscellaneous] #与文件路径同名 + comment = linhut #描述 + path = /miscellaneous #共享资源名 + #read list= silene #有读权限用户 + write list = akira #读写权限用户 + browseable = yes #共享资源是否允许用户浏览 + hosts allow = 172.25.12. #允许访问的(这里题目DNS域解析为这个段) + +``` + +###### 查看SMB分享 + +`smbclietn -L //192.168.0.1` + +###### 设置smb密码 + +`smbpasswd -a fantj` + +###### 服务重启 +`systemctl restart smb;systemctl restart nmb` +#### 在防火墙中开启服务支持 + +`firewall-cmd --permanent --add-rich-rule 'rule family=ipv4 source address=172.25.xx.xx/24 service name="samba" reject` + +### 3. 挂载 + +#### 3.1 安装cifs支持 + +`yum install cifs-utils -y` + +#### 3.2 设置密码文件 + +`echo "username"=xxx >> /root/smb-multiuser.txt` +`echo "password"=xxx >> /root/smb-multiuser.txt` + +#### 3.3 挂载 +`vim /etc/fstab` + +``` +//serverX/miscellaneous /mnt/multi cifs credentials=/root/smb-multiuser.txt,multiuser,sec=ntlmssp 0 0 +``` \ No newline at end of file diff --git "a/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/httpd.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/httpd.md" new file mode 100644 index 0000000..9bf857c --- /dev/null +++ "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/httpd.md" @@ -0,0 +1,172 @@ + + +### 1. 安装 +`yum install httpd -y` + +#### 开启端口 + +`firewall-cmd --permanent --add-service=http` + +### 2. 配置 +>配置文件都在`/etc/httpd/conf`下。 + + +##### 查看已经加载的模块 + +`httpd -M` +##### 主配置文件 + +`/etc/httpd/conf/httpd.conf` + +``` +ServerRoot "/etc/httpd" 根目录 +Listen 80 监听80端口 +Include conf.modules.d/*.conf 扫描配置文件目录 +User apache 所有者 +Group apache 所属组 +ServerAdmin root@localhost 设置管理员邮箱 +ServerName www.example.com:80 域名 + 访问目录 + AllowOverride none 不允许覆盖 + Require all denied 所有都拒绝访问 + + +DocumentRoot "/var/www/html" html目录 + + + AllowOverride None + # Allow open access: + Require all granted 所有请求都授权 + + + + Options Indexes FollowSymLinks Indexes:类似ftp功能,FollowSymLinks:支持软连接 + AllowOverride None 配置不被覆盖 + Require all granted + + +Alias /path /realpath 访问/path的时候实则访问/realpath +当然,完成上面的alias功能还需要添加访问权限,如下: + + Options Indexes FollowSymLinks Indexes:类似ftp功能,FollowSymLinks:支持软连接 + AllowOverride None 配置不被覆盖 + Require all granted + + + + ScriptAlias /cgi-bin/ "/var/www/cgi-bin/" 动态脚本支持,访问/cgi-bi时,实际访问本地/var/www/cgi-bin/ + + +ErrorLog "logs/error_log" 错误日志 + +EnableSendfile on +``` + +##### 简单配置虚拟主机 + +从`/usr/share/doc/httpd-2.4.6/httpd-vhosts.conf`中拷贝一份模版到`/etc/httpd/conf.d/`中 + +``` + + DocumentRoot "/var/www/html" + ServerName fantj1.com + +``` +然后在`/var/www/html/`下添加index.html文件。 + +重启服务请求`fantj1.com`即可访问到index.html。(前提是hosts认识fantj1.com这个域名) + +### 3. 配置CA + + +##### 3.1 生成自签名CA证书 +`cd /etc/pki/tls/certs/`然后执行`make caname.crt` + +``` +[root@s168 certs]# make fantj.crt +umask 77 ; \ +/usr/bin/openssl genrsa -aes128 2048 > fantj.key +Generating RSA private key, 2048 bit long modulus +...............+++ +....................+++ +e is 65537 (0x10001) +Enter pass phrase: +Verifying - Enter pass phrase: +``` +然后目录里多了两个文件:`fantj.crt`和`fantj.key` +##### 3.2 将私钥公钥拷贝 +`cp fantj.* /etc/httpd/conf/` + +##### 3.3 安装https模块 +`yum install mod_ssl -y` + +编辑`/etc/httpd/conf.d/ssl.conf`文件。 +``` +Listen 443 https 监听端口 + + +SSLPassPhraseDialog exec:/usr/libexec/httpd-ssl-pass-dialog + +SSLSessionCache shmcb:/run/httpd/sslcache(512000) +SSLSessionCacheTimeout 300 + +SSLRandomSeed startup file:/dev/urandom 256 +SSLRandomSeed connect builtin + +SSLCryptoDevice builtin + + + 虚拟主机配置 + + +ErrorLog logs/ssl_error_log +TransferLog logs/ssl_access_log +LogLevel warn 日志信息 + +SSLEngine on ssl开关 + +SSLProtocol all -SSLv2 -SSLv3 支持ssl协议 + +SSLCipherSuite HIGH:3DES:!aNULL:!MD5:!SEED:!IDEA + + +SSLCertificateFile 证书 /etc/pki/tls/certs/localhost.crt + +SSLCertificateKeyFile /etc/pki/tls/private/localhost.key 私钥 + + + + + + + SSLOptions +StdEnvVars + + + SSLOptions +StdEnvVars + + +BrowserMatch "MSIE [2-5]" \ + nokeepalive ssl-unclean-shutdown \ + downgrade-1.0 force-response-1.0 + +CustomLog logs/ssl_request_log \ + "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b" + + +``` + +其中我们需要修改`SSLCertificateFile`和`SSLCertificateKeyFile`这两个配置,它们分别用来设置crt文件目录和key文件目录。其他保持默认配置即可。 + +``` +SSLCertificateFile /etc/httpd/conf/fantj.crt +SSLCertificateKeyFile /etc/httpd/conf/fantj.key +``` +###### 重启httpd服务 +``` +[root@s168 conf.d]# systemctl restart httpd +Enter SSL pass phrase for www.example.com:443 (RSA) : ****** +``` +好了,设置成功. + + + diff --git "a/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/iscsi.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/iscsi.md" new file mode 100644 index 0000000..3bdfc2c --- /dev/null +++ "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/iscsi.md" @@ -0,0 +1,145 @@ +>iSCSI技术是一种由IBM公司研究开发的,是一个供硬件设备使用的可以在IP协议的上层运行的SCSI指令集,这种指令集合可以实现在IP网络上运行SCSI协议,使其能够在诸如高速千兆以太网上进行路由选择。iSCSI技术是一种新储存技术,该技术是将现有SCSI接口与以太网络(Ethernet)技术结合,使服务器可与使用IP网络的储存装置互相交换资料。 + +###### iSCSI的工作过程: +1. 当iSCSI主机应用程序发出数据读写请求后,操作系统会生成一个相应的SCSI命令, +2. 该SCSI命令在iSCSI initiator层被封装成ISCSI消息包并通过TCP/IP传送到设备侧, +3. 设备侧的iSCSI target层会解开iSCSI消息包,得到SCSI命令的内容,然后传送给SCSI设备执行; +4. 设备执行SCSI命令后的响应,在经过设备侧iSCSI target层时被封装成ISCSI响应PDU,通过TCP/IP网络传送给主机的ISCSI initiator层, +5. iSCSI initiator会从ISCSI响应PDU里解析出SCSI响应并传送给操作系统,操作系统再响应给应用程序。 + + +共享设备:可以是磁盘,也可以是本地设备。 + +# 服务端 +### 1. 安装 + +###### 1.1 查找targetcli依赖 +`yum list targetcli\*` + +###### 1.2 安装 +`yum install targetcli\* -y` + +###### 1.3 开放端口 +`firewall-cmd --permanent --add-port=3260/tcp` + +### 2. 使用 + +#### 2.1 使用targetcli +进入cli:`targetcli` + +如果是共享磁盘,则放到block下 + +如果是共享文件,则放到fileio下 + + +``` +cd backstores/ + +创建磁盘:block/ create block1 /dev/sdb1 + +创建fileio:fileio/ create file1 /xxx/file + +查看创建项目:ls + +删除项目:fileio/ delete file1 + + +``` + +#### 2.2 创建target +>需要进入到iscsi目录下操作。 + +``` +cd / + +cd iscsi + +ls + +创建target:create ign.2018-09.com.fantj:disk + +删除target:delete ign.2018-09.com.fantj:disk +``` + +#### 2.3 建立设备和target的联系 + +``` +cd ign.2018-09.com.fantj:disk/ + +cd tpg1/ + +ls + +创建ACL(相当于密码): acls/ create ign.2018-09.com.fantj:xx + +删除ACL:/acl delete ign.2018-09.com.fantj:xx + +创建LUN(创建联系):luns/ create /backstores/fileio/file1 + +ls + +设置portals(访问地址):portals/ create 192.168.27.100 + +ls 查看自动分配的端口 + +退出 :exit +``` +默认配置文件保存在`/etc/target/saveconfig.json`文件中。 + +###### 重启服务/开机启动 +`systemctl restart target` + +`systemctl enable target` + +--- +# 客户端 + +### 1. 安装 +`yum install iscsi\*` + +##### 启动服务 +`systemctl start iscsi` + +### 2. 使用 + +###### 2.1 寻找/扫描服务 +>扫描某个ip下的共享设备列表。 + +`iscsiadm -m discovery -t st -p 192.168.27.100` + +``` +[root@fantj]# iscsiadm -m discovery -t st -p 192.168.27.100 +192.168.27.100:3260 ign.2018-09.com.fantj:disk +``` +``` +iscsiadm参数: + +-m 模式model +-t 类型type +-T target名字 +-p 地址portal +``` + +###### 2.2 加载设备 +``` +[root@fantj]# iscsiadm -m node -T ign.2018-09.com.fantj:disk -p 192.168.27.100 -l +此时是加载不成功的,因为在服务端有配置ACL加密信息。 + +解决ACL加密: +1. 编辑/etc/iscsi/initiatorname.iscsi文件, +2. 将值改为我们服务端设置的ACL:ign.2018-09.com.fantj:xx + +重启服务: systemctl restart iscsi + +重复上面步骤:扫描->加载 +``` + +###### 2.3 查看加载结果 + +`cat /etc/proc/partitions` +或者 +`iscsiadm -m session` + +开机自动连接,只需要做好挂载就行。 + +如果不想自动连接:修改配置`/etc/iscsi/iscsi.conf`将`node.startup=automatic 改成 manual` \ No newline at end of file diff --git "a/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/vim.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/vim.md" new file mode 100644 index 0000000..d8940af --- /dev/null +++ "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/vim.md" @@ -0,0 +1,89 @@ +### 插入命令 + +a 在光标所在字符后插入 +A 在光标所在行尾插入 + +i 在光标所在字符前插入 +I 在光标所在行行首插入 + +o 在光标下插入新行 +O 在光标上插入新行 + + +### 定位命令 + +:set nu 设置行号 +:set nonu 取消行号 + +gg 到第一行 +GG 到最后一行 + +nG 到第n行 +:n 到第n行 + +$ 移至行尾 +0 移至行首 + + +### 删除命令 + +x 删除光标所在字符 +nx 删除后n个字符 +dd 删除所在行 + +dG 删除到文件末尾 +D 删除到行尾 + +:n1,n2d 删除指定范围的行 + + +### 复制和剪切命令 + +yy 复制当前行 +nyy 复制当前以下n行 +dd 剪切当前行 +ndd 。。。 +p 粘贴在行下 +P 粘贴在行上 + +### 替换和取消 + +r 取代光标所在字符 +R 从当前开始替换字符,esc结束 +u 取消上一步操作 + + +### 搜索和搜索替换命令 + +/string 搜索字符串string + +n 查看下一个string + +:%s/old/new/g 全文替换 + +:n1,n2s/old/new/g 替换范围行 + +### 保存和退出命令 + +:w +:wq +:wq! +:q! + +ZZ :wq的捷键 + +:w new_filename 另存为指定文件 + + +### 连续行注释 + +:n1,n2s/^/#/g ^表示行首 + + + + + + + + + diff --git "a/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/\346\226\207\344\273\266\347\263\273\347\273\237\347\256\241\347\220\206\357\274\232\345\210\206\345\214\272\343\200\201\346\214\202\350\275\275....md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/\346\226\207\344\273\266\347\263\273\347\273\237\347\256\241\347\220\206\357\274\232\345\210\206\345\214\272\343\200\201\346\214\202\350\275\275....md" new file mode 100644 index 0000000..61bb286 --- /dev/null +++ "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/\346\226\207\344\273\266\347\263\273\347\273\237\347\256\241\347\220\206\357\274\232\345\210\206\345\214\272\343\200\201\346\214\202\350\275\275....md" @@ -0,0 +1,411 @@ +### 1. 文件系统常用命令 + +#### 当前分区信息查看 +`cat /proc/partitions ` + +``` +[roo@localhost ~]$ cat /proc/partitions +major minor #blocks name + + 8 0 20971520 sda + 8 1 512000 sda1 + 8 2 20458496 sda2 + 11 0 3655680 sr0 + 253 0 18358272 dm-0 + 253 1 2097152 dm-1 +``` + +#### 1.1 df、du、fsck、dump2fs + +##### `df`统计文件系统占用情况 +`df [选项] [挂载点]` +``` +-a 显示所有文件系统信息 +-h 人性化容量显示 +-T 文件系统类型 +-m 以MB为单位显示容量 +-k 以KB为单位显示容量 +``` + +##### `du`统计目录或文件大小 +`du [optionals] [dir or filename]` +``` +-a 显示每个子文件的占用量 +-h 人性化显示占用量 +-s 统计总占用量 +``` +用ls也可以查看文件大小,但是ls的查看的文件夹大小是不包含文件夹内文件大小。 + +可以用`du -sh /xxx`来查看文件占用量,但是du看到的只是文件大小。df查到的是所有空间内的大小。 + +##### `fsck`文件系统修复命令 +`fsck [选项] 分区设备文件名` +``` +-a 不显示用户提示,自动修复文件系统 +-y 自动修复 +``` +系统开机自动检测,不需要手工用。 + +##### `dump2fs`显示磁盘状态命令 +`dump2fs 分区设备文件名` +所有磁盘的相关信息都会显示:挂载、格式等。 + +#### 1.2 挂载命令 + +##### 查询挂载 +查询系统已挂载的设备,-l显示卷标名称:`mount [-l]` + +##### 自动挂载 +依据配置文件/etc/fstab的内容,自动挂载:`mount -a` + +##### 挂载命令 +`mount [-t文件系统] [-L卷标名] [-o特殊选项] 设备文件名 挂载点` +``` +选项: + -t 文件系统,ext3/ext4/iso9660等 + -L 卷标名,指定卷标分区, + -o 特殊选项:可以指定挂载的额外选项 + 比如 remount 重新挂载、 + exec/noexec 是否允许文件系统高中可执行文件的执行 + mount -o remount,noexec /home :将home重新挂载,并禁止执行文件执行 +``` +#### 1.3 挂载光盘与U盘 +>如果需要不断的切换光盘来读取数据,那么我们就需要设置一个目录和该设备绑定,只要进入这个目录,系统就自动挂载光盘。这个会在1.3最后面讲到。 +##### 1.3.1 挂载光盘 +>RHEL7中,默认光盘的设备文件名是:/dev/sr0,系统默认挂载点是 /run/media/roo/ + +``` +mkdir /mnt/cdrom/ 建立挂载点 + +mount -t iso9660 /dev/sr0 /mnt/cdrom/ 挂载光盘 + +/dev/sr0 on /run/media/roo/RHEL-7.0 Server.x86_64 type iso9660 挂载信息 + +mount /dev/sr0 /mnt/cdrom/ +``` + +##### 1.3.2 卸载光盘 + +`umount 设备文件名或挂载点` +``` +umount /dev/sr0 卸载光盘 +``` + +##### 挂载U盘 +>注意默认不支持NTFS系统,详情见1.4 +``` +fdisk -l 查看U盘设备文件名是啥,假设是/dev/sdb1 + +mount -t vfat /dev/sdb1 /mnt/usb/ 挂载U盘 +``` +##### 卸载U盘 + + +`umount 设备文件名或挂载点` +``` +umount /dev/sdb1 卸载U盘 +``` + +##### 自动挂载光盘 +>如果需要不断的切换光盘来读取数据,那么我们就需要设置一个目录和该设备绑定,只要进入这个目录,系统就自动挂载光盘。 + +1. 编辑文件`/etc/auto.master` + +如果没有该文件,则先安装autofs: + +`yum install -y autofs +` +然后编辑`/etc/auto.master` + +在`/misc /etc/auto.misc`后面添加一行: +`/home/udisk /etc/auto.udisk` + +2. 创建并编辑文件auto.udisk + +` cp /etc/auto.misc /etc/auto.udisk +` + +编辑`/etc/auto.udisk` + +``` +cd -fstype=iso9660,ro,nosuid,nodev :/dev/cdrom +mycd -fstype=iso9660,ro,nosuid,nodev :/dev/cdrom +``` +其中,mycd是/home/udisk的子目录,然后设置文件类型,文件所在路径等。 + +然后重启`autofs`服务,即可实现自动检测挂载。 + +#### 单用户模式下mount -o + +我们的Linux系统在无法启动时候,通常需要进入单用户模式下进行修改一些配置文件,或调整一些参数方可。但是在进入单用户模式后,/文件系统是只读模式,任何用户都无法进行修改,那么这个时候我们就需要用到一条命令:mount -o remount,rw / 这个命令来让我们的/文件系统为可读可写模式,这样就可以实现自由修改了。 + + +#### 1.4 ntfs文件系统的支持 +>大概有两种方法: + +1. 内核编译(不常用) +2. 下载插件 NTFS-3G https://www.tuxera.com/community/open-source-ntfs-3g/ + + +##### 安装步骤 +``` +wget https://tuxera.com/opensource/ntfs-3g_ntfsprogs-2017.3.23.tgz + +tar ntfs-3g_ntfsprogs-2017.3.23.tgz + +cd ntfs-3g_ntfsprogs-2017.3.23 + +./configure 编译器准备 + +make 编译 + +make install 编译安装 + +mount -t ntfs-3g 分区设备文件名 挂载点 eg:mount -t ntfs-3g /dev/sda1 /mnt/windows + +``` + +### 2. fdisk分区 +>fdisk操作完成后,记着一定要w来保存更改,如果w报错,则需要启动内核对新分区表重新读取,命令是`partpr /dev/sda` + +##### fdisk命令分区过程 +`fdisk -l` 查看新硬盘 + +###### 使用fdisk命令分区 + +`fdisk /dev/sda` + +###### 分主分区: +``` +[root@localhost ~]# fdisk /dev/sda +欢迎使用 fdisk (util-linux 2.23.2)。 + +更改将停留在内存中,直到您决定将更改写入磁盘。 +使用写入命令前请三思。 + + +命令(输入 m 获取帮助):m +命令操作 + + d delete a partition 删除一个分区 + l list known partition types 显示文件系统类型,82为swap分区,83为分区 + m print this menu 显示帮助菜单 + n add a new partition 新建分区 + p print the partition table 显示分区列表 + q quit without saving changes 不保存退出 + w write table to disk and exit 保存退出 +命令(输入 m 获取帮助):n +Partition type: + p primary (2 primary, 0 extended, 2 free) + e extended +Select (default p): p +分区号 (3,4,默认 3):3 +First cylinder...: +Last cylinder ...: +500M +``` +###### 删除分区 +d + +``` +命令(输入 m 获取帮助):p + +磁盘 /dev/sda:21.5 GB, 21474836480 字节,41943040 个扇区 +Units = 扇区 of 1 * 512 = 512 bytes +扇区大小(逻辑/物理):512 字节 / 512 字节 +I/O 大小(最小/最佳):512 字节 / 512 字节 +磁盘标签类型:dos +磁盘标识符:0x00092946 + + 设备 Boot Start End Blocks Id System +/dev/sda1 * 2048 1026047 512000 83 Linux +/dev/sda2 1026048 41943039 20458496 8e Linux LVM + +命令(输入 m 获取帮助):d +分区号 (1,2,默认 2):2 +分区 2 已删除 +``` + +###### 分区类型转换 +t + +``` +命令(输入 m 获取帮助):t +分区号 (1,2,默认 2):2 +Hex 代码(输入 L 列出所有代码):L + + 0 空 24 NEC DOS 81 Minix / 旧 Linu bf Solaris + 1 FAT12 27 隐藏的 NTFS Win 82 Linux 交换 / So c1 DRDOS/sec (FAT- + 2 XENIX root 39 Plan 9 83 Linux c4 DRDOS/sec (FAT- + 3 XENIX usr 3c PartitionMagic 84 OS/2 隐藏的 C: c6 DRDOS/sec (FAT- + 4 FAT16 <32M 40 Venix 80286 85 Linux 扩展 c7 Syrinx + 5 扩展 41 PPC PReP Boot 86 NTFS 卷集 da 非文件系统数据 + 6 FAT16 42 SFS 87 NTFS 卷集 db CP/M / CTOS / . + 7 HPFS/NTFS/exFAT 4d QNX4.x 88 Linux 纯文本 de Dell 工具 + 8 AIX 4e QNX4.x 第2部分 8e Linux LVM df BootIt + 9 AIX 可启动 4f QNX4.x 第3部分 93 Amoeba e1 DOS 访问 + a OS/2 启动管理器 50 OnTrack DM 94 Amoeba BBT e3 DOS R/O + b W95 FAT32 51 OnTrack DM6 Aux 9f BSD/OS e4 SpeedStor + c W95 FAT32 (LBA) 52 CP/M a0 IBM Thinkpad 休 eb BeOS fs + e W95 FAT16 (LBA) 53 OnTrack DM6 Aux a5 FreeBSD ee GPT + f W95 扩展 (LBA) 54 OnTrackDM6 a6 OpenBSD ef EFI (FAT-12/16/ +10 OPUS 55 EZ-Drive a7 NeXTSTEP f0 Linux/PA-RISC +11 隐藏的 FAT12 56 Golden Bow a8 Darwin UFS f1 SpeedStor +12 Compaq 诊断 5c Priam Edisk a9 NetBSD f4 SpeedStor +14 隐藏的 FAT16 <3 61 SpeedStor ab Darwin 启动 f2 DOS 次要 +16 隐藏的 FAT16 63 GNU HURD or Sys af HFS / HFS+ fb VMware VMFS +17 隐藏的 HPFS/NTF 64 Novell Netware b7 BSDI fs fc VMware VMKCORE +18 AST 智能睡眠 65 Novell Netware b8 BSDI swap fd Linux raid 自动 +1b 隐藏的 W95 FAT3 70 DiskSecure 多启 bb Boot Wizard 隐 fe LANstep +1c 隐藏的 W95 FAT3 75 PC/IX be Solaris 启动 ff BBT +1e 隐藏的 W95 FAT1 80 旧 Minix +Hex 代码(输入 L 列出所有代码):fd +已将分区“Linux”的类型更改为“Linux raid autodetect” +``` + +###### 分扩展分区: +>扩展分区唯一的作用是包含逻辑分区,不能格式化,不能写入数据。 + +``` +首先先new一个扩展分区 +命令(输入 m 获取帮助):n +Partition type: + p primary (3 primary, 0 extended, 1 free) + e extended +Select (default e): e +已选择分区 4 +起始 扇区 (3074048-41943039,默认为 3074048): +将使用默认值 3074048 +Last 扇区, +扇区 or +size{K,M,G} (3074048-41943039,默认为 41943039): +将使用默认值 41943039 +分区 4 已设置为 Extended 类型,大小设为 18.5 GiB + + +扩展分区创建完毕 + +开始创建逻辑分区sda5 + +命令(输入 m 获取帮助):p + +磁盘 /dev/sda:21.5 GB, 21474836480 字节,41943040 个扇区 +Units = 扇区 of 1 * 512 = 512 bytes +扇区大小(逻辑/物理):512 字节 / 512 字节 +I/O 大小(最小/最佳):512 字节 / 512 字节 +磁盘标签类型:dos +磁盘标识符:0x00092946 + + 设备 Boot Start End Blocks Id System +/dev/sda1 * 2048 1026047 512000 83 Linux +/dev/sda2 1026048 2050047 512000 83 Linux +/dev/sda3 2050048 3074047 512000 83 Linux +/dev/sda4 3074048 41943039 19434496 5 Extended + +命令(输入 m 获取帮助):n +All primary partitions are in use +添加逻辑分区 5 +起始 扇区 (3076096-41943039,默认为 3076096): +将使用默认值 3076096 +Last 扇区, +扇区 or +size{K,M,G} (3076096-41943039,默认为 41943039):+500M +分区 5 已设置为 Linux 类型,大小设为 500 MiB + +命令(输入 m 获取帮助):p + +磁盘 /dev/sda:21.5 GB, 21474836480 字节,41943040 个扇区 +Units = 扇区 of 1 * 512 = 512 bytes +扇区大小(逻辑/物理):512 字节 / 512 字节 +I/O 大小(最小/最佳):512 字节 / 512 字节 +磁盘标签类型:dos +磁盘标识符:0x00092946 + + 设备 Boot Start End Blocks Id System +/dev/sda1 * 2048 1026047 512000 83 Linux +/dev/sda2 1026048 2050047 512000 83 Linux +/dev/sda3 2050048 3074047 512000 83 Linux +/dev/sda4 3074048 41943039 19434496 5 Extended +/dev/sda5 3076096 4100095 512000 83 Linux + +命令(输入 m 获取帮助):n +All primary partitions are in use +添加逻辑分区 6 +起始 扇区 (4102144-41943039,默认为 4102144): +将使用默认值 4102144 +Last 扇区, +扇区 or +size{K,M,G} (4102144-41943039,默认为 41943039): +将使用默认值 41943039 +分区 6 已设置为 Linux 类型,大小设为 18 GiB +``` +别忘了最后w保存修改。 +###### 格式化分区 +`mkfs -t ext4 /dev/sda2` + + +###### 建立挂载点并挂载 +``` +mkdir /disk1 + +mount /dev/sdb1 /disk1 +``` + +##### 分区自动挂载与fstab文件修复 +>命令挂载,reboot之后挂载会消失,所以我们需要开机自动挂载,写入/etc/fstab文件,写入文件要小心,否则系统崩溃。 + +`/etc/fstab` +``` +[root@localhost dev]# cat /etc/fstab + +# +# /etc/fstab +分区设备文件名或uuid:挂载点:文件系统名称:挂在参数:指定分区是否被dump备份(0不,1每天备份,2不定期备份):指定分区是否被fsck检测(0不,其他表示优先级1>2) +/dev/mapper/rhel-root / xfs defaults 1 1 +UUID=c2bb7ebb-1d45-4d22-81ee-11ece3d6e1b0 /boot xfs defaults 1 2 +/dev/mapper/rhel-swap swap swap defaults 0 0 + +``` +写完后 `mount -a `查看是否有报错。 +### 3. /etc/fstab 文件修复 +>一旦文件报错,则需要修复。 + +``` +1. 开机进入报错界面,最下面提示输入root密码 +2. 登录root后vim /etc/fstab。 +3. 如果有权限不够,则需要mount -o remount,rw / 将分区可读写 +4. 修改/etc/fstab + +``` +### 4. 分配swap分区 +>当我们内存不够用,就会使用swap分区暂时来与cpu做交互,性质与windows的虚拟内存类似(sys.page文件)。 + +##### 查看交换分区 + +`swapon -s` +``` +[roo@localhost ~]$ swapon -s +文件名 类型 大小 已用 权限 +/dev/dm-1 partition 2097148 0 -1 +``` +或者查看文件`/proc/swaps` +``` +[roo@localhost ~]$ cat /proc/swaps +Filename Type Size Used Priority +/dev/dm-1 partition 2097148 0 -1 +``` +其中,Priority表示swap分区的优先级,可以通过`swapon -p x /dev/sda3` +##### 设置交换分区 +``` +mkswap /dev/sda3 格式化分区,注意此时sda3并没有成为swap分区 + +swapon /dev/sda3 将sda3设置成swap分区 + +swapon 查看设置结果 +``` + +##### 关闭交换分区 +`swapoff /dev/sda3` + +##### 永久生效 + +将其写入`/etc/fstab`文件中 + +写入格式: +``` +/dev/sdba3 swap swap defaults 0 0 +``` +然后重新挂载`mount -a` diff --git "a/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/\347\224\250\346\210\267\345\222\214\347\224\250\346\210\267\347\273\204.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/\347\224\250\346\210\267\345\222\214\347\224\250\346\210\267\347\273\204.md" new file mode 100644 index 0000000..65a305e --- /dev/null +++ "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/\347\224\250\346\210\267\345\222\214\347\224\250\346\210\267\347\273\204.md" @@ -0,0 +1,199 @@ +### 用户信息文件 +/etc/passwd + +``` +[root@FantJ ~]# cat /etc/passwd +用户名:密码标志:UID:GID:用户说明:家目录:登录后的shell +root:x:0:0:root:/root:/bin/bash +bin:x:1:1:bin:/bin:/sbin/nologin +daemon:x:2:2:daemon:/sbin:/sbin/nologin +adm:x:3:4:adm:/var/adm:/sbin/nologin +lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin +sync:x:5:0:sync:/sbin:/bin/sync +shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown + +``` +uid:0是超级用户;1-499是系统用户;500-65535普通用户 +### 影子文件 +/etc/shadow +``` +[root@FantJ ~]# cat /etc/shadow +用户名:加密密码(SHA512加密,!!和*代码无密,不能登录):密码最后一次修改日期(1970.1.1起):两次密码修改时间间隔:密码有效期:密码到期警告天数:密码过期后宽限天数(0代表立即失效,1代表永远不失效):账号失效时间:保留字段 +root:$6$kNnqtN1f$9mhJiIF50OVbQdCSxsbleqGdG8.9nyXDexn/yNkjsSczgNmWqKe6y3mgVyStOwfmaXulppdXCBLDBBaisHUr/.:17638:0:99999:7::: +bin:*:16659:0:99999:7::: +daemon:*:16659:0:99999:7::: +adm:*:16659:0:99999:7::: +lp:*:16659:0:99999:7::: +sync:*:16659:0:99999:7::: +shutdown:*:16659:0:99999:7::: +``` +### 组信息文件 +/etc/group + +``` +[root@FantJ ~]# cat /etc/group +组名:组密码标志:GID:组中附加用户 +root:x:0: +bin:x:1: +daemon:x:2: +sys:x:3: +adm:x:4: +tty:x:5: +disk:x:6: +lp:x:7: + +``` +### 组密码文件 +/etc/gshadow +和用户密码文件原理类似 + + +### 用户家目录 +普通用户:/home/用户名,权限默认700 + +超级用户:/root/,默认权限550 + +### 用户的邮箱 + +`/var/spool/mail/用户名/` + + +### 用户模版目录 +>用户创建时候需要从该处copy一份配置到家目录下。一般为隐藏目录 + +`/etc/skel` + +### 用户管理命令 + +##### `useradd` 用户添加命令 +`useradd [optional] 用户名` + +-u uid + +-d 家目录 + +-c 用户说明 + +-g 初始组 + +-G 附加组 + +-s 指定登录shell,默认/bin/bash + +###### useradd配置文件 +>有两个配置文件来管理useradd的配置,一个是`/etc/default/useradd`,一个是`/etc/login.defs` + +``` +[root@FantJ ~]# cat /etc/default/useradd +# useradd defaults file +GROUP=100 用户默认组 +HOME=/home 用户家目录 +INACTIVE=-1 密码过期宽限天数 +EXPIRE= 密码失效时间 +SHELL=/bin/bash 默认shell +SKEL=/etc/skel 模版目录 +CREATE_MAIL_SPOOL=yes 是否建立邮箱 + +``` +``` +[root@FantJ ~]# cat /etc/login.defs + +MAIL_DIR /var/spool/mail + +PASS_MAX_DAYS 99999 密码有效期 +PASS_MIN_DAYS 0 密码修改间隔 +PASS_MIN_LEN 5 密码最小5位 +PASS_WARN_AGE 7 密码到期警告 + +UID_MIN 1000 最小和最大uid +UID_MAX 60000 +# System accounts +SYS_UID_MIN 201 +SYS_UID_MAX 999 + +GID_MIN 1000 +GID_MAX 60000 +# System accounts +SYS_GID_MIN 201 +SYS_GID_MAX 999 + +CREATE_HOME yes + +# The permission mask is initialized to this value. If not specified, +# the permission mask will be initialized to 022. +UMASK 077 + +USERGROUPS_ENAB yes + +# Use SHA512 to encrypt password. +ENCRYPT_METHOD SHA512 加密模式 +``` + +##### `passwd` 修改用户密码 +`passwd [] 用户名` + +-S 查询用户密码状态,仅root可用 + +-l 暂时锁定用户,仅root可用 + +-u 解锁用户,仅root可用 + +--stdin 可通过grep输出数据作为用户密码 + +普通用户修改密码时直接`passwd`命令回车 + +###### 查看密码状态 +`passwd -S xxx` + +``` +[root@FantJ ~]# passwd -S ftpuser +用户名 :密码设定时间:密码修改时间间隔:密码有效期:警告时间:密码不失效 +ftpuser PS 2018-04-27 0 99999 7 -1 (Password set, SHA512 crypt.) +``` +###### 用户锁定 +`passwd -l xxx` + +相当于在shadow 文件中,用户密码前加了两个感叹号。 +##### `usermod` 修改用户信息 +`usermod [] 用户名` + +-u uid +-c 用户说明 +-G 组名 +-L 临时锁定用户 +-U 解锁用户 +##### `chage` 修改密码状态 +`chage [] 用户名` + +略,不常用 +##### `userdel` 删除用户 +`userdel [-r] 用户名` + +-r 删除用户的同时删除用户家目录 +### 添加用户组 +`groupadd [] 组名` + +-g gid + +##### 修改用户组名 +`groupmod -n name1 name2` + +-n 修改组名 + +##### 删除用户组 +`groupdel groupName` + +注意:删组要先删用户 + + +##### 修改用户所在组 +`gpasswd -a source target` +##### 从组中删除用户 +`gpasswd -d source target` + +##### 添加已有用户到已有组 + +`usermod -a -G groupName userName` + +##### 给文件设置用户组 +'chgrp groupname file' \ No newline at end of file diff --git "a/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/\347\263\273\347\273\237\345\220\257\345\212\250\350\277\207\347\250\213\345\222\214\346\225\205\351\232\234\344\277\256\345\244\215.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/\347\263\273\347\273\237\345\220\257\345\212\250\350\277\207\347\250\213\345\222\214\346\225\205\351\232\234\344\277\256\345\244\215.md" new file mode 100644 index 0000000..f31aaad --- /dev/null +++ "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/\347\263\273\347\273\237\345\220\257\345\212\250\350\277\207\347\250\213\345\222\214\346\225\205\351\232\234\344\277\256\345\244\215.md" @@ -0,0 +1,70 @@ +bios > MBR + + +bios:检测计算机硬件是否有问题。 + +MBR:硬盘开头特殊分区,主引导扇区。大小512B。分别是:446节(引导程序grub)+64B分区表(16*4)+2B结束符。 + +grub:有效的将内核加载到内存。grub会生成一个微系统。文件放在`/boot/grub2/grub.cfg`,该配置文件由`grub2-mkconfig`工具生成,该工具通过读取`/etc/grub.d/`目录下的脚本来生成配置,该目录下的`00_header`文件从`/etc/default/grup`中获取参数来加载配置,每个脚本文件的功能不同,所以,如果我们想改一些grub配置,只需要修改`/etc/default/grup`文件中的参数,然后执行`grub2-mkconfig -o /boot/grub2/grub.cfg`来重新生成grub.cfg文件即可。 + + + +#### 一级修复:进入单用户模式 + +开机按`e`进入编辑,然后在linux内核信息那行最后边加`s`,然后ctrl+x启动。 + +单用户使得执行线程数减少。 + +#### 二级修复:进入应急模式 + +开机按`e`进入编辑,然后在linux内核信息那行最后边加`emergency`,然后ctrl+x启动。 + +会以最小的要求来启动系统。 + +#### :修复root密码 + +开机按`e`进入编辑,然后在linux内核信息那行最后边先删除`rhgb quiet`然后再加`init=/bin/sh`,然后ctrl+x启动。 + +然后`mount -o remount,rw /`挂载为可读可写 + +然后执行`passwd`命令。 + + +#### grub加密 +>那如果都这样改密码,系统岂不是很不安全。所以我们需要一些手段:修改grub机制 + +``` +[root@s169 grub2]# cd /etc/grub.d/ +[root@s169 grub.d]# ls +00_header 01_users 20_linux_xen 30_os-prober 41_custom +00_tuned 10_linux 20_ppc_terminfo 40_custom README +``` +然后修改`00_header`文件,在最后面插入两行 +``` +cat <为了网络通讯的提高性能和容灾备份,同时将两个网卡与交换机来通讯,能增大通信速率,但是并不是简单的1+1,而是逻辑上将两个网卡合成一个网卡(称作term)然后与交换机通信,此时,term可以看做master节点,两个本机网卡可以看做是slave节点。 + +优点:双卡热备、负载均衡 + +### 配置 + +###### 添加team + +`nmcli connection add type team ifname team0 config {\"runner\":{\"name\":\"activebackup\"}}` + +注意要转义冒号。 +``` +[root@fantj network-scripts]# nmcli connection add type team ifname \ +> team0 config {"runner":{"name":"activebackup"}} +Connection 'team-team0' (86b19368-5b73-4ade-b5ff-f765c6e24856) successfully added. +[root@fantj network-scripts]# nmcli connection show +名称 UUID 类型 设备 +eth2 15ee8a85-8003-4ac2-8c5b-642efa0ca832 802-3-ethernet -- +ens33 13756690-ac77-b776-4fc1-f5535cee6f16 802-3-ethernet eno16777736 +eth1 bd5b981e-3518-4dee-8aad-0b756622ab55 802-3-ethernet -- +team-team0 86b19368-5b73-4ade-b5ff-f765c6e24856 team -- +``` + +##### 给team配置ip +赋予ip:`nmcli connection modify team-team0 ipv4.address "192.168.27.155/24"` + +改为手动: +`nmcli connection modify team-team0 ipv4.method manual` + +##### 给team配置slave +`nmcli connection add type team-slave con-name team0-port1 ifname eth1 master team-team0` + +同理,给网卡二也加入slave + + +##### 关闭原来网卡,开启slave节点网卡 +`nmcli connection up/down xxx ` +然后会发现,三个网卡的ip地址是一样的。 + + +##### team状态管理 + +`teamnl teamname state` + + +##### teamm设置属性值 +获取属性值: +`teamnl teamname getoption xxx` + + +设置属性值: +`teamnl teamname setoption xxx` + + + +### 桥接 + +>桥,与交换机的功能相似,当有数据到达时,会根据报文中的 MAC 信息进行广播、转发、丢弃处理。 + + +##### 创建交换机 +以前的方式: +`brctl addbr br0 +` + +rhel7方式:` nmcli connection add con-name br1 type bridge ifname br2 +` +##### 查看交换机 + +`nmcli connection show ` +``` +[root@fantj network-scripts]# nmcli connection show +名称 UUID 类型 设备 +team-team0 86b19368-5b73-4ade-b5ff-f765c6e24856 team -- +ens33 13756690-ac77-b776-4fc1-f5535cee6f16 802-3-ethernet eno33554992 +ethernet-eth2 74563dc7-d64c-4dde-b8a5-1f67a1c0465e 802-3-ethernet eno16777736 +br0 eafdc224-ff09-4fa6-bfd1-fd75d3a9e6f9 bridge br0 +``` + +##### 创建桥接 + +`nmcli connection add type bridge-slave con-name br0-port1 ifname eth1 master br0` \ No newline at end of file diff --git "a/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/\350\275\257\344\273\266\345\214\205\347\256\241\347\220\206rpm.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/\350\275\257\344\273\266\345\214\205\347\256\241\347\220\206rpm.md" new file mode 100644 index 0000000..3aefe6f --- /dev/null +++ "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/\350\275\257\344\273\266\345\214\205\347\256\241\347\220\206rpm.md" @@ -0,0 +1,74 @@ +### 软件包分类 +1. 源码包:脚本安装包:安装慢但能看到源码 +2. 二进制包(RPM、系统默认包):安装快但不知道源码 + +#### rpm包命名规则 +``` +httpd-2.215-15.e16.centos.1.i686,rpm + +httpd 软件包名 +2.2.15 软件版本 +15 软件发布次数 +e16.centos 适合的linux平台 +i686 适合的硬件平台 +rpm rpm包扩展名 + +``` + +#### rpm包依赖性 + +树形依赖:a>b>c +环形依赖:a>b>c>a 需要abc同时装 +模块依赖:查询网站 www.rpmfind.net + + +### RPM安装 +`rpm -ivh 包全名` + +-i install +-v 显示详细信息verbose +-h 显示进度hash +--nodeps 不检测依赖性 + +### RPM包查询 +`rpm -qa |grep xxx` + +### RPM包升级 +`rpm -Uvh 包全名` +-U upgrade升级 + +### RPM包卸载 +`rpm -e 包名` +-e erase卸载 +--nodeps 不检查依赖性 + + +### 查询RPM包是否安装 +`rpm -q 包名` +-q query查询 +-a all所有 + +### 查询RPM包详细信息 +`rpm -qi 包名` +-p package包信息 +-i information软件信息 + +### 查询RPM包中文件安装位置 +`rpm -ql 包名` + +-l list列表 + +-p 包信息package + +### 查询软件包的依赖性 +`rpm -qR 包名` + +-R 依赖性requires + +### RPM包校验 +>可以查看对原生包做的修改 + +`rpm -V 已安装包名` +-V verify校验 + + diff --git "a/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/\351\202\256\344\273\266\346\234\215\345\212\241.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/\351\202\256\344\273\266\346\234\215\345\212\241.md" new file mode 100644 index 0000000..e7ed929 --- /dev/null +++ "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/\351\202\256\344\273\266\346\234\215\345\212\241.md" @@ -0,0 +1,61 @@ +>rhel默认有postfix服务并且默认激活。默认端口25. + +``` +[root@fantj ~]# systemctl is-enabled postfix.service +enabled +[root@fantj ~]# systemctl is-active postfix.service +active +``` + +日志地址:`/var/log/maillog` + +### 1. 打开端口/服务 + +`firewall-cmd --permanent --add-port=25/tcp` +``` +[root@fantj ~]# firewall-cmd --permanent --add-port=25/tcp +success +``` +### 2. 配置文件 + +`/etc/postfix` + +``` +[root@fantj ~]# cd /etc/postfix/ +[root@fantj postfix]# ls +access canonical generic header_checks main.cf master.cf relocated transport virtual +``` +###### 查看所有默认参数 + +`postconf -d`统计行信息:`postconf -d | wc -l` + +###### 查看某个参数 +`postconf paramName` + +###### 命令行修改参数 + +`postconf -e paramName=xxxx` + +##### 主要配置文件1:`main.cf` +>主要控制组件和队列的信息,一般不需要修改,默认值就好。 + +1. `myhostname`和`mydomain`取值。`myhostname`默认取本机`hostname`,`mydomain`从m`yhostname`的第一个点后开始取值。。 +2. `myorigin`指的是@后面的值。 +3. `inet_interfaces = all`对所有ip开启端口。相当于0.0.0.0 +4. `inet_protocols=all`支持ipv4和ipv6 +5. `mydestination=` + +``` + +5. mydestination = $myhostname, localhost.$mydomain, localhost ,$mydomain + 设置目的地,若@后面的值(也就是myorigin值)在这里找不到,邮件将发送不出去 + +6. local_transport 收到邮件后如何进行投递和转发,默认值local:$myhostname + +7. relayhost =[ip.xx.xx.xx] 设置邮件网关,任何邮件都会被转发到此ip处理 + + +``` + +##### 主要配置文件2:`master.cf` +> \ No newline at end of file diff --git "a/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/\351\230\262\347\201\253\345\242\231.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/\351\230\262\347\201\253\345\242\231.md" new file mode 100644 index 0000000..e1edd59 --- /dev/null +++ "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/RedHat\350\200\203\347\272\247/\351\230\262\347\201\253\345\242\231.md" @@ -0,0 +1,215 @@ +### 1. 系统自带防火墙tcpwraps +>系统自带两个文件`/etc/hosts.allow`和`/etc/hosts.deny` + +###### 匹配规则: +1. 首先到/etc/hosts.allow里面去匹配,如果匹配成功,则直接通过 +2. 在allow里没有匹配成功的话,则到/etc/hosts.deny里去匹配,如果在deny匹配成功,则被拒绝,否则会通过。 + + +###### 测试: + +配置allow规则:`sshd : xxx.xxx.xxx.xx` +``` +[root@fantj etc]# cat hosts.allow +# +# hosts.allow This file contains access rules which are used to +sshd : 192.168.27.1 +``` +配置deny同理。 + +配置允许一个网段:`sshd : 192.168.27. EXCEPT 192.168.27.110`允许192.168.27这个网段中除了110ip以外的ip访问。 + +配置deny同理。 + +如果allow和deny同时都有一个相同的ip,则会允许,因为系统扫完allow确定了allow结果,不会再扫deny了。 + +写完立即生效,不需要重启服务。 + +tcpwraps所支持的服务是有限的。一般就是限制ssh。 + + +### 2. firewall +动态防火墙,所设置的配置立即生效。 +>在rhel7中,默认同时存在3中防火墙:firewalld iptables ebtables,后两者默认没有启用,firewall默认是启用的。因为三个daemon胡想冲突,建议mask其他两种daemon。 + +mask 是屏蔽(让它不能启动)的意思。 + +##### 两个工具 +firewall底层调用iptables命令,工具有两个:`firewall-cmd`和`firewall-config`图形化。 + + +##### 查看有多少个域 +>什么是域。系统中有默认的zone,不经过zone过滤不能进入内核,这些域过滤的强度不同,也就是不同的zone里对数据流量的信任度不同,信任排序:(默认域是public) + +注意:**一个网络接口只能连接一个域**。比如:eth1连接drop域,用来拒绝这个网络接口的所有请求。 + +1. drop:所有进来的流量直接丢弃 +2. block:所有进来的流量也被拒绝。允许通过开机时候dhcp获取ip。 +3. public:默认允许几个服务可以通过,可通过`firewall-cmd --list-services`查看。如果需要某个服务通过,将服务添加至public即可。 +4. trusted:所有进来的流量直接请求。(一般与内网接口相接) + +如果一个接口没有zone,则会使用默认规则,默认规则可通过`firewall-cmd --get-default-zone`查看。 + +###### 给接口添加zone +`firewall-cmd --zone=public --add-interface=eth0` + +###### 给接口修改zone +`firewall-cmd --zone=trusted --change-interface=eth0` + +###### 从域中移除接口 +`firewall-cmd --zone=trusted --remove-interface=eth0` + +###### 获取域信息 + +`firewall-cmd --list-all-zones`或者`firewall-cmd --get-zones` + +###### 获取默认域 + +`firewall-cmd --get-default-zone` + +``` +[root@fantj etc]# firewall-cmd --get-default-zone +public +``` + +###### 修改默认域 + +`firewall-cmd --set-default-zone=external` + +###### 查看所支持的服务 +`firewall-cmd --get-services` + + +###### 查看public域里开启的服务 + +`firewall-cmd --zone=public --list-services` + +也可以用`firewall-cmd --zone=public --list-all`来查看详情 + +###### 从域中取消服务 +`firewall-cmd --zone=public --remove-service=httpd` +同理,添加是`--add-service` + +###### 查看网卡接口所属域 +>上文讲过一个接口对应一个域。 + +`firewall-cmd --get-zone-of-interface=eth0` + +###### firewall服务重载 + +`firewall-cmd --reload` + +###### 查看域中开放端口 + +`firewall-cmd --zone=public query-port=80/tcp` + +###### 开启域中端口 +`firewall-cmd --zone=public --add-port=80/tcp` + +###### 关闭域中端口 +`firewall-cmd --zone=public --remove-port=80/tcp` + + +#### 2.2 设置ip源去哪个域 +>设置凡是来自192.168.27.0/24这个网段的数据包都走public这个域。 + + +###### 添加源 +>和一个接口对应一个zone一样,一个源也是对应一个zone。 + +添加源:`firewall-cmd --zone=public --add-source=192.168.27.0/24` + +查看源列表:`firewall-cmd --zone=public --list-sources` + +#### 2.3 特殊的 ping 规则 + +>ping是ICMP协议,默认不选是允许的,选中是拒绝的。ping有两个服务:`echo-request`和`echo-reply`. + +#### 2.4 启动应急模式 +>拒绝所有访问。 +`firewall-cmd --panic-on` + +同理,关闭为`firewall-cmd --panic-off` + +#### 2.5 永久修改 + +`firewall-cmd --permanent xxxx`及时成效。 + +#### 2.6 请求转发 + + +###### 本地端口转发至别的ip: +`firewall-cmd --add-forward-port=port=80:proto=tcp:toaddr=192.168.27.120` + +###### 本地端口转发至别的ip端口 + +将本地的80端口的请求转发到120的8080端口: +`firewall-cmd --add-forward-port=port=80:proto=tcp:toaddr=192.168.27.120:toport=8080` + +###### 删除请求转发 + +把`add` 改为`remove` +###### 查看请求转发列表 + +`firewall-cmd --list-forward-ports` + + +#### 2.7 富规则 + +格式:`firewall-cmd --add-rich-rule='rule xxx'` + +`firewall-cmd --add-rich-rule='rule family=ipv4 source address=192.168.111.0/24'` +默认会给源地址加伪装。 + + +###### 查看富规则列表 + +`firewall-cmd --list-rich-rules` + +###### 删除富规则 +`firewall-cmd --remove=rich-rule='xxxxx'` + +#### 2.8 伪装 +>给所有的子网做NAT转换。但是如果需要对子网网段的权限做划分(192.168.27可以访问,192.168.28不能访问)的话,就不适合使用了。可以用富规则去实现。 + +###### 设置伪装 +`firewall-cmd --add-masquerade` + + +注意:该设置对全局子网有效,如果子网中有不同权限的分段处理操作,请用富规则来实现。 + +#### 2.9 限流 + +`firewall-cmd --add-rich-rule='rule family=ipv4 service name=ftp limit value=2/m reject'` + +service name=ftp,表示ftp服务 + +2表示包个数,/m表示分钟。 + + +### SELinux(了解) + + +###### 查看所有端口上下文 + +`semanage port -l` + +``` +[root@fantj ~]# semanage port -l +SELinux 端口类型 协议 端口号 + +afs3_callback_port_t tcp 7001 +afs3_callback_port_t udp 7001 +``` + +###### 给端口添加上下文 + +`semanage port -a -t afs3_callback_port_t -p tcp 7002` 给`afs3_callback_port_t`添加70002端口上下文。 + +###### 删除端口上下文 + +-d + +###### 修改端口上下文 + +-m diff --git "a/RedHat-CentOS/Redhat7-x-\344\277\256\346\224\271\351\230\277\351\207\214\344\272\221yum\346\272\220.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/Redhat7-x-\344\277\256\346\224\271\351\230\277\351\207\214\344\272\221yum\346\272\220.md" similarity index 100% rename from "RedHat-CentOS/Redhat7-x-\344\277\256\346\224\271\351\230\277\351\207\214\344\272\221yum\346\272\220.md" rename to "7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/Redhat7-x-\344\277\256\346\224\271\351\230\277\351\207\214\344\272\221yum\346\272\220.md" diff --git "a/Centos\345\256\211\350\243\205\345\267\245\345\205\267\346\226\207\351\233\206/SSH\346\227\240\345\257\206\347\240\201\347\231\273\345\275\225\350\256\276\347\275\256.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/SSH\346\227\240\345\257\206\347\240\201\347\231\273\345\275\225\350\256\276\347\275\256.md" similarity index 100% rename from "Centos\345\256\211\350\243\205\345\267\245\345\205\267\346\226\207\351\233\206/SSH\346\227\240\345\257\206\347\240\201\347\231\273\345\275\225\350\256\276\347\275\256.md" rename to "7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/SSH\346\227\240\345\257\206\347\240\201\347\231\273\345\275\225\350\256\276\347\275\256.md" diff --git "a/RedHat-CentOS/centos-\345\206\205\347\275\221\347\251\277\351\200\217.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/centos-\345\206\205\347\275\221\347\251\277\351\200\217.md" similarity index 100% rename from "RedHat-CentOS/centos-\345\206\205\347\275\221\347\251\277\351\200\217.md" rename to "7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/centos-\345\206\205\347\275\221\347\251\277\351\200\217.md" diff --git "a/RedHat-CentOS/centos\350\277\236\346\216\245vpn.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/centos\350\277\236\346\216\245vpn.md" similarity index 100% rename from "RedHat-CentOS/centos\350\277\236\346\216\245vpn.md" rename to "7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/centos\350\277\236\346\216\245vpn.md" diff --git "a/RedHat-CentOS/fedora U\347\233\230\344\277\256\345\244\215grub\345\274\225\345\257\274.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/fedora U\347\233\230\344\277\256\345\244\215grub\345\274\225\345\257\274.md" similarity index 100% rename from "RedHat-CentOS/fedora U\347\233\230\344\277\256\345\244\215grub\345\274\225\345\257\274.md" rename to "7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/fedora U\347\233\230\344\277\256\345\244\215grub\345\274\225\345\257\274.md" diff --git "a/RedHat-CentOS/ipv4\347\275\221\347\273\234.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/ipv4\347\275\221\347\273\234.md" similarity index 100% rename from "RedHat-CentOS/ipv4\347\275\221\347\273\234.md" rename to "7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/ipv4\347\275\221\347\273\234.md" diff --git "a/RedHat-CentOS/scp\347\224\250\346\263\225\347\256\200\344\273\213.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/scp\347\224\250\346\263\225\347\256\200\344\273\213.md" similarity index 100% rename from "RedHat-CentOS/scp\347\224\250\346\263\225\347\256\200\344\273\213.md" rename to "7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/scp\347\224\250\346\263\225\347\256\200\344\273\213.md" diff --git "a/RedHat-CentOS/shell-\344\270\200\344\272\233\347\256\200\345\215\225\345\270\270\347\224\250\347\232\204\350\204\232\346\234\254.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/shell-\344\270\200\344\272\233\347\256\200\345\215\225\345\270\270\347\224\250\347\232\204\350\204\232\346\234\254.md" similarity index 100% rename from "RedHat-CentOS/shell-\344\270\200\344\272\233\347\256\200\345\215\225\345\270\270\347\224\250\347\232\204\350\204\232\346\234\254.md" rename to "7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/shell-\344\270\200\344\272\233\347\256\200\345\215\225\345\270\270\347\224\250\347\232\204\350\204\232\346\234\254.md" diff --git "a/RedHat-CentOS/vim\344\270\255\346\226\207\344\271\261\347\240\201.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/vim\344\270\255\346\226\207\344\271\261\347\240\201.md" similarity index 100% rename from "RedHat-CentOS/vim\344\270\255\346\226\207\344\271\261\347\240\201.md" rename to "7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/vim\344\270\255\346\226\207\344\271\261\347\240\201.md" diff --git "a/RedHat-CentOS/\345\220\214\346\255\245\346\227\266\351\227\264.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/\345\220\214\346\255\245\346\227\266\351\227\264.md" similarity index 100% rename from "RedHat-CentOS/\345\220\214\346\255\245\346\227\266\351\227\264.md" rename to "7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/\345\220\214\346\255\245\346\227\266\351\227\264.md" diff --git "a/RedHat-CentOS/\345\256\211\350\243\205yum\346\272\220.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/\345\256\211\350\243\205yum\346\272\220.md" similarity index 100% rename from "RedHat-CentOS/\345\256\211\350\243\205yum\346\272\220.md" rename to "7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/\345\256\211\350\243\205yum\346\272\220.md" diff --git "a/RedHat-CentOS/Linux\345\221\275\344\273\244\345\217\202\346\225\260\350\257\246\347\273\206\350\247\243\346\236\220-cp.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/\345\270\270\347\224\250\345\221\275\344\273\244/Linux\345\221\275\344\273\244\345\217\202\346\225\260\350\257\246\347\273\206\350\247\243\346\236\220-cp.md" similarity index 100% rename from "RedHat-CentOS/Linux\345\221\275\344\273\244\345\217\202\346\225\260\350\257\246\347\273\206\350\247\243\346\236\220-cp.md" rename to "7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/\345\270\270\347\224\250\345\221\275\344\273\244/Linux\345\221\275\344\273\244\345\217\202\346\225\260\350\257\246\347\273\206\350\247\243\346\236\220-cp.md" diff --git "a/RedHat-CentOS/Linux\345\221\275\344\273\244\345\217\202\346\225\260\350\257\246\347\273\206\350\247\243\346\236\220-curl.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/\345\270\270\347\224\250\345\221\275\344\273\244/Linux\345\221\275\344\273\244\345\217\202\346\225\260\350\257\246\347\273\206\350\247\243\346\236\220-curl.md" similarity index 100% rename from "RedHat-CentOS/Linux\345\221\275\344\273\244\345\217\202\346\225\260\350\257\246\347\273\206\350\247\243\346\236\220-curl.md" rename to "7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/\345\270\270\347\224\250\345\221\275\344\273\244/Linux\345\221\275\344\273\244\345\217\202\346\225\260\350\257\246\347\273\206\350\247\243\346\236\220-curl.md" diff --git "a/RedHat-CentOS/Linux\345\221\275\344\273\244\345\217\202\346\225\260\350\257\246\347\273\206\350\247\243\346\236\220-cut.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/\345\270\270\347\224\250\345\221\275\344\273\244/Linux\345\221\275\344\273\244\345\217\202\346\225\260\350\257\246\347\273\206\350\247\243\346\236\220-cut.md" similarity index 100% rename from "RedHat-CentOS/Linux\345\221\275\344\273\244\345\217\202\346\225\260\350\257\246\347\273\206\350\247\243\346\236\220-cut.md" rename to "7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/\345\270\270\347\224\250\345\221\275\344\273\244/Linux\345\221\275\344\273\244\345\217\202\346\225\260\350\257\246\347\273\206\350\247\243\346\236\220-cut.md" diff --git "a/RedHat-CentOS/Linux\345\221\275\344\273\244\345\217\202\346\225\260\350\257\246\347\273\206\350\247\243\346\236\220-diff.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/\345\270\270\347\224\250\345\221\275\344\273\244/Linux\345\221\275\344\273\244\345\217\202\346\225\260\350\257\246\347\273\206\350\247\243\346\236\220-diff.md" similarity index 100% rename from "RedHat-CentOS/Linux\345\221\275\344\273\244\345\217\202\346\225\260\350\257\246\347\273\206\350\247\243\346\236\220-diff.md" rename to "7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/\345\270\270\347\224\250\345\221\275\344\273\244/Linux\345\221\275\344\273\244\345\217\202\346\225\260\350\257\246\347\273\206\350\247\243\346\236\220-diff.md" diff --git "a/RedHat-CentOS/Linux\345\221\275\344\273\244\345\217\202\346\225\260\350\257\246\347\273\206\350\247\243\346\236\220-mv.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/\345\270\270\347\224\250\345\221\275\344\273\244/Linux\345\221\275\344\273\244\345\217\202\346\225\260\350\257\246\347\273\206\350\247\243\346\236\220-mv.md" similarity index 100% rename from "RedHat-CentOS/Linux\345\221\275\344\273\244\345\217\202\346\225\260\350\257\246\347\273\206\350\247\243\346\236\220-mv.md" rename to "7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/\345\270\270\347\224\250\345\221\275\344\273\244/Linux\345\221\275\344\273\244\345\217\202\346\225\260\350\257\246\347\273\206\350\247\243\346\236\220-mv.md" diff --git "a/RedHat-CentOS/Linux\345\221\275\344\273\244\345\217\202\346\225\260\350\257\246\347\273\206\350\247\243\346\236\220-ping.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/\345\270\270\347\224\250\345\221\275\344\273\244/Linux\345\221\275\344\273\244\345\217\202\346\225\260\350\257\246\347\273\206\350\247\243\346\236\220-ping.md" similarity index 100% rename from "RedHat-CentOS/Linux\345\221\275\344\273\244\345\217\202\346\225\260\350\257\246\347\273\206\350\247\243\346\236\220-ping.md" rename to "7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/\345\270\270\347\224\250\345\221\275\344\273\244/Linux\345\221\275\344\273\244\345\217\202\346\225\260\350\257\246\347\273\206\350\247\243\346\236\220-ping.md" diff --git "a/RedHat-CentOS/Linux\345\221\275\344\273\244\345\217\202\346\225\260\350\257\246\347\273\206\350\247\243\346\236\220-vim.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/\345\270\270\347\224\250\345\221\275\344\273\244/Linux\345\221\275\344\273\244\345\217\202\346\225\260\350\257\246\347\273\206\350\247\243\346\236\220-vim.md" similarity index 100% rename from "RedHat-CentOS/Linux\345\221\275\344\273\244\345\217\202\346\225\260\350\257\246\347\273\206\350\247\243\346\236\220-vim.md" rename to "7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/\345\270\270\347\224\250\345\221\275\344\273\244/Linux\345\221\275\344\273\244\345\217\202\346\225\260\350\257\246\347\273\206\350\247\243\346\236\220-vim.md" diff --git "a/RedHat-CentOS/Linux\345\221\275\344\273\244\345\217\202\346\225\260\350\257\246\347\273\206\350\247\243\346\236\220-wget.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/\345\270\270\347\224\250\345\221\275\344\273\244/Linux\345\221\275\344\273\244\345\217\202\346\225\260\350\257\246\347\273\206\350\247\243\346\236\220-wget.md" similarity index 100% rename from "RedHat-CentOS/Linux\345\221\275\344\273\244\345\217\202\346\225\260\350\257\246\347\273\206\350\247\243\346\236\220-wget.md" rename to "7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/\345\270\270\347\224\250\345\221\275\344\273\244/Linux\345\221\275\344\273\244\345\217\202\346\225\260\350\257\246\347\273\206\350\247\243\346\236\220-wget.md" diff --git "a/RedHat-CentOS/Linux\345\221\275\344\273\244\345\217\202\346\225\260\350\257\246\347\273\206\350\247\243\346\236\220-yum.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/\345\270\270\347\224\250\345\221\275\344\273\244/Linux\345\221\275\344\273\244\345\217\202\346\225\260\350\257\246\347\273\206\350\247\243\346\236\220-yum.md" similarity index 100% rename from "RedHat-CentOS/Linux\345\221\275\344\273\244\345\217\202\346\225\260\350\257\246\347\273\206\350\247\243\346\236\220-yum.md" rename to "7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/\345\270\270\347\224\250\345\221\275\344\273\244/Linux\345\221\275\344\273\244\345\217\202\346\225\260\350\257\246\347\273\206\350\247\243\346\236\220-yum.md" diff --git "a/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/\345\270\270\347\224\250\345\221\275\344\273\244/\347\275\221\347\273\234\345\221\275\344\273\244.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/\345\270\270\347\224\250\345\221\275\344\273\244/\347\275\221\347\273\234\345\221\275\344\273\244.md" new file mode 100644 index 0000000..922a6d3 --- /dev/null +++ "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/\345\270\270\347\224\250\345\221\275\344\273\244/\347\275\221\347\273\234\345\221\275\344\273\244.md" @@ -0,0 +1,110 @@ +### write +>给用户发信息,ctrl+d保存结束。 + +条件:用户必须在线 + +### wall +>给所有用户发信息(广播信息) + +`wall hello` + + +### ping +>给远程计算机发信息包 + +`ping www.baidu.com` + +-c 可以指定ping几次 + + +### ifconfig +>查看当前网络信息 + +eth 代表真实网卡 +lo 代表回还网卡(127.0.0.1) + + +### mail +>查看发送电子邮件 + +`mail root` +ctrl+d 发送 + + +### last +>查看目前和过去用户登录信息 + + +``` +[root@FantJ ~]# last +用户 终端 ip地址 日期 状态 +root pts/1 101.7.155.145 Sat Sep 15 18:19 still logged in(当前在线) +root pts/1 101.7.180.144 Thu Sep 13 19:41 - 22:42 (03:01) (持续三分钟) +root pts/1 1.68.71.222 Wed Sep 12 15:24 - 16:15 (00:51) +root pts/1 218.26.72.133 Tue Sep 11 21:30 - 22:08 (00:37) +root pts/2 101.7.148.254 Mon Sep 10 19:40 - 21:32 (01:51) +``` +### lastlog +>查看某特性用户上次登录时间 + +``` +[root@FantJ ~]# lastlog +Username Port From Latest +root pts/1 101.7.155.145 Sat Sep 15 18:19:37 +0800 2018 +bin **Never logged in** +daemon **Never logged in** +el pts/1 Wed Apr 25 20:30:47 +0800 2018 +es pts/1 Sat Apr 28 13:17:40 +0800 2018 +``` + +查看某用户登录记录:`lastlog -u uid` + +### traceroute +>显示数据包到主机间的路径 + + +``` +traceroute www.baidu.com +``` + +### netstat +>显示网络相关信息 + +-t tcp协议 +-u udp协议 +-l 监听 +-r 路由 +-n 显示ip地址和端口号 + +示例: +``` +netstat -tlun 查看本机监听端口 +netstat -an 查看本机所有网络连接 +netstat -rn 查看本机路由表 +``` +``` +[root@FantJ ~]# netstat -tlun +Active Internet connections (only servers) +Proto Recv-Q Send-Q Local Address Foreign Address State +tcp 0 0 0.0.0.0:9000 0.0.0.0:* LISTEN +tcp 0 0 0.0.0.0:9002 0.0.0.0:* LISTEN +tcp 0 0 0.0.0.0:9003 0.0.0.0:* LISTEN +tcp 0 0 0.0.0.0:26379 0.0.0.0:* LISTEN +tcp 0 0 0.0.0.0:6379 0.0.0.0:* LISTEN + +... +[root@FantJ ~]# netstat -rn +Kernel IP routing table +Destination Gateway Genmask Flags MSS Window irtt Iface +0.0.0.0 172.31.239.253 0.0.0.0 UG 0 0 0 eth0 +169.254.0.0 0.0.0.0 255.255.0.0 U 0 0 0 eth0 +172.31.224.0 0.0.0.0 255.255.240.0 U 0 0 0 eth0 + +``` + +### setup +过时了 + + +### NetworkManager + diff --git "a/RedHat-CentOS/\346\200\216\346\240\267\346\260\270\344\271\205\346\214\202\350\275\275.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/\346\200\216\346\240\267\346\260\270\344\271\205\346\214\202\350\275\275.md" similarity index 100% rename from "RedHat-CentOS/\346\200\216\346\240\267\346\260\270\344\271\205\346\214\202\350\275\275.md" rename to "7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/\346\200\216\346\240\267\346\260\270\344\271\205\346\214\202\350\275\275.md" diff --git "a/RedHat-CentOS/\346\226\207\344\273\266\346\237\245\346\211\276.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/\346\226\207\344\273\266\346\237\245\346\211\276.md" similarity index 100% rename from "RedHat-CentOS/\346\226\207\344\273\266\346\237\245\346\211\276.md" rename to "7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/\346\226\207\344\273\266\346\237\245\346\211\276.md" diff --git "a/RedHat-CentOS/\347\243\201\347\233\230\347\256\241\347\220\206.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/\347\243\201\347\233\230\347\256\241\347\220\206.md" similarity index 100% rename from "RedHat-CentOS/\347\243\201\347\233\230\347\256\241\347\220\206.md" rename to "7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/\347\243\201\347\233\230\347\256\241\347\220\206.md" diff --git "a/Centos\345\256\211\350\243\205\345\267\245\345\205\267\346\226\207\351\233\206/\350\201\224\345\220\210\346\237\245\346\235\200\350\277\233\347\250\213&sed\345\221\275\344\273\244&\350\277\207\346\273\244\346\263\250\351\207\212\345\222\214\347\251\272\346\240\274.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/\350\201\224\345\220\210\346\237\245\346\235\200\350\277\233\347\250\213&sed\345\221\275\344\273\244&\350\277\207\346\273\244\346\263\250\351\207\212\345\222\214\347\251\272\346\240\274.md" similarity index 100% rename from "Centos\345\256\211\350\243\205\345\267\245\345\205\267\346\226\207\351\233\206/\350\201\224\345\220\210\346\237\245\346\235\200\350\277\233\347\250\213&sed\345\221\275\344\273\244&\350\277\207\346\273\244\346\263\250\351\207\212\345\222\214\347\251\272\346\240\274.md" rename to "7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/\350\201\224\345\220\210\346\237\245\346\235\200\350\277\233\347\250\213&sed\345\221\275\344\273\244&\350\277\207\346\273\244\346\263\250\351\207\212\345\222\214\347\251\272\346\240\274.md" diff --git "a/RedHat-CentOS/Emm\357\274\214qW3xT-2\357\274\210\347\237\277\346\234\272\350\277\233\347\250\213\357\274\211.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/\350\256\260\345\275\225\344\270\200\346\254\241\346\234\215\345\212\241\345\231\250\350\242\253\345\275\223\347\237\277\346\234\272\347\232\204\347\273\217\345\216\206.md" similarity index 100% rename from "RedHat-CentOS/Emm\357\274\214qW3xT-2\357\274\210\347\237\277\346\234\272\350\277\233\347\250\213\357\274\211.md" rename to "7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/\350\256\260\345\275\225\344\270\200\346\254\241\346\234\215\345\212\241\345\231\250\350\242\253\345\275\223\347\237\277\346\234\272\347\232\204\347\273\217\345\216\206.md" diff --git "a/RedHat-CentOS/\350\277\234\347\250\213\346\226\207\344\273\266\346\213\267\350\264\235.md" "b/7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/\350\277\234\347\250\213\346\226\207\344\273\266\346\213\267\350\264\235.md" similarity index 100% rename from "RedHat-CentOS/\350\277\234\347\250\213\346\226\207\344\273\266\346\213\267\350\264\235.md" rename to "7. Linux\344\270\223\351\242\230/7.1 \345\237\272\347\241\200\347\257\207/\350\277\234\347\250\213\346\226\207\344\273\266\346\213\267\350\264\235.md" diff --git "a/Centos\345\256\211\350\243\205\345\267\245\345\205\267\346\226\207\351\233\206/CentOS-\346\220\255\345\273\272-ftp-\346\234\215\345\212\241.md" "b/7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/CentOS-FTP\346\234\215\345\212\241\346\220\255\345\273\272.md" similarity index 100% rename from "Centos\345\256\211\350\243\205\345\267\245\345\205\267\346\226\207\351\233\206/CentOS-\346\220\255\345\273\272-ftp-\346\234\215\345\212\241.md" rename to "7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/CentOS-FTP\346\234\215\345\212\241\346\220\255\345\273\272.md" diff --git "a/Centos\345\256\211\350\243\205\345\267\245\345\205\267\346\226\207\351\233\206/CentOS7-0-\345\256\211\350\243\205-ElasticSearch-\346\220\234\347\264\242\345\274\225\346\223\216.md" "b/7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/CentOS7-ElasticSearch\345\256\211\350\243\205.md" similarity index 100% rename from "Centos\345\256\211\350\243\205\345\267\245\345\205\267\346\226\207\351\233\206/CentOS7-0-\345\256\211\350\243\205-ElasticSearch-\346\220\234\347\264\242\345\274\225\346\223\216.md" rename to "7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/CentOS7-ElasticSearch\345\256\211\350\243\205.md" diff --git "a/Centos\345\256\211\350\243\205\345\267\245\345\205\267\346\226\207\351\233\206/ftp+nginx\345\256\236\347\216\260\346\226\207\344\273\266\346\234\215\345\212\241\345\231\250.md" "b/7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/CentOS7-FTP+Nginx\345\256\236\347\216\260\346\226\207\344\273\266\346\234\215\345\212\241\345\231\250.md" similarity index 100% rename from "Centos\345\256\211\350\243\205\345\267\245\345\205\267\346\226\207\351\233\206/ftp+nginx\345\256\236\347\216\260\346\226\207\344\273\266\346\234\215\345\212\241\345\231\250.md" rename to "7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/CentOS7-FTP+Nginx\345\256\236\347\216\260\346\226\207\344\273\266\346\234\215\345\212\241\345\231\250.md" diff --git "a/Centos\345\256\211\350\243\205\345\267\245\345\205\267\346\226\207\351\233\206/FastDFS-&&-Nginx\345\256\236\347\216\260\345\210\206\345\270\203\345\274\217\346\226\207\344\273\266\346\234\215\345\212\241\345\231\250\357\274\210\344\270\200\357\274\211.md" "b/7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/CentOS7-FastDFS&Nginx\345\256\236\347\216\260\345\210\206\345\270\203\345\274\217\346\226\207\344\273\266\346\234\215\345\212\241\345\231\250.md" similarity index 100% rename from "Centos\345\256\211\350\243\205\345\267\245\345\205\267\346\226\207\351\233\206/FastDFS-&&-Nginx\345\256\236\347\216\260\345\210\206\345\270\203\345\274\217\346\226\207\344\273\266\346\234\215\345\212\241\345\231\250\357\274\210\344\270\200\357\274\211.md" rename to "7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/CentOS7-FastDFS&Nginx\345\256\236\347\216\260\345\210\206\345\270\203\345\274\217\346\226\207\344\273\266\346\234\215\345\212\241\345\231\250.md" diff --git "a/Centos\345\256\211\350\243\205\345\267\245\345\205\267\346\226\207\351\233\206/CentOS-7-x-\345\256\211\350\243\205JDK\345\222\214Hadoop.md" "b/7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/CentOS7-Hadoop\345\256\211\350\243\205.md" similarity index 100% rename from "Centos\345\256\211\350\243\205\345\267\245\345\205\267\346\226\207\351\233\206/CentOS-7-x-\345\256\211\350\243\205JDK\345\222\214Hadoop.md" rename to "7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/CentOS7-Hadoop\345\256\211\350\243\205.md" diff --git "a/Centos\345\256\211\350\243\205\345\267\245\345\205\267\346\226\207\351\233\206/CentOS7-x-\345\256\211\350\243\205Hive.md" "b/7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/CentOS7-Hive\345\256\211\350\243\205.md" similarity index 100% rename from "Centos\345\256\211\350\243\205\345\267\245\345\205\267\346\226\207\351\233\206/CentOS7-x-\345\256\211\350\243\205Hive.md" rename to "7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/CentOS7-Hive\345\256\211\350\243\205.md" diff --git "a/Centos\345\256\211\350\243\205\345\267\245\345\205\267\346\226\207\351\233\206/CentOS7-0\345\256\211\350\243\205Maven.md" "b/7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/CentOS7-Maven\345\256\211\350\243\205.md" similarity index 100% rename from "Centos\345\256\211\350\243\205\345\267\245\345\205\267\346\226\207\351\233\206/CentOS7-0\345\256\211\350\243\205Maven.md" rename to "7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/CentOS7-Maven\345\256\211\350\243\205.md" diff --git "a/Centos\345\256\211\350\243\205\345\267\245\345\205\267\346\226\207\351\233\206/redis\345\256\211\350\243\205\344\273\245\345\217\212java\350\277\236\346\216\245.md" "b/7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/CentOS7-Redis\345\256\211\350\243\205.md" similarity index 100% rename from "Centos\345\256\211\350\243\205\345\267\245\345\205\267\346\226\207\351\233\206/redis\345\256\211\350\243\205\344\273\245\345\217\212java\350\277\236\346\216\245.md" rename to "7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/CentOS7-Redis\345\256\211\350\243\205.md" diff --git "a/Centos\345\256\211\350\243\205\345\267\245\345\205\267\346\226\207\351\233\206/CentOS7-0-\345\256\211\350\243\205-RocketMq.md" "b/7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/CentOS7-RocketMq\345\256\211\350\243\205.md" similarity index 100% rename from "Centos\345\256\211\350\243\205\345\267\245\345\205\267\346\226\207\351\233\206/CentOS7-0-\345\256\211\350\243\205-RocketMq.md" rename to "7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/CentOS7-RocketMq\345\256\211\350\243\205.md" diff --git "a/Centos\345\256\211\350\243\205\345\267\245\345\205\267\346\226\207\351\233\206/CentOS7-x-\345\256\211\350\243\205-Spark\345\222\214\351\233\206\347\276\244\346\220\255\345\273\272.md" "b/7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/CentOS7-Spark\345\256\211\350\243\205\345\222\214\351\233\206\347\276\244\346\220\255\345\273\272.md" similarity index 100% rename from "Centos\345\256\211\350\243\205\345\267\245\345\205\267\346\226\207\351\233\206/CentOS7-x-\345\256\211\350\243\205-Spark\345\222\214\351\233\206\347\276\244\346\220\255\345\273\272.md" rename to "7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/CentOS7-Spark\345\256\211\350\243\205\345\222\214\351\233\206\347\276\244\346\220\255\345\273\272.md" diff --git "a/Centos\345\256\211\350\243\205\345\267\245\345\205\267\346\226\207\351\233\206/CentOS7-x-\345\256\211\350\243\205Sqoop.md" "b/7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/CentOS7-Sqoop\345\256\211\350\243\205.md" similarity index 100% rename from "Centos\345\256\211\350\243\205\345\267\245\345\205\267\346\226\207\351\233\206/CentOS7-x-\345\256\211\350\243\205Sqoop.md" rename to "7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/CentOS7-Sqoop\345\256\211\350\243\205.md" diff --git "a/7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/CentOS7-ZK\345\215\225\346\234\272\346\220\255\345\273\272.md" "b/7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/CentOS7-ZK\345\215\225\346\234\272\346\220\255\345\273\272.md" new file mode 100644 index 0000000..6f551b2 --- /dev/null +++ "b/7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/CentOS7-ZK\345\215\225\346\234\272\346\220\255\345\273\272.md" @@ -0,0 +1,30 @@ +### 单机安装 + +#### 目录结构 +![](https://upload-images.jianshu.io/upload_images/5786888-30439c11210644fe.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +contrib:附加功能 + +dist-maven:maven编译后产生的目录 + +recipes:demo案例 + +lib:依赖的jar包 + + +#### 配置zoo.cfg +``` +# 用于计算的时间单元。 +tickTime=2000 +# The number of ticks that the initial +# 用于集群,允许从节点连接并同步到master节点的初始化连接时间,以tickTime的倍数来表示 +initLimit=10 +# 用于集群,主从节点通讯请求和应答时间(心跳机制) +syncLimit=5 +# 数据目录 +dataDir=/tmp/zookeeper +# 客户端端口(用户客户端连接) +clientPort=2181 + +``` +#### 启动 +`./zkServer.sh start` \ No newline at end of file diff --git "a/Centos\345\256\211\350\243\205\345\267\245\345\205\267\346\226\207\351\233\206/CentOS7-0\345\256\211\350\243\205jdk-+-tomcat.md" "b/7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/CentOS7-jdk\343\200\201tomcat\345\256\211\350\243\205.md" similarity index 100% rename from "Centos\345\256\211\350\243\205\345\267\245\345\205\267\346\226\207\351\233\206/CentOS7-0\345\256\211\350\243\205jdk-+-tomcat.md" rename to "7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/CentOS7-jdk\343\200\201tomcat\345\256\211\350\243\205.md" diff --git "a/Centos\345\256\211\350\243\205\345\267\245\345\205\267\346\226\207\351\233\206/CentOS7-x-\345\256\211\350\243\205scala.md" "b/7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/CentOS7-scala\345\256\211\350\243\205.md" similarity index 100% rename from "Centos\345\256\211\350\243\205\345\267\245\345\205\267\346\226\207\351\233\206/CentOS7-x-\345\256\211\350\243\205scala.md" rename to "7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/CentOS7-scala\345\256\211\350\243\205.md" diff --git "a/Centos\345\256\211\350\243\205\345\267\245\345\205\267\346\226\207\351\233\206/CentOS-7-x-\345\256\211\350\243\205\351\230\277\351\207\214yum\346\272\220.md" "b/7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/CentOS7-\351\230\277\351\207\214yum\346\272\220\345\256\211\350\243\205.md" similarity index 100% rename from "Centos\345\256\211\350\243\205\345\267\245\345\205\267\346\226\207\351\233\206/CentOS-7-x-\345\256\211\350\243\205\351\230\277\351\207\214yum\346\272\220.md" rename to "7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/CentOS7-\351\230\277\351\207\214yum\346\272\220\345\256\211\350\243\205.md" diff --git "a/Centos\345\256\211\350\243\205\345\267\245\345\205\267\346\226\207\351\233\206/CentOS\345\256\211\350\243\205\351\230\277\351\207\214\344\272\221\345\205\215\350\264\271SSL\350\257\201\344\271\246(Nginx\347\211\210\346\234\254).md" "b/7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/CentOS7-\351\230\277\351\207\214\344\272\221\345\205\215\350\264\271SSL\350\257\201\344\271\246\345\256\211\350\243\205(Nginx\347\211\210\346\234\254).md" similarity index 100% rename from "Centos\345\256\211\350\243\205\345\267\245\345\205\267\346\226\207\351\233\206/CentOS\345\256\211\350\243\205\351\230\277\351\207\214\344\272\221\345\205\215\350\264\271SSL\350\257\201\344\271\246(Nginx\347\211\210\346\234\254).md" rename to "7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/CentOS7-\351\230\277\351\207\214\344\272\221\345\205\215\350\264\271SSL\350\257\201\344\271\246\345\256\211\350\243\205(Nginx\347\211\210\346\234\254).md" diff --git "a/Docker/docker-compose\345\256\211\350\243\205.md" "b/7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/Docker-compose/1. docker-compose\345\256\211\350\243\205.md" similarity index 100% rename from "Docker/docker-compose\345\256\211\350\243\205.md" rename to "7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/Docker-compose/1. docker-compose\345\256\211\350\243\205.md" diff --git "a/Docker/docker-compose\345\256\211\350\243\205rabbitMQ.md" "b/7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/Docker-compose/docker-compose\345\256\211\350\243\205rabbitMQ.md" similarity index 100% rename from "Docker/docker-compose\345\256\211\350\243\205rabbitMQ.md" rename to "7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/Docker-compose/docker-compose\345\256\211\350\243\205rabbitMQ.md" diff --git "a/Docker/docker-compose\351\203\250\347\275\262gitlab\344\270\255\346\226\207\347\211\210.md" "b/7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/Docker-compose/docker-compose\351\203\250\347\275\262gitlab\344\270\255\346\226\207\347\211\210.md" similarity index 100% rename from "Docker/docker-compose\351\203\250\347\275\262gitlab\344\270\255\346\226\207\347\211\210.md" rename to "7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/Docker-compose/docker-compose\351\203\250\347\275\262gitlab\344\270\255\346\226\207\347\211\210.md" diff --git "a/Docker/docker-compose\351\203\250\347\275\262mysql.md" "b/7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/Docker-compose/docker-compose\351\203\250\347\275\262mysql.md" similarity index 100% rename from "Docker/docker-compose\351\203\250\347\275\262mysql.md" rename to "7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/Docker-compose/docker-compose\351\203\250\347\275\262mysql.md" diff --git "a/Docker/docker-compose\351\203\250\347\275\262tomcat.md" "b/7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/Docker-compose/docker-compose\351\203\250\347\275\262tomcat.md" similarity index 100% rename from "Docker/docker-compose\351\203\250\347\275\262tomcat.md" rename to "7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/Docker-compose/docker-compose\351\203\250\347\275\262tomcat.md" diff --git "a/Docker/\347\254\254\344\270\200\347\253\240\357\274\232docker\345\256\211\350\243\205.md" "b/7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/Docker/1. Docker-\345\256\211\350\243\205.md" similarity index 100% rename from "Docker/\347\254\254\344\270\200\347\253\240\357\274\232docker\345\256\211\350\243\205.md" rename to "7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/Docker/1. Docker-\345\256\211\350\243\205.md" diff --git "a/Docker/Docker-\345\270\270\347\224\250\345\221\275\344\273\244\346\200\273\347\273\223.md" "b/7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/Docker/2. Docker-\345\270\270\347\224\250\345\221\275\344\273\244\346\200\273\347\273\223.md" similarity index 100% rename from "Docker/Docker-\345\270\270\347\224\250\345\221\275\344\273\244\346\200\273\347\273\223.md" rename to "7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/Docker/2. Docker-\345\270\270\347\224\250\345\221\275\344\273\244\346\200\273\347\273\223.md" diff --git "a/Docker/Docker-\345\256\236\347\216\260\351\230\277\351\207\214\344\272\221\345\212\240\351\200\237.md" "b/7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/Docker/3. Docker-\345\256\236\347\216\260\351\230\277\351\207\214\344\272\221\345\212\240\351\200\237.md" similarity index 100% rename from "Docker/Docker-\345\256\236\347\216\260\351\230\277\351\207\214\344\272\221\345\212\240\351\200\237.md" rename to "7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/Docker/3. Docker-\345\256\236\347\216\260\351\230\277\351\207\214\344\272\221\345\212\240\351\200\237.md" diff --git "a/Docker/Docker-\344\270\212\344\274\240\351\225\234\345\203\217&\346\213\211\345\217\226\351\225\234\345\203\217.md" "b/7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/Docker/4. Docker-\344\270\212\344\274\240\351\225\234\345\203\217&\346\213\211\345\217\226\351\225\234\345\203\217.md" similarity index 100% rename from "Docker/Docker-\344\270\212\344\274\240\351\225\234\345\203\217&\346\213\211\345\217\226\351\225\234\345\203\217.md" rename to "7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/Docker/4. Docker-\344\270\212\344\274\240\351\225\234\345\203\217&\346\213\211\345\217\226\351\225\234\345\203\217.md" diff --git "a/Docker/Docker-\346\220\255\345\273\272\347\247\201\346\234\211\344\273\223\345\272\223Registry&Harbor.md" "b/7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/Docker/5. Docker-\346\220\255\345\273\272\347\247\201\346\234\211\344\273\223\345\272\223Registry&Harbor.md" similarity index 100% rename from "Docker/Docker-\346\220\255\345\273\272\347\247\201\346\234\211\344\273\223\345\272\223Registry&Harbor.md" rename to "7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/Docker/5. Docker-\346\220\255\345\273\272\347\247\201\346\234\211\344\273\223\345\272\223Registry&Harbor.md" diff --git "a/Centos\345\256\211\350\243\205\345\267\245\345\205\267\346\226\207\351\233\206/Fedora(\345\217\212Redhat\346\211\200\346\234\211\347\263\273\345\210\227)\346\241\214\351\235\242\347\272\247\345\267\245\345\205\267\346\225\264\345\220\210.md" "b/7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/Fedora-Redhat\346\211\200\346\234\211\347\263\273\345\210\227-\346\241\214\351\235\242\347\272\247\345\267\245\345\205\267\346\225\264\345\220\210.md" similarity index 100% rename from "Centos\345\256\211\350\243\205\345\267\245\345\205\267\346\226\207\351\233\206/Fedora(\345\217\212Redhat\346\211\200\346\234\211\347\263\273\345\210\227)\346\241\214\351\235\242\347\272\247\345\267\245\345\205\267\346\225\264\345\220\210.md" rename to "7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/Fedora-Redhat\346\211\200\346\234\211\347\263\273\345\210\227-\346\241\214\351\235\242\347\272\247\345\267\245\345\205\267\346\225\264\345\220\210.md" diff --git "a/Centos\345\256\211\350\243\205\345\267\245\345\205\267\346\226\207\351\233\206/Fedora\344\270\213Shadowsocks\345\256\242\346\210\267\347\253\257\347\232\204\351\205\215\347\275\256.md" "b/7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/Fedora-Shadowsocks\345\256\242\346\210\267\347\253\257\347\232\204\351\205\215\347\275\256.md" similarity index 100% rename from "Centos\345\256\211\350\243\205\345\267\245\345\205\267\346\226\207\351\233\206/Fedora\344\270\213Shadowsocks\345\256\242\346\210\267\347\253\257\347\232\204\351\205\215\347\275\256.md" rename to "7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/Fedora-Shadowsocks\345\256\242\346\210\267\347\253\257\347\232\204\351\205\215\347\275\256.md" diff --git "a/7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/\347\224\250mwget\345\267\245\345\205\267\346\235\245\346\217\220\345\215\207wget\344\270\213\350\275\275\351\200\237\345\272\246.md" "b/7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/\347\224\250mwget\345\267\245\345\205\267\346\235\245\346\217\220\345\215\207wget\344\270\213\350\275\275\351\200\237\345\272\246.md" new file mode 100644 index 0000000..1de4c32 --- /dev/null +++ "b/7. Linux\344\270\223\351\242\230/7.2 \345\270\270\347\224\250\346\234\215\345\212\241\346\220\255\345\273\272/\347\224\250mwget\345\267\245\345\205\267\346\235\245\346\217\220\345\215\207wget\344\270\213\350\275\275\351\200\237\345\272\246.md" @@ -0,0 +1,31 @@ +>mwget是一个多线程实现wget的一个工具。 + +### 1. 安装 +``` +wget http://jaist.dl.sourceforge.net/project/kmphpfm/mwget/0.1/mwget_0.1.0.orig.tar.bz2 +# 安装bzip2压缩工具 +yum install bzip2 +# 安装c++ +yum install gcc-c++ + +# 解压 +tar -jxvf mwget_0.1.0.orig.tar.bz2 ./ + +cd mwget_0.1.0.orig + +./configure + +make&make install +``` +### 2. 使用 + +``` +将wget命令改成mwget +``` + +### 3. 效果 +![image.png](https://upload-images.jianshu.io/upload_images/5786888-8647b6cce0a1b190.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +![欢迎关注公众号](https://user-gold-cdn.xitu.io/2020/2/22/1706c6ee573189ff?w=258&h=258&f=jpeg&s=15621) + +GitHub地址: https://github.com/fantj2016/java-reader \ No newline at end of file diff --git "a/BigData/Shell\350\204\232\346\234\254-\346\216\247\345\210\266\345\244\232\344\270\273\346\234\272\346\223\215\344\275\234.md" "b/7. Linux\344\270\223\351\242\230/Shell/Shell\350\204\232\346\234\254-\346\216\247\345\210\266\345\244\232\344\270\273\346\234\272\346\223\215\344\275\234.md" similarity index 100% rename from "BigData/Shell\350\204\232\346\234\254-\346\216\247\345\210\266\345\244\232\344\270\273\346\234\272\346\223\215\344\275\234.md" rename to "7. Linux\344\270\223\351\242\230/Shell/Shell\350\204\232\346\234\254-\346\216\247\345\210\266\345\244\232\344\270\273\346\234\272\346\223\215\344\275\234.md" diff --git "a/BigData/\347\224\250Shell\345\206\231\344\270\200\344\270\252\346\227\245\345\277\227\346\224\266\351\233\206\350\204\232\346\234\254.md" "b/7. Linux\344\270\223\351\242\230/Shell/\347\224\250Shell\345\206\231\344\270\200\344\270\252\346\227\245\345\277\227\346\224\266\351\233\206\350\204\232\346\234\254.md" similarity index 100% rename from "BigData/\347\224\250Shell\345\206\231\344\270\200\344\270\252\346\227\245\345\277\227\346\224\266\351\233\206\350\204\232\346\234\254.md" rename to "7. Linux\344\270\223\351\242\230/Shell/\347\224\250Shell\345\206\231\344\270\200\344\270\252\346\227\245\345\277\227\346\224\266\351\233\206\350\204\232\346\234\254.md" diff --git "a/8. \345\244\247\346\225\260\346\215\256/8.1 \346\234\215\345\212\241\346\220\255\345\273\272\347\257\207/CentOS7-Hadoop\345\256\211\350\243\205.md" "b/8. \345\244\247\346\225\260\346\215\256/8.1 \346\234\215\345\212\241\346\220\255\345\273\272\347\257\207/CentOS7-Hadoop\345\256\211\350\243\205.md" new file mode 100644 index 0000000..6fe5ef3 --- /dev/null +++ "b/8. \345\244\247\346\225\260\346\215\256/8.1 \346\234\215\345\212\241\346\220\255\345\273\272\347\257\207/CentOS7-Hadoop\345\256\211\350\243\205.md" @@ -0,0 +1,37 @@ +### 下载tar包 + +### 解压tar包 + + +### 设置环境变量 +``` +export JAVA_HOME=/home/fantj/jdk +export PATH=$PATH:$JAVA_HOME/bin + +export HADOOP_HOME=/home/fantj/hadoop +export PATH=$PATH:$HADOOP_HOME/bin:$PATH:$HADOOP_HOME/sbin + +``` + +### 查看java版本 + +``` +[root@localhost ~]# java -version +java version "1.8.0_171" +Java(TM) SE Runtime Environment (build 1.8.0_171-b11) +Java HotSpot(TM) 64-Bit Server VM (build 25.171-b11, mixed mode) + +``` + +### 查看hadoop版本 + +``` +[root@localhost ~]# hadoop version +Hadoop 2.7.0 +Subversion Unknown -r Unknown +Compiled by root on 2015-05-21T03:49Z +Compiled with protoc 2.5.0 +From source with checksum a9e90912c37a35c3195d23951fd18f +This command was run using /home/fantj/download/hadoop-2.7.0/share/hadoop/common/hadoop-common-2.7.0.jar + +``` diff --git "a/8. \345\244\247\346\225\260\346\215\256/8.1 \346\234\215\345\212\241\346\220\255\345\273\272\347\257\207/CentOS7-Hive\345\256\211\350\243\205.md" "b/8. \345\244\247\346\225\260\346\215\256/8.1 \346\234\215\345\212\241\346\220\255\345\273\272\347\257\207/CentOS7-Hive\345\256\211\350\243\205.md" new file mode 100644 index 0000000..278f82a --- /dev/null +++ "b/8. \345\244\247\346\225\260\346\215\256/8.1 \346\234\215\345\212\241\346\220\255\345\273\272\347\257\207/CentOS7-Hive\345\256\211\350\243\205.md" @@ -0,0 +1,145 @@ +### 1. 准备 +###### 1.1 `apache-hive-2.1.0-bin.tar.gz`包 + +###### 1.2 mysql中创建新的数据库`hive` +### 2. 解压 +### 3. 修改环境变量 +``` +vim /etc/profile + + +export HIVE_HOME=xxxx +export PATH=$PATH:$HIVE_HOME/bin +``` +然后刷新配置`source /etc/profile` + +### 4. 修改配置文件 + +首先需要下载并把`mysql-connector-java-5.1.17.jar`拷贝到`hive/lib`目录下,作为驱动要用到。 + +配置文件都在`hive/conf`目录下 +##### 4.1 hive-site.xml +更名:`mv hive-default.xml.template hive-site.xml` +然后搜索关键字,把下面这部分做修改 +``` + + javax.jdo.option.ConnectionURL + jdbc:mysql://192.168.27.166:3306/hive?createDatabaseIfNotExist=true + + + javax.jdo.option.ConnectionDriverName + com.mysql.jdbc.Driver + + + javax.jdo.option.ConnectionUserName + hive + + + javax.jdo.option.ConnectionPassword + hive + + + hive.metastore.warehouse.dir + /user/hive/warehouse + +``` +然后把全文中的`${system:java.io.tmpdir}` 替换成` /home/fantj/hive/fantj` + +`${system:user.name} `替换成 `root ` + +最后,创建该目录 `mkdir -p /home/fantj/hive/fantj/root` + +###### 4.2 hive-env.sh +更名:`mv hive-env.sh.template hive-env.sh` +添加环境参数: +``` +export JAVA_HOME=/soft/jdk +export HIVE_HOME=/soft/hive +export HADOOP_HOME=/soft/hadoop +``` + +### 5. 创建数据库表到mysql里面 +`schematool -initSchema -dbType mysql` +``` +[root@s166 conf]# schematool -initSchema -dbType mysql +which: no hbase in (/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/home/fantj/jdk/bin:/home/fantj/hadoop/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/home/fantj/jdk/bin:/home/fantj/hadoop/sbin:/root/bin:/home/fantj/jdk/bin:/home/fantj/hadoop/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/home/fantj/jdk/bin:/home/fantj/hadoop/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/home/fantj/jdk/bin:/home/fantj/hadoop/sbin:/root/bin:/home/fantj/jdk/bin:/home/fantj/hadoop/sbin:/home/fantj/hive/bin) +SLF4J: Class path contains multiple SLF4J bindings. +SLF4J: Found binding in [jar:file:/home/fantj/download/apache-hive-2.1.0-bin/lib/log4j-slf4j-impl-2.4.1.jar!/org/slf4j/impl/StaticLoggerBinder.class] +SLF4J: Found binding in [jar:file:/home/fantj/download/hadoop-2.7.0/share/hadoop/common/lib/slf4j-log4j12-1.7.10.jar!/org/slf4j/impl/StaticLoggerBinder.class] +SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation. +SLF4J: Actual binding is of type [org.apache.logging.slf4j.Log4jLoggerFactory] +Metastore connection URL: jdbc:mysql://192.168.27.166:3306/hive?createDatabaseIfNotExist=true +Metastore Connection Driver : com.mysql.jdbc.Driver +Metastore connection User: hive +Starting metastore schema initialization to 2.1.0 +Initialization script hive-schema-2.1.0.mysql.sql +Initialization script completed +schemaTool completed + +``` + +###### 运行成功后查看hive数据库: + +![](https://upload-images.jianshu.io/upload_images/5786888-276dce4d02fddd8e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + +### 6. 测试hive环境 +>注意:注意hadoop要启动 + +###### 6.1 输入hive命令 +``` +[root@s166 bin]# hive +which: no hbase in (/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/home/fantj/jdk/bin:/home/fantj/hadoop/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/home/fantj/jdk/bin:/home/fantj/hadoop/sbin:/root/bin:/home/fantj/jdk/bin:/home/fantj/hadoop/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/home/fantj/jdk/bin:/home/fantj/hadoop/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/home/fantj/jdk/bin:/home/fantj/hadoop/sbin:/root/bin:/home/fantj/jdk/bin:/home/fantj/hadoop/sbin:/home/fantj/hive/bin) +SLF4J: Class path contains multiple SLF4J bindings. +SLF4J: Found binding in [jar:file:/home/fantj/download/apache-hive-2.1.0-bin/lib/log4j-slf4j-impl-2.4.1.jar!/org/slf4j/impl/StaticLoggerBinder.class] +SLF4J: Found binding in [jar:file:/home/fantj/download/hadoop-2.7.0/share/hadoop/common/lib/slf4j-log4j12-1.7.10.jar!/org/slf4j/impl/StaticLoggerBinder.class] +SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation. +SLF4J: Actual binding is of type [org.apache.logging.slf4j.Log4jLoggerFactory] + +Logging initialized using configuration in jar:file:/home/fantj/download/apache-hive-2.1.0-bin/lib/hive-common-2.1.0.jar!/hive-log4j2.properties Async: true +Hive-on-MR is deprecated in Hive 2 and may not be available in the future versions. Consider using a different execution engine (i.e. spark, tez) or using Hive 1.X releases. +hive> +``` + +###### 6.2 sql语言测试 +```shell +hive> show databases; +OK +default +mydb2 +Time taken: 1.55 seconds, Fetched: 2 row(s) +hive> create database fantj; +OK +Time taken: 0.801 seconds +hive> use fantj; +OK +Time taken: 0.035 seconds +hive> create table test(id int,name string,age int); +OK +Time taken: 0.833 seconds +hive> insert into test values(1,'fantj',18); +WARNING: Hive-on-MR is deprecated in Hive 2 and may not be available in the future versions. Consider using a different execution engine (i.e. spark, tez) or using Hive 1.X releases. +Query ID = root_20180727115808_c39d95f3-9bbd-4a60-b627-d5f0016ff6c3 +Total jobs = 3 +Launching Job 1 out of 3 +Number of reduce tasks is set to 0 since there's no reduce operator +Job running in-process (local Hadoop) +07-27 11:58:19,477 Stage-1 map = 0%, reduce = 0% +07-27 11:58:20,487 Stage-1 map = 100%, reduce = 0% +Ended Job = job_local1311590634_0001 +Stage-4 is selected by condition resolver. +Stage-3 is filtered out by condition resolver. +Stage-5 is filtered out by condition resolver. +Moving data to directory hdfs://s166/user/hive/warehouse/fantj.db/test/.hive-staging_hive_07-27_11-58-08_359_7490410987943534015-1/-ext-10000 +Loading data to table fantj.test +[Warning] could not update stats. +MapReduce Jobs Launched: +Stage-Stage-1: HDFS Read: 11 HDFS Write: 88 SUCCESS +Total MapReduce CPU Time Spent: 0 msec +OK +Time taken: 39.138 seconds +hive> select * from test; +OK +1 fantj 18 +Time taken: 3.26 seconds, Fetched: 1 row(s) + +``` diff --git "a/8. \345\244\247\346\225\260\346\215\256/8.1 \346\234\215\345\212\241\346\220\255\345\273\272\347\257\207/CentOS7-Spark\345\256\211\350\243\205\345\222\214\351\233\206\347\276\244\346\220\255\345\273\272.md" "b/8. \345\244\247\346\225\260\346\215\256/8.1 \346\234\215\345\212\241\346\220\255\345\273\272\347\257\207/CentOS7-Spark\345\256\211\350\243\205\345\222\214\351\233\206\347\276\244\346\220\255\345\273\272.md" new file mode 100644 index 0000000..1b33990 --- /dev/null +++ "b/8. \345\244\247\346\225\260\346\215\256/8.1 \346\234\215\345\212\241\346\220\255\345\273\272\347\257\207/CentOS7-Spark\345\256\211\350\243\205\345\222\214\351\233\206\347\276\244\346\220\255\345\273\272.md" @@ -0,0 +1,112 @@ + +### 准备工作 +首先得安装scala:[CentOS7.x 安装scala](https://www.jianshu.com/p/1995f34d0054) + +### 下载解压 + +### 配置 + +##### 1. 配置环境变量 +`/etc/profile` +``` +export SPARK_HOME=/home/fantj/spark +export PATH=$PATH:$SPARK_HOME/bin +export CLASSPAHT=.:$CLASSPATH:$JAVA_HOME/lib:$JAVA_HOME/jre/lib +``` +##### 2. 配置`/conf/spark-env.sh` +` cp spark-env.sh.template spark-env.sh` + +给尾部添加环境变量: +``` +export JAVA_HOME=/home/fantj/jdk +export SCALA_HOME=/home/fantj/scala +export SPARK_MASTER_IP=s166 +export SPARK_WORKER_MEMORY=1g +export HADOOP_CONF_DIR=/home/fantj/hadoop/etc/hadoop +``` +##### 3. 配置`/conf/slaves.conf` +`cp slaves.template slaves.conf` + +新添数据: +``` +spark2 +spark3 +spark4 +``` +### 同步配置到slave节点 +将spark和scala 和配置文件拷贝到每个slave节点。 +``` + 1099 scp -r scala-2.11.7 spark-1.5.1-bin-hadoop2.4/ s168:/home/fantj/download/ + 1100 scp -r scala-2.11.7 spark-1.5.1-bin-hadoop2.4/ s169:/home/fantj/download/ + + 1135 scp /etc/profile s167:/etc/profile + 1136 scp /etc/profile s168:/etc/profile + 1137 scp /etc/profile s169:/etc/profile +``` +### 启动spark +1. 首先得启动hadoop或者只启动hdfs。`start-dfs.sh`命令。 + +2. jps查看并确保主从机的hadoop的dfs都启动后。(主:NameNode,从:DataNode) + +3. 在`spark`的根目录下执行`./sbin/start-all.sh`,如果想要slave节点也跟着启动,需要做免密码登录。没有做的话可以用相同的命令一个一个节点去启动。 + +``` +[root@s166 spark]# ./sbin/start-all.sh +starting org.apache.spark.deploy.master.Master, logging to /home/fantj/download/spark-1.5.1-bin-hadoop2.4/sbin/../logs/spark-root-org.apache.spark.deploy.master.Master-1-s166.out +localhost: starting org.apache.spark.deploy.worker.Worker, logging to /home/fantj/download/spark-1.5.1-bin-hadoop2.4/sbin/../logs/spark-root-org.apache.spark.deploy.worker.Worker-1-s166.out +localhost: starting org.apache.spark.deploy.worker.Worker, logging to /home/fantj/download/spark-1.5.1-bin-hadoop2.4/sbin/../logs/spark-root-org.apache.spark.deploy.worker.Worker-1-s167.out +localhost: starting org.apache.spark.deploy.worker.Worker, logging to /home/fantj/download/spark-1.5.1-bin-hadoop2.4/sbin/../logs/spark-root-org.apache.spark.deploy.worker.Worker-1-s168.out +localhost: starting org.apache.spark.deploy.worker.Worker, logging to /home/fantj/download/spark-1.5.1-bin-hadoop2.4/sbin/../logs/spark-root-org.apache.spark.deploy.worker.Worker-1-s169.out +``` +4. 再查看jps +``` +-------s166 jps ------- +1397 NameNode +52854 Worker +1559 SecondaryNameNode +53671 Jps +52719 Master +-------s167 jps ------- +1764 DataNode +29092 Jps +28414 Worker +-------s168 jps ------- +33921 Worker +1756 DataNode +34063 Jps +-------s169 jps ------- +27384 Jps +1754 DataNode +27242 Worker +``` +可以看到,一个`Master`三个`Worker`。 +然后再访问主节点ip的8080端口。 + +![](https://upload-images.jianshu.io/upload_images/5786888-98af72ef76d6462c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) + + +### 打开Spark-shell +``` +[root@s166 bin]# spark-shell +18/07/30 12:34:16 WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using builtin-java classes where applicable +18/07/30 12:34:20 INFO spark.SecurityManager: Changing view acls to: root +18/07/30 12:34:20 INFO spark.SecurityManager: Changing modify acls to: root +18/07/30 12:34:20 INFO spark.SecurityManager: SecurityManager: authentication disabled; ui acls disabled; users with view permissions: Set(root); users with modify permissions: Set(root) +18/07/30 12:34:22 INFO spark.HttpServer: Starting HTTP Server +18/07/30 12:34:23 INFO server.Server: jetty-8.y.z-SNAPSHOT +18/07/30 12:34:23 INFO server.AbstractConnector: Started SocketConnector@0.0.0.0:35005 +18/07/30 12:34:23 INFO util.Utils: Successfully started service 'HTTP class server' on port 35005. +... +... +18/07/30 12:38:39 INFO session.SessionState: Created local directory: /tmp/2c350bb0-1297-40d8-a9bd-47446b116bf3_resources +18/07/30 12:38:39 INFO session.SessionState: Created HDFS directory: /tmp/hive/root/2c350bb0-1297-40d8-a9bd-47446b116bf3 +18/07/30 12:38:39 INFO session.SessionState: Created local directory: /tmp/root/2c350bb0-1297-40d8-a9bd-47446b116bf3 +18/07/30 12:38:40 INFO session.SessionState: Created HDFS directory: /tmp/hive/root/2c350bb0-1297-40d8-a9bd-47446b116bf3/_tmp_space.db +18/07/30 12:38:40 INFO repl.SparkILoop: Created sql context (with Hive support).. +SQL context available as sqlContext. + +scala> +``` +这就证明开启成功了,同理访问`4040`端口。 + +![](https://upload-images.jianshu.io/upload_images/5786888-d662e1f314e4ba0d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) diff --git "a/8. \345\244\247\346\225\260\346\215\256/8.1 \346\234\215\345\212\241\346\220\255\345\273\272\347\257\207/CentOS7-Sqoop\345\256\211\350\243\205.md" "b/8. \345\244\247\346\225\260\346\215\256/8.1 \346\234\215\345\212\241\346\220\255\345\273\272\347\257\207/CentOS7-Sqoop\345\256\211\350\243\205.md" new file mode 100644 index 0000000..d39336c --- /dev/null +++ "b/8. \345\244\247\346\225\260\346\215\256/8.1 \346\234\215\345\212\241\346\220\255\345\273\272\347\257\207/CentOS7-Sqoop\345\256\211\350\243\205.md" @@ -0,0 +1,33 @@ +>sqoop是apache旗下一款“Hadoop和关系数据库服务器之间传送数据”的工具。 + + + +###1. 下载并解压 + +### 2. 修改配置文件 +##### 2.1 进入`/sqoop/conf`目录 +`mv sqoop-env-template.sh sqoop-env.sh` +``` +export HADOOP_COMMON_HOME=/home/fantj/hadoop/ +export HADOOP_MAPRED_HOME=/home/fantj/hadoop/ +export HIVE_HOME=/home/fantj/hive +``` + +##### 2.2 配置`/etc/profile` + +``` +export SQOOP_HOME=/xxx/sqoop +export PATH=$PATH:$SQOOP_HOME/bin +``` +### 3. 加入mysql的jdbc驱动包 +`/hive/lib/mysql-connector-java-5.1.28.jar` + +### 4. 验证环境 +`sqoop-version` +``` +[root@s166 ~]# sqoop-version +Sqoop 1.4.2 +git commit id +Compiled by ag on Tue Aug 14 18:38:15 IST 2012 + +``` diff --git "a/8. \345\244\247\346\225\260\346\215\256/8.1 \346\234\215\345\212\241\346\220\255\345\273\272\347\257\207/CentOS7-scala\345\256\211\350\243\205.md" "b/8. \345\244\247\346\225\260\346\215\256/8.1 \346\234\215\345\212\241\346\220\255\345\273\272\347\257\207/CentOS7-scala\345\256\211\350\243\205.md" new file mode 100644 index 0000000..143a9ea --- /dev/null +++ "b/8. \345\244\247\346\225\260\346\215\256/8.1 \346\234\215\345\212\241\346\220\255\345\273\272\347\257\207/CentOS7-scala\345\256\211\350\243\205.md" @@ -0,0 +1,19 @@ +### 下载解压 +略。 +下载官网:https://www.lightbend.com/scala + +### 配置环境变量 +`/etc/profile`配置: + +``` +export SCALA_HOME=/home/fantj/scala +export PATH=$PATH:$SCALA_HOME/bin +``` + +`source /etc/profile` + +### 检测版本 +``` +[root@s166 scala]# scala -version +Scala code runner version 2.11.7 -- Copyright 2002-2013, LAMP/EPFL +``` diff --git "a/BigData/Azkaban-\347\256\200\345\215\225\345\205\245\351\227\250.md" "b/8. \345\244\247\346\225\260\346\215\256/8.2 \345\256\236\346\210\230\347\257\207/Azkaban/Azkaban-\347\256\200\345\215\225\345\205\245\351\227\250.md" similarity index 100% rename from "BigData/Azkaban-\347\256\200\345\215\225\345\205\245\351\227\250.md" rename to "8. \345\244\247\346\225\260\346\215\256/8.2 \345\256\236\346\210\230\347\257\207/Azkaban/Azkaban-\347\256\200\345\215\225\345\205\245\351\227\250.md" diff --git "a/BigData/Flume\345\205\245\351\227\250.md" "b/8. \345\244\247\346\225\260\346\215\256/8.2 \345\256\236\346\210\230\347\257\207/Flume/Flume\345\205\245\351\227\250.md" similarity index 100% rename from "BigData/Flume\345\205\245\351\227\250.md" rename to "8. \345\244\247\346\225\260\346\215\256/8.2 \345\256\236\346\210\230\347\257\207/Flume/Flume\345\205\245\351\227\250.md" diff --git "a/BigData/CentOS7-x-Hadoop\351\233\206\347\276\244\346\220\255\345\273\272.md" "b/8. \345\244\247\346\225\260\346\215\256/8.2 \345\256\236\346\210\230\347\257\207/Hadoop/1. Hadoop-\351\233\206\347\276\244\346\220\255\345\273\272-CentOS.md" similarity index 100% rename from "BigData/CentOS7-x-Hadoop\351\233\206\347\276\244\346\220\255\345\273\272.md" rename to "8. \345\244\247\346\225\260\346\215\256/8.2 \345\256\236\346\210\230\347\257\207/Hadoop/1. Hadoop-\351\233\206\347\276\244\346\220\255\345\273\272-CentOS.md" diff --git "a/BigData/Hadoop\346\211\200\346\234\211\345\221\275\344\273\244\350\257\246\350\247\243.md" "b/8. \345\244\247\346\225\260\346\215\256/8.2 \345\256\236\346\210\230\347\257\207/Hadoop/2. Hadoop-\346\211\200\346\234\211\345\221\275\344\273\244\350\257\246\350\247\243.md" similarity index 100% rename from "BigData/Hadoop\346\211\200\346\234\211\345\221\275\344\273\244\350\257\246\350\247\243.md" rename to "8. \345\244\247\346\225\260\346\215\256/8.2 \345\256\236\346\210\230\347\257\207/Hadoop/2. Hadoop-\346\211\200\346\234\211\345\221\275\344\273\244\350\257\246\350\247\243.md" diff --git "a/BigData/Hadoop\345\215\225\350\257\215\347\273\237\350\256\241.md" "b/8. \345\244\247\346\225\260\346\215\256/8.2 \345\256\236\346\210\230\347\257\207/Hadoop/3. Hadoop-\345\215\225\350\257\215\347\273\237\350\256\241.md" similarity index 100% rename from "BigData/Hadoop\345\215\225\350\257\215\347\273\237\350\256\241.md" rename to "8. \345\244\247\346\225\260\346\215\256/8.2 \345\256\236\346\210\230\347\257\207/Hadoop/3. Hadoop-\345\215\225\350\257\215\347\273\237\350\256\241.md" diff --git "a/BigData/Hive\345\270\270\347\224\250\346\223\215\344\275\234.md" "b/8. \345\244\247\346\225\260\346\215\256/8.2 \345\256\236\346\210\230\347\257\207/Hive/Hive\345\270\270\347\224\250\346\223\215\344\275\234.md" similarity index 100% rename from "BigData/Hive\345\270\270\347\224\250\346\223\215\344\275\234.md" rename to "8. \345\244\247\346\225\260\346\215\256/8.2 \345\256\236\346\210\230\347\257\207/Hive/Hive\345\270\270\347\224\250\346\223\215\344\275\234.md" diff --git "a/BigData/Spark-\344\273\216\351\233\266\345\210\260\345\274\200\345\217\221\357\274\210\344\270\200\357\274\211\345\210\235\350\257\206.md" "b/8. \345\244\247\346\225\260\346\215\256/8.2 \345\256\236\346\210\230\347\257\207/Spark/1. Spark-\345\210\235\350\257\206.md" similarity index 100% rename from "BigData/Spark-\344\273\216\351\233\266\345\210\260\345\274\200\345\217\221\357\274\210\344\270\200\357\274\211\345\210\235\350\257\206.md" rename to "8. \345\244\247\346\225\260\346\215\256/8.2 \345\256\236\346\210\230\347\257\207/Spark/1. Spark-\345\210\235\350\257\206.md" diff --git "a/BigData/Spark-\344\273\216\351\233\266\345\210\260\345\274\200\345\217\221\357\274\210\344\272\214\357\274\211Spark\345\256\211\350\243\205\345\222\214\351\233\206\347\276\244\346\220\255\345\273\272.md" "b/8. \345\244\247\346\225\260\346\215\256/8.2 \345\256\236\346\210\230\347\257\207/Spark/2. Spark-\345\256\211\350\243\205\345\222\214\351\233\206\347\276\244\346\220\255\345\273\272.md" similarity index 100% rename from "BigData/Spark-\344\273\216\351\233\266\345\210\260\345\274\200\345\217\221\357\274\210\344\272\214\357\274\211Spark\345\256\211\350\243\205\345\222\214\351\233\206\347\276\244\346\220\255\345\273\272.md" rename to "8. \345\244\247\346\225\260\346\215\256/8.2 \345\256\236\346\210\230\347\257\207/Spark/2. Spark-\345\256\211\350\243\205\345\222\214\351\233\206\347\276\244\346\220\255\345\273\272.md" diff --git "a/BigData/Spark-\344\273\216\351\233\266\345\210\260\345\274\200\345\217\221\357\274\210\344\270\211\357\274\211\345\210\235\350\257\206RDD.md" "b/8. \345\244\247\346\225\260\346\215\256/8.2 \345\256\236\346\210\230\347\257\207/Spark/3. Spark-\345\210\235\350\257\206RDD.md" similarity index 100% rename from "BigData/Spark-\344\273\216\351\233\266\345\210\260\345\274\200\345\217\221\357\274\210\344\270\211\357\274\211\345\210\235\350\257\206RDD.md" rename to "8. \345\244\247\346\225\260\346\215\256/8.2 \345\256\236\346\210\230\347\257\207/Spark/3. Spark-\345\210\235\350\257\206RDD.md" diff --git "a/BigData/Spark-\344\273\216\351\233\266\345\210\260\345\274\200\345\217\221\357\274\210\345\233\233\357\274\211\345\215\225\350\257\215\350\256\241\346\225\260\347\232\204\344\270\211\347\247\215\347\216\257\345\242\203\345\256\236\347\216\260.md" "b/8. \345\244\247\346\225\260\346\215\256/8.2 \345\256\236\346\210\230\347\257\207/Spark/4. Spark-\345\215\225\350\257\215\350\256\241\346\225\260\347\232\204\344\270\211\347\247\215\347\216\257\345\242\203\345\256\236\347\216\260.md" similarity index 100% rename from "BigData/Spark-\344\273\216\351\233\266\345\210\260\345\274\200\345\217\221\357\274\210\345\233\233\357\274\211\345\215\225\350\257\215\350\256\241\346\225\260\347\232\204\344\270\211\347\247\215\347\216\257\345\242\203\345\256\236\347\216\260.md" rename to "8. \345\244\247\346\225\260\346\215\256/8.2 \345\256\236\346\210\230\347\257\207/Spark/4. Spark-\345\215\225\350\257\215\350\256\241\346\225\260\347\232\204\344\270\211\347\247\215\347\216\257\345\242\203\345\256\236\347\216\260.md" diff --git "a/BigData/Spark-\344\273\216\351\233\266\345\210\260\345\274\200\345\217\221\357\274\210\344\272\224\357\274\211\345\210\235\350\257\206Spark-SQL.md" "b/8. \345\244\247\346\225\260\346\215\256/8.2 \345\256\236\346\210\230\347\257\207/Spark/5. Spark-\345\210\235\350\257\206Spark-SQL.md" similarity index 100% rename from "BigData/Spark-\344\273\216\351\233\266\345\210\260\345\274\200\345\217\221\357\274\210\344\272\224\357\274\211\345\210\235\350\257\206Spark-SQL.md" rename to "8. \345\244\247\346\225\260\346\215\256/8.2 \345\256\236\346\210\230\347\257\207/Spark/5. Spark-\345\210\235\350\257\206Spark-SQL.md" diff --git "a/BigData/Spark-\344\273\216\351\233\266\345\210\260\345\274\200\345\217\221\357\274\210\345\205\255\357\274\211HiveContext.md" "b/8. \345\244\247\346\225\260\346\215\256/8.2 \345\256\236\346\210\230\347\257\207/Spark/6. Spark-HiveContext.md" similarity index 100% rename from "BigData/Spark-\344\273\216\351\233\266\345\210\260\345\274\200\345\217\221\357\274\210\345\205\255\357\274\211HiveContext.md" rename to "8. \345\244\247\346\225\260\346\215\256/8.2 \345\256\236\346\210\230\347\257\207/Spark/6. Spark-HiveContext.md" diff --git "a/BigData/Spark-\344\273\216\351\233\266\345\210\260\345\274\200\345\217\221\357\274\210\344\270\203\357\274\211Spark-SQL\345\222\214DataFrame.md" "b/8. \345\244\247\346\225\260\346\215\256/8.2 \345\256\236\346\210\230\347\257\207/Spark/7. Spark-SparkSQL\345\222\214DataFrame.md" similarity index 100% rename from "BigData/Spark-\344\273\216\351\233\266\345\210\260\345\274\200\345\217\221\357\274\210\344\270\203\357\274\211Spark-SQL\345\222\214DataFrame.md" rename to "8. \345\244\247\346\225\260\346\215\256/8.2 \345\256\236\346\210\230\347\257\207/Spark/7. Spark-SparkSQL\345\222\214DataFrame.md" diff --git "a/BigData/Spark-\344\273\216\351\233\266\345\210\260\345\274\200\345\217\221\357\274\210\345\205\253\357\274\211nginx\346\227\245\345\277\227\346\270\205\346\264\227\345\271\266\346\214\201\344\271\205\345\214\226\345\256\236\346\210\230.md" "b/8. \345\244\247\346\225\260\346\215\256/8.2 \345\256\236\346\210\230\347\257\207/Spark/8. Spark-nginx\346\227\245\345\277\227\346\270\205\346\264\227\345\271\266\346\214\201\344\271\205\345\214\226\345\256\236\346\210\230.md" similarity index 100% rename from "BigData/Spark-\344\273\216\351\233\266\345\210\260\345\274\200\345\217\221\357\274\210\345\205\253\357\274\211nginx\346\227\245\345\277\227\346\270\205\346\264\227\345\271\266\346\214\201\344\271\205\345\214\226\345\256\236\346\210\230.md" rename to "8. \345\244\247\346\225\260\346\215\256/8.2 \345\256\236\346\210\230\347\257\207/Spark/8. Spark-nginx\346\227\245\345\277\227\346\270\205\346\264\227\345\271\266\346\214\201\344\271\205\345\214\226\345\256\236\346\210\230.md" diff --git "a/BigData/Spark\346\234\254\345\234\260-\351\233\206\347\276\244\346\211\247\350\241\214wordcount\347\250\213\345\272\217.md" "b/8. \345\244\247\346\225\260\346\215\256/8.2 \345\256\236\346\210\230\347\257\207/Spark/Spark\346\234\254\345\234\260-\351\233\206\347\276\244\346\211\247\350\241\214wordcount\347\250\213\345\272\217.md" similarity index 100% rename from "BigData/Spark\346\234\254\345\234\260-\351\233\206\347\276\244\346\211\247\350\241\214wordcount\347\250\213\345\272\217.md" rename to "8. \345\244\247\346\225\260\346\215\256/8.2 \345\256\236\346\210\230\347\257\207/Spark/Spark\346\234\254\345\234\260-\351\233\206\347\276\244\346\211\247\350\241\214wordcount\347\250\213\345\272\217.md" diff --git "a/BigData/Sqoop\346\225\260\346\215\256\345\257\274\345\205\245-\345\257\274\345\207\272.md" "b/8. \345\244\247\346\225\260\346\215\256/8.2 \345\256\236\346\210\230\347\257\207/Sqoop/Sqoop\346\225\260\346\215\256\345\257\274\345\205\245-\345\257\274\345\207\272.md" similarity index 100% rename from "BigData/Sqoop\346\225\260\346\215\256\345\257\274\345\205\245-\345\257\274\345\207\272.md" rename to "8. \345\244\247\346\225\260\346\215\256/8.2 \345\256\236\346\210\230\347\257\207/Sqoop/Sqoop\346\225\260\346\215\256\345\257\274\345\205\245-\345\257\274\345\207\272.md" diff --git "a/9. \346\236\266\346\236\204\350\256\276\350\256\241/\347\224\261\346\265\205\345\205\245\346\267\261\347\220\206\350\247\243Raft\345\215\217\350\256\256.md" "b/9. \346\236\266\346\236\204\350\256\276\350\256\241/\347\224\261\346\265\205\345\205\245\346\267\261\347\220\206\350\247\243Raft\345\215\217\350\256\256.md" new file mode 100644 index 0000000..7d3d846 --- /dev/null +++ "b/9. \346\236\266\346\236\204\350\256\276\350\256\241/\347\224\261\346\265\205\345\205\245\346\267\261\347\220\206\350\247\243Raft\345\215\217\350\256\256.md" @@ -0,0 +1,2 @@ +分享一个同事的公众号写的非常详细: +https://mp.weixin.qq.com/s/20Rno9Er_x4gg6kERSF1bA \ No newline at end of file diff --git "a/RedHat-CentOS/\351\253\230\345\217\257\347\224\250\344\271\213\350\243\202\350\204\221\351\227\256\351\242\230.md" "b/9. \346\236\266\346\236\204\350\256\276\350\256\241/\351\253\230\345\217\257\347\224\250\344\271\213\350\243\202\350\204\221\351\227\256\351\242\230.md" similarity index 100% rename from "RedHat-CentOS/\351\253\230\345\217\257\347\224\250\344\271\213\350\243\202\350\204\221\351\227\256\351\242\230.md" rename to "9. \346\236\266\346\236\204\350\256\276\350\256\241/\351\253\230\345\217\257\347\224\250\344\271\213\350\243\202\350\204\221\351\227\256\351\242\230.md" diff --git "a/Centos\345\256\211\350\243\205\345\267\245\345\205\267\346\226\207\351\233\206/CentOS7-0\345\256\211\350\243\205Redis\346\234\215\345\212\241.md" "b/Centos\345\256\211\350\243\205\345\267\245\345\205\267\346\226\207\351\233\206/CentOS7-0\345\256\211\350\243\205Redis\346\234\215\345\212\241.md" deleted file mode 100644 index bcc4062..0000000 --- "a/Centos\345\256\211\350\243\205\345\267\245\345\205\267\346\226\207\351\233\206/CentOS7-0\345\256\211\350\243\205Redis\346\234\215\345\212\241.md" +++ /dev/null @@ -1,7 +0,0 @@ -1. $ wget http://download.redis.io/releases/redis-3.2.1.tar.gz -2. $ tar xzf redis-3.2.1.tar.gz -3. $ cd redis-3.2.1 -4. $ make - -#####Redis基础使用教程 -https://www.jianshu.com/nb/21461220 diff --git "a/Centos\345\256\211\350\243\205\345\267\245\345\205\267\346\226\207\351\233\206/Gnome\346\241\214\351\235\242--desktop-\346\226\207\344\273\266\347\232\204\344\275\215\347\275\256.md" "b/Centos\345\256\211\350\243\205\345\267\245\345\205\267\346\226\207\351\233\206/Gnome\346\241\214\351\235\242--desktop-\346\226\207\344\273\266\347\232\204\344\275\215\347\275\256.md" deleted file mode 100644 index 831a226..0000000 --- "a/Centos\345\256\211\350\243\205\345\267\245\345\205\267\346\226\207\351\233\206/Gnome\346\241\214\351\235\242--desktop-\346\226\207\344\273\266\347\232\204\344\275\215\347\275\256.md" +++ /dev/null @@ -1,10 +0,0 @@ - -### 以 root 权限安装的程序, - -其快捷入口大多创建在 `/usr/share/applications `目录下, - -也可以创建在` ~/.local/share/applications` 目录, - -### 以用户权限安装的程序 - -则只能将快捷入口创建在` ~/.local/share/applications` 目录下。 diff --git "a/HR\351\235\242\350\257\225\346\212\200\345\267\247/HR\351\200\232\345\270\270\346\217\220\347\232\204\344\270\200\344\272\233\351\235\242\350\257\225\351\227\256\351\242\230.md" "b/HR\351\235\242\350\257\225\346\212\200\345\267\247/HR\351\200\232\345\270\270\346\217\220\347\232\204\344\270\200\344\272\233\351\235\242\350\257\225\351\227\256\351\242\230.md" deleted file mode 100644 index faad802..0000000 --- "a/HR\351\235\242\350\257\225\346\212\200\345\267\247/HR\351\200\232\345\270\270\346\217\220\347\232\204\344\270\200\344\272\233\351\235\242\350\257\225\351\227\256\351\242\230.md" +++ /dev/null @@ -1,161 +0,0 @@ - ->如果你是应届毕业生,请一定看完!如果你正在找工作,请一定看完!看完这篇文章都会对你有帮助的,最刁钻的10大面试问题一次性帮你解决(均附参考建议及回答)全程干货无废话。文末链接50个面试必答问题技巧! -### 1.你最大的缺点问题分析: ->考察候选人的自我认知能力,个人优点是否符合岗位要求。 - -参考建议: - -第一,不宜说自己没有缺点,只要是人就有缺点,这么说一定会令人反感。 - -第二,年轻经验不足、缺乏磨炼、有些着急、对待效率低下的人缺乏耐心等根据以上的关键点,缺点参考回复:首先,我刚毕业,经验方面不足,我会在工作中积极完成工作,积累各方面经验其次,性子急,对待效率低下的人缺乏耐心,但是我平时和别人聊天的时候会控制自己语速和讲话,慢慢培养自己耐心,避免浮躁。(遵循一个原则避重就轻) - -Tips:利用你的优点改正你的缺点,比如,工作追求细节极致,导致项目无法按时完成,通过时间管理,得以解决。一定不能说对应聘岗位的硬伤的缺点,以及无法弥补的缺点。 - - -### 2.面试最难:自我介绍问题分析: ->这道题主要考察应聘者的逻辑思维、语言表达、自我认知等能力。 - -参考建议: - -第一,条理清晰,层次分明,突出与岗位要求相吻合的技能、个人所长、行为风格、实际经验等。 - -第二,现场表达必须与个人简历所写保持一致。 - -第三,控制时间,一般不超过 3 分钟。 - -第四,尽量口语化,语言平实可信。 - -▲根据以上的关键点,参考回复:首先,我叫xxx,xxx大学xxx专业毕业,我在学校获得xxx荣誉(或者证书)……这样的信息(基本信息介绍)其次,在工作方面,我在xxx公司实习(或者学校活动),我负责xx工作,为了完成这个工作,我做了xxx努力,最后取得xxx成果,结尾,还可以总结一下通过这次活动或者项目有什么收获。关键点,在做了什么努力这部分要体现做的深度。(利用STAR法则)第三,在大型比赛中取得xxx的成绩,如果没有,可以讲自己参加过的公益类活动,例如支教,敬老院看望爷爷奶奶。(特殊经历亮点加分项)(ps:最好准备一个1分钟自我介绍,一个3-5 分钟自我介绍,多手准备!!) - -Tips:在面试前一天,一定一定要熟记自我介绍,自己也可以对着镜子模拟面试情景反复刻意练习自我介绍。这样才能保证面试从容,不紧张。 - - -### 3.面试必答题:谈谈薪酬待遇? ->问题分析:判断候选人对薪资待遇的要求是否与单位能够提供的标准相匹配。写到这里我就想起自己第一次面试这道问题,我是这样回答的: - -Q:你期望薪资多少? - -A:都可以,够在这个城市生活就好…… - - -参考建议: - -第一,每家单位都有自己的薪酬标准。 - -第二,可以先提交一个薪酬区间,一旦被录用,人力资源部一定会有专人与您进行薪酬沟通,到时再友好协商也不晚。 - -Tips:每个单位都有薪资宽带就最低最高界限,评估自己能力及自己生活所需,可以先提交一个薪酬区间,如果你能力强可以往上限靠,如果一般取中间值。 - -▲提示:关于正确提问薪资待遇 - -方法一:不能谈薪资,为什么说不能谈薪资呢?作为一个毕业生,一个初入职场的人的时候,我们不能跟谈薪资,我们只能听薪资,适合我就做,不适合我就走。 - -方法二:谈薪资,首先,你要证明自己的价值。第二,你要让HR认可你的价值。这两点做到了,就可以谈薪资了。 - -### 4.你的兴趣爱好? ->问题分析:了解候选人的心态、性格、价值观、责任感等当HR问到兴趣爱好,这时大脑一片空白,一时想不到,拍脑袋回答没什么爱好,或者随便回答,那你就会掉进坑里了。 - -参考建议: - -第一,常见的爱好无外乎运动、旅游、听音乐、读书等,比如篮球:团队精神,古典音乐、阅读、书法:细心耐心,旅游:适应能力学习能力,演讲:沟通能力,唱歌、舞蹈:性格外向,沟通能力 - -第二,如果有表现突出的文体爱好,例如书法、羽毛球、小提琴、写文章等获得过有关奖项,可以适当加分 - -第三,如果热衷社会公益,参与过某些公益组织,为困难人群提供过无私、积极的帮助的,可以突出介绍,可以给面试官留下更好地印象。 - -▲根据以上的关键点,参考回复:eg1:写与岗位匹配的爱好,假如是应聘文案类(编辑)岗位,你说你喜欢写文章,发表过文章,还获得奖,加分!加分!(没得奖也不怕给HR看写过的文章,体现你是有潜力的)eg2:我喜欢读书,一年读了xx本,收获xxx。 - -Tips:说岗位需求匹配的爱好,有助于工作的爱好;回答要真实,否则HR接着深入一问,容易露馅。 - -### 5.介绍未来5年职业规划(必考题!!!) ->问题分析:考察候选人对自己未来发展的设想、职业生涯的规划能力。除非是目标非常明确的人,或者有多年工作经验的职场人,不然很难回答清楚,那么怎么说才能回答好这个问题呢? - -不要说“几年当主管”,"几年当经理"毫无意义。 - -参考建议: - -第一,介绍自己认真思考过这个问题,自己的规划是基于目前的实际情况来设计的。 - -第二,在工作方面,突出自己打算通过积极完成工作任务,积累各方面的经验,让自己成为这个领域的专业人士,也希望有机会能够带领团队,成为优秀的管理者,为单位做出更大贡献,获得双赢。 - -第三,在学习方面,打算在专业领域做进一步学习和研究,将实践经验与专业知识相结合,为自己的职业成长做好铺垫,打好基础。 - - - -Tips:回答这个问题强调你稳定性,踏实工作的态度,重点在工作技能方面的提升与内在积累,不要描述外在的东西,比如职位,薪资。 - - -### 6.面试入坑题:怎样看待加班? ->问题分析:考察候选人的责任心和职业道德。五花八门的回答:“我不愿意接受无意义加班”“没问题,随时都可以加班” - -参考建议: - -第一,任何一家单位都有可能要加班。 - -第二,自身的工作任务没有完成,加班是理所当然的,当然,自己会不断提高专业技能,以尽量减少不必要的加班,之前也是这么做的。 - -第三,如果遇到紧急任务或突发情况时,需要加班,自己会尽己所能,希望能够尽快顺利地完成团队面临的任务。 - - -Tips:表现出自己愿意牺牲自己的一部分个人时间,提升个人能力,为公司创造更多利益;明确岗位是否需要经常加班,表明自己态度。 - -### 7.面试陷阱题:希望与怎样的领导共事?一类题:希望与怎样的领导合作?怎样处理与领导的关系? ->问题分析:考察候选人的人际交往能力、主动适应能力。如果你回答:我希望我的上级比较有经验,能够给饿哦一些帮助,陷阱!这样会暴露自己短处。 - -参考建议: - -第一,尽量不要提及对领导的具体要求,而应该突出自己会认真向领导学习,尽快熟悉和适应工作环境,主动向领导请教,保质保量完成本职工作。 - -第二,如果有做得不到的地方,会诚恳地向领导请教,可以在哪些地方多多改善。这才是一位职业人作为下属应该秉持的工作态度。Tips:切忌一切围绕工作进行,着重谈论对自己有要求,自身努力的方向,千万不要提及前任领导的缺点。 - -### 8.若领导布置了大量的工作,而完成时间又十分有限,为了完成任务,您怎么办? ->问题分析:考察候选人的时间管理能力。 - -参考建议:第 - -一,分清任务的轻重缓急,紧急又重要的任务先完成。 - -第二,发动团队其他成员,借力完成。 - -第三,鼓励老人带新人,提高工作效率。Tips:实在是过重,以上方法全部用上了都不行,可以与领导协商,先完成几成,其他不重要的任务可以缓办。 - -### 9.为什么应聘这个岗位? ->问题分析:考察候选人的求职动机、求职意向及对岗位的认知能力。 - -参考建议: - -第一 ,是要突出个人经验和技能与该职位的匹配度相对比较高。 - -第二,提前做功课,仔细查阅用人单位的网站和视频资料,最好是要在应答中提到招聘单位的规模、品牌、知名度、规范性、愿景等等。 - -第三,强调用人单位是适合个人职业发展的平台。 - -Tips:重点突出个人经验和技能与该职位的匹配度。如果之前有与这个单位有直接交往的正面案例,也可以顺便提出来,这是个加分项,说明对方是自己心仪的单位,希望能够加盟这个优秀的团队。 - - - -### 10.面试终极必杀问题:还有什么要问的吗? ->问题分析:考察候选人的情商,是否对这个公司或者行业很了解,是否用心准备。 - -参考建议: - -第一,可以问本职岗位工作要求、职责。例如,这个部门人员设置是怎么样的。 - -第二,可以问公司、公司的业务、体系、行业、客户。eg:为了胜任该职位,需要我提前学习哪些技术知识?eg:贵公司业务及战略的未来发展?eg:团队、公司现在面临的最大挑战是什么? - - -Tips:切忌纠缠薪资,如果回答没问题,HR会误会,你对岗位没有太大兴趣。在每道题回答之后,加两字,谢谢!最后推荐职场相关电影当幸福来敲门肖申克救赎终极面试阿甘正传书籍《不要等到毕业以后》(ps:如果有启发,请点个小赞鼓励一下哈,听说点赞的童鞋面试必过哦~皮一下感谢感恩!) - - - -### 声明 -文章转载自知乎:https://www.zhihu.com/question/24192778/answer/631081857 - -作者:职研社De圆圆 - -著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 - - -还有,不要以为这些就够用了,还需要去再多了解了解。知乎就是个不错的选择:https://www.zhihu.com/question/24192778 - -https://www.zhihu.com/search?type=content&q=hr%E9%9D%A2%E8%AF%95%E4%B8%80%E8%88%AC%E4%BC%9A%E9%97%AE%E5%95%A5 \ No newline at end of file diff --git "a/JVM/JVM\345\255\246\344\271\240\347\254\224\350\256\260\342\200\224\342\200\224\347\250\213\345\272\217\350\256\241\346\225\260\345\231\250.md" "b/JVM/JVM\345\255\246\344\271\240\347\254\224\350\256\260\342\200\224\342\200\224\347\250\213\345\272\217\350\256\241\346\225\260\345\231\250.md" deleted file mode 100644 index aef955e..0000000 --- "a/JVM/JVM\345\255\246\344\271\240\347\254\224\350\256\260\342\200\224\342\200\224\347\250\213\345\272\217\350\256\241\346\225\260\345\231\250.md" +++ /dev/null @@ -1,33 +0,0 @@ - - -######在网上挑选了这么几段话,精简易懂 - ->程序计数器(program counter register)只占用了一块比较小的内存空间,至于小到什么程度呢,这样说吧,有时可以忽略不计的。 - - ->可以看作是当前线程所执行的字节码文件(class)的行号指示器。在虚拟机的世界中,字节码解释器就是通过改变计数器的值来选取下一条执行的字节码指令,分支、循环、跳转、异常处理、线程恢复都需要这玩意来实现的。 - -上面提到线程恢复,那下面介绍下其详细流程: ->java虚拟机多线程是通过线程间轮流切换来分配给处理器执行时间;在确定时间节点,一个处理器(一核)只会执行一个线程的指令;为保证 线程切换 回来后能恢复到原执行位置,各个线程间计数器互相不影响,独立存储(称之为 线程私有 的内存); - ->如果执行的是java方法,那么记录的是正在执行的虚拟机字节码指令的地址的地址,如果是native方法,计数器的值为空(undefined)。 - -最后,动动自己的大脑,想下面的问题: ->这个内存区域是唯一一个在java虚拟界规范中没有规定任何OutOfMemoryError的情况的区域。至于为什么没有这个异常呢,要是一个计数的功能在出这个异常,那么我也是醉了。 - - -####介绍下我的所有文集: -###### 流行框架 -[SpringCloud](https://www.jianshu.com/nb/18726057) -[springboot](https://www.jianshu.com/nb/19053594) -[nginx](https://www.jianshu.com/nb/18436827) -[redis](https://www.jianshu.com/nb/21461220) - -######底层实现原理: -[Java NIO教程](https://www.jianshu.com/nb/21635138) -[Java reflection 反射详解](https://www.jianshu.com/nb/21989596) -[Java并发学习笔录](https://www.jianshu.com/nb/22549959) -[Java Servlet教程](https://www.jianshu.com/nb/22065472) -[jdbc组件详解](https://www.jianshu.com/nb/22774157) -[Java NIO教程](https://www.jianshu.com/nb/21635138) -[Java语言/版本 研究](https://www.jianshu.com/nb/19137666) diff --git "a/Java-\346\225\260\346\215\256\347\273\223\346\236\204/Java\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225(\344\270\200)-\346\225\260\347\273\204.md" "b/Java-\346\225\260\346\215\256\347\273\223\346\236\204/Java\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225(\344\270\200)-\346\225\260\347\273\204.md" deleted file mode 100644 index 1e2dc58..0000000 --- "a/Java-\346\225\260\346\215\256\347\273\223\346\236\204/Java\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225(\344\270\200)-\346\225\260\347\273\204.md" +++ /dev/null @@ -1,134 +0,0 @@ -### 1. 无序数组 -``` -package com.fantj.dataStruct.array; - -/** - * Created by Fant.J. - * 2017/12/20 18:16 - */ -public class MyArray { - private long[] arr; - //表示有效数据的长度 - private int elements; - - public MyArray() { - arr = new long[50]; - } - - public MyArray(int maxsize) { - arr = new long[maxsize]; - } - - /** - * 添加数据 - */ - public void insert(long value){ - arr[elements] = value; - elements++; - } - - /** - * 显示数据 - */ - public void display(){ - System.out.print("["); - for (int i = 0;i < elements;i++){ - System.out.print(arr[i]+" "); - } - System.out.print("]"); - } - - /** - * 查找数据(根据元素查找) - */ - public int search(long value){ - int i; - for (i = 0;i < elements;i++){ - if (value == arr[i]){ - break; - } - } - //是否查到最后一个了 - if (i == elements){ - return -1; //查找不到 - }else { - return i; - } - } - /** - * 根据索引查找 - */ - public long get(int index){ - if (index >= elements || index < 0){ - throw new ArrayIndexOutOfBoundsException(); - }else { - return arr[index]; - } - } - /** - * 删除数据 - */ - public void delete(int index){ - if (index >= elements || index < 0){ - throw new ArrayIndexOutOfBoundsException(); - }else { - for (int i = index;i < elements;i++){ - arr[index] = arr[index+1]; - } - elements--; - } - } - /** - * 更新数据 - */ - public void update(int index,long newvalue){ - if (index >= elements || index < 0){ - throw new ArrayIndexOutOfBoundsException(); - }else { - arr[index] = newvalue; - } - } -} -``` -### 2. 有序数组(只在添加数据的时候做了改动) -``` - /** - * 添加数据 - */ - public void insert(long value){ - int i; - for (i = 0;i value){ - break; - } - } - for (int j = elements;j>i;j--){ - arr[j] = arr[j-1]; - } - arr[i] = value; - elements++; - } -``` -### 3.二分法查找(前提是有序数组) -``` - /** - * 二分法查找 - */ - public int binarySearch(long value){ - int pow = elements; - int low = 0; - int middle; - while (true){ - middle = (pow+low)/2; - if (arr[middle] == value){ - return middle; - }else { - if (arr[middle]>value){ - pow = middle - 1; //如果数组中间的值比value大,middle-1 - }else { - low = middle + 1; //如果小,最小届加一 - } - } - } - } -``` diff --git "a/Java-\346\225\260\346\215\256\347\273\223\346\236\204/Java\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225(\344\270\203)-\345\277\253\351\200\237\346\216\222\345\272\217.md" "b/Java-\346\225\260\346\215\256\347\273\223\346\236\204/Java\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225(\344\270\203)-\345\277\253\351\200\237\346\216\222\345\272\217.md" deleted file mode 100644 index cf97a52..0000000 --- "a/Java-\346\225\260\346\215\256\347\273\223\346\236\204/Java\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225(\344\270\203)-\345\277\253\351\200\237\346\216\222\345\272\217.md" +++ /dev/null @@ -1,13 +0,0 @@ -### 一、快速排序思想 ->快速排序由C. A. R. Hoare在1962年提出。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以[递归](https://baike.baidu.com/item/%E9%80%92%E5%BD%92)进行,以此达到整个数据变成有序[序列](https://baike.baidu.com/item/%E5%BA%8F%E5%88%97/1302588)。 -### 二、如何进行划分&关键字的设定 ->首先任意选取一个数据(通常选用数组的第一个数)作为关键数据,然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面,这个过程称为一趟快速排序。值得注意的是,快速排序不是一种稳定的[排序算法](https://baike.baidu.com/item/%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95),也就是说,多个相同的值的相对位置也许会在算法结束时产生变动。 -### 思路 ->一趟快速排序的算法是: -1)设置两个变量i、j,[排序](https://baike.baidu.com/item/%E6%8E%92%E5%BA%8F)开始的时候:i=0,j=N-1; -2)以第一个数组元素作为关键数据,赋值给**key**,即**key**=A[0]; -3.)从j开始向前搜索,即由后开始向前搜索(j--),找到第一个小于**key**的值A[j],将A[j]和A[i]互换; -4) 从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于**key**的A[i],将 A[i]和A[j]互换; -5)重复第3、4步,直到i=j; (3,4步中,没找到符合条件的值,即3中A[j]不小于**key**,4中A[i]不大于**key**的时候改变j、i的值,使得j=j-1,i=i+1,直至找到为止。找到符合条件的值,进行交换的时候i, j指针位置不变。另外,i==j这一过程一定正好是i+或j-完成的时候,此时令循环结束)。 - -累。。明天更 diff --git "a/Java-\346\225\260\346\215\256\347\273\223\346\236\204/Java\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225(\344\270\211)-\346\240\210\345\222\214\351\230\237\345\210\227.md" "b/Java-\346\225\260\346\215\256\347\273\223\346\236\204/Java\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225(\344\270\211)-\346\240\210\345\222\214\351\230\237\345\210\227.md" deleted file mode 100644 index dc63969..0000000 --- "a/Java-\346\225\260\346\215\256\347\273\223\346\236\204/Java\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225(\344\270\211)-\346\240\210\345\222\214\351\230\237\345\210\227.md" +++ /dev/null @@ -1,168 +0,0 @@ -### 1.栈 -先进后出,后进先出 ->栈(stack)又名堆栈,它是一种运算受限的线性表。其限制是仅允许在表的一端进行插入和删除运算。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。 -![stack.png](http://upload-images.jianshu.io/upload_images/5786888-2862cf08582e5dd3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - -``` -package com.fantj.dataStruct.stack; - -/** - * 栈 - * Created by Fant.J. - * 2017/12/21 10:20 - */ -public class MyStack { - //底层是一个数组 - private long []arr; - private int top; - /** - * 默认构造方法 - */ - public MyStack(){ - arr = new long[10]; - top = -1; - } - /** - * 带参数的构造方法 - */ - public MyStack(int maxsize){ - arr = new long[maxsize]; - top = -1; - } - /** - * 添加数据 - */ - public void push(int value){ - arr[++top] = value; - } - /** - * 移除pop数据 - */ - public long pop(){ - return arr[top--]; //返回数据并递减 - } - /** - * 查看数据 - */ - public long peek(){ - return arr[top]; //返回数据 - } - /** - * 判断 是否是空 - */ - public boolean isEmpty(){ - return top == -1; //top为-1,就是空 - } - /** - * 判断 是否满了 - */ - public boolean isFull(){ - return top == arr.length-1; - } -} - -``` -### 2.队列 -先进先出 ->队列是一种特殊的[线性表](https://baike.baidu.com/item/%E7%BA%BF%E6%80%A7%E8%A1%A8),特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头 -![queue.png](http://upload-images.jianshu.io/upload_images/5786888-ae5bbca06e3e6400.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - -``` -package com.fantj.dataStruct.queue; - -/** - * 队列(先进先出) - * Created by Fant.J. - * 2017/12/21 10:44 - */ -public class MyQueue { - //底层使用数组 - private long []arr; - //有效数据的多少 - private int elements; - //队头 - private int front; - //队尾 - private int end; - /** - * 默认构造方法 - */ - public MyQueue(){ - arr = new long[10]; - elements = 0; - front = 0; - end = -1; - } - /** - * 带参数的构造方法,参数为数组大小 - */ - public MyQueue(int maxsize){ - arr = new long[maxsize]; - elements = 0; - front = 0; - end = -1; - } - /** - * 添加数据,从队尾插入 - */ - public void insert(long value){ - arr[++end] = value; - elements++; - } - /** - * 删除数据,从队尾删除 - */ - public long remove(){ - elements--; - return arr[front++]; - } - /** - * 查看数据,从对头查看 - */ - public long peek(){ - return arr[front]; - } - /** - * 判断是否为空 - */ - public boolean isEmpty(){ - return elements == 0; - } - /** - * 判断是否满了 - */ - public boolean isFull(){ - return elements == arr.length; - } -} -``` -但是普通队列有个问题,就是你在进行一轮存取数据操作后,因为end=arr.length-1;front = arr.lenth-1;所以,会报错越界异常。因此我们会在插入删除前加入判断,让这个队列循环利用。 --循环队列 -### 2.队列pro --循环队列 ->为充分利用向量空间,克服"[假溢出](https://baike.baidu.com/item/%E5%81%87%E6%BA%A2%E5%87%BA)"现象的方法是:将向量空间想象为一个首尾相接的圆环,并称这种向量为循环向量。存储在其中的队列称为循环队列(Circular Queue)。这种循环队列可以以[单链表](https://baike.baidu.com/item/%E5%8D%95%E9%93%BE%E8%A1%A8)的方式来在实际编程应用中来实现。 - -修改两处:insert和remove -``` -/** - * 添加数据,从队尾插入 - */ - public void insert(long value){ - if (end == arr.length-1){ - end = -1; //如果到达了队列尽头,初始化end - } - arr[++end] = value; - elements++; - } - /** - * 删除数据,从队尾删除 - */ - public long remove(){ - long value = arr[front++]; - if (front == arr.length){ - front=0; //如果到达了队列尽头,初始化front - } - elements--; - return value; - } -``` -源码地址:[git地址](https://github.com/jiaofanting/Java-dataStruct/tree/master/src/com.fantj.dataStruct/queue) - diff --git "a/Java-\346\225\260\346\215\256\347\273\223\346\236\204/Java\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225(\344\271\235)-\345\223\210\345\270\214\350\241\250.md" "b/Java-\346\225\260\346\215\256\347\273\223\346\236\204/Java\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225(\344\271\235)-\345\223\210\345\270\214\350\241\250.md" deleted file mode 100644 index c8f30b5..0000000 --- "a/Java-\346\225\260\346\215\256\347\273\223\346\236\204/Java\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225(\344\271\235)-\345\223\210\345\270\214\350\241\250.md" +++ /dev/null @@ -1,99 +0,0 @@ -### 1. 什么是哈希表 ->[散列表](https://baike.baidu.com/item/%E6%95%A3%E5%88%97%E8%A1%A8)(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的[数据结构](https://baike.baidu.com/item/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84).也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做[散列函数](https://baike.baidu.com/item/%E6%95%A3%E5%88%97%E5%87%BD%E6%95%B0),存放记录的[数组](https://baike.baidu.com/item/%E6%95%B0%E7%BB%84)叫做[散列表](https://baike.baidu.com/item/%E6%95%A3%E5%88%97%E8%A1%A8)。 -也是基于数组来实现。 - -  Hash表也称散列表,也有直接译作哈希表,Hash表是一种特殊的数据结构,它同数组、链表以及二叉排序树等相比较有很明显的区别,它能够快速定位到想要查找的记录,而不是与表中存在的记录的关键字进行比较来进行查找。这个源于Hash表设计的特殊性,它采用了函数映射的思想将记录的存储位置与记录的关键字关联起来,从而能够很快速地进行查找。 - -1.Hash表的设计思想 -  对于一般的线性表,比如链表,如果要存储联系人信息:  -张三 13980593357 -李四 15828662334 -王五 13409821234 -张帅 13890583472 -  那么可能会设计一个结构体包含姓名,手机号码这些信息,然后把4个联系人的信息存到一张链表中。当要查找”李四 15828662334“这条记录是否在这张链表中或者想要得到李四的手机号码时,可能会从链表的头结点开始遍历,依次将每个结点中的姓名同”李四“进行比较,直到查找成功或者失败为止,这种做法的时间复杂度为O(n)。即使采用二叉排序树进行存储,也最多为O(logn)。假设能够通过”李四“这个信息直接获取到该记录在表中的存储位置,就能省掉中间关键字比较的这个环节,复杂度直接降到O(1)。Hash表就能够达到这样的效果。 - -  Hash表采用一个映射函数 f : key —> address 将关键字映射到该记录在表中的存储位置,从而在想要查找该记录时,可以直接根据关键字和映射关系计算出该记录在表中的存储位置,通常情况下,这种映射关系称作为Hash函数,而通过Hash函数和关键字计算出来的存储位置(注意这里的存储位置只是表中的存储位置,并不是实际的物理地址)称作为Hash地址。比如上述例子中,假如联系人信息采用Hash表存储,则当想要找到“李四”的信息时,直接根据“李四”和Hash函数计算出Hash地址即可。下面讨论一下Hash表设计中的几个关键问题。 - -1.1. Hash函数的设计 - -  Hash函数设计的好坏直接影响到对Hash表的操作效率。下面举例说明: - -  假如对上述的联系人信息进行存储时,采用的Hash函数为:姓名的每个字的拼音开头大写字母的ASCII码之和。 - -  因此address(张三)=ASCII(Z)+ASCII(S)=90+83=173; - -    address(李四)=ASCII(L)+ASCII(S)=76+83=159; - -    address(王五)=ASCII(W)+ASCII(W)=87+87=174; - -    address(张帅)=ASCII(Z)+ASCII(S)=90+83=173; - -  假如只有这4个联系人信息需要进行存储,这个Hash函数设计的很糟糕。首先,它浪费了大量的存储空间,假如采用char型数组存储联系人信息的话,则至少需要开辟174*12字节的空间,空间利用率只有4/174,不到5%;另外,根据Hash函数计算结果之后,address(张三)和address(李四)具有相同的地址,这种现象称作冲突,对于174个存储空间中只需要存储4条记录就发生了冲突,这样的Hash函数设计是很不合理的。所以在构造Hash函数时应尽量考虑关键字的分布特点来设计函数使得Hash地址随机均匀地分布在整个地址空间当中。通常有以下几种构造Hash函数的方法: - -  1)直接定址法 - -  取关键字或者关键字的某个线性函数为Hash地址,即address(key)=a*key+b;如知道学生的学号从2000开始,最大为4000,则可以将address(key)=key-2000作为Hash地址。 - -  2)平方取中法 - -  对关键字进行平方运算,然后取结果的中间几位作为Hash地址。假如有以下关键字序列{421,423,436},平方之后的结果为{177241,178929,190096},那么可以取{72,89,00}作为Hash地址。 - -  3)折叠法 - -  将关键字拆分成几部分,然后将这几部分组合在一起,以特定的方式进行转化形成Hash地址。假如知道图书的ISBN号为8903-241-23,可以将address(key)=89+03+24+12+3作为Hash地址。 - -  4)除留取余法 - -  如果知道Hash表的最大长度为m,可以取不大于m的最大质数p,然后对关键字进行取余运算,address(key)=key%p。 - -  在这里p的选取非常关键,p选择的好的话,能够最大程度地减少冲突,p一般取不大于m的最大质数。 - -2.Hash表大小的确定 - -  Hash表大小的确定也非常关键,如果Hash表的空间远远大于最后实际存储的记录个数,则造成了很大的空间浪费,如果选取小了的话,则容易造成冲突。在实际情况中,一般需要根据最终记录存储个数和关键字的分布特点来确定Hash表的大小。还有一种情况时可能事先不知道最终需要存储的记录个数,则需要动态维护Hash表的容量,此时可能需要重新计算Hash地址。 - -3.冲突的解决 - -  在上述例子中,发生了冲突现象,因此需要办法来解决,否则记录无法进行正确的存储。通常情况下有2种解决办法: - -  1)开放定址法 - -  即当一个关键字和另一个关键字发生冲突时,使用某种探测技术在Hash表中形成一个探测序列,然后沿着这个探测序列依次查找下去,当碰到一个空的单元时,则插入其中。比较常用的探测方法有线性探测法,比如有一组关键字{12,13,25,23,38,34,6,84,91},Hash表长为14,Hash函数为address(key)=key%11,当插入12,13,25时可以直接插入,而当插入23时,地址1被占用了,因此沿着地址1依次往下探测(探测步长可以根据情况而定),直到探测到地址4,发现为空,则将23插入其中。 - -  2)链地址法 - -   采用数组和链表相结合的办法,将Hash地址相同的记录存储在一张线性表中,而每张表的表头的序号即为计算得到的Hash地址。如上述例子中,采用链地址法形成的Hash表存储表示为:    -![image.png](http://upload-images.jianshu.io/upload_images/5786888-a7c273ceda699e37.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - -   虽然能够采用一些办法去减少冲突,但是冲突是无法完全避免的。因此需要根据实际情况选取解决冲突的办法。 - -4.Hash表的平均查找长度 - -  Hash表的平均查找长度包括查找成功时的平均查找长度和查找失败时的平均查找长度。 - -  查找成功时的平均查找长度=表中每个元素查找成功时的比较次数之和/表中元素个数; - -  查找不成功时的平均查找长度相当于在表中查找元素不成功时的平均比较次数,可以理解为向表中插入某个元素,该元素在每个位置都有可能,然后计算出在每个位置能够插入时需要比较的次数,再除以表长即为查找不成功时的平均查找长度。 - -  下面举个例子: - -  有一组关键字{23,12,14,2,3,5},表长为14,Hash函数为key%11,则关键字在表中的存储如下: - -  地址     0     1     2     3      4     5    6   7   8    9  10   11   12    13 - -  关键字        23    12   14     2     3    5 - - 比较次数         1      2    1     3     3     2 - -  因此查找成功时的平均查找长度为(1+2+1+3+3+2)/6=11/6; - -  查找失败时的平均查找长度为(1+7+6+5+4+3+2+1+1+1+1+1+1+1)/14=38/14; - -  这里有一个概念装填因子=表中的记录数/哈希表的长度,如果装填因子越小,表明表中还有很多的空单元,则发生冲突的可能性越小;而装填因子越大,则发生冲突的可能性就越大,在查找时所耗费的时间就越多。因此,Hash表的平均查找长度和装填因子有关。有相关文献证明当装填因子在0.5左右的时候,Hash的性能能够达到最优。因此,一般情况下,装填因子取经验值0.5。 - -5.Hash表的优缺点 - -  Hash表存在的优点显而易见,能够在常数级的时间复杂度上进行查找,并且插入数据和删除数据比较容易。但是它也有某些缺点,比如不支持排序,一般比用线性表存储需要更多的空间,并且记录的关键字不能重复。 - -借鉴博客:[海子](http://www.cnblogs.com/dolphin0520/) diff --git "a/Java-\346\225\260\346\215\256\347\273\223\346\236\204/Java\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225(\344\272\214)-\347\256\200\345\215\225\346\216\222\345\272\217.md" "b/Java-\346\225\260\346\215\256\347\273\223\346\236\204/Java\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225(\344\272\214)-\347\256\200\345\215\225\346\216\222\345\272\217.md" deleted file mode 100644 index ac1d180..0000000 --- "a/Java-\346\225\260\346\215\256\347\273\223\346\236\204/Java\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225(\344\272\214)-\347\256\200\345\215\225\346\216\222\345\272\217.md" +++ /dev/null @@ -1,105 +0,0 @@ -##### 1.冒泡排序 ->冒泡排序(Bubble Sort),是一种计算机科学领域的较简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越大的元素会经由交换慢慢“浮”到数列的顶端。 -##### 2.选择排序 ->选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。 选择排序是不稳定的排序方法。 - -* 每一轮遍历,都去寻找一个最小值,然后把当前的位置与最小值交换。 -这样下来得到的是一组 从小到大的排序。 -##### 3.插入排序 ->插入排序基本操作就是将一个数据插入到已经排好序的有序数据中,从而得到一个新的、个数加一的有序数据,算法适用于少量数据的排序,时间复杂度为O(n^2)。是稳定的排序方法。插入排序的基本思想是:每步将一个待排序的纪录,按其关键码值的大小插入前面已经排序的文件中适当位置上,直到全部插入完为止。 - -* 先拿出第二个数(然后依次往后拿),如果发现前面有比它小的数字,该元素后移。 -可以看下动态图先做了解 -[点击查看排序动态图模拟网](http://www.atool.org/sort.php) -也特别感谢这位作者 -### 1.冒泡排序 -略,很常见的排序 -``` -package com.fantj.dataStruct.simplesort; - -/** - * 冒泡排序 - * Created by Fant.J. - * 2017/12/20 19:43 - */ -public class BubbleSort { - //小值往前排 - public static void sort(long [] arr){ - long tmp = 0; - for (int i = 0;i < arr.length;i++){ - for (int j = arr.length-1;j>i;j--){ - if (arr[j] 0 && target < arr[j - 1]) - { - arr[j] = arr[j - 1]; - j--; - } - arr[j] = target; - } - } -} - -``` -[git地址](https://github.com/jiaofanting/Java-dataStruct/tree/master/src/com.fantj.dataStruct/simplesort) diff --git "a/Java-\346\225\260\346\215\256\347\273\223\346\236\204/Java\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225(\344\272\224)-\345\217\214\345\220\221\351\223\276\350\241\250.md" "b/Java-\346\225\260\346\215\256\347\273\223\346\236\204/Java\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225(\344\272\224)-\345\217\214\345\220\221\351\223\276\350\241\250.md" deleted file mode 100644 index 5882e0a..0000000 --- "a/Java-\346\225\260\346\215\256\347\273\223\346\236\204/Java\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225(\344\272\224)-\345\217\214\345\220\221\351\223\276\350\241\250.md" +++ /dev/null @@ -1,149 +0,0 @@ -* 什么是双向链表 -每个结点除了保存了xui下一个结点的引用,同时还保存这对前一个节点的引用。 -* 从头部进行哈如 -要对链表进行判断,如果为空则这是尾结点为信添加的结点。如果不为空,还需要设置投结点的前一个结点为心田的结点。 -* 从尾部进行插入 -如果链表为空,则直接设置头结点为新添加的结点,否则设置尾结点的后一个结点为新添加的结点。同时设置新添加的结点的前一个结点为尾结点。 -* 从头部进行删除 -判断头结点是否有下一个结点,如果没有则设置为结点为null。否则设置头结点的下一个结点的previous为null。 -* 从尾部进行删除 -如果头结点后没有其他结点,则设置尾结点为null。否则设置尾结点前一个结点的next为null。设置尾结点为其前一个结点。 -* 删除方法 -不需要再使用一个0时的指针域。 -``` -package com.fantj.dataStruct.doublelistnode; - -/** - * 双向链表,比双端链表多了一个头结点的指向 - * Created by Fant.J. - * 2017/12/21 19:49 - */ -public class DoubleLinkList { - //头结点 - private Node first; - //尾结点 - private Node last; - - public DoubleLinkList(){ - first = null; - } - /** - * 插入一个结点,在头结点后进行插入 - */ - public void insertFirst(long value){ - Node node = new Node(value); - //如果是第一次插入 - if (isEmpty()){ - last = node; - }else { - first.previous = node; - } - node.next = first; - first = node; - } - /** - * 插入一个结点,从尾结点进行插入 - */ - public void insertLast(long value){ - Node node = new Node(value); - if (isEmpty()){ - first = node; - }else { - last.next = node; - node.previous = last; - } - last = node; - } - /** - * 删除一个结点,在头结点后进行删除 - */ - public Node deleteFirst(){ - Node temp = first; - if (first.next == null){ - last = null; - }else { - first.next.previous = null; - } - first = temp.next; - return temp; - } - /** - * 显示方法 - */ - public void display(){ - Node current = first; - while (current != null){ - current.display(); //打印结点 - current = current.next; - } - } - /** - * 查找方法 - */ - public Node find(long value){ - Node current = first; - while (current.data != value){ - if (current.next == null){ - return null; - } - current = current.next;//继续往下找 - } - return current; - } - /** - * 删除方法,根据数据域来进行删除 - */ - public Node delete(long value){ - Node current = first; - Node previous = first;//表示前一个结点 - while (current.data != value){ - if (current.next == null){ - return null; - } - previous = current; //提取出当前结点作为前一个结点(用该结点的next指向删除结点的后一个结点) - current = current.next; //继续往下找 - } - if (current == first){ - first = first.next; - }else { - previous.next = current.next; - } - return current; - } - /** - * 判断是否为空 - */ - public boolean isEmpty(){ - return (first == null); - } -} -``` -``` -package com.fantj.dataStruct.doublelistnode; - -/** - * 链表结构,链结点 - * Created by Fant.J. - * 2017/12/19 22:19 - */ -public class Node { - //数据域 - public long data; - //结点域(指针域) - public Node next; - public Node previous; - - - public Node(long value){ - this.data = value; - } - - /** - * 显示方法 - */ - public void display(){ - System.out.print(data+" "); - } -} -``` -查看源码:[git地址](https://github.com/jiaofanting/Java-dataStruct/tree/master/src/com.fantj.dataStruct/doublelistnode) diff --git "a/Java-\346\225\260\346\215\256\347\273\223\346\236\204/Java\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225(\345\205\253)-\344\272\214\345\217\211\346\240\221.md" "b/Java-\346\225\260\346\215\256\347\273\223\346\236\204/Java\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225(\345\205\253)-\344\272\214\345\217\211\346\240\221.md" deleted file mode 100644 index 02f1791..0000000 --- "a/Java-\346\225\260\346\215\256\347\273\223\346\236\204/Java\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225(\345\205\253)-\344\272\214\345\217\211\346\240\221.md" +++ /dev/null @@ -1,252 +0,0 @@ -###一、为什么要使用树 -* 有序数组插入、删除数据慢。 -* 链表查找数据慢 -* 树可以解决这两个问题 -###二、相关术语 -* 树的结点:包含一个数据元素及若干指向子树的分支; -* 孩子结点:结点的子树的根称为该结点的孩子; -* 双亲结点:B 结点是A 结点的孩子,则A结点是B 结点的双亲; -* 兄弟结点:同一双亲的孩子结点; 堂兄结点:同一层上结点; -* 祖先结点: 从根到该结点的所经分支上的所有结点子孙结点:以某结点为根的子树中* 任一结点都称为该结点的子孙 -* 结点层:根结点的层定义为1;根的孩子为第二层结点,依此类推; -* 树的深度:树中最大的结点层 -* 结点的度:结点子树的个数 -* 树的度: 树中最大的结点度。 -* 叶子结点:也叫终端结点,是度为 0 的结点; -* 分枝结点:度不为0的结点; -* 有序树:子树有序的树,如:家族树; -* 无序树:不考虑子树的顺序; -###三、基本操作 -1. 插入结点 - - 从根结点开始查找一个相应的结点,这个结点成为新插入结点的父节点,当父节点找到后,通过判断新结点的值比父节点的值的大小来决定是连接到左子结点还是右子结点 -2. 查找结点 - - 从根结点开始查找,如果查找的结点值比当前的结点的值小,则继续查找其左子树,否则查找其右子树。 -3. 遍历二叉树 - * 遍历树是根据一个特定的顺序访问树的没一个节点,根据顺序的不通氛围前序、中序、后序三中遍历。 - 1. 前序 - 1. 访问跟节点 - 2. 前序遍历左子树 - 3.遍历右子树 - 2. 中序 - 1. 遍历左子树 - 2. 访问跟节点 - 3.遍历右子树 - 3. 后序 - 1. 遍历左子树 - 2. 遍历右子树 - 3.访问跟节点 -4. 删除二叉树节点 -* 删除是最复杂的,在删除之前首先要查找要删的节点。找到节点后,这个要删除的节点可能会有三中情况需要考虑。 -1. 该节点是叶子节点,没有子节点 - - 要删除叶子节点,只需要改变该节点的父节点的引用值,将指向该节点的引用设置为null就可以了。 -2. 该节点有一个子节点 - - 改变父节点的引用,将其直接指向要删除节点的子节点。 -3. 该节点右两个子节点 - - 要删除右两个子节点的节点,就需要使用他的中序后继来替代该节点。 - -###代码实现 -``` -package com.fantj.dataStruct.tree; - -/** - * 二叉树结点 - * Created by Fant.J. - * 2017/12/22 16:09 - */ -public class Node { - //数据项 - public long data; - //左子结点 - public Node leftChild; - //右子结点 - public Node rightChild; - //构造方法 - public Node(long data){ - this.data = data; - } -} -``` -``` -package com.fantj.dataStruct.tree; - -/** - * 二叉树 - * Created by Fant.J. - * 2017/12/22 16:11 - */ -public class Tree { - //根结点 - public Node root; - /** 插入结点 */ - public void insert(long value){ - //封装结点 - Node newNode = new Node(value); - //引用当前结点 - Node current = root; - //引用父节点 - Node parent; - //如果root为null,也就是第一次插入的时候 - if (root == null){ - root = newNode; - return; - }else { - while (true){ - //父节点指向当前结点 - parent = current; - //如果当前指向的结点数据比插入的要大,则向左走 - if (current.data > value){ - current = current.leftChild; - if (current == null){ - parent.leftChild = newNode; - return; - } - }else { - //生成一个右子节点,并且赋值为 newNode - current = current.rightChild; - if (current == null){ - parent.rightChild = newNode; - return; - } - } - } - } - - } - /* 查找节点 **/ - public Node find(long value){ - //引用当前节点,从根节点开始 - Node current = root; - //循环,只要查找值不等于当前节点值 - while (current.data != value){ - //进行比较,比较查找值和当前节点的大小 - if (current.data > value){ - current = current.leftChild; - }else { - current = current.rightChild; - } - //如果是空,则退出 - if (current == null){ - return null; - } - } - return current; - } - /* 前序遍历 **/ - public void frontOrder(Node localNode){ - if (localNode != null){ - //访问根节点 - System.out.print(localNode.data+","); - //前序遍历左子树 - frontOrder(localNode.leftChild); - //前序遍历右子树 - frontOrder(localNode.rightChild); - } - } - /* 中序遍历 **/ - public void inOrder(Node localNode){ - if (localNode != null){ - //中序遍历左子树 - inOrder(localNode.leftChild); - //访问根节点 - System.out.print(localNode.data+","); - //中序遍历右子树 - inOrder(localNode.rightChild); - } - } - /* 后序遍历 **/ - public void afterOrder(Node localNode){ - if (localNode != null){ - //后序遍历左子树 - afterOrder(localNode.leftChild); - //后序遍历右子树 - afterOrder(localNode.rightChild); - //访问根节点 - System.out.print(localNode.data+","); - } - } - - /* 删除结点 **/ -} -``` -这里我把删除节点的操作单独拿出来*(因为比较复杂) -``` - /* 删除结点 **/ - public boolean delete(long value){ - //引用当前节点,从根节点开始 - Node current = root; - //引用当前节点的父节点 - Node parent = root; - //是否右左子节点 - boolean isLeftChild = true; - while (current.data != value){ - parent = current; - //进行比较,比较value和当前节点 - if (current.data > value){ - current = current.leftChild; - isLeftChild = true; - }else { - current = current.rightChild; - isLeftChild = false; - } - //如果查找不到 - if (current ==null){ - return false; - } - } - //删除叶子节点,也就是该节点没有子节点 - if (current.leftChild == null && current.rightChild == null){ - if (current == root){ - root = null; - } - //如果是左子节点 - if (isLeftChild){ - parent.leftChild = null; - }else { - parent.rightChild = null; - } - }else if (current.rightChild == null){ - if (current == root){ - root = current.leftChild; - }else if (isLeftChild){ - parent.leftChild = current.leftChild; - }else { - parent.rightChild = current.leftChild; - } - }else if(current.leftChild == null){ - if (root == current){ - root = current.rightChild; - } - if (isLeftChild){ - parent.leftChild = current.rightChild; - }else { - parent.rightChild = current.rightChild; - } - }else { - //获取中序后继节点 - Node succeed = getSucceed(current); - if (current == root){ - root = succeed; - }else if (isLeftChild){ - parent.leftChild = succeed; - } - succeed.leftChild = current.leftChild; - } - return true; - } - /* 找到后继(succeed)节点 , 后继是按照中序先找右子树,然后找左节点**/ - public Node getSucceed(Node delNode){ - Node succeed = delNode; - Node succeedParent = delNode; - Node current = delNode.rightChild; - while (current != null){ - succeedParent = succeed; - succeed = current; - current = current.leftChild; - } - if (succeed != delNode.rightChild){ - succeedParent.leftChild = succeed.rightChild; - succeed.rightChild = delNode.rightChild; - } - return succeed; - } -``` diff --git "a/Java-\346\225\260\346\215\256\347\273\223\346\236\204/Java\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225(\345\205\255)-\345\270\214\345\260\224\346\216\222\345\272\217.md" "b/Java-\346\225\260\346\215\256\347\273\223\346\236\204/Java\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225(\345\205\255)-\345\270\214\345\260\224\346\216\222\345\272\217.md" deleted file mode 100644 index 2d13f0e..0000000 --- "a/Java-\346\225\260\346\215\256\347\273\223\346\236\204/Java\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225(\345\205\255)-\345\270\214\345\260\224\346\216\222\345\272\217.md" +++ /dev/null @@ -1,87 +0,0 @@ -###一、希尔排序的产生 ->希尔排序(Shell Sort)是[插入排序](https://baike.baidu.com/item/%E6%8F%92%E5%85%A5%E6%8E%92%E5%BA%8F)的一种。也称缩小[增量](https://baike.baidu.com/item/%E5%A2%9E%E9%87%8F)排序,是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。该方法因DL.Shell于1959年提出而得名。 -希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。 - -###二、希尔排序是基于插入排序的以下两点性质而提出改进方法的: -插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率。 -但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位。 -![希尔排序.png](http://upload-images.jianshu.io/upload_images/5786888-5db6ad4c21dd7e29.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) -![希尔排序.png](http://upload-images.jianshu.io/upload_images/5786888-8d4cd18d50f123c0.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) -``` -public static void main(String [] args) -{ - int[]a={49,38,65,97,76,13,27,49,78,34,12,64,1}; - System.out.println("排序之前:"); - for(int i=0;i=0&&a[j]>temp;j=j-d) - { - a[j+d]=a[j]; - } - a[j+d]=temp; - } - } - if(d==1) - { - break; - } - } - System.out.println(); - System.out.println("排序之后:"); - for(int i=0;i 0){ - //进行插入排序 - long temp = 0; - for (int i = 1; i< arr.length ;i++){ - temp = arr[i]; - int j = i; - while (j>h-1 && arr[j-h] >= temp){ - arr[j] = arr[j-1]; - j -= h; - } - arr[j] = temp; - } - //减小间隔 - h = (h -1) / 3; - } - } -} - -``` diff --git "a/Java-\346\225\260\346\215\256\347\273\223\346\236\204/Java\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225(\345\215\201)-\345\233\276.md" "b/Java-\346\225\260\346\215\256\347\273\223\346\236\204/Java\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225(\345\215\201)-\345\233\276.md" deleted file mode 100644 index 83b5c4a..0000000 --- "a/Java-\346\225\260\346\215\256\347\273\223\346\236\204/Java\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225(\345\215\201)-\345\233\276.md" +++ /dev/null @@ -1,301 +0,0 @@ -###1.什么是图 -* 图是一种和树相像的数据结构,通常有一个固定的形状,这是有物理或者抽象的问题来决定的。 -###2.邻接 -* 如果两个定点被同一条便连接,就称这两个定点是邻接的。 -###3.路径 -* 路径是从一个定点到另一个定点经过的边的序列。 -###4. 连通图和非连通图 -* 至少有一挑路径可以连接所有的定点,那么这个图就是连通的,否则是非连通的。 -###5.有向图和无向图 -* 有向图的边是有方向的,入只能从A到B,不能从B到A。 -* 无向图的边是没有方向的,可以从A到B,也可以从B到A。 -###6.带权图 -* 在有些图中,边被富裕了一个权值,权值是一个数字,他可以代表如两个顶点的物理距离,或者是一个顶点到另一个顶点的时间等等,这样的图叫做带权图。 -###7.程序实现 -``` -#include - -#define MAXVEX 100 //最大顶点数 -#define INFINITY 65535 //最大权值 - -typedef int EdgeType; //权值类型自己定义 -typedef char VertexType; //顶点类型自己定义 -#pragma once - -#pragma region 邻接矩阵结构体 -typedef struct -{ - VertexType vex[MAXVEX]; //顶点表 - EdgeType arg[MAXVEX][MAXVEX]; ///权值表-邻接矩阵 - int numVertexes,numEdges; //图中的边数和顶点数 -}GraphArray; - -#pragma endregion - -#pragma region 邻接表结构体 -//边表结点 -typedef struct EdgeNode -{ - int nNodevex; //邻接点的点表中结点的坐标 - EdgeType nNodeWeight; //用于网图中边的权值 - EdgeNode* next; //链域,指向下一个邻接点 -}EdgeNode,*pEdgeNode; -//顶点表结点 -typedef struct VertexNode -{ - VertexType nNodeData; //顶点表中存储的数据 - pEdgeNode pFirstNode; //顶点表和边表中关联指针,指向边表头指针 - -}VertexNode,pVertexNode,VertexList[MAXVEX]; -//图结构 -typedef struct -{ - VertexList vertexList; - int numVertess,numEdges; -}GraphList; - -#pragma endregion - -class GraphData -{ -public: - GraphData(void); - ~GraphData(void); - #pragma region 创建邻接矩阵 - void CreateGraphArray(GraphArray* pGraphArray,int numVer,int numEdegs); - int GetGraphLocation(GraphArray* pGraphArrray,char chpoint); - #pragma endregion - - #pragma region 创建邻接表 - void CreateGraphList(GraphList* pList,int numVer,int numEdegs); - int GetGraphListLocation(GraphList* pList,char chpoint); - #pragma endregion - -}; -``` -``` -#include "GraphData.h" - - -GraphData::GraphData(void) -{ -} - - -GraphData::~GraphData(void) -{ -} - -int GraphData::GetGraphLocation(GraphArray* pGraphArrray,char chpoint) -{ - int i = 0; - for (i = 0;i< pGraphArrray->numVertexes;i++) - { - if (pGraphArrray->vex[i] == chpoint) - { - break;; - } - } - if (i >= pGraphArrray->numVertexes) - { - return -1; - } - return i; -} -/// -/// 创建邻接矩阵 -/// -void GraphData::CreateGraphArray(GraphArray* pGraphArray,int numVer,int numEdegs) -{ - int weight = 0; - pGraphArray->numVertexes = numVer; - pGraphArray->numEdges = numEdegs; - - //创建顶点表 - for (int i= 0; i < numVer;i++) - { - pGraphArray->vex[i] = getchar(); - while(pGraphArray->vex[i] == '\n') - { - pGraphArray->vex[i] = getchar(); - } - } - - //创建邻接表的边矩阵 - for (int i = 0; i < numEdegs; i++) - { - for (int j = 0;j < numEdegs ; j++) - { - pGraphArray->arg[i][j] = INFINITY; - } - } - for(int k = 0; k < pGraphArray->numEdges; k++) - { - char p, q; - printf("输入边(vi,vj)上的下标i,下标j和权值:\n"); - - p = getchar(); - while(p == '\n') - { - p = getchar(); - } - q = getchar(); - while(q == '\n') - { - q = getchar(); - } - scanf("%d", &weight); - - int m = -1; - int n = -1; - m = GetGraphLocation(pGraphArray, p); - n = GetGraphLocation(pGraphArray, q); - if(n == -1 || m == -1) - { - fprintf(stderr, "there is no this vertex.\n"); - return; - } - //getchar(); - pGraphArray->arg[m][n] = weight; - pGraphArray->arg[n][m] = weight; //因为是无向图,矩阵对称 - } - -} - -#pragma region -void GraphData::CreateGraphList(GraphList* pList,int numVer,int numEdegs) -{ - int weight = 0; - GraphList *pGraphList = pList; - pGraphList->numVertess = numVer; - pGraphList->numEdges = numEdegs; - EdgeNode* firstNode,*secondNode; - //创建顶点表 - for (int i= 0; i < numVer;i++) - { - pGraphList->vertexList[i].nNodeData = getchar(); - pGraphList->vertexList[i].pFirstNode = NULL; - while(pGraphList->vertexList[i].nNodeData == '\n') - { - pGraphList->vertexList[i].nNodeData = getchar(); - } - } - - //创建边表 - for(int k = 0; k < pGraphList->numEdges; k++) - { - char p, q; - printf("输入边(vi,vj)上的下标i,下标j和权值:\n"); - - p = getchar(); - while(p == '\n') - { - p = getchar(); - } - q = getchar(); - while(q == '\n') - { - q = getchar(); - } - scanf("%d", &weight); - - int m = -1; - int n = -1; - m = GetGraphListLocation(pGraphList, p); - n = GetGraphListLocation(pGraphList, q); - if(n == -1 || m == -1) - { - fprintf(stderr, "there is no this vertex.\n"); - return; - } - //getchar(); - //字符p在顶点表的坐标为m,与坐标n的结点建立联系权重为weight - firstNode = new EdgeNode(); - firstNode->nNodevex = n; - firstNode->next = pGraphList->vertexList[m].pFirstNode; - firstNode->nNodeWeight = weight; - pGraphList->vertexList[m].pFirstNode = firstNode; - - //第二个字符second - secondNode = new EdgeNode(); - secondNode->nNodevex = m; - secondNode->next = pGraphList->vertexList[n].pFirstNode; - secondNode->nNodeWeight = weight; - pGraphList->vertexList[n].pFirstNode = secondNode; - - } -} - -int GraphData::GetGraphListLocation(GraphList* pList,char chpoint) -{ - GraphList *pGraphList = pList; - int i = 0; - for (i = 0;i< pGraphList->numVertess;i++) - { - if (pGraphList->vertexList[i].nNodeData == chpoint) - { - break;; - } - } - if (i >= pGraphList->numVertess) - { - return -1; - } - return i; -} - -#pragma endregion -``` - -``` -#include -#include "GraphData.h" -using namespace std; -// - -void PrintGrgph(GraphList *pGraphList) -{ - int i =0; - while(pGraphList->vertexList[i].pFirstNode != NULL && ivertexList[i].nNodeData); - EdgeNode *e = NULL; - e = pGraphList->vertexList[i].pFirstNode; - while(e != NULL) - { - printf("%d ", e->nNodevex); - e = e->next; - } - i++; - printf("\n"); - } - -} -int main() -{ - int numVexs,numEdges; - GraphData* pTestGraph = new GraphData(); - GraphArray graphArray; - GraphArray* pGraphArray = &graphArray; - GraphList* pGgraphList = new GraphList(); - - cout<<"输入顶点数和边数"<>numVexs>>numEdges; - cout<<"顶点数和边数为:"<CreateGraphArray(pGraphArray,numVexs,numEdges); - for(int i = 0; i< numEdges;i++) - { - for (int j = 0;j< numEdges;j++) - { - cout<arg[i][j]; - } - cout<CreateGraphList(pGgraphList,numVexs,numEdges); - PrintGrgph(pGgraphList); - system("pause"); -} -``` - -借鉴文章[不归路](http://www.cnblogs.com/polly333/) diff --git "a/Java-\346\225\260\346\215\256\347\273\223\346\236\204/Java\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225(\345\233\233)-\351\223\276\350\241\250.md" "b/Java-\346\225\260\346\215\256\347\273\223\346\236\204/Java\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225(\345\233\233)-\351\223\276\350\241\250.md" deleted file mode 100644 index de51eeb..0000000 --- "a/Java-\346\225\260\346\215\256\347\273\223\346\236\204/Java\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225(\345\233\233)-\351\223\276\350\241\250.md" +++ /dev/null @@ -1,109 +0,0 @@ ->链表是一种物理[存储单元](https://baike.baidu.com/item/%E5%AD%98%E5%82%A8%E5%8D%95%E5%85%83) -上非连续、非顺序的[存储结构](https://baike.baidu.com/item/%E5%AD%98%E5%82%A8%E7%BB%93%E6%9E%84),[数据元素](https://baike.baidu.com/item/%E6%95%B0%E6%8D%AE%E5%85%83%E7%B4%A0)的逻辑顺序是通过链表中的[指针](https://baike.baidu.com/item/%E6%8C%87%E9%92%88) -链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储[数据元素](https://baike.baidu.com/item/%E6%95%B0%E6%8D%AE%E5%85%83%E7%B4%A0)的数据域,另一个是存储下一个结点地址的[指针](https://baike.baidu.com/item/%E6%8C%87%E9%92%88) -域。 相比于[线性表](https://baike.baidu.com/item/%E7%BA%BF%E6%80%A7%E8%A1%A8)[顺序结构](https://baike.baidu.com/item/%E9%A1%BA%E5%BA%8F%E7%BB%93%E6%9E%84),操作复杂。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而线性表和顺序表相应的时间复杂度分别是O(logn)和O(1)。 - -``` -package com.fantj.dataStruct.listnode; - -/** - * 链表结构,链结点 - * Created by Fant.J. - * 2017/12/19 22:19 - */ -public class Node { - //数据域 - public long data; - //结点域(指针域) - public Node next; - - public Node(long value){ - this.data = value; - } - - /** - * 显示方法 - */ - public void display(){ - System.out.print(data+" "); - } -} -``` -``` -package com.fantj.dataStruct.listnode; - -/** - * 链表 - * Created by Fant.J. - * 2017/12/21 11:26 - */ -public class LinkList { - //头结点 - private Node first; - - public LinkList(){ - first = null; - } - /** - * 插入一个结点,在头结点后进行插入 - */ - public void insertFirst(long value){ - Node node = new Node(value); - node.next = first; - first = node; - } - /** - * 删除一个结点,在头结点后进行删除 - */ - public Node deleteFirst(){ - Node temp = first; - first = temp.next; - return temp; - } - /** - * 显示方法 - */ - public void display(){ - Node current = first; - while (current != null){ - current.display(); //打印结点 - current = current.next; - } - } - /** - * 查找方法 - */ - public Node find(long value){ - Node current = first; - while (current.data != value){ - if (current.next == null){ - return null; - } - current = current.next;//继续往下找 - } - return current; - } - /** - * 删除方法,根据数据域来进行删除 - */ - public Node delete(long value){ - Node current = first; - Node previous = first;//表示前一个结点 - while (current.data != value){ - if (current.next == null){ - return null; - } - previous = current; //提取出当前结点作为前一个结点(用该结点的next指向删除结点的后一个结点) - current = current.next; //继续往下找 - } - if (current == first){ - first = first.next; - }else { - previous.next = current.next; - } - return current; - } - -} -``` -源码:[git地址](https://github.com/jiaofanting/Java-dataStruct/tree/master/src/com.fantj.dataStruct/listnode) diff --git "a/Java/Java--\345\233\233\347\247\215\345\274\225\347\224\250\347\232\204\350\247\243\350\257\273.md" "b/Java/Java--\345\233\233\347\247\215\345\274\225\347\224\250\347\232\204\350\247\243\350\257\273.md" deleted file mode 100644 index 502648b..0000000 --- "a/Java/Java--\345\233\233\347\247\215\345\274\225\347\224\250\347\232\204\350\247\243\350\257\273.md" +++ /dev/null @@ -1,77 +0,0 @@ -> Java从1.2版本开始引入了4种引用,这4种引用的级别由高到低依次为:强引用 > 软引用 > 弱引用 > 虚引用。 - - - -### 1. 强引用(StrongReference) ->强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。 - -只要引用存在,垃圾回收器永远不会回收。 -例如: -``` -Object object = new Object(); -String str = "hello"; -``` ->但是我们也知道,不是所有的强引用在任何时间都是有效的,那jvm如何处理的呢? - -如果想中断强引用和某个对象之间的关联,可以显示地将引用赋值为null,这样一来的话,JVM在合适的时间就会回收该对象。 -比如Vector类的clear方法中就是通过将引用赋值为null来实现清理工作的: -``` - public synchronized E remove(int index) { - modCount++; - if (index >= elementCount) - throw new ArrayIndexOutOfBoundsException(index); - Object oldValue = elementData[index]; - - int numMoved = elementCount - index - 1; - if (numMoved > 0) - System.arraycopy(elementData, index+1, elementData, index, - numMoved); - elementData[--elementCount] = null; // Let gc do its work - - return (E)oldValue; - } -``` -### 2. 软引用(SoftReference) ->如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。 - -``` - public static void main(String[] args) { - SoftReference sr = new SoftReference(new String("hello")); - System.out.println(sr.get()); - } -``` -控制台打印一hello,当内存不足时,会回收这部分对象。 -### 3. 弱引用(WeakReference) ->弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。 - -弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。 -``` - public static void main(String[] args) { - WeakReference sr = new WeakReference(new String("hello")); - System.out.println(sr.get()); - System.gc(); - System.out.println(sr.get()); - } -``` -控制台打印: -``` -hello -null -``` -可以看到,只要显示调用gc,弱引用对象就被回收。 - -### 4. 虚引用(PhantomReference) ->形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。 - -虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之 关联的引用队列中。 -``` - public static void main(String[] args) { - ReferenceQueue queue = new ReferenceQueue(); - PhantomReference pr = new PhantomReference(new String("hello"), queue); - System.out.println(pr.get()); - } -``` -``` -null -``` - diff --git "a/Java/Java-\345\217\221\351\200\201qq\351\202\256\344\273\266\345\237\272\347\241\200\345\222\214\345\260\201\350\243\205.md" "b/Java/Java-\345\217\221\351\200\201qq\351\202\256\344\273\266\345\237\272\347\241\200\345\222\214\345\260\201\350\243\205.md" deleted file mode 100644 index 96d9280..0000000 --- "a/Java/Java-\345\217\221\351\200\201qq\351\202\256\344\273\266\345\237\272\347\241\200\345\222\214\345\260\201\350\243\205.md" +++ /dev/null @@ -1,411 +0,0 @@ ->前文摘自 菜鸟教程 :http://www.runoob.com/java/java-sending-email.html - -使用Java应用程序发送 E-mail 十分简单,但是首先你应该在你的机器上安装 JavaMail API 和Java Activation Framework (JAF) 。 - -* 您可以从 Java 网站下载最新版本的 [JavaMail](http://www.oracle.com/technetwork/java/javamail/index.html),打开网页右侧有个 **Downloads** 链接,点击它下载。 -* 您可以从 Java 网站下载最新版本的 [JAF(版本 1.1.1)](http://www.oracle.com/technetwork/articles/java/index-135046.html)。 - -你也可以使用本站提供的下载链接: - -* [JavaMail mail.jar 1.4.5](http://static.runoob.com/download/mail.jar) - -* [JAF(版本 1.1.1) activation.jar](http://static.runoob.com/download/activation.jar) - -下载并解压缩这些文件,在新创建的顶层目录中,您会发现这两个应用程序的一些 jar 文件。您需要把 **mail.jar** 和 **activation.jar**文件添加到您的 CLASSPATH 中。 - -如果你使用第三方邮件服务器如QQ的SMTP服务器,可查看文章底部用户认证完整的实例。 - -####发送一封简单的 E-mail -下面是一个发送简单E-mail的例子。假设你的localhost已经连接到网络。 - -如果需要提供用户名和密码给e-mail服务器来达到用户认证的目的,你可以通过如下设置来完成: -``` -props.put("mail.smtp.auth", "true"); -props.setProperty("mail.user", "myuser"); -props.setProperty("mail.password", "mypwd"); -``` - -####需要用户名密码验证邮件发送实例: -你需要在登录QQ邮箱后台在"设置"=》账号中开启POP3/SMTP服务 ,如下图所示: -![](https://upload-images.jianshu.io/upload_images/5786888-3cb816c22419a498.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) -我这里已经开启了。需要生成授权码,仔细看说明就行。生成授权码后会给你一串字符,它是密码 -SendEmail2.java -``` -// 需要用户名密码邮件发送实例 -//文件名 SendEmail2.java -//本实例以QQ邮箱为例,你需要在qq后台设置 - -import java.util.Properties; - -import javax.mail.Authenticator; -import javax.mail.Message; -import javax.mail.MessagingException; -import javax.mail.PasswordAuthentication; -import javax.mail.Session; -import javax.mail.Transport; -import javax.mail.internet.InternetAddress; -import javax.mail.internet.MimeMessage; - -public class SendEmail2 -{ - public static void main(String [] args) - { - // 收件人电子邮箱 - String to = "xxx@qq.com"; - - // 发件人电子邮箱 - String from = "xxx@qq.com"; - - // 指定发送邮件的主机为 smtp.qq.com - String host = "smtp.qq.com"; //QQ 邮件服务器 - - // 获取系统属性 - Properties properties = System.getProperties(); - - // 设置邮件服务器 - properties.setProperty("mail.smtp.host", host); - - properties.put("mail.smtp.auth", "true"); - // 获取默认session对象 - Session session = Session.getDefaultInstance(properties,new Authenticator(){ - public PasswordAuthentication getPasswordAuthentication() - { - return new PasswordAuthentication("xxx@qq.com", "qq邮箱密码"); //发件人邮件用户名、密码 - } - }); - - try{ - // 创建默认的 MimeMessage 对象 - MimeMessage message = new MimeMessage(session); - - // Set From: 头部头字段 - message.setFrom(new InternetAddress(from)); - - // Set To: 头部头字段 - message.addRecipient(Message.RecipientType.TO, - new InternetAddress(to)); - - // Set Subject: 头部头字段 - message.setSubject("This is the Subject Line!"); - - // 设置消息体 - message.setText("This is actual message"); - - // 发送消息 - Transport.send(message); - System.out.println("Sent message successfully....from runoob.com"); - }catch (MessagingException mex) { - mex.printStackTrace(); - } - } -} -``` - -###企业级开发封装 - - -##### 实体类 -MailEntity .java -``` -package com.fantj.myEmail; - -/** - * 邮件实体类 - * Created by Fant.J. - */ -@Data -public class MailEntity implements Serializable { - //此处填写SMTP服务器 - private String smtpService; - //设置端口号 - private String smtpPort; - //设置发送邮箱 - private String fromMailAddress; - // 设置发送邮箱的STMP口令 - private String fromMailStmpPwd; - //设置邮件标题 - private String title; - //设置邮件内容 - private String content; - //内容格式(默认采用html) - private String contentType; - //接受邮件地址集合 - private List list = new ArrayList<>(); -} - -``` -##### enum 类 -MailContentTypeEnum .java -``` -package com.fantj.myEmail.emailEnum; - -/** - * 自定义的枚举类型,枚举类型包含了邮件内容的类型 - * Created by Fant.J. - */ -public enum MailContentTypeEnum { - HTML("text/html;charset=UTF-8"), //html格式 - TEXT("text") - ; - private String value; - - MailContentTypeEnum(String value) { - this.value = value; - } - - public String getValue() { - return value; - } -} -package com.fantj.myEmail.emailEnum; - -/** - * 自定义的枚举类型,枚举类型包含了邮件内容的类型 - * Created by Fant.J. - */ -public enum MailContentTypeEnum { - HTML("text/html;charset=UTF-8"), //html格式 - TEXT("text") - ; - private String value; - - MailContentTypeEnum(String value) { - this.value = value; - } - - public String getValue() { - return value; - } -} - -``` -#####邮件发送类 -MailSender .java -``` -package com.fantj.myEmail; - -/** - * 邮件发送类 - * Created by Fant.J. - */ -public class MailSender { - //邮件实体 - private static MailEntity mail = new MailEntity(); - - /** - * 设置邮件标题 - * @param title 标题信息 - * @return - */ - public MailSender title(String title){ - mail.setTitle(title); - return this; - } - - /** - * 设置邮件内容 - * @param content - * @return - */ - public MailSender content(String content) - { - mail.setContent(content); - return this; - } - - /** - * 设置邮件格式 - * @param typeEnum - * @return - */ - public MailSender contentType(MailContentTypeEnum typeEnum) - { - mail.setContentType(typeEnum.getValue()); - return this; - } - - /** - * 设置请求目标邮件地址 - * @param targets - * @return - */ - public MailSender targets(List targets) - { - mail.setList(targets); - return this; - } - - /** - * 执行发送邮件 - * @throws Exception 如果发送失败会抛出异常信息 - */ - public void send() throws Exception - { - //默认使用html内容发送 - if(mail.getContentType() == null) { - mail.setContentType(MailContentTypeEnum.HTML.getValue()); - } - if(mail.getTitle() == null || mail.getTitle().trim().length() == 0) - { - throw new Exception("邮件标题没有设置.调用title方法设置"); - } - - if(mail.getContent() == null || mail.getContent().trim().length() == 0) - { - throw new Exception("邮件内容没有设置.调用content方法设置"); - } - - if(mail.getList().size() == 0) - { - throw new Exception("没有接受者邮箱地址.调用targets方法设置"); - } - - //读取/resource/mail_zh_CN.properties文件内容 - final PropertiesUtil properties = new PropertiesUtil("mail"); - // 创建Properties 类用于记录邮箱的一些属性 - final Properties props = new Properties(); - // 表示SMTP发送邮件,必须进行身份验证 - props.put("mail.smtp.auth", "true"); - //此处填写SMTP服务器 - props.put("mail.smtp.host", properties.getValue("mail.smtp.service")); - //设置端口号,QQ邮箱给出了两个端口465/587 - props.put("mail.smtp.port", properties.getValue("mail.smtp.prot")); - // 设置发送邮箱 - props.put("mail.user", properties.getValue("mail.from.address")); - // 设置发送邮箱的16位STMP口令 - props.put("mail.password", properties.getValue("mail.from.smtp.pwd")); - - // 构建授权信息,用于进行SMTP进行身份验证 - Authenticator authenticator = new Authenticator() { - @Override - protected PasswordAuthentication getPasswordAuthentication() { - // 用户名、密码 - String userName = props.getProperty("mail.user"); - String password = props.getProperty("mail.password"); - return new PasswordAuthentication(userName, password); - } - }; - // 使用环境属性和授权信息,创建邮件会话 - Session mailSession = Session.getInstance(props, authenticator); - // 创建邮件消息 - MimeMessage message = new MimeMessage(mailSession); - // 设置发件人 - String nickName = MimeUtility.encodeText(properties.getValue("mail.from.nickname")); - InternetAddress form = new InternetAddress(nickName + " <" + props.getProperty("mail.user") + ">"); - message.setFrom(form); - - // 设置邮件标题 - message.setSubject(mail.getTitle()); - //html发送邮件 - if(mail.getContentType().equals(MailContentTypeEnum.HTML.getValue())) { - // 设置邮件的内容体 - message.setContent(mail.getContent(), mail.getContentType()); - } - //文本发送邮件 - else if(mail.getContentType().equals(MailContentTypeEnum.TEXT.getValue())){ - message.setText(mail.getContent()); - } - //发送邮箱地址 - List targets = mail.getList(); - for(int i = 0;i < targets.size();i++){ - try { - // 设置收件人的邮箱 - InternetAddress to = new InternetAddress(targets.get(i)); - message.setRecipient(Message.RecipientType.TO, to); - // 最后当然就是发送邮件啦 - Transport.send(message); - }catch (Exception e) - { - continue; - } - - } - } -} -``` -####配置文件的读取工具类 -PropertiesUtil .java -``` -package com.fantj.myEmail; - -/** - * PropertiesUtil是用于读取*.properties配置文件的工具类 - * Created by Fant.J. - */ -public class PropertiesUtil { - private final ResourceBundle resource; - private final String fileName; - - /** - * 构造函数实例化部分对象,获取文件资源对象 - * - * @param fileName - */ - public PropertiesUtil(String fileName) - { - this.fileName = fileName; - this.resource = ResourceBundle.getBundle(this.fileName, Locale.SIMPLIFIED_CHINESE); - } - - /** - * 根据传入的key获取对象的值 getValue - * - * @param key properties文件对应的key - * @return String 解析后的对应key的值 - */ - public String getValue(String key) - { - String message = this.resource.getString(key); - return message; - } - - /** - * 获取properties文件内的所有key值
- * @return - */ - public Enumeration getKeys(){ - return resource.getKeys(); - } -} - -``` - -####配置文件 -mail.properties -``` -mail.smtp.service=smtp.qq.com -mail.smtp.prot=587 -mail.from.address=844072586@qq.com -mail.from.smtp.pwd=这里填写自己的授权码 -mail.from.nickname=这里填写 将名字转换成ascii码放在这里 -``` - -####测试类 -MailTest .java -``` -package com.fantj.myEmail; - - -/** - * Created by Fant.J. - */ -public class MailTest { - @Test - public void test() throws Exception { - for (int i = 0;i<20;i++){ - new MailSender() - .title("焦哥给你发送的邮件") - .content("你就是傻") - .contentType(MailContentTypeEnum.TEXT) - .targets(new ArrayList(){{ - add("xxxxx@qq.com"); - }}) - .send(); - Thread.sleep(1000); - System.out.println("第"+i+"次发送成功!"); - } - } -} - -``` -ok了,自己动手试试吧。 diff --git "a/Java/Java-\346\263\233\345\236\213----extends-T-\345\222\214---super-T--\350\257\246\350\247\243.md" "b/Java/Java-\346\263\233\345\236\213----extends-T-\345\222\214---super-T--\350\257\246\350\247\243.md" deleted file mode 100644 index 0793a03..0000000 --- "a/Java/Java-\346\263\233\345\236\213----extends-T-\345\222\214---super-T--\350\257\246\350\247\243.md" +++ /dev/null @@ -1,41 +0,0 @@ -大概来讲: -* 是"上界通配符" -* 是"下界通配符" -#####1. 为什么要用通配符和边界? - -使用泛型的过程中,经常出现一种很别扭的情况。比如按照题主的例子,我们有Fruit类,和它的派生类Apple类。 -```java -public class Apple extends Fruit{ -} - -public class Fruit { - -} -``` -然后我在main方法里创建实例对象: -![image.png](http://upload-images.jianshu.io/upload_images/5786888-629b9c7155be9162.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) -逻辑上水果盘子当然可以装苹果,但实际上Java编译器不允许这个操作。会报错,“装苹果的盘子”无法转换成“装水果的盘子”。 -``` -Error:(9, 30) java: 不兼容的类型: com.generic.Plate无法转换为com.generic.Plate -``` - -所以,就算容器里装的东西之间有继承关系,但容器之间是没有继承关系的。所以我们不可以把Plate的引用传递给Plate. - -为了让泛型用起来更舒服,Sun的大脑袋们就想出了的办法,来让”水果盘子“和”苹果盘子“之间发生关系。 - -![image.png](http://upload-images.jianshu.io/upload_images/5786888-3b1d8c4a2776ba95.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) -可以看出编译不再报错,程序正常运行。 - -#####? super T -这个也就不多解释了,上面的extend是说明实例对象必须是T的派生类。 -则super则说明实例对象必须是T的基类。 - -###PECS原则 -PECS(Producer Extends Consumer Super)原则 -* 频繁往外读取内容的,适合用上界Extends。 -* 经常往里插入的,适合用下界Super。 - -###总结 -1) 参数写成:T,对于这个泛型,?代表容器里的元素类型,由于只规定了元素必须是B的超类,导致元素没有明确统一的“根”(除了Object这个必然的根),所以这个泛型你其实无法使用它,对吧,除了把元素强制转成Object。所以,对把参数写成这样形态的函数,你函数体内,只能对这个泛型做插入操作,而无法读 - -2) 参数写成: T,由于指定了B为所有元素的“根”,你任何时候都可以安全的用B来使用容器里的元素,但是插入有问题,由于供奉B为祖先的子树有很多,不同子树并不兼容,由于实参可能来自于任何一颗子树,所以你的插入很可能破坏函数实参,所以,对这种写法的形参,禁止做插入操作,只做读取。 diff --git "a/Java/Java-\351\201\215\345\216\206Map\351\233\206\345\220\210\347\232\204\345\220\204\347\247\215\345\247\277\345\212\277.md" "b/Java/Java-\351\201\215\345\216\206Map\351\233\206\345\220\210\347\232\204\345\220\204\347\247\215\345\247\277\345\212\277.md" deleted file mode 100644 index 2a46ead..0000000 --- "a/Java/Java-\351\201\215\345\216\206Map\351\233\206\345\220\210\347\232\204\345\220\204\347\247\215\345\247\277\345\212\277.md" +++ /dev/null @@ -1,46 +0,0 @@ -###最常用,在键值都需要时使用。 -``` -Map map = new HashMap(); -for (Map.Entry entry : map.entrySet()) { - System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue()); -} -``` -###在for-each循环中遍历keys或values。 -``` -Map map = new HashMap(); -//遍历map中的键 -for (Integer key : map.keySet()) { - System.out.println("Key = " + key); -} -//遍历map中的值 -for (Integer value : map.values()) { - System.out.println("Value = " + value); -} -``` -###使用Iterator遍历 -``` -Map map = new HashMap(); -Iterator> entries = map.entrySet().iterator(); -while (entries.hasNext()) { - Map.Entry entry = entries.next(); - System.out.println("Key = " + entry.getKey() + ", Value = " + entry.getValue()); -} -``` - -``` -Map map = new HashMap(); -Iterator entries = map.entrySet().iterator(); -while (entries.hasNext()) { - Map.Entry entry = (Map.Entry) entries.next(); - Integer key = (Integer)entry.getKey(); - Integer value = (Integer)entry.getValue(); - System.out.println("Key = " + key + ", Value = " + value); -} -``` -###通过键找值遍历(效率低) -``` -Map map = new HashMap(); -for (Integer key : map.keySet()) { - Integer value = map.get(key); - System.out.println("Key = " + key + ", Value = " + value); -``` diff --git "a/Java/Java10-\345\210\235\344\275\223\351\252\214(\345\256\236\346\210\230).md" "b/Java/Java10-\345\210\235\344\275\223\351\252\214(\345\256\236\346\210\230).md" deleted file mode 100644 index b53aaee..0000000 --- "a/Java/Java10-\345\210\235\344\275\223\351\252\214(\345\256\236\346\210\230).md" +++ /dev/null @@ -1,69 +0,0 @@ ->最近 IDEA 发布支持 java10的新版本。 - -#####Java10 简介: -详细版本更新特性请查看国外的一篇文章:https://www.azul.com/109-new-features-in-jdk-10/ - -我在这里只简单的介绍 最热的一个特性:局部变量的类型推断 - -简单demo: -``` -var list = new ArrayList(); // infers ArrayList -var stream = list.stream(); // infers Stream -``` - -是不是很像js?但是我们要知道,java依旧是强类型语言,只是jvm帮助我们做了变量类型推断。 - - -好了开始正文,java10需要最新版本的IDEA支持。否则JDK你都加不进去。 - -#####所以我们先下载最新版的idea: - - -最新IDEA下载地址:https://www.jetbrains.com/idea/download/#section=windows - -安装好后,启动IDEA。 - -随便进一个项目,然后打开项目架构 快捷键 ctrl + shift + alt + s - -##### 添加SDK -![](https://upload-images.jianshu.io/upload_images/5786888-ecc9c06ea8de4f52.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - -##### 给项目适配JDK10 - -![](https://upload-images.jianshu.io/upload_images/5786888-5007b60ac0af192a.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - -##### 测试 -我们都听说过java10的新特性吧。最热的一个特性是 用var 来声明变量,是的,就像js一样。 - -那接下来直接进入让java粉迫不及待的场面。 -``` -/** - * Created by Fant.J. - */ -public class NewJavaTest { - public static void main(String[] args) { - - var list = new ArrayList<>(); - list.add(1); - list.add("fantj"); - list.add(1.00); - - - list.forEach(System.out::println); - } -} - -``` -控制台输出: -``` -1 -fantj -1.0 -``` - -我在这里故意不给ArrayList 赋泛型,因为它默认就是Object,这样我可以给list赋任意类型的变量,给人感觉很像弱类型语言,但是我们应该清楚是因为jvm帮我们猜测了类型。 - - -最后附上java10的官方更新文档:http://openjdk.java.net/jeps/286 diff --git "a/Java/Java\344\270\255\346\212\275\350\261\241\347\261\273\345\222\214\346\216\245\345\217\243\346\234\211\344\273\200\344\271\210\345\274\202\345\220\214-.md" "b/Java/Java\344\270\255\346\212\275\350\261\241\347\261\273\345\222\214\346\216\245\345\217\243\346\234\211\344\273\200\344\271\210\345\274\202\345\220\214-.md" deleted file mode 100644 index d8656b0..0000000 --- "a/Java/Java\344\270\255\346\212\275\350\261\241\347\261\273\345\222\214\346\216\245\345\217\243\346\234\211\344\273\200\344\271\210\345\274\202\345\220\214-.md" +++ /dev/null @@ -1,115 +0,0 @@ -### 1. 抽象类 -1. 什么是抽象类 ->包含一个抽象方法的类就是抽象类 -2. 抽象方法 ->声明而未被实现的方法,抽象方法必须使用`abstract`关键词字声明 -``` -public abstract class People { //关键词abstract,声明该类为抽象类 - public int age; - public void Num() { - } - public abstract Name(); //声明该方法为抽象方法 -}   -``` -3. 抽象类被子类`继承`,子类(如果不是抽象类)`必须重写`抽象类中的所有抽象方法 - -4. 抽象类`不能被直接实例化`,要通过其子类进行实例化 - -5. 只要包含一个抽象方法的抽象类,该方法必须要定义成抽象类,不管是否还包含有其他方法。 - -6. 子类中的抽象方法不能与父类的抽象方法同名。 - -7. `abstract`不能与`final`并列修饰同一个类。 - -8. `abstract `不能与`private、static、final`或`native`并列修饰同一个方法。 - -###### 为什么需要用到抽象类? - 在下面这个例子中,把打印机类封装成抽象类,把`print`定义成抽象类,当`HPPrinter`和`CannonPrinter`要继承`printer`类的时候,必须重写`print`函数。这样写的好处是,每个打印机都有自己打印方式,此举可以防止子类没有写明打印方式。 -``` -public abstract void Printer() { - public void open() { - system.out.print("打印机开启"); - } - public void close() { - system.out.print("打印机关闭"); - } - abstract void print(); //把print方法定义为抽象方法 -} - -public HPPrinter extends Printer { - void print(){ //必须重写抽象方法print() - system.out.print("惠普打印机开始打印"); - }; -} - -public CanonPrinter extends Printer { - void print(){ //必须重写抽象方法print() - system.out.print("佳能打印机开始打印"); - }; -} -``` -### 2. 接口 -1. 接口是java中最重要的概念,接口可以理解为一种特殊的类,里面全部是由全局常量和公共的抽象方法组成,接口中所有方法都是抽象方法.接口当中所有的方法都是public类型. - -2. 接口的格式: -``` -interface interfaceName{ - 全局常量 - 抽象方法 -} -``` -3. 接口的实现也必须通过子类,使用关键字implements,而且接口是可以多实现的 -``` -class A implements Inter1,Inter2{ //Inter1和Inter2是接口 - ... -} -``` -4. 一个类可以同时继承抽象类和接口 -``` -class A extends Abs implements Inter1,Inter2{ //Abs是一个抽象类 - ... -} -``` -5. 一个接口不能继承抽象类,但可以通过extends关键字继承多个接口,实现接口的多继承 -``` -interface Inter implements Inter1,Inter2{ //Inter、Inter1、Inter2都为接口 - ... -} -``` -### 3. 抽象类和接口区别 - -##### 3.1 语法层次 -``` -public abstract class People { //关键词abstract,声明该类为抽象类 - void Num();       - abstract void Name();    //声明该方法为抽象方法 -} - -Interface Person { -  void Num(); -  void Name(); -}  -``` -* 抽象类方式中,抽象类可以拥有任意范围的成员数据,同时也可以拥有自己的非抽象方法, -* 但是接口方式中,它仅能够有静态、不能修改的成员数据(但是我们一般是不会在接口中使用成员数据),同时它所有的方法都必须是抽象的。 -* 在某种程度上来说,接口是抽象类的特殊化。 -* 对子类而言,它只能继承一个抽象类(这是java为了数据安全而考虑的),但是却可以实现多个接口。 - - -##### 3.2 设计层次 -###### 1. 抽象层次不同 -抽象类是对类抽象,而接口是对行为的抽象。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口却是对类局部(行为)进行抽象。 - -###### 2. 跨域不同 -抽象类所跨域的是具有相似特点的类,而接口却可以跨域不同的类。我们知道抽象类是从子类中发现公共部分,然后泛化成抽象类,子类继承该父类即可,但是接口不同。实现它的子类可以不存在任何关系,共同之处。例如猫、狗可以抽象成一个动物类抽象类,具备叫的方法。鸟、飞机可以实现飞Fly接口,具备飞的行为,这里我们总不能将鸟、飞机共用一个父类吧!所以说抽象类所体现的是一种继承关系,要想使得继承关系合理,父类和派生类之间必须存在"is-a" 关系,即父类和派生类在概念本质上应该是相同的。对于接口则不然,并不要求接口的实现者和接口定义在概念本质上是一致的, 仅仅是实现了接口定义的契约而已。 - -###### 3. 设计层次不同 -* 对于抽象类而言,它是自下而上来设计的,我们要先知道子类才能抽象出父类,而接口则不同,它根本就不需要知道子类的存在,只需要定义一个规则即可,至于什么子类、什么时候怎么实现它一概不知。比如我们只有一个猫类在这里,如果你这是就抽象成一个动物类,是不是设计有点儿过度?我们起码要有两个动物类,猫、狗在这里,我们在抽象他们的共同点形成动物抽象类吧!所以说抽象类往往都是通过重构而来的! -* 但是接口就不同,比如说飞,我们根本就不知道会有什么东西来实现这个飞接口,怎么实现也不得而知,我们要做的就是事前定义好飞的行为接口。所以说抽象类是自底向上抽象而来的,接口是自顶向下设计出来的。 - - -### 语法规范区别: -1)接口不能有构造方法,抽象类可以有。 -2)接口不能有方法体,抽象类可以有。 -3)接口不能有静态方法,抽象类可以有。 -4)在接口中凡是变量必须是public static final,而在抽象类中没有要求。 diff --git "a/Java/java-Base64\345\212\240\345\257\206\347\232\204\344\270\211\347\247\215\345\247\277\345\212\277.md" "b/Java/java-Base64\345\212\240\345\257\206\347\232\204\344\270\211\347\247\215\345\247\277\345\212\277.md" deleted file mode 100644 index 437d502..0000000 --- "a/Java/java-Base64\345\212\240\345\257\206\347\232\204\344\270\211\347\247\215\345\247\277\345\212\277.md" +++ /dev/null @@ -1,111 +0,0 @@ -Java的3种Base64加密方法 , 分别是 jdk默认实现的加密方式, 使用cc的加密方式和使用bc的加密方式 -``` -import java.io.IOException; - -import org.apache.commons.codec.binary.Base64; - -import sun.misc.BASE64Decoder; -import sun.misc.BASE64Encoder; - - - -public class Main { - - public static void main(String[] args) { - // TODO Auto-generated method stub - - - /*使用jdk默认的base64加解密*/ - String str ="这是要加密的字符串,使用jdk"; - str = jdkBase64Encoder(str); - System.out.println("加密后的字符串为:"+str); - str = jdkBase64Decoder(str); - if(str!=null) - { - System.out.println("解密后的字符串:"+str); - } - else - { - System.out.println("解密失败"); - } - /*使用commons-codec的base64加解密*/ - str ="这是要加密的字符串,使用CC"; - str = CCBase64Encoder(str); - System.out.println("加密后的字符串为:"+str); - str=CCBase64Decoder(str); - System.out.println("解密后的字符串为:"+str); - /*使用bcprov的base64加解密*/ - str = "这是要加密的字符串,使用bc"; - str = BCBase64Endoer(str); - System.out.println("加密后的字符串为:"+str); - str = BCBase64Decoder(str); - System.out.println(str); - - } - - - /** - * 使用jdk的base64 加密字符串 - * */ - public static String jdkBase64Encoder(String str) - { - BASE64Encoder encoder = new BASE64Encoder(); - String encode = encoder.encode(str.getBytes()); - return encode; - } - /** - * 使用jdk的base64 解密字符串 - * 返回为null表示解密失败 - * */ - public static String jdkBase64Decoder(String str) - { - BASE64Decoder decoder = new BASE64Decoder(); - String decode=null; - try { - decode = new String( decoder.decodeBuffer(str)); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - return decode; - } - - /** - * 使用commons-codec的base64 加密字符串 - * */ - public static String CCBase64Encoder(String str) - { - - return new String(Base64.encodeBase64(str.getBytes())); - } - - /** - * 使用commons-codec的base64 解密字符串 - * */ - public static String CCBase64Decoder(String str) - { - return new String(Base64.decodeBase64(str.getBytes())); - - } - - /** - * 使用bcprov的base64加密字符串 - * */ - public static String BCBase64Endoer(String str) - { - byte[] arr =org.bouncycastle.util.encoders.Base64.encode(str.getBytes()); - - return new String(arr); - } - - /** - * 使用bcprov的base64加密字符串 - * */ - public static String BCBase64Decoder(String str) - { - byte[] arr = org.bouncycastle.util.encoders.Base64.decode(str.getBytes()); - - return new String(arr); - } -} -``` diff --git "a/Java/\344\275\240\347\237\245\351\201\223for\343\200\201foreach\345\222\214Iterator\351\201\215\345\216\206\346\234\211\344\273\200\344\271\210(\346\225\210\347\216\207)\345\214\272\345\210\253\345\220\227.md" "b/Java/\344\275\240\347\237\245\351\201\223for\343\200\201foreach\345\222\214Iterator\351\201\215\345\216\206\346\234\211\344\273\200\344\271\210(\346\225\210\347\216\207)\345\214\272\345\210\253\345\220\227.md" deleted file mode 100644 index ebfbd26..0000000 --- "a/Java/\344\275\240\347\237\245\351\201\223for\343\200\201foreach\345\222\214Iterator\351\201\215\345\216\206\346\234\211\344\273\200\344\271\210(\346\225\210\347\216\207)\345\214\272\345\210\253\345\220\227.md" +++ /dev/null @@ -1,32 +0,0 @@ -###1.在形式上 - for的形式是 -`for(int i=0;i it){ - while(it.hasNext()){ - system.out.print(it.next()+""); - } - } -``` -当我们需要遍历不同的集合时,我们只需要传递集合的iterator(如arr.iterator())看懂了吧,这就是iterator的好处,他不包含任何有关他所遍历的序列的类型信息,能够将遍历序列的操作与序列底层的结构分离。迭代器统一了对容器的访问方式。这也是接口的解耦的最好体现。 -###3.用法差别 -* for循环一般用来处理比较简单的有序的,可预知大小的集合或数组 -* foreach可用于遍历任何集合或数组,而且操作简单易懂,他唯一的不好就是需要了解集合内部类型 -* iterator是最强大的,他可以随时修改或者删除集合内部的元素,并且是在不需要知道元素和集合的类 型的情况下进行的(原因可参考第三点:多态差别),当你需要对不同的容器实现同样的遍历方式时,迭代器是最好的选择! -###5.效率差别 -同样遍历一个集合,iterator和foreach用时不相上下。for循环用时最少。 diff --git "a/Java/\346\212\275\350\261\241\347\261\273abstract\347\232\204\344\270\200\344\272\233\346\200\235\350\200\203.md" "b/Java/\346\212\275\350\261\241\347\261\273abstract\347\232\204\344\270\200\344\272\233\346\200\235\350\200\203.md" deleted file mode 100644 index b2e3886..0000000 --- "a/Java/\346\212\275\350\261\241\347\261\273abstract\347\232\204\344\270\200\344\272\233\346\200\235\350\200\203.md" +++ /dev/null @@ -1,115 +0,0 @@ ->最近有个小朋友问我什么是抽象类,他一直搞不懂抽象类,让我通俗的给他讲下。Emmmmmm,我问他:你用抽象类干过哪些事情。他的回答也在意料之中:压根没用过。这就加大了我的难度,我不知道从哪下手给他科普。 - ->然后我接着问:你对抽象类有什么了解呢? -他:我只知道它不能new。 -我灵机一动:那你知道它为啥不能被new(实例化)呢? -他:好像是...(这乱略了,怕把你也搞懵) - -### 问题 -他说了一大堆,我听出来的几个问题就是: -1. 和接口概念混淆 -2. 对抽象类的使用缺乏理解 - -### 答案 -好了,那今天就简单的解决下这个问题。 -1. 首先和interface的区别我之前有个文章:[Java中抽象类和接口有什么异同?](https://www.jianshu.com/p/b0cf5d770a86) -2. 对抽象类的使用缺乏理解,我认为就是用的太少,如果你用过抽象类工厂的开发模式,你一定会对抽象类有很深的理解。我简单了写了一个demo: - -###### Phone.class -``` -/** - * Created by Fant.J. - */ -public abstract class Phone { - void print(){ - System.out.println("this is a Phone abstract class method"); - } - - abstract void show(); -} -``` - -###### PhoneMi.class -``` -/** - * Created by Fant.J. - */ -public class PhoneMi extends Phone { - @Override - void show() { - System.out.println("my name is mi"); - } -} - -``` -###### 测试类Test.class -``` -/** - * Created by Fant.J. - */ -public class Test { - - public static void main(String[] args) { - Phone phone = new Phone() { - @Override - void show() { - System.out.println("my name is mi"); - } - }; - phone.show(); - - Phone phone1 = new PhoneMi(); - phone1.show(); - - phone1.print(); - - } - -} - -``` -###### 执行结果 -``` -my name is mi -my name is mi -this is a Phone abstract class method -``` - -### 疑点解答: -这几个问题清楚了,为什么不能实例化abstract类的思路就很清晰了。 - -###### 1. Phone类里的print方法如何能被调用? -子类继承Phone后,会把print方法继承,通过子类对象就可以调用到print方法 - - -###### 2. new Phone会有什么结果? -我在测试类里main方法的第一行就是在重写Phone类,实例化Phone是走不通的。重写这个类实质上也是对该类的一个继承实现。即,我重写了show方法,和PhoneMi里的show方法一模一样,那么我们就可以认为,我们通过实现内部类的形式实例化了一个新的子类对象 phone ,通过phone,我们也可以调用print方法,因为它的本质是Phone的子类实现。而不是抽象类本身。 - - -###### 3. 为什么能new Phone的子类PhoneMi -我们实例化Phone的子类PhoneMi是行的通的,因为PhoneMi是前者的子类,作为抽象类的子类,必须要继承抽象类方法,只有重写了抽象类方法,才符合java规范(不会报错),此时,我们就可以把PhoneMi当作一个普通的类使用,和普通类不一样的是:它可以调用抽象类(父类)的方法。 - ->小朋友继续问:那如果我写一个普通类,里面写上show方法和print方法,不是更加简洁吗? -我:emmmmm(心里一万个草泥马,我前面一堆是白说了吗?然后想了想是不是自己说的还不够清澈,思考了一会说)我认为抽象类本身就是在做设计模式,它的存在就是为了更好的设计而存在,你之前不是说过你从来没有用过它就可以完成任务工作麽?对,就是这个道理。如果你真正想解决冗余、效率、代码架构清晰等问题,你才会考虑设计模式。 - - - -好了,还看?最后一些客气的话还需要我贴出来吗?如果你现在对它还不够清楚,下方留言,看到后会回复。谢谢! - - - - - - - - - - - - - - - - - - diff --git "a/Java\345\271\266\345\217\221/Java\345\271\266\345\217\221\347\274\226\347\250\213\357\274\210\344\272\214\357\274\211\344\274\230\345\212\277\345\222\214\351\243\216\351\231\251.md" "b/Java\345\271\266\345\217\221/Java\345\271\266\345\217\221\347\274\226\347\250\213\357\274\210\344\272\214\357\274\211\344\274\230\345\212\277\345\222\214\351\243\216\351\231\251.md" deleted file mode 100644 index 88e20c2..0000000 --- "a/Java\345\271\266\345\217\221/Java\345\271\266\345\217\221\347\274\226\347\250\213\357\274\210\344\272\214\357\274\211\344\274\230\345\212\277\345\222\214\351\243\216\351\231\251.md" +++ /dev/null @@ -1,40 +0,0 @@ -##### 一、优势 -并发编程之所以让人迷惑是因为有不止一种问题的解决需要使用并发,也有不止一种方法去实现并发,而且他们之间也没有清晰的映射。 - -使用并发编程来解决的问题可以划分为两类,即“speed”和“designmanageability”。 - - - - 1. 速度优势: -* 多处理器:多处理器上面并发变成无疑会让程序运行很快。 -* 单处理器:如果是单处理器的机器,那么并发编程可能相对于顺序编程没有什么变化。但是,如果其中某一个任务也许会发生阻塞的话,那么即使是单处理器,使用并发编程也会带来很大的好处,这样,某个任务阻塞的时候,其他任务也可以继续运行了。 -* 反应灵敏的用户界面:在单处理器上面性能提升最典型的列子就是“事件驱动的编程”,比如创建一个有反应的用户界面,其中有个按钮,如果我们不使用并发编程,那么我们需要在我们编写的每一个代码片段中都要有对用户输入的检测,如果我们使用并发编程,我们只需要重新开启一个线程去监听用户的输入即可。 -并发的实现:实现并发的最直接的方式是在操作系统级别,使用进程,进程一种自包含的程序,使用自己的地址空间,操作系统会让进程之间相互隔离,所以进程编程相对容易一些,不需要考虑共享资源的同步等问题。但是在Java中的并发编程,由于线程之间共享相同的memory或者IO等资源,所以Java多线程编程中需要考虑共享资源的同步问题。 -进程和Java线程之间的选择:进程的确是一种实现并发的方式,butunfortunately there are generally quantity and overhead limitations toprocesses that prevent their applicability across the concurrency spectrum. - - -2. 设计上的优势: -* 一般来说,线程使得你能够创建更加松耦合的设计。 -* 单处理器:尽管单处理器上面的并发编程在同一时刻处理器仍然只能做一件事情,但是带来一个组织上面的重要优势:就是你的设计(design)会极大地简化。比如仿真。 -* 仿真举例:如果没有并发,仿真将变得非常困难。 一般来说仿真涉及到多个交互元素,其中每一个都有“自己的想法”,尽管从程序员的角度来看每一个仿真元素都是被同一个处理器所驱动,但是设计上来看,每一个仿真元素都假装有自己的处理器以及运行独立的任务。 - -##### 二、风险 -1. 安全性问题 - -主要是多个线程共享数据时可能会产生于期望不相符的结果 - - - -2. 活跃性问题(liveness) - -当某个操作无法继续进行下去时,就会发生活跃性问题。比如死锁、饥饿、活锁等问题。(死锁、饥饿、活锁可自行百度) - - - -3. 性能问题 - -a. 线程过多时会使得CPU频繁切换,花在调度上时间太多。 - -b. 多线程环境必须使用同步机制,导致很多编译器想做的优化被抑制。 - -c. 线程过多还会消耗过多内存。 diff --git "a/Java\345\271\266\345\217\221/\347\224\250Condition\345\256\236\347\216\260\346\234\211\347\225\214\351\230\237\345\210\227.md" "b/Java\345\271\266\345\217\221/\347\224\250Condition\345\256\236\347\216\260\346\234\211\347\225\214\351\230\237\345\210\227.md" deleted file mode 100644 index 8b13789..0000000 --- "a/Java\345\271\266\345\217\221/\347\224\250Condition\345\256\236\347\216\260\346\234\211\347\225\214\351\230\237\345\210\227.md" +++ /dev/null @@ -1 +0,0 @@ - diff --git "a/Java\346\272\220\347\240\201\345\210\206\346\236\220/JAVA\344\270\255\347\232\204\345\217\215\345\260\204\346\234\272\345\210\266.md" "b/Java\346\272\220\347\240\201\345\210\206\346\236\220/JAVA\344\270\255\347\232\204\345\217\215\345\260\204\346\234\272\345\210\266.md" deleted file mode 100644 index 802bb1f..0000000 --- "a/Java\346\272\220\347\240\201\345\210\206\346\236\220/JAVA\344\270\255\347\232\204\345\217\215\345\260\204\346\234\272\345\210\266.md" +++ /dev/null @@ -1,105 +0,0 @@ ->通俗的说,反射就是可以获得类的信息,比如类里面有什么方法、属性、构造函数等,也可以对类实例化(不是所有的实例化都是用new,new必须知道这个类是什么,而实际情况中很多时候是不能预先知道这个类名),很多框架都是使用反射的原理,比如spring ----- -#### 1. 反射的概念 ->主要是指程序可以访问,检测和修改它本身状态或行为的一种能力,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。 - 反射是java中一种强大的工具,能够使我们很方便的创建灵活的代码,这些代码可以再运行时装配,无需在组件之间进行源代码链接。但是**反射使用不当会成本很高**!! -####2. 反射机制的作用: - 1. 反编译:.class-->.java - 2. 通过反射机制访问java对象的属性,方法,构造方法等; -####3. java我们提供了那些反射机制中的类: -``` -java.lang.Class; -java.lang.reflect.Constructor; -java.lang.reflect.Field; -java.lang.reflect.Method; -java.lang.reflect.Modifier; -``` -强调的是API是我们最好的老师。 -#### 4. 具体功能实现: -1. 反射机制获取类有三种方法,我们来获取Employee类型 -``` -//第一种方式: -Classc1 = Class.forName("Employee"); -//第二种方式: -//java中每个类型都有class 属性. -Classc2 = Employee.class; -//第三种方式: -//java语言中任何一个java对象都有getClass 方法 -Employeee = new Employee(); -Classc3 = e.getClass(); //c3是运行时类 (e的运行时类是Employee) -``` -2. 创建对象:获取类以后我们来创建它的对象,利用newInstance: -``` -Class c =Class.forName("Employee"); -//创建此Class 对象所表示的类的一个新实例 -Objecto = c.newInstance(); //调用了Employee的无参数构造方法. -``` -3. 获取属性:分为所有的属性和指定的属性: - a. 先看获取所有的属性的写法: -``` - //获取整个类 - Class c = Class.forName("java.lang.Integer"); - //获取所有的属性? - Field[] fs = c.getDeclaredFields(); - - //定义可变长的字符串,用来存储属性 - StringBuffer sb = new StringBuffer(); - //通过追加的方法,将每个属性拼接到此字符串中 - //最外边的public定义 - sb.append(Modifier.toString(c.getModifiers()) + " class " + c.getSimpleName() +"{\n"); - //里边的每一个属性 - for(Field field:fs){ - sb.append("\t");//空格 - sb.append(Modifier.toString(field.getModifiers())+" ");//获得属性的修饰符,例如public,static等等 - sb.append(field.getType().getSimpleName() + " ");//属性的类型的名字 - sb.append(field.getName()+";\n");//属性的名字+回车 - } - - sb.append("}"); - System.out.println(sb); -``` - b.获取特定的属性,对比着传统的方法来学习: -``` -public static void main(String[] args) throws Exception{ - - //以前的方式: - /* - User u = new User(); - u.age = 12; //set - System.out.println(u.age); //get - */ - - //获取类 - Class c = Class.forName("User"); - //获取id属性 - Field idF = c.getDeclaredField("id"); - //实例化这个类赋给o - Object o = c.newInstance(); - //打破封装 - idF.setAccessible(true); //使用反射机制可以打破封装性,导致了java对象的属性不安全。 - //给o对象的id属性赋值"110" - idF.set(o, "110"); //set - //get - System.out.println(idF.get(o)); -} -``` -4,获取方法,和构造方法,不再详细描述,只来看一下关键字: -| 方法关键字 | 含义 | -| :-------- | --------:| -|getDeclaredMethods()|获取所有的方法| -|getReturnType()|获得方法的放回类型| -|getParameterTypes()|获得方法的传入参数类型| -|getDeclaredMethod("方法名",参数类型.class,……)|获得特定的方法| - - -| 构造方法关键字 | 含义 | -| :-------- | --------:| -|getDeclaredConstructors()|获取所有的构造方法| -|getDeclaredConstructor(参数类型.class,……)|获取特定的构造方法| - - -|父类和父接口|含义| -| :-------- | --------:| -|getSuperclass()|获取某类的父类| -|getInterfaces()|获取某类实现的接口| - 这样我们就可以获得类的各种内容,进行了反编译。对于JAVA这种先编译再运行的语言来说,反射机制可以使代码更加灵活,更加容易实现面向对象。 diff --git "a/Java\346\272\220\347\240\201\345\210\206\346\236\220/Java\347\272\277\347\250\213\346\261\240\346\272\220\347\240\201\350\247\243\346\236\220.md" "b/Java\346\272\220\347\240\201\345\210\206\346\236\220/Java\347\272\277\347\250\213\346\261\240\346\272\220\347\240\201\350\247\243\346\236\220.md" deleted file mode 100644 index 5530b16..0000000 --- "a/Java\346\272\220\347\240\201\345\210\206\346\236\220/Java\347\272\277\347\250\213\346\261\240\346\272\220\347\240\201\350\247\243\346\236\220.md" +++ /dev/null @@ -1 +0,0 @@ ->线程使应用能够更加充分的利用CPU、内存、网络、IO等系统资源。 diff --git "a/Java\346\272\220\347\240\201\345\210\206\346\236\220/StringBuilder\344\270\216-StringBuffer.md" "b/Java\346\272\220\347\240\201\345\210\206\346\236\220/StringBuilder\344\270\216-StringBuffer.md" deleted file mode 100644 index 5dfd1c8..0000000 --- "a/Java\346\272\220\347\240\201\345\210\206\346\236\220/StringBuilder\344\270\216-StringBuffer.md" +++ /dev/null @@ -1,11 +0,0 @@ -速度方面的比较:StringBuilder > StringBuffer > String ->因为给String添加字符时,JVM 会给String 创建新的对象。重新赋值,所以速度很慢。 - - -* String - * 操作少量的数据用 -* StringBuilder:线程非安全的 - *尽量在单线程下使用,字符串缓冲去被多个线程使用时,JVM不能保证StringBuilder操作安全性。 -* StringBuffer:线程安全的 - * 多线程操作字符串缓冲区 下操作大量数据 - diff --git "a/Java\346\272\220\347\240\201\345\210\206\346\236\220/Throwable.md" "b/Java\346\272\220\347\240\201\345\210\206\346\236\220/Throwable.md" deleted file mode 100644 index 1004512..0000000 --- "a/Java\346\272\220\347\240\201\345\210\206\346\236\220/Throwable.md" +++ /dev/null @@ -1,9 +0,0 @@ ->所有异常的根基类。 -Exception 是Throwable类的一个主要子类。 - -**Error**类和**Exception**类的父类都是throwable类,他们的区别是: - -- Error类一般是指与*虚拟机*相关的问题,如*系统崩溃*,*虚拟机错误*,*内存空间不足*,*方法调用栈溢*等。对于这类错误的导致的**应用程序中断**,仅靠程序**本身无法恢复和和预防**,遇到这样的错误,建议让程序终止。 - --- Exception类表示程序**可以处理的异常,可以捕获且可能恢复**。遇到这类异常,应该尽可能处理异常,使程序恢复运行,而不应该随意终止异常。 - -Exception类又分为运行时异常(Runtime Exception)和受检查的异常(Checked Exception ),运行时异常;ArithmaticException,IllegalArgumentException,编译能通过,但是一运行就终止了,程序不会处理运行时异常,出现这类异常,程序会终止。而受检查的异常,要么用try。。。catch捕获,要么用throws字句声明抛出,交给它的父类处理,否则编译不会通过。 diff --git "a/Java\346\272\220\347\240\201\345\210\206\346\236\220/Unicode\345\222\214UTF-8\347\232\204\346\225\205\344\272\213.md" "b/Java\346\272\220\347\240\201\345\210\206\346\236\220/Unicode\345\222\214UTF-8\347\232\204\346\225\205\344\272\213.md" deleted file mode 100644 index 83dba43..0000000 --- "a/Java\346\272\220\347\240\201\345\210\206\346\236\220/Unicode\345\222\214UTF-8\347\232\204\346\225\205\344\272\213.md" +++ /dev/null @@ -1,49 +0,0 @@ - -举一个例子:It's 知乎日报你看到的unicode字符集是这样的编码表: -``` -I 0049 -t 0074 -' 0027 -s 0073 - 0020 -知 77e5 -乎 4e4e -日 65e5 -报 62a5 -``` -每一个字符对应一个十六进制数字。计算机只懂二进制,因此,严格按照unicode的方式(UCS-2),应该这样存储: -``` -I 00000000 01001001 -t 00000000 01110100 -' 00000000 00100111 -s 00000000 01110011 - 00000000 00100000 -知 01110111 11100101 -乎 01001110 01001110 -日 01100101 11100101 -报 01100010 10100101 -``` -这个字符串总共占用了18个字节,但是对比中英文的二进制码,可以发现,英文前9位都是0!浪费啊,浪费硬盘,浪费流量。怎么办?UTF。UTF-8是这样做的:1. 单字节的字符,字节的第一位设为0,对于英语文本,UTF-8码只占用一个字节,和ASCII码完全相同;2. n个字节的字符(n>1),第一个字节的前n位设为1,第n+1位设为0,后面字节的前两位都设为10,这n个字节的其余空位填充该字符unicode码,高位用0补足。这样就形成了如下的UTF-8标记位: -``` -0xxxxxxx -110xxxxx 10xxxxxx -1110xxxx 10xxxxxx 10xxxxxx -11110xxx 10xxxxxx 10xxxxxx 10xxxxxx -111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx -1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx... ... -``` -于是,”It's 知乎日报“就变成了: -``` -I 01001001 -t 01110100 -' 00100111 -s 01110011 - 00100000 -知 11100111 10011111 10100101 -乎 11100100 10111001 10001110 -日 11100110 10010111 10100101 -报 11100110 10001010 10100101 -``` -和上边的方案对比一下,英文短了,每个中文字符却多用了一个字节。但是整个字符串只用了17个字节,比上边的18个短了一点点。下边是课后作业:请将”It's 知乎日报“的GB2312和GBK码(自行google)转成二进制。不考虑历史因素,从技术角度解释为什么在unicode和UTF-8大行其道的同时,GB2312和GBK仍在广泛使用。剧透:一切都是为了节省你的硬盘和流量。 - -文章借鉴:https://www.zhihu.com/question/23374078/answer/65352538 diff --git "a/Java\346\272\220\347\240\201\345\210\206\346\236\220/\344\270\272\344\273\200\344\271\210\350\246\201\347\273\247\346\211\277Serializable\347\261\273\357\274\237.md" "b/Java\346\272\220\347\240\201\345\210\206\346\236\220/\344\270\272\344\273\200\344\271\210\350\246\201\347\273\247\346\211\277Serializable\347\261\273\357\274\237.md" deleted file mode 100644 index fb31323..0000000 --- "a/Java\346\272\220\347\240\201\345\210\206\346\236\220/\344\270\272\344\273\200\344\271\210\350\246\201\347\273\247\346\211\277Serializable\347\261\273\357\274\237.md" +++ /dev/null @@ -1,47 +0,0 @@ -####为什么要实现Serializable? - -最重要的两个原因是: -  1、将对象的状态保存在存储媒体中以便可以在以后重新创建出完全相同的副本; -  2、按值将对象从一个应用程序域发送至另一个应用程序域。 -        通俗的说:在分布式应用中,你就得实现序列化,如果你不需要分布式应用,那就没那个必要实现序列化。 - -拓展: -`Serializable`是一个空接口,没有什么具体内容,它的目的只是简单的标识一个类的对象可以被序列化。 - ->>什么情况下需要序列化 ->* a)当你**想把的内存中的对象写入到硬盘**的时候; ->* b)当你想用套接字在**网络上传送对象**的时候; ->* c)当你想通过RMI传输对象的时候;再稍微解释一下: -> * a)比如说你的内存不够用了,那计算机就要将内存里面*的一部分对象暂时的保存到硬盘中,等到要用的时候再读入到内存中*,硬盘的那部分存储空间就是所谓的[虚拟内存](https://www.baidu.com/s?wd=%E8%99%9A%E6%8B%9F%E5%86%85%E5%AD%98&tn=44039180_cpr&fenlei=mv6quAkxTZn0IZRqIHckPjm4nH00T1YvmhDYmvndrHR4nj-BnWD30ZwV5Hcvrjm3rH6sPfKWUMw85HfYnjn4nH6sgvPsT6KdThsqpZwYTjCEQLGCpyw9Uz4Bmy-bIi4WUvYETgN-TLwGUv3EPHmknjfkn1cd)。在比如过你要将某个特定的对象保存到文件中,我隔几天在把它拿出来用,那么这时候就要实现Serializable接口; - > -* b)在进行java的**Socket**编程的时候,你有时候可能要传输某一类的对象,那么也就要实现Serializable接口;最常见的你传输一个字符串,它是JDK里面的类,也实现了Serializable接口,所以可以在网络上传输。 -> ** c)如果要通过远程的方法调用(RMI)去调用一个远程对象的方法,如在计算机A中调用另一台计算机B的对象的方法,那么你需要通过JNDI服务获取计算机B目标对象的引用,将对象从B传送到A,就需要实现序列化接口。 -####底层实现原理 - ``` -Foo myFoo = new Foo(); -myFoo .setWidth(37); -myFoo.setHeight(70); - ``` - 当 通过下面的代码序列化之后,MyFoo对象中的width和Height实例变量的值(37,70)都被保存到foo.ser文件中,这样以后又可以把它 从文件中读出来,重新在堆中创建原来的对象。当然保存时候不仅仅是保存对象的实例变量的值,JVM还要保存一些小量信息,比如类的类型等以便恢复原来的对象。 -``` -FileOutputStream fs = new FileOutputStream("foo.ser"); -ObjectOutputStream os = new ObjectOutputStream(fs); -os.writeObject(myFoo); -``` -######大概步骤 -1. Make a FileOutputStream -`FileOutputStream fs = new FileOutputStream("foo.ser"); ` -2. Make a ObjectOutputStream -`ObjectOutputStream os = new ObjectOutputStream(fs); ` -3. write the object -`os.writeObject(myObject1); -os.writeObject(myObject2); -os.writeObject(myObject3); ` -4. close the ObjectOutputStream -`os.close();` - -> serialVersionUID适用于Java的序列化机制。简单来说,Java的序列化机制是通过判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常,即是InvalidCastException。 - -* serialVersionUID有两种显示的生成方式: - * 一是默认的1L,比如:private static final long serialVersionUID = 1L; - * 二是根据类名、接口名、成员方法及属性等来生成一个64位的哈希字段,比如: -private static final long serialVersionUID = xxxxL; diff --git "a/Java\346\272\220\347\240\201\345\210\206\346\236\220/\350\256\241\347\256\227\346\234\272\347\274\226\347\240\201\345\216\206\347\250\213(what-is-Unicode&UTF8-).md" "b/Java\346\272\220\347\240\201\345\210\206\346\236\220/\350\256\241\347\256\227\346\234\272\347\274\226\347\240\201\345\216\206\347\250\213(what-is-Unicode&UTF8-).md" deleted file mode 100644 index 4c6e77f..0000000 --- "a/Java\346\272\220\347\240\201\345\210\206\346\236\220/\350\256\241\347\256\227\346\234\272\347\274\226\347\240\201\345\216\206\347\250\213(what-is-Unicode&UTF8-).md" +++ /dev/null @@ -1,39 +0,0 @@ ->很久很久以前,有一群人,他们决定用8个可以开合的晶体管来组合成不同的状态,以表示世界上的万物。他们看到8个开关状态是好的,于是他们把这称为”字节“。再后来,他们又做了一些可以处理这些字节的机器,机器开动了,可以用字节来组合出很多状态,状态开始变来变去。他们看到这样是好的,于是它们就这机器称为”计算机“。 -开始计算机只在美国用。八位的字节一共可以组合出256(2的8次方)种不同的状态。 他们把其中的编号从0开始的32种状态分别规定了特殊的用途,一但终端、打印机遇上约定好的这些字节被传过来时,就要做一些约定的动作:遇上0×10, 终端就换行;遇上0×07, 终端就向人们嘟嘟叫;遇上0x1b, 打印机就打印反白的字,或者终端就用彩色显示字母。 - -他们看到这样很好,于是就把这些0×20以下的字节状态称为”控制码”。他们又把所有的空 格、标点符号、数字、大小写字母分别用连续的字节状态表示,一直编到了第127号,这样计算机就可以用不同字节来存储英语的文字了。大家看到这样,都感觉 很好,于是大家都把这个方案叫做 ANSI 的”Ascii”编码(American Standard Code for Information Interchange,美国信息互换标准代码)。当时世界上所有的计算机都用同样的ASCII方案来保存英文文字。 - -后来,就像建造巴比伦塔一样,世界各地都开始使用计算机,但是很多国家用的不是英文,他们的字母里有许多是ASCII里没有的,为了可以在计算机保存他们的文字,他们决定采用 127号之后的空位来表示这些新的字母、符号,还加入了很多画表格时需要用下到的横线、竖线、交叉等形状,一直把序号编到了最后一个状态255。从128 到255这一页的字符集被称”扩展字符集“。从此之后,贪婪的人类再没有新的状态可以用了,美帝国主义可能没有想到还有第三世界国家的人们也希望可以用到计算机吧! - -等中国人们得到计算机时,已经没有可以利用的字节状态来表示汉字,况且有6000多个常用汉字需要保存呢。但是这难不倒智慧的中国人民,我们不客气地把那些127号之后的奇异符号们直接取消掉, 规定:一个小于127的字符的意义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,前面的一个字节(他称之为高字节)从0xA1用到0xF7,后面一个字节(低字节)从0xA1到0xFE,这样我们就可以组合出大约7000多个简体汉字了。在这些编码里,我们还把数学符号、罗马希腊的字母、日文的假名们都编进去了,连在 ASCII 里本来就有的数字、标点、字母都统统重新编了两个字节长的编码,这就是常说的”全角”字符,而原来在127号以下的那些就叫”半角”字符了。中国人民看到这样很不错,于是就把这种汉字方案叫做 “GB2312“。GB2312 是对 ASCII 的中文扩展。 - -但是中国的汉字太多了,我们很快就就发现有许多人的人名没有办法在这里打出来,特别是某些很会麻烦别人的国家领导人。于是我们不得不继续把GB2312 没有用到的码位找出来老实不客气地用上。后来还是不够用,于是干脆不再要求低字节一定是127号之后的内码,只要第一个字节是大于127就固定表示这是一个汉字的开始,不管后面跟的是不是扩展字符集里的内容。结果扩展之后的编码方案被称为 GBK 标准,GBK包括了GB2312 的所有内容,同时又增加了近20000个新的汉字(包括繁体字)和符号。 后来少数民族也要用电脑了,于是我们再扩展,又加了几千个新的少数民族的字,GBK扩成了 GB18030。从此之后,中华民族的文化就可以在计算机时代中传承了。 中国的程序员们看到这一系列汉字编码的标准是好的,于是通称他们叫做 “DBCS“(Double Byte Charecter Set 双字节字符集)。在DBCS系列标准里,最大的特点是两字节长的汉字字符和一字节长的英文字符并存于同一套编码方案里,因此他们写的程序为了支持中文处理,必须要注意字串里的每一个字节的值,如果这个值是大于127的,那么就认为一个双字节字符集里的字符出现了。那时候凡是受过加持,会编程的计算机僧侣们都要每天念下面这个咒语数百遍: “一个汉字算两个英文字符!一个汉字算两个英文字符……” - -因为当时各个国家都像中国这样搞出一套自己的编码标准,结果互相之间谁也不懂谁的编码,谁也不支持别人的编码,连大陆和台湾这样只相隔了150海里,使用着同一种语言的兄弟地区,也分别采用了不同的 DBCS 编码方案——当时的中国人想让电脑显示汉字,就必须装上一个”汉字系统”,专门用来处理汉字的显示、输入的问题,像是那个台湾的愚昧封建人士写的算命程序就必须加装另一套支持 BIG5 编码的什么”倚天汉字系统”才可以用,装错了字符系统,显示就会乱了套!这怎么办?而且世界民族之林中还有那些一时用不上电脑的穷苦人民,他们的文字又怎么办? 真是计算机的巴比伦塔命题啊! - -正在这时,大天使加百列及时出现了——一个叫 ISO(国际标谁化组织)的国际组织决定着手解决这个问题。他们采用的方法很简单:废了所有的地区性编码方案,重新搞一个包括了地球上所有文化、所有字母和符号 的编码!他们打算叫它”Universal Multiple-Octet Coded Character Set”,简称 UCS, 俗称 “unicode“。 - -unicode开始制订时,计算机的存储器容量极大地发展了,空间再也不成为问题了。于是 ISO 就直接规定必须用两个字节,也就是16位来统一表示所有的字符,对于ASCII里的那些“半角”字符,unicode包持其原编码不变,只是将其长度由原来的8位扩展为16位,而其他文化和语言的字符则全部重新统一编码。由于”半角”英文符号只需要用到低8位,所以其高8位永远是0,因此这种大气的方案在保存英文文本时会多浪费一倍的空间。 - -这时候,从旧社会里走过来的程序员开始发现一个奇怪的现象:他们的 strlen 函数靠不住了,一个汉字不再是相当于两个字符了,而是一个!是的,从unicode开始,无论是半角的英文字母,还是全角的汉字,它们都是统一的”一个字符“!同时,也都是统一的”两个字节“,请注意”字符”和”字节”两个术语的不同,“字节”是一个8位的物理存贮单元,而“字符”则是一个文化相关的符号。在unicode中,一个字符就是两个字节。一个汉字算两个英文字符的时代已经快过去了。 - -unicode同样也不完美,这里就有两个的问题,一个是,如何才能区别unicode和ascii?计算机怎么知道三个字节表示一个符号,而不是分别表示三个符号呢?第二个问题是,我们已经知道,英文字母只用一个字节表示就够了,如果unicode统一规定,每个符号用三个或四个字节表示,那么每个英文字母前都必然有二到三个字节是0,这对于存储空间来说是极大的浪费,文本文件的大小会因此大出二三倍,这是难以接受的。 - -unicode在很长一段时间内无法推广,直到互联网的出现,为解决unicode如何在网络上传输的问题,于是面向传输的众多 UTF(UCS Transfer Format)标准出现了,顾名思义,UTF-8就是每次8个位传输数据,而UTF-16就是每次16个位。UTF-8就是在互联网上使用最广的一种unicode的实现方式,这是为传输而设计的编码,并使编码无国界,这样就可以显示全世界上所有文化的字符了。UTF-8最大的一个特点,就是它是一种变长的编码方式。它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度,当字符在ASCII码的范围时,就用一个字节表示,保留了ASCII字符一个字节的编码做为它的一部分,注意的是unicode一个中文字符占2个字节,而UTF-8一个中文字符占3个字节)。从unicode到uft-8并不是直接的对应,而是要过一些算法和规则来转换。 - -|Unicode符号范围 | UTF-8编码方式| -|:-------------:|:----------:| -|(十六进制) | (二进制) -|0000 0000-0000 007F | 0xxxxxxx| -|0000 0080-0000 07FF | 110xxxxx 10xxxxxx | -|0080-0000 |07FF | -0000 0800-0000 FFFF |1110xxxx 10xxxxxx 10xxxxxx | -|0001 0000-0010 FFFF|11110xxx 10xxxxxx 10xxxxxx 10xxxxxx | - -最后简单总结一下: -* 中国人民通过对 ASCII 编码的中文扩充改造,产生了 GB2312 编码,可以表示6000多个常用汉字。 -* 汉字实在是太多了,包括繁体和各种字符,于是产生了 GBK 编码,它包括了 GB2312 中的编码,同时扩充了很多。 -* 中国是个多民族国家,各个民族几乎都有自己独立的语言系统,为了表示那些字符,继续把 GBK 编码扩充为 GB18030 编码。 -* 每个国家都像中国一样,把自己的语言编码,于是出现了各种各样的编码,如果你不安装相应的编码,就无法解释相应编码想表达的内容。 -* 终于,有个叫 ISO 的组织看不下去了。他们一起创造了一种编码 UNICODE ,这种编码非常大,大到可以容纳世界上任何一个文字和标志。所以只要电脑上有 UNICODE 这种编码系统,无论是全球哪种文字,只需要保存文件的时候,保存成 UNICODE 编码就可以被其他电脑正常解释。 -* UNICODE 在网络传输中,出现了两个标准 UTF-8 和 UTF-16,分别每次传输 8个位和 16个位。于是就会有人产生疑问,UTF-8 既然能保存那么多文字、符号,为什么国内还有这么多使用 GBK 等编码的人?因为 UTF-8 等编码体积比较大,占电脑空间比较多,如果面向的使用人群绝大部分都是中国人,用 GBK 等编码也可以。 diff --git "a/Java\346\272\220\347\240\201\345\210\206\346\236\220/\350\264\237\346\225\260\347\232\204\344\272\214\350\277\233\345\210\266\350\241\250\347\244\272.md" "b/Java\346\272\220\347\240\201\345\210\206\346\236\220/\350\264\237\346\225\260\347\232\204\344\272\214\350\277\233\345\210\266\350\241\250\347\244\272.md" deleted file mode 100644 index c708ddf..0000000 --- "a/Java\346\272\220\347\240\201\345\210\206\346\236\220/\350\264\237\346\225\260\347\232\204\344\272\214\350\277\233\345\210\266\350\241\250\347\244\272.md" +++ /dev/null @@ -1,31 +0,0 @@ -我们已经知道计算机中,所有数据最终都是使用二进制数表达。 -我们也已经学会如何将一个10进制数如何转换为二进制数以及如何将如何将一个16进制数如何转换为二进制数,详见下图。 -![image.png](http://upload-images.jianshu.io/upload_images/5786888-16796a0a4835a7af.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) -不过,我们仍然没有学习一个负数如何用二进制表达。 -比如,假设有一 int 类型的数,值为5,那么,我们知道它在计算机中表示为:`00000000 00000000 00000000 00000101` -5转换成二制是101,不过int类型的数占用4字节(32位),所以前面填了一堆0。 -现在想知道,-5在计算机中如何表示? - -**在计算机中,负数以其正值的补码形式表达** -什么叫补码呢?这得从原码,反码说起。 -**原码:一个整数,按照绝对值大小转换成的二进制数,称为原码。** -比如 `00000000 00000000 00000000 00000101` 是 5的 原码。 -**反码:将二进制数按位取反,所得的新二进制数称为原二进制数的反码。** -取反操作指:原为1,得0;原为0,得1。(1变0; 0变1) -比如:将`00000000 00000000 00000000 00000101`每一位取反,得`11111111 11111111 11111111 11111010`。 -称:`11111111 11111111 11111111 11111010 `是 `00000000 00000000 00000000 00000101` 的反码。 -反码是相互的,所以也可称: -`11111111 11111111 11111111 11111010` 和` 00000000 00000000 00000000 00000101` 互为反码。 -**补码:反码加1称为补码。** -也就是说,要得到一个数的补码,先得到反码,然后将反码加上1,所得数称为补码。 -比如:`00000000 00000000 00000000 00000101 `的反码是:`11111111 11111111 11111111 11111010`。 -那么,补码为: -`11111111 11111111 11111111 11111010 + 1 = 11111111 11111111 11111111 11111011` -所以,-5 在计算机中表达为:`11111111 11111111 11111111 11111011`。 -转换为十六进制:`0xFFFFFFFB`。 -再举一例,我们来看整数-1在计算机中如何表示。 -假设这也是一个int类型,那么: -1、先取1的原码:`00000000 00000000 00000000 00000001` -2、得反码:     `11111111 11111111 11111111 11111110` -3、得补码:     `11111111 11111111 11111111 11111111` -可见,-1在计算机里用二进制表达就是全1。16进制为:`0xFFFFFF。` diff --git a/README.md b/README.md index e32e776..1993530 100644 --- a/README.md +++ b/README.md @@ -1,97 +1,141 @@ # java-reader [![License](https://img.shields.io/badge/License-MIT-green.svg)](https://github.com/fantj2016/java-reader/blob/master/LICENSE) -[![微信交流群](https://img.shields.io/badge/%E5%BE%AE%E4%BF%A1%E7%BE%A4-%E4%BA%8C%E7%BB%B4%E7%A0%81-orange.svg)](https://upload-images.jianshu.io/upload_images/5786888-fa5ab6bde180e7ec.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) -[![微信公众号](https://img.shields.io/badge/%E5%85%AC%E4%BC%97%E5%8F%B7-PlayInJava-red.svg)](https://upload-images.jianshu.io/upload_images/5786888-fa5ab6bde180e7ec.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +[![我的微信](https://img.shields.io/badge/%E5%BE%AE%E4%BF%A1%E7%BE%A4-%E4%BA%8C%E7%BB%B4%E7%A0%81-orange.svg)](https://upload-images.jianshu.io/upload_images/5786888-aceaf4a8c7d17891.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) +[![微信公众号](https://img.shields.io/badge/%E5%85%AC%E4%BC%97%E5%8F%B7-PlayInJava-red.svg)](https://upload-images.jianshu.io/upload_images/5786888-74bca7fff151cfb8.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/500) ->这是我学习java的一些历程,分享给大家,有一些写的不好的完善后再上线,谢谢大家的支持,转载请标明出处,谢谢。 +>【Java入门 -> 进大厂笔记📖📒】未来的你一定会感谢今天学习的自己! 本项目主打Java基础、算法、框架实战、源码、中间件、大数据、面试等知识积累和经验总结。文章共300多篇, 大部分为原创, 部分翻译和转载已标明出处。 谢谢大家的支持, 转载请标明出处, 谢谢。 -**不断完善、不断更新** +[![Star History Chart](https://api.star-history.com/svg?repos=fantj2016/java-reader&type=Date)](https://star-history.com/#fantj2016/java-reader) + +感兴趣的朋友可以加入我们, 一起完善, 投稿请加微信。 + +推荐书籍: 《深入理解Java虚拟机》、《并发编程的艺术》、《Java多线程核心编程艺术》、《Java8函数式编程》、《Redis设计与实现》、《RocketMQ技术内幕》、《Spring技术内幕》、《Spring源码深度解析》、《剑指Offer》、《大话设计模式》... 详细书单请在公众号获取。 + +我的简书:https://www.jianshu.com/u/f223a6ff7f2a + +我的掘金:https://juejin.im/user/5a6adbdb6fb9a01ca6030469 + +我的开源项目: [https://github.com/fantj2016/java-reader](https://github.com/fantj2016/java-reader/blob/master/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE/%E5%BC%80%E6%BA%90%E9%A1%B9%E7%9B%AE.md) ### 1. 算法 ->Java开发高频考试简单算法。 -* [Leetcode题目解析](https://github.com/fantj2016/java-reader/tree/master/leetcode) -* [Leetcode项目代码](https://github.com/fantj2016/data-structures-with-algorithm/tree/master/main/com/algorithm/leetcode) -* [剑指Offer项目代码](https://github.com/fantj2016/data-structures-with-algorithm/tree/master/main/com/algorithm/offer) -* [剑指Offer题目解析](https://github.com/fantj2016/java-reader/tree/master/%E5%89%91%E6%8C%87offer) -* [各种排序项目代码](https://github.com/fantj2016/data-structures-with-algorithm/tree/master/main/com/algorithm/leetcode/sort) + +* [各种排序项目代码](https://github.com/fantj2016/java-reader/tree/master/1.%20%E7%AE%97%E6%B3%95%E5%9F%BA%E7%A1%80/1.1%20%E6%8E%92%E5%BA%8F%E7%AE%97%E6%B3%95) +* [Leetcode](https://github.com/fantj2016/java-reader/tree/master/1.%20%E7%AE%97%E6%B3%95%E5%9F%BA%E7%A1%80/1.3%20leetcode) +* [剑指Offer](https://github.com/fantj2016/java-reader/tree/master/1.%20%E7%AE%97%E6%B3%95%E5%9F%BA%E7%A1%80/1.2%20%E5%89%91%E6%8C%87offer) ### 2. Java基础 ->JVM、并发、NIO、类加载、Tomcat类加载、JDBC详解、Servlet详解、Java反射详解、函数式编程(Lambda+Stream)、数据结构、设计模式 - -* [NIO](https://github.com/fantj2016/java-reader/tree/master/Java-NIO) -* [JVM](https://github.com/fantj2016/java-reader/tree/master/JVM) -* [jvm类加载器](https://github.com/fantj2016/java-reader/tree/master/%E7%B1%BB%E5%8A%A0%E8%BD%BD%E5%99%A8/jvm) -* [Tomcat类加载器]() -* [Servlet详解](https://github.com/fantj2016/java-reader/tree/master/Java-Servlet) -* [JDBC详解](https://github.com/fantj2016/java-reader/tree/master/jdbc%E7%BB%84%E4%BB%B6%E8%AF%A6%E8%A7%A3) -* [反射详解](https://github.com/fantj2016/java-reader/tree/master/Java-reflection-%E5%8F%8D%E5%B0%84%E8%AF%A6%E8%A7%A3) -* [Java并发、多线程](https://github.com/fantj2016/java-reader/tree/master/Java%E5%B9%B6%E5%8F%91) -* [Java函数式编程](https://github.com/fantj2016/java-reader/tree/master/Java%E5%87%BD%E6%95%B0%E5%BC%8F%E7%BC%96%E7%A8%8B) + +#### 2.1 基础 +* [JVM](https://github.com/fantj2016/java-reader/tree/master/2.%20Java%E5%9F%BA%E7%A1%80/2.1%20%E5%9F%BA%E7%A1%80%E7%AF%87/1.%20JVM%E7%AF%87) +* [Java并发、多线程](https://github.com/fantj2016/java-reader/tree/master/2.%20Java%E5%9F%BA%E7%A1%80/2.1%20%E5%9F%BA%E7%A1%80%E7%AF%87/2.%20%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%E7%AF%87) +* [反射详解](https://github.com/fantj2016/java-reader/tree/master/2.%20Java%E5%9F%BA%E7%A1%80/2.1%20%E5%9F%BA%E7%A1%80%E7%AF%87/3.%20%E5%8F%8D%E5%B0%84%E7%AF%87) +* [JDBC详解](https://github.com/fantj2016/java-reader/tree/master/2.%20Java%E5%9F%BA%E7%A1%80/2.1%20%E5%9F%BA%E7%A1%80%E7%AF%87/4.%20JDBC%E7%AF%87) +* [Servlet详解](https://github.com/fantj2016/java-reader/tree/master/2.%20Java%E5%9F%BA%E7%A1%80/2.1%20%E5%9F%BA%E7%A1%80%E7%AF%87/5.%20Servlet%E7%AF%87) +* [NIO](https://github.com/fantj2016/java-reader/tree/master/2.%20Java%E5%9F%BA%E7%A1%80/2.1%20%E5%9F%BA%E7%A1%80%E7%AF%87/Java-NIO) +* [Java函数式编程](https://github.com/fantj2016/java-reader/tree/master/2.%20Java%E5%9F%BA%E7%A1%80/2.1%20%E5%9F%BA%E7%A1%80%E7%AF%87/Java%E5%87%BD%E6%95%B0%E5%BC%8F%E7%BC%96%E7%A8%8B) * [设计模式代码+解读](https://github.com/fantj2016/GOF23) -* [设计模式文章](https://github.com/fantj2016/java-reader/tree/master/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F) -* [Java手写数据结构](https://github.com/fantj2016/Java-dataStruct) +* [设计模式文章](https://github.com/fantj2016/java-reader/tree/master/2.%20Java%E5%9F%BA%E7%A1%80/2.1%20%E5%9F%BA%E7%A1%80%E7%AF%87/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F) +* [Tomcat类加载器(待完成)]() + +#### 2.2 源码 -##### 2.1 源码篇 ->ArrayList、HashMap、LinkedList、ThreadPoolExector源码解析 -* [Java源码、集合源码](https://github.com/fantj2016/java-reader/tree/master/Java%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90) -* [手写一个Tomcat](https://github.com/fantj2016/MyTomcat) -* [手写一个Jedis](https://github.com/fantj2016/easy-jedis) -##### 2.2 面试题 -* [Java基础面试题总结](https://github.com/fantj2016/java-reader/tree/master/Java%E9%9D%A2%E8%AF%95) +* [Java源码、集合源码](https://github.com/fantj2016/java-reader/tree/master/2.%20Java%E5%9F%BA%E7%A1%80/2.2%20%E6%BA%90%E7%A0%81%E7%AF%87) --- -### 3. 框架部分 -##### 3.1 实战篇 -* [SpringBoot整合&实战](https://github.com/fantj2016/java-reader/tree/master/springboot) -* [Mybatis使用&常见问题解决](https://github.com/fantj2016/java-reader/tree/master/Mybatis) -* [SpringSecurity安全框架实战](https://github.com/fantj2016/java-reader/tree/master/Spring-Security) -* [SpringCloud实战](https://github.com/fantj2016/java-reader/tree/master/SpringCloud) -* [Dubbo实战](https://github.com/fantj2016/java-reader/tree/master/dubbo) -* [Zookeeper部分](https://github.com/fantj2016/java-reader/tree/master/zk) -* [MQ从入门到实战](https://github.com/fantj2016/java-reader/tree/master/mq) - -##### 3.2 源码篇 -* [Spring源码解析](https://github.com/fantj2016/java-reader/tree/master/Spring%E6%BA%90%E7%A0%81) -* [SpringMvc源码解析](https://github.com/fantj2016/java-reader/tree/master/SpringMvc%E6%BA%90%E7%A0%81) -* [Mybatis源码解析](https://github.com/fantj2016/java-reader/tree/master/Mybatis%E6%BA%90%E7%A0%81) -* [两种动态代理]() - -##### 3.3 面试题 -* [Spring面试题]() -* [SpringMvc面试题]() -* [Mybatis面试题]() +### 3. 框架专题 +#### 3.1 实战 +* [SpringBoot整合&实战](https://github.com/fantj2016/java-reader/tree/master/3.%20%E6%A1%86%E6%9E%B6%E4%B8%93%E9%A2%98/3.1%20%E5%AE%9E%E6%88%98%E7%AF%87/SpringBoot) +* [Mybatis使用&常见问题解决](https://github.com/fantj2016/java-reader/tree/master/3.%20%E6%A1%86%E6%9E%B6%E4%B8%93%E9%A2%98/3.1%20%E5%AE%9E%E6%88%98%E7%AF%87/Mybatis) +* [SpringSecurity安全框架实战](https://github.com/fantj2016/java-reader/tree/master/3.%20%E6%A1%86%E6%9E%B6%E4%B8%93%E9%A2%98/3.1%20%E5%AE%9E%E6%88%98%E7%AF%87/SpringSecurity) +* [SpringCloud实战](https://github.com/fantj2016/java-reader/tree/master/3.%20%E6%A1%86%E6%9E%B6%E4%B8%93%E9%A2%98/3.1%20%E5%AE%9E%E6%88%98%E7%AF%87/SpringCloud) +* [Dubbo实战](https://github.com/fantj2016/java-reader/tree/master/3.%20%E6%A1%86%E6%9E%B6%E4%B8%93%E9%A2%98/3.1%20%E5%AE%9E%E6%88%98%E7%AF%87/Dubbo) +* [Zookeeper部分](https://github.com/fantj2016/java-reader/tree/master/5.%20%E4%B8%AD%E9%97%B4%E4%BB%B6%E4%B8%93%E9%A2%98/5.1%20%E5%AE%9E%E6%88%98%E7%AF%87/3.%20Zookeeper) +* [MQ从入门到实战](https://github.com/fantj2016/java-reader/tree/master/5.%20%E4%B8%AD%E9%97%B4%E4%BB%B6%E4%B8%93%E9%A2%98/5.1%20%E5%AE%9E%E6%88%98%E7%AF%87/4.%20MQ) +* [JPA](https://github.com/fantj2016/java-reader/tree/master/3.%20%E6%A1%86%E6%9E%B6%E4%B8%93%E9%A2%98/3.1%20%E5%AE%9E%E6%88%98%E7%AF%87/JPA) +* [Netty](https://github.com/fantj2016/java-reader/tree/master/3.%20%E6%A1%86%E6%9E%B6%E4%B8%93%E9%A2%98/3.1%20%E5%AE%9E%E6%88%98%E7%AF%87/Netty) +* [Spring](https://github.com/fantj2016/java-reader/tree/master/3.%20%E6%A1%86%E6%9E%B6%E4%B8%93%E9%A2%98/3.1%20%E5%AE%9E%E6%88%98%E7%AF%87/Spring) + +#### 3.2 源码解析 +* [Spring源码解析](https://github.com/fantj2016/java-reader/tree/master/3.%20%E6%A1%86%E6%9E%B6%E4%B8%93%E9%A2%98/3.2%20%E6%BA%90%E7%A0%81%E7%AF%87/Spring%E6%BA%90%E7%A0%81) +* [SpringBoot源码](https://github.com/fantj2016/java-reader/tree/master/3.%20%E6%A1%86%E6%9E%B6%E4%B8%93%E9%A2%98/3.2%20%E6%BA%90%E7%A0%81%E7%AF%87/SpringBoot%E6%BA%90%E7%A0%81) +* [SpringMvc源码解析](https://github.com/fantj2016/java-reader/tree/master/3.%20%E6%A1%86%E6%9E%B6%E4%B8%93%E9%A2%98/3.2%20%E6%BA%90%E7%A0%81%E7%AF%87/SpringMVC%E6%BA%90%E7%A0%81) +* [Mybatis源码解析](https://github.com/fantj2016/java-reader/tree/master/3.%20%E6%A1%86%E6%9E%B6%E4%B8%93%E9%A2%98/3.2%20%E6%BA%90%E7%A0%81%E7%AF%87/Mybatis%E6%BA%90%E7%A0%81) +* [Redis源码解析](https://github.com/fantj2016/java-reader/tree/master/5.%20%E4%B8%AD%E9%97%B4%E4%BB%B6%E4%B8%93%E9%A2%98/5.2%20%E6%BA%90%E7%A0%81%E7%AF%87/Redis%E6%BA%90%E7%A0%81) +* [Dubbo源码](https://github.com/fantj2016/java-reader/tree/master/3.%20%E6%A1%86%E6%9E%B6%E4%B8%93%E9%A2%98/3.2%20%E6%BA%90%E7%A0%81%E7%AF%87/Dubbo%E6%BA%90%E7%A0%81) + + + +#### 3.3 框架实现 +* [Tomcat框架简单实现](https://github.com/fantj2016/MyTomcat) +* [Jedis框架简单实现](https://github.com/fantj2016/easy-jedis) +* [MVC框架简单实现](https://github.com/fantj2016/easy-springmvc) +--- + +### 4. 分布式专题 +* [分布式事务](https://github.com/fantj2016/java-reader/tree/master/4.%20%E5%88%86%E5%B8%83%E5%BC%8F%E4%B8%93%E9%A2%98/4.1%20%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1) +* 分布式锁..待更新 +--- +### 5. 中间件专题 + +#### 5.1 实战 +* [Nginx入门](https://github.com/fantj2016/java-reader/tree/master/5.%20%E4%B8%AD%E9%97%B4%E4%BB%B6%E4%B8%93%E9%A2%98/5.1%20%E5%AE%9E%E6%88%98%E7%AF%87/1.%20Nginx) +* [Redis入门](https://github.com/fantj2016/java-reader/tree/master/5.%20%E4%B8%AD%E9%97%B4%E4%BB%B6%E4%B8%93%E9%A2%98/5.1%20%E5%AE%9E%E6%88%98%E7%AF%87/2.%20Redis) +* [Zookeeper部分](https://github.com/fantj2016/java-reader/tree/master/5.%20%E4%B8%AD%E9%97%B4%E4%BB%B6%E4%B8%93%E9%A2%98/5.1%20%E5%AE%9E%E6%88%98%E7%AF%87/3.%20Zookeeper) +* [MQ从入门到实战](https://github.com/fantj2016/java-reader/tree/master/5.%20%E4%B8%AD%E9%97%B4%E4%BB%B6%E4%B8%93%E9%A2%98/5.1%20%E5%AE%9E%E6%88%98%E7%AF%87/4.%20MQ) + +#### 5.2 源码解析 +* [Redis源码分析](https://github.com/fantj2016/java-reader/tree/master/5.%20%E4%B8%AD%E9%97%B4%E4%BB%B6%E4%B8%93%E9%A2%98/5.2%20%E6%BA%90%E7%A0%81%E7%AF%87/Redis%E6%BA%90%E7%A0%81) + +### 6. 高效研发 +* [Git使用&问题排查](https://github.com/fantj2016/java-reader/tree/master/6.%20%E9%AB%98%E6%95%88%E5%BC%80%E5%8F%91/6.1%20Git) +* [Maven高级](https://github.com/fantj2016/java-reader/tree/master/6.%20%E9%AB%98%E6%95%88%E5%BC%80%E5%8F%91/6.2%20Maven) +* [Jenkins使用](https://github.com/fantj2016/java-reader/tree/master/6.%20%E9%AB%98%E6%95%88%E5%BC%80%E5%8F%91/6.3%20Jenkins) +* [IDEA使用](https://github.com/fantj2016/java-reader/tree/master/6.%20%E9%AB%98%E6%95%88%E5%BC%80%E5%8F%91/6.4%20IDEA) --- -### 4. 分布式 -* [分布式事务](https://github.com/fantj2016/java-reader/tree/master/%E5%88%86%E5%B8%83%E5%BC%8F%E4%BA%8B%E5%8A%A1) -* [分布式锁]() +### 7. Linux专题 -##### 4.1 分布式面试题 +#### 7.1 基础 +* [CentOS命令&系统配置](https://github.com/fantj2016/java-reader/tree/master/7.%20Linux%E4%B8%93%E9%A2%98/7.1%20%E5%9F%BA%E7%A1%80%E7%AF%87) ---- +#### 7.2 常用服务搭建 +* [常用服务搭建:Hadoop+Redis+Es+FTP...](https://github.com/fantj2016/java-reader/tree/master/7.%20Linux%E4%B8%93%E9%A2%98/7.2%20%E5%B8%B8%E7%94%A8%E6%9C%8D%E5%8A%A1%E6%90%AD%E5%BB%BA) +* [Docker从入门到使用](https://github.com/fantj2016/java-reader/tree/master/7.%20Linux%E4%B8%93%E9%A2%98/7.2%20%E5%B8%B8%E7%94%A8%E6%9C%8D%E5%8A%A1%E6%90%AD%E5%BB%BA/Docker) +* [Docker-compose](https://github.com/fantj2016/java-reader/tree/master/7.%20Linux%E4%B8%93%E9%A2%98/7.2%20%E5%B8%B8%E7%94%A8%E6%9C%8D%E5%8A%A1%E6%90%AD%E5%BB%BA/Docker-compose) +* [Shell脚本](https://github.com/fantj2016/java-reader/tree/master/7.%20Linux%E4%B8%93%E9%A2%98/Shell) -### 5. 常用开发工具 -* [Git使用&问题排查](https://github.com/fantj2016/java-reader/tree/master/Git) -* [Maven高级](https://github.com/fantj2016/java-reader/tree/master/Maven) --- -### 6. 大数据 -* [Hadoop、Hive、Sqoop、Flume、Azkaban、Spark服务搭建](https://github.com/fantj2016/java-reader/tree/master/Centos%E5%AE%89%E8%A3%85%E5%B7%A5%E5%85%B7%E6%96%87%E9%9B%86) -* [Hadoop、Hive、Sqoop、Flume、Azkaban、Spark入门到实战](https://github.com/fantj2016/java-reader/tree/master/BigData) - -### 6. Linux运维部分 -* [CentOS安装工具集](https://github.com/fantj2016/java-reader/tree/master/Centos%E5%AE%89%E8%A3%85%E5%B7%A5%E5%85%B7%E6%96%87%E9%9B%86) -* [CentOS命令&本地环境配置](https://github.com/fantj2016/java-reader/tree/master/RedHat-CentOS) -* [Docker从入门到使用](https://github.com/fantj2016/java-reader/tree/master/Docker) -* [Nginx部分](https://github.com/fantj2016/java-reader/tree/master/nginx) -* [Nginx&Keepalived实现高可用](https://github.com/fantj2016/java-reader/blob/master/nginx/Nginx%26Keepalived-%E5%AE%9E%E7%8E%B0%E9%AB%98%E5%8F%AF%E7%94%A8.md) -* [Redis部分](https://github.com/fantj2016/java-reader/tree/master/redis) +### 8. 大数据 +* [服务搭建](https://github.com/fantj2016/java-reader/tree/master/8.%20%E5%A4%A7%E6%95%B0%E6%8D%AE/8.1%20%E6%9C%8D%E5%8A%A1%E6%90%AD%E5%BB%BA%E7%AF%87) +* [实战](https://github.com/fantj2016/java-reader/tree/master/8.%20%E5%A4%A7%E6%95%B0%E6%8D%AE/8.2%20%E5%AE%9E%E6%88%98%E7%AF%87) + +### 9. 架构设计 +* [架构设计](https://github.com/fantj2016/java-reader/tree/master/9.%20%E6%9E%B6%E6%9E%84%E8%AE%BE%E8%AE%A1) + +### 公众号 +>github阅读不方便?添加公众号,随时随地当reader,不定时发放福利 + +![我的公众号](https://upload-images.jianshu.io/upload_images/5786888-74bca7fff151cfb8.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/500) + +#### 公众号留言 +刚入门的小伙伴、或者是学习方向上遇到障碍的小伙伴可以在公众号留言,把你的问题描述清楚,我将在24H内认真回复。 + +#### 公众号福利 +添加本人微信留言:领取福利。 + +### 交流群 +>不定时会分享一些学习方法, 书籍, 企业技术, 算法等知识, 一起成长、一起进步。 有需要阿里社招内推的同学也可以联系哈,会帮你一直跟进流程。 + +![微信群加我备注进群](https://upload-images.jianshu.io/upload_images/5786888-9d87c2d1812f322e.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/200) + +![QQ交流群](https://upload-images.jianshu.io/upload_images/5786888-bc946ca74be7d601.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/200) --- diff --git "a/RedHat-CentOS/\346\227\240\346\240\207\351\242\230\346\226\207\347\253\240.md" "b/RedHat-CentOS/\346\227\240\346\240\207\351\242\230\346\226\207\347\253\240.md" deleted file mode 100644 index 8b13789..0000000 --- "a/RedHat-CentOS/\346\227\240\346\240\207\351\242\230\346\226\207\347\253\240.md" +++ /dev/null @@ -1 +0,0 @@ - diff --git "a/Spring\346\272\220\347\240\201/spring\350\207\252\345\256\232\344\271\211\346\263\250\350\247\243\357\274\210\346\263\250\350\247\243\346\272\220\347\240\201\350\247\243\346\236\220\357\274\211.md" "b/Spring\346\272\220\347\240\201/spring\350\207\252\345\256\232\344\271\211\346\263\250\350\247\243\357\274\210\346\263\250\350\247\243\346\272\220\347\240\201\350\247\243\346\236\220\357\274\211.md" deleted file mode 100644 index 8b13789..0000000 --- "a/Spring\346\272\220\347\240\201/spring\350\207\252\345\256\232\344\271\211\346\263\250\350\247\243\357\274\210\346\263\250\350\247\243\346\272\220\347\240\201\350\247\243\346\236\220\357\274\211.md" +++ /dev/null @@ -1 +0,0 @@ - diff --git a/a-code/src/Entry.class b/a-code/src/Entry.class new file mode 100644 index 0000000..9b7a328 Binary files /dev/null and b/a-code/src/Entry.class differ diff --git a/a-code/src/Entry.java b/a-code/src/Entry.java new file mode 100644 index 0000000..95f9f98 --- /dev/null +++ b/a-code/src/Entry.java @@ -0,0 +1,12 @@ +public class Entry { + private int total = 10; + + public int getTotal() { + return total; + } + + public Entry setTotal(int total) { + this.total = total; + return this; + } +} diff --git a/a-code/src/Main.class b/a-code/src/Main.class new file mode 100644 index 0000000..7bb683c Binary files /dev/null and b/a-code/src/Main.class differ diff --git a/a-code/src/Main.java b/a-code/src/Main.java new file mode 100644 index 0000000..ac07454 --- /dev/null +++ b/a-code/src/Main.java @@ -0,0 +1,5 @@ +public class Main { + public static void main(String[] args) { + Entry entry = new Entry(); + } +} \ No newline at end of file diff --git "a/dubbo/Dubbo-\346\212\245\351\224\231-com-alibaba-dubbo-rpc-RpcException.md" "b/dubbo/Dubbo-\346\212\245\351\224\231-com-alibaba-dubbo-rpc-RpcException.md" deleted file mode 100644 index 0550688..0000000 --- "a/dubbo/Dubbo-\346\212\245\351\224\231-com-alibaba-dubbo-rpc-RpcException.md" +++ /dev/null @@ -1,4 +0,0 @@ - -这个报错是因为用到的一些类没有序列化造成的,这个错误是由于我service用到了一些工具类,工具类没有实例化造成的。 - -只要是需要注册到dbbo上的每一个对象都需要做序列化。 diff --git "a/js/Ajax\345\270\270\347\224\250.md" "b/js/Ajax\345\270\270\347\224\250.md" deleted file mode 100644 index 291d2a2..0000000 --- "a/js/Ajax\345\270\270\347\224\250.md" +++ /dev/null @@ -1,29 +0,0 @@ -### 获取class下的子标签,并替换标签内容 -``` -$('.r strong').text(result.totalElements); -``` - -### checkbox 上传 -``` - function datadel(){ - var array = new Array(); - $.each($('input:checkbox:checked'),function(){ - array.push($(this).val()); - window.alert("你选了:"+ - $('input[type=checkbox]:checked').length+"个,其中有:"+$(this).val()); - }); - // alert(array.toString()) - // alert(JSON.stringify(array)); - $.ajax({ - url: "http://127.0.0.1:8081/student/deleteInBatch/", - type: "post", - data: {"ids":array.toString()}, - dataType: "json", - async: false, - success: function (result) { - console.log(result); - alert("批量删除成功!"); - } - }); - } -``` diff --git "a/js/Js\345\210\244\346\226\255\344\270\213\346\213\211\346\241\206\346\230\257\345\220\246\344\270\272\347\251\272\345\200\274.md" "b/js/Js\345\210\244\346\226\255\344\270\213\346\213\211\346\241\206\346\230\257\345\220\246\344\270\272\347\251\272\345\200\274.md" deleted file mode 100644 index 3793ede..0000000 --- "a/js/Js\345\210\244\346\226\255\344\270\213\346\213\211\346\241\206\346\230\257\345\220\246\344\270\272\347\251\272\345\200\274.md" +++ /dev/null @@ -1,49 +0,0 @@ -``` -Js判断下拉框 - -
- - -
-``` -实例: -``` - - - - -``` diff --git "a/js/js\344\277\256\346\224\271\346\227\266\351\227\264\346\240\274\345\274\217.md" "b/js/js\344\277\256\346\224\271\346\227\266\351\227\264\346\240\274\345\274\217.md" deleted file mode 100644 index 171880c..0000000 --- "a/js/js\344\277\256\346\224\271\346\227\266\351\227\264\346\240\274\345\274\217.md" +++ /dev/null @@ -1,9 +0,0 @@ -``` -function getDateStr(str) - { - var strDate = new Date(str); - var sDate = strDate.toLocaleString().split(' ')[0]; - return sDate.replace(/年|月/g, '-').replace(/日/g, ''); - } - -``` diff --git "a/js/js\346\210\252\345\217\226\350\266\205\351\223\276\346\216\245\345\220\216\345\217\202\346\225\260.md" "b/js/js\346\210\252\345\217\226\350\266\205\351\223\276\346\216\245\345\220\216\345\217\202\346\225\260.md" deleted file mode 100644 index cd7a0d3..0000000 --- "a/js/js\346\210\252\345\217\226\350\266\205\351\223\276\346\216\245\345\220\216\345\217\202\346\225\260.md" +++ /dev/null @@ -1,9 +0,0 @@ -超链接: http://xxxxxxx.html?id=10 -``` -$(function(){ - var idPre = window.location.href.split("?")[1]; - var id = idPre.split("=")[1]; -``` -window.location.href 获取整个链接 -window.location.href.split("?")[ 1 ] ; 从?切开,[1]的意思是取右边,即取出了 id = 10 -再对=分割,取右边[1],就得到了 数字10 diff --git "a/js/js\351\231\220\345\210\266\346\230\276\347\244\272\345\255\227\346\225\260.md" "b/js/js\351\231\220\345\210\266\346\230\276\347\244\272\345\255\227\346\225\260.md" deleted file mode 100644 index a21b44b..0000000 --- "a/js/js\351\231\220\345\210\266\346\230\276\347\244\272\345\255\227\346\225\260.md" +++ /dev/null @@ -1,9 +0,0 @@ -``` - -function limitWords(txt){ - var str = txt; - str = str.substr(0,200) + '...'; - return str; - } - -``` diff --git "a/js/\347\224\250js\347\273\231\344\270\213\346\213\211\346\241\206select\350\256\276\347\275\256\347\202\271\345\207\273\344\272\213\344\273\266.md" "b/js/\347\224\250js\347\273\231\344\270\213\346\213\211\346\241\206select\350\256\276\347\275\256\347\202\271\345\207\273\344\272\213\344\273\266.md" deleted file mode 100644 index e3c8cce..0000000 --- "a/js/\347\224\250js\347\273\231\344\270\213\346\213\211\346\241\206select\350\256\276\347\275\256\347\202\271\345\207\273\344\272\213\344\273\266.md" +++ /dev/null @@ -1,14 +0,0 @@ -``` - -``` -``` -$(function(){ - $("#select_table").change(function(){ - var typeCodePre = $("#select_table").val(); -``` diff --git a/mq/ActiveMQ.md b/mq/ActiveMQ.md deleted file mode 100644 index 0adbbff..0000000 --- a/mq/ActiveMQ.md +++ /dev/null @@ -1,14 +0,0 @@ -### 1. JMS简介 ->JMS是java的一套API标准。 - -同步转异步。 - -分流。 - -### 名词 -Destination 目的地 -Producer 服务者 -Consumer 消费者 -Message 消息 -ConnectionFactory 链接工厂 - diff --git "a/mq/RocketMQ\357\274\210\344\270\200\357\274\211\345\277\253\351\200\237\345\274\200\345\247\213.md" "b/mq/RocketMQ\357\274\210\344\270\200\357\274\211\345\277\253\351\200\237\345\274\200\345\247\213.md" deleted file mode 100644 index 9e7139c..0000000 --- "a/mq/RocketMQ\357\274\210\344\270\200\357\274\211\345\277\253\351\200\237\345\274\200\345\247\213.md" +++ /dev/null @@ -1,7 +0,0 @@ -###环境准备 -1. 64bit OS, Linux/Unix/Mac is recommended; -2. 64bit JDK 1.8+; -3. Maven 3.2.x -4. Git -###版本下载和构建 -https://www.jianshu.com/p/c6aeab993d44 diff --git "a/mysql/Mysql-8-\346\235\245\344\272\206-,-windows\345\256\211\350\243\205.md" "b/mysql/Mysql-8-\346\235\245\344\272\206-,-windows\345\256\211\350\243\205.md" deleted file mode 100644 index 9c98438..0000000 --- "a/mysql/Mysql-8-\346\235\245\344\272\206-,-windows\345\256\211\350\243\205.md" +++ /dev/null @@ -1,37 +0,0 @@ -### 1. 下载安装 - -先贴一个官网大轮播图: -![](https://upload-images.jianshu.io/upload_images/5786888-fe7af466fafc4b30.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - -8.0文档官方:https://dev.mysql.com/doc/refman/8.0/en/document-store.html - -8版本的特性和使用我很快会出一片教程。 - -![](https://upload-images.jianshu.io/upload_images/5786888-145b2b7524430045.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - -安装规规矩矩,一路next,有一部让选安装组件,只选server就行。 - -### 2. Navicat 破解 - -baidu云链接: https://pan.baidu.com/s/15b0qpvNngdFXFQr_UeQ4lw 提取码: 78mx - -1. 先安装`Navicat.exe` -2. 执行`patchNavicat.exe` 然后选择你安装的`navicat.exe`文件, 即可完成破解。 -### 3. mysql登录 - -如果完成了上面两步,你在用Navicat连接本地数据库会报错:`Client does not support authentication protocol requested by server` - -所以需要重置密码(即使安装的时候让你填了密码)。 - -``` -mysql> use mysql; -Database changed -mysql> alter user 'root'@'localhost' identified with mysql_native_password by 'root'; -Query OK, 0 rows affected (0.03 sec) - -mysql> flush privileges; -Query OK, 0 rows affected (0.00 sec) -``` - -然后再使用Navicat连接。 diff --git "a/mysql/Mysql-\346\212\245\351\224\231\350\257\246\350\247\243.md" "b/mysql/Mysql-\346\212\245\351\224\231\350\257\246\350\247\243.md" deleted file mode 100644 index 8749bec..0000000 --- "a/mysql/Mysql-\346\212\245\351\224\231\350\257\246\350\247\243.md" +++ /dev/null @@ -1,7 +0,0 @@ -##### 1. mysql Error 1040 too many connection -当最大连接数比较小时,可能会出现“1040 too many connection”错误。 -解决方法: -1. 首先需要重启mysql服务,执行命令:service mysql restar -2. 登录mysql:mysql -uroot -p -3. 登录成功后执行以下语句查询当前的最大连接数:select VARIABLE_VALUE from information_schema.GLOBAL_VARIABLES where VARIABLE_NAME='MAX_CONNECTIONS'; -4. 执行以下语句修改最大连接数:set global max_connections = 3600; diff --git "a/nginx/\347\254\254\344\270\200\347\253\240\357\274\232nginx\347\216\257\345\242\203\346\220\255\345\273\272.md" "b/nginx/\347\254\254\344\270\200\347\253\240\357\274\232nginx\347\216\257\345\242\203\346\220\255\345\273\272.md" deleted file mode 100644 index 3899f5b..0000000 --- "a/nginx/\347\254\254\344\270\200\347\253\240\357\274\232nginx\347\216\257\345\242\203\346\220\255\345\273\272.md" +++ /dev/null @@ -1,59 +0,0 @@ -[TOC] -### 1.四项确认(本次实例用redhat系列系统) -1. 确认系统网络 ping www.baidu.com -2. 确认yum可用 yum list |grep gcc -3. 确认关闭iptables规则 ` iptables -L`/`iptables -t nat -L ` 如果有的话 `iptables -F`/`iptables -t nat -F` 关闭规则 -4. 确认停用selinux getenforce 显示应该为 Disabled , 如果不是。`setenforce 0` -### 2.两项安装(yum list | grep gcc ) -1. `yum -y install gcc gcc-c++ autoconf pcre pcre-devel make automake ` 系统基本库 -2. `yum -y install wget httpd-tools vim` 一些基本工具 -### 3.目录介绍 -`cd /opt;` -`mkdir app(src) download(src package) logs(log) work(shell脚本) backup(配置文件)` -![image.png](http://upload-images.jianshu.io/upload_images/5786888-117f21b5834363bc.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - -###### nginx 中间件架构介绍(了解) -1. nginx是一个开源且高性能、可靠的HTTP中间件,代理服务。 -2. 常见的HTTP服务 - HTTPD -Apache - IIS - ms - GWS -Google -3. 为什么选择Nginx - 1. IO多路复用epoll(select模型和epoll模型) 多路复用:一个线程内并发交替的顺序完成 - 2. 功能模块少 (只保留了核心代码,轻量级) 代码模块化(易读,阿里借鉴开发) - 3. CPU亲和(affinity) - 是一种把CPU核心和Nginx工作进程绑定方式,把每个worker进程固定在一个cpu上执行,减少切换cpu的cache miss, - 获得更好的性能。 - 4. sendfile工作机制(借鉴与linux的0拷贝) - 直接通过内核空间来将静态资源拷贝到socket中(原始需要先通过用户空间) -###4. Nginx快读搭建与基本参数使用 ->Mainline version -开发版 -Stable version -稳定版 -Legacy version -历史版本 -Changes 有哪些改变 -######1. 修改yum源(添加nginx依赖) -* 在 etc/yum.repos.d/下创建一个nginx.repo -``` -[nginx] -name=nginx repo -baseurl=http://nginx.org/packages/centos/7/$basearch/ -gpgcheck=0 -enabled=1 -``` - -* 然后` yum list | grep nginx `测试是否成功添加yum源 -成功后的页面:![image.png](http://upload-images.jianshu.io/upload_images/5786888-a908df1b37b1fdee.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) -* 然后 `yum install nginx`安装。 -* 测试是否安装成功 - 1. nginx -V 编译信息 - nginx -v 版本信息 - 2. 安装时的编译参数 - --user=nginx - --group=nginx 设定nginx进程启动的用户和用户组 - 3. nginx.conf 介绍 - systemctl restart nginx.service 重启服务 -#####[ 拓展 ] 一些目录的介绍 -![image.png](http://upload-images.jianshu.io/upload_images/5786888-ec855623076f1c7e.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) -![image.png](http://upload-images.jianshu.io/upload_images/5786888-afcfa4ee3d24a346.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) -![image.png](http://upload-images.jianshu.io/upload_images/5786888-c9e6d5c351feed57.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) -[^1]: 脚注内容 diff --git "a/nginx/\347\254\254\344\270\203\347\253\240\357\274\232nginx\347\232\204rewrite\350\247\204\345\210\231\350\257\246\350\247\243.md" "b/nginx/\347\254\254\344\270\203\347\253\240\357\274\232nginx\347\232\204rewrite\350\247\204\345\210\231\350\257\246\350\247\243.md" deleted file mode 100644 index 561e39e..0000000 --- "a/nginx/\347\254\254\344\270\203\347\253\240\357\274\232nginx\347\232\204rewrite\350\247\204\345\210\231\350\257\246\350\247\243.md" +++ /dev/null @@ -1,269 +0,0 @@ -#####模块ngx_http_rewrite_module ->该ngx_http_rewrite_module模块用于使用PCRE正则表达式更改请求URI,返回重定向,并有条件地选择配置。 -``` -句法: break; #停止处理当前的一组 ngx_http_rewrite_module指令。 -句法: if (condition) { ... } #如果为true,则在花括号内指定的模块指令被执行,并且该请求被分配给if指令内的配置 -句法:return code [text];return code URL;return URL; #停止处理并将指定的内容返回code给客户端。 -句法:rewrite regex replacement [flag];可选`*flag*`参数可以是以下之一: - last: 停止处理当前的一组 `ngx_http_rewrite_module`指令并开始搜索与改变的URI匹配的新位置; - break:`ngx_http_rewrite_module`像break指令那样 停止处理当前的一组指令; - redirect: 返回302代码的临时重定向; 如果替换字符串不以“ `http://`”,“ `https://`”或“ `$scheme`” 开头,则使用该字符串; - permanent: 返回301代码的永久重定向。 -句法:rewrite_log on | off; #启用或禁用将ngx_http_rewrite_module模块指令处理结果记录到级别的error_log中。 -句法:set $variable value; #设置一个value指定的variable。该value可以包含文本,变量,他们的组合。 -句法:uninitialized_variable_warn on | off; #控制是否记录有关未初始化变量的警告。 -默认: - -语境: server,location,if -``` -先给一段官方demo config -``` -if ($http_user_agent ~ MSIE) { - rewrite ^(.*)$ /msie/$1 break; -} - -if ($http_cookie ~* "id=([^;]+)(?:;|$)") { - set $id $1; -} - -if ($request_method = POST) { - return 405; -} - -if ($slow) { - limit_rate 10k; -} - -if ($invalid_referer) { - return 403; -} - -server { - ... - rewrite ^(/download/.*)/media/(.*)\..*$ $1/mp3/$2.mp3 last; - rewrite ^(/download/.*)/audio/(.*)\..*$ $1/mp3/$2.ra last; - return 403; - ... -} - -location /download/ { - if ($forbidden) { - return 403; - } - - if ($slow) { - limit_rate 10k; - } - - rewrite ^/(download/.*)/media/(.*)\..*$ /$1/mp3/$2.mp3 break; -} -``` -一. 正则表达式 -* ~ 为区分大小写匹配 -* ~* 为不区分大小写匹配 -* !~和!~*分别为区分大小写不匹配及不区分大小写不匹配 - - -二.文件及目录匹配,其中: -* -f和!-f用来判断是否存在文件 -* -d和!-d用来判断是否存在目录 -* -e和!-e用来判断是否存在文件或目录 -* -x和!-x用来判断文件是否可执行 - -三.rewrite指令的最后一项参数为flag标记,flag标记有: -1. last 相当于apache里面的[L]标记,表示rewrite。 -2. break本条规则匹配完成后,终止匹配,不再匹配后面的规则。 -3. redirect 返回302临时重定向,浏览器地址会显示跳转后的URL地址。 -4. permanent 返回301永久重定向,浏览器地址会显示跳转后的URL地址。 - -######last和break区别: -1. 使用last和break实现URI重写,浏览器地址栏不变。 -2. 而且两者有细微差别,使用alias指令必须用last标记;使用proxy_pass指令时,需要使用break标记。 -3. Last标记在本条rewrite规则执行完毕后,会对其所在server{......}标签**重新发起请求**,而break标记则在本条规则匹配完成后,**终止匹配**。 -例如: -`如果我们将类似URL/photo/123456 重定向到/path/to/photo/12/1234/123456.png -rewrite "/photo/([0-9]{2})([0-9]{2})([0-9]{2})"/path/to/photo/$1/$1$2/$1$2$3.png ; -四.NginxRewrite 规则相关指令` - -1.break指令 -使用环境:server,location,if; -该指令的作用是完成当前的规则集,不再处理rewrite指令。 - -2.if指令 -使用环境:server,location -该指令用于检查一个条件是否符合,如果条件符合,则执行大括号内的语句。If指令不支持嵌套,不支持多个条件&&和||处理。 - -3.return指令 -语法:returncode ; -使用环境:server,location,if; -该指令用于结束规则的执行并返回状态码给客户端。 -示例:如果访问的URL以".sh"或".bash"结尾,则返回403状态码 -location ~ .*\.(sh|bash)?$ -{ -return 403; -} - -4.rewrite 指令 -语法:rewriteregex replacement flag -使用环境:server,location,if -该指令根据表达式来重定向URI,或者修改字符串。指令根据配置文件中的顺序来执行。注意重写表达式只对相对路径有效。如果你想配对主机名,你应该使用if语句,示例如下: -if( $host ~* www\.(.*) ) -{ -set $host_without_www $1; -rewrite ^(.*)$ http://$host_without_www$1permanent; -} - -5.Set指令 -语法:setvariable value ; 默认值:none; 使用环境:server,location,if; -该指令用于定义一个变量,并给变量赋值。变量的值可以为文本、变量以及文本变量的联合。 -示例:set$varname "hello world"; - -6.Uninitialized_variable_warn指令 -语法:uninitialized_variable_warnon|off -使用环境:http,server,location,if -该指令用于开启和关闭未初始化变量的警告信息,默认值为开启。 - - -五.Nginx的Rewrite规则编写实例 -``` 1.当访问的文件和目录不存在时,重定向到某个php文件 -if( !-e $request_filename ) -{ -rewrite ^/(.*)$ index.php last; -} - -2.目录对换 /123456/xxxx ====> /xxxx?id=123456 -rewrite ^/(\d+)/(.+)/ /$2?id=$1 last; - -3.如果客户端使用的是IE浏览器,则重定向到/ie目录下 -```if( $http_user_agent ~ MSIE) -{ -rewrite ^(.*)$ /ie/$1 break; -} - -4.禁止访问多个目录 -location ~ ^/(cron|templates)/ -{ -deny all; -break; -} - -5.禁止访问以/data开头的文件 -location ~ ^/data -{ -deny all; -} - -6.禁止访问以.sh,.flv,.mp3为文件后缀名的文件 -location ~ .*\.(sh|flv|mp3)$ -{ -return 403; -} - -7.设置某些类型文件的浏览器缓存时间 -location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ -{ -expires 30d; -} -location ~ .*\.(js|css)$ -{ -expires 1h; -} - -8.给favicon.ico和robots.txt设置过期时间; -这里为favicon.ico为99天,robots.txt为7天并不记录404错误日志 -location ~(favicon.ico) { -log_not_found off; -expires 99d; -break; -} -location ~(robots.txt) { -log_not_found off; -expires 7d; -break; -} - -9.设定某个文件的过期时间;这里为600秒,并不记录访问日志 -location ^~ /html/scripts/loadhead_1.js { -access_log off; -root /opt/lampp/htdocs/web; -expires 600; -break; -} - -10.文件反盗链并设置过期时间 -这里的return412 为自定义的http状态码,默认为403,方便找出正确的盗链的请求 -“rewrite ^/ http://img.linuxidc.net/leech.gif;”显示一张防盗链图片 -“access_log off;”不记录访问日志,减轻压力 -“expires 3d”所有文件3天的浏览器缓存 - -location ~*^.+\.(jpg|jpeg|gif|png|swf|rar|zip|css|js)$ { -valid_referers none blocked *.linuxidc.com*.linuxidc.net localhost 208.97.167.194; -if ($invalid_referer) { -rewrite ^/ http://img.linuxidc.net/leech.gif; -return 412; -break; -} -access_log off; -root /opt/lampp/htdocs/web; -expires 3d; -break; -} - -11.只允许固定ip访问网站,并加上密码 - -root /opt/htdocs/www; -allow 208.97.167.194; -allow 222.33.1.2; -allow 231.152.49.4; -deny all; -auth_basic “C1G_ADMIN”; -auth_basic_user_file htpasswd; - -12将多级目录下的文件转成一个文件,增强seo效果 -/job-123-456-789.html 指向/job/123/456/789.html - -rewrite^/job-([0-9]+)-([0-9]+)-([0-9]+)\.html$ /job/$1/$2/jobshow_$3.html last; - -13.文件和目录不存在的时候重定向: - -if (!-e $request_filename) { -proxy_pass http://127.0.0.1; -} - -14.将根目录下某个文件夹指向2级目录 -如/shanghaijob/ 指向 /area/shanghai/ -如果你将last改成permanent,那么浏览器地址栏显是/location/shanghai/ -rewrite ^/([0-9a-z]+)job/(.*)$ /area/$1/$2last; -上面例子有个问题是访问/shanghai时将不会匹配 -rewrite ^/([0-9a-z]+)job$ /area/$1/ last; -rewrite ^/([0-9a-z]+)job/(.*)$ /area/$1/$2last; -这样/shanghai 也可以访问了,但页面中的相对链接无法使用, -如./list_1.html真实地址是/area/shanghia/list_1.html会变成/list_1.html,导至无法访问。 -那我加上自动跳转也是不行咯 -(-d $request_filename)它有个条件是必需为真实目录,而我的rewrite不是的,所以没有效果 -if (-d $request_filename){ -rewrite ^/(.*)([^/])$ http://$host/$1$2/permanent; -} -知道原因后就好办了,让我手动跳转吧 -rewrite ^/([0-9a-z]+)job$ /$1job/permanent; -rewrite ^/([0-9a-z]+)job/(.*)$ /area/$1/$2last; - -15.域名跳转 -server -{ -listen 80; -server_name jump.linuxidc.com; -index index.html index.htm index.php; -root /opt/lampp/htdocs/www; -rewrite ^/ http://www.linuxidc.com/; -access_log off; -} - -16.多域名转向 -server_name www.linuxidc.comwww.linuxidc.net; -index index.html index.htm index.php; -root /opt/lampp/htdocs; -if ($host ~ "linuxidc\.net") { -rewrite ^(.*) http://www.linuxidc.com$1permanent; -} -``` -参考文献:[实例讲解Nginx下的rewrite规则](http://blog.csdn.net/wj291314/article/details/52277574)以及[nginx官网模块之rewrite](http://nginx.org/en/docs/http/ngx_http_rewrite_module.html) diff --git "a/nginx/\347\254\254\344\270\211\347\253\240\357\274\232nginx\346\227\245\345\277\227\347\232\204\351\205\215\347\275\256.md" "b/nginx/\347\254\254\344\270\211\347\253\240\357\274\232nginx\346\227\245\345\277\227\347\232\204\351\205\215\347\275\256.md" deleted file mode 100644 index 96a5439..0000000 --- "a/nginx/\347\254\254\344\270\211\347\253\240\357\274\232nginx\346\227\245\345\277\227\347\232\204\351\205\215\347\275\256.md" +++ /dev/null @@ -1,11 +0,0 @@ -#####配置文件路径 -`vim /etc/nginx/nginx.conf` -#####日志模块 -关系我在这个图中都标记出来了 -* ![image.png](http://upload-images.jianshu.io/upload_images/5786888-17c4f1c861715a31.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - -#####error.log 和 access_log 区别 -* error.log 按不同级别记录运行状态 -* access_log 记录请求访问状态 - -在main中可以选择自己想打印的**变量**,**变量**详情查看官网文档http://nginx.org/en/docs/syslog.html diff --git "a/nginx/\347\254\254\344\272\214\347\253\240\357\274\232nginx\351\205\215\347\275\256\346\226\207\344\273\266\350\257\246\350\247\243.md" "b/nginx/\347\254\254\344\272\214\347\253\240\357\274\232nginx\351\205\215\347\275\256\346\226\207\344\273\266\350\257\246\350\247\243.md" deleted file mode 100644 index 8d3b9b0..0000000 --- "a/nginx/\347\254\254\344\272\214\347\253\240\357\274\232nginx\351\205\215\347\275\256\346\226\207\344\273\266\350\257\246\350\247\243.md" +++ /dev/null @@ -1,336 +0,0 @@ -####nginx配置 /etc/nginx/nginx.conf -``` -######Nginx配置文件nginx.conf中文详解##### - -#定义Nginx运行的用户和用户组 -user www www; - -#nginx进程数,建议设置为等于CPU总核心数。 -worker_processes 8; - -#全局错误日志定义类型,[ debug | info | notice | warn | error | crit ] -error_log /usr/local/nginx/logs/error.log info; - -#进程pid文件 -pid /usr/local/nginx/logs/nginx.pid; - -#指定进程可以打开的最大描述符:数目 -#工作模式与连接数上限 -#这个指令是指当一个nginx进程打开的最多文件描述符数目,理论值应该是最多打开文件数(ulimit -n)与nginx进程数相除,但是nginx分配请求并不是那么均匀,所以最好与ulimit -n 的值保持一致。 -#现在在linux 2.6内核下开启文件打开数为65535,worker_rlimit_nofile就相应应该填写65535。 -#这是因为nginx调度时分配请求到进程并不是那么的均衡,所以假如填写10240,总并发量达到3-4万时就有进程可能超过10240了,这时会返回502错误。 -worker_rlimit_nofile 65535; - - -events -{ - #参考事件模型,use [ kqueue | rtsig | epoll | /dev/poll | select | poll ]; epoll模型 - #是Linux 2.6以上版本内核中的高性能网络I/O模型,linux建议epoll,如果跑在FreeBSD上面,就用kqueue模型。 - #补充说明: - #与apache相类,nginx针对不同的操作系统,有不同的事件模型 - #A)标准事件模型 - #Select、poll属于标准事件模型,如果当前系统不存在更有效的方法,nginx会选择select或poll - #B)高效事件模型 - #Kqueue:使用于FreeBSD 4.1+, OpenBSD 2.9+, NetBSD 2.0 和 MacOS X.使用双处理器的MacOS X系统使用kqueue可能会造成内核崩溃。 - #Epoll:使用于Linux内核2.6版本及以后的系统。 - #/dev/poll:使用于Solaris 7 11/99+,HP/UX 11.22+ (eventport),IRIX 6.5.15+ 和 Tru64 UNIX 5.1A+。 - #Eventport:使用于Solaris 10。 为了防止出现内核崩溃的问题, 有必要安装安全补丁。 - use epoll; - - #单个进程最大连接数(最大连接数=连接数*进程数) - #根据硬件调整,和前面工作进程配合起来用,尽量大,但是别把cpu跑到100%就行。每个进程允许的最多连接数,理论上每台nginx服务器的最大连接数为。 - worker_connections 65535; - - #keepalive超时时间。 - keepalive_timeout 60; - - #客户端请求头部的缓冲区大小。这个可以根据你的系统分页大小来设置,一般一个请求头的大小不会超过1k,不过由于一般系统分页都要大于1k,所以这里设置为分页大小。 - #分页大小可以用命令getconf PAGESIZE 取得。 - #[root@web001 ~]# getconf PAGESIZE - #4096 - #但也有client_header_buffer_size超过4k的情况,但是client_header_buffer_size该值必须设置为“系统分页大小”的整倍数。 - client_header_buffer_size 4k; - - #这个将为打开文件指定缓存,默认是没有启用的,max指定缓存数量,建议和打开文件数一致,inactive是指经过多长时间文件没被请求后删除缓存。 - open_file_cache max=65535 inactive=60s; - - #这个是指多长时间检查一次缓存的有效信息。 - #语法:open_file_cache_valid time 默认值:open_file_cache_valid 60 使用字段:http, server, location 这个指令指定了何时需要检查open_file_cache中缓存项目的有效信息. - open_file_cache_valid 80s; - - #open_file_cache指令中的inactive参数时间内文件的最少使用次数,如果超过这个数字,文件描述符一直是在缓存中打开的,如上例,如果有一个文件在inactive时间内一次没被使用,它将被移除。 - #语法:open_file_cache_min_uses number 默认值:open_file_cache_min_uses 1 使用字段:http, server, location 这个指令指定了在open_file_cache指令无效的参数中一定的时间范围内可以使用的最小文件数,如果使用更大的值,文件描述符在cache中总是打开状态. - open_file_cache_min_uses 1; - - #语法:open_file_cache_errors on | off 默认值:open_file_cache_errors off 使用字段:http, server, location 这个指令指定是否在搜索一个文件是记录cache错误. - open_file_cache_errors on; -} - - - -#设定http服务器,利用它的反向代理功能提供负载均衡支持 -http -{ - #文件扩展名与文件类型映射表 - include mime.types; - - #默认文件类型 - default_type application/octet-stream; - - #默认编码 - #charset utf-8; - - #服务器名字的hash表大小 - #保存服务器名字的hash表是由指令server_names_hash_max_size 和server_names_hash_bucket_size所控制的。参数hash bucket size总是等于hash表的大小,并且是一路处理器缓存大小的倍数。在减少了在内存中的存取次数后,使在处理器中加速查找hash表键值成为可能。如果hash bucket size等于一路处理器缓存的大小,那么在查找键的时候,最坏的情况下在内存中查找的次数为2。第一次是确定存储单元的地址,第二次是在存储单元中查找键 值。因此,如果Nginx给出需要增大hash max size 或 hash bucket size的提示,那么首要的是增大前一个参数的大小. - server_names_hash_bucket_size 128; - - #客户端请求头部的缓冲区大小。这个可以根据你的系统分页大小来设置,一般一个请求的头部大小不会超过1k,不过由于一般系统分页都要大于1k,所以这里设置为分页大小。分页大小可以用命令getconf PAGESIZE取得。 - client_header_buffer_size 32k; - - #客户请求头缓冲大小。nginx默认会用client_header_buffer_size这个buffer来读取header值,如果header过大,它会使用large_client_header_buffers来读取。 - large_client_header_buffers 4 64k; - - #设定通过nginx上传文件的大小 - client_max_body_size 8m; - - #开启高效文件传输模式,sendfile指令指定nginx是否调用sendfile函数来输出文件,对于普通应用设为 on,如果用来进行下载等应用磁盘IO重负载应用,可设置为off,以平衡磁盘与网络I/O处理速度,降低系统的负载。注意:如果图片显示不正常把这个改成off。 - #sendfile指令指定 nginx 是否调用sendfile 函数(zero copy 方式)来输出文件,对于普通应用,必须设为on。如果用来进行下载等应用磁盘IO重负载应用,可设置为off,以平衡磁盘与网络IO处理速度,降低系统uptime。 - sendfile on; - - #开启目录列表访问,合适下载服务器,默认关闭。 - autoindex on; - - #此选项允许或禁止使用socke的TCP_CORK的选项,此选项仅在使用sendfile的时候使用 - tcp_nopush on; - - tcp_nodelay on; - - #长连接超时时间,单位是秒 - keepalive_timeout 120; - - #FastCGI相关参数是为了改善网站的性能:减少资源占用,提高访问速度。下面参数看字面意思都能理解。 - fastcgi_connect_timeout 300; - fastcgi_send_timeout 300; - fastcgi_read_timeout 300; - fastcgi_buffer_size 64k; - fastcgi_buffers 4 64k; - fastcgi_busy_buffers_size 128k; - fastcgi_temp_file_write_size 128k; - - #gzip模块设置 - gzip on; #开启gzip压缩输出 - gzip_min_length 1k; #最小压缩文件大小 - gzip_buffers 4 16k; #压缩缓冲区 - gzip_http_version 1.0; #压缩版本(默认1.1,前端如果是squid2.5请使用1.0) - gzip_comp_level 2; #压缩等级 - gzip_types text/plain application/x-javascript text/css application/xml; #压缩类型,默认就已经包含textml,所以下面就不用再写了,写上去也不会有问题,但是会有一个warn。 - gzip_vary on; - - #开启限制IP连接数的时候需要使用 - #limit_zone crawler $binary_remote_addr 10m; - - - - #负载均衡配置 - upstream piao.jd.com { - - #upstream的负载均衡,weight是权重,可以根据机器配置定义权重。weigth参数表示权值,权值越高被分配到的几率越大。 - server 192.168.80.121:80 weight=3; - server 192.168.80.122:80 weight=2; - server 192.168.80.123:80 weight=3; - - #nginx的upstream目前支持4种方式的分配 - #1、轮询(默认) - #每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。 - #2、weight - #指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。 - #例如: - #upstream bakend { - # server 192.168.0.14 weight=10; - # server 192.168.0.15 weight=10; - #} - #2、ip_hash - #每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。 - #例如: - #upstream bakend { - # ip_hash; - # server 192.168.0.14:88; - # server 192.168.0.15:80; - #} - #3、fair(第三方) - #按后端服务器的响应时间来分配请求,响应时间短的优先分配。 - #upstream backend { - # server server1; - # server server2; - # fair; - #} - #4、url_hash(第三方) - #按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存时比较有效。 - #例:在upstream中加入hash语句,server语句中不能写入weight等其他的参数,hash_method是使用的hash算法 - #upstream backend { - # server squid1:3128; - # server squid2:3128; - # hash $request_uri; - # hash_method crc32; - #} - - #tips: - #upstream bakend{#定义负载均衡设备的Ip及设备状态}{ - # ip_hash; - # server 127.0.0.1:9090 down; - # server 127.0.0.1:8080 weight=2; - # server 127.0.0.1:6060; - # server 127.0.0.1:7070 backup; - #} - #在需要使用负载均衡的server中增加 proxy_pass http://bakend/; - - #每个设备的状态设置为: - #1.down表示单前的server暂时不参与负载 - #2.weight为weight越大,负载的权重就越大。 - #3.max_fails:允许请求失败的次数默认为1.当超过最大次数时,返回proxy_next_upstream模块定义的错误 - #4.fail_timeout:max_fails次失败后,暂停的时间。 - #5.backup: 其它所有的非backup机器down或者忙的时候,请求backup机器。所以这台机器压力会最轻。 - - #nginx支持同时设置多组的负载均衡,用来给不用的server来使用。 - #client_body_in_file_only设置为On 可以讲client post过来的数据记录到文件中用来做debug - #client_body_temp_path设置记录文件的目录 可以设置最多3层目录 - #location对URL进行匹配.可以进行重定向或者进行新的代理 负载均衡 - } - -``` - -#虚拟主机的配置 /etc/nginx/conf.d/default.conf -``` - server - { - #监听端口 - listen 80; - - #域名可以有多个,用空格隔开 - server_name www.jd.com jd.com; - index index.html index.htm index.php; - root /data/www/jd; - - #对******进行负载均衡 - location ~ .*.(php|php5)?$ - { - fastcgi_pass 127.0.0.1:9000; - fastcgi_index index.php; - include fastcgi.conf; - } - - #图片缓存时间设置 - location ~ .*.(gif|jpg|jpeg|png|bmp|swf)$ - { - expires 10d; - } - - #JS和CSS缓存时间设置 - location ~ .*.(js|css)?$ - { - expires 1h; - } - - #日志格式设定 - #$remote_addr与$http_x_forwarded_for用以记录客户端的ip地址; - #$remote_user:用来记录客户端用户名称; - #$time_local: 用来记录访问时间与时区; - #$request: 用来记录请求的url与http协议; - #$status: 用来记录请求状态;成功是200, - #$body_bytes_sent :记录发送给客户端文件主体内容大小; - #$http_referer:用来记录从那个页面链接访问过来的; - #$http_user_agent:记录客户浏览器的相关信息; - #通常web服务器放在反向代理的后面,这样就不能获取到客户的IP地址了,通过$remote_add拿到的IP地址是反向代理服务器的iP地址。反向代理服务器在转发请求的http头信息中,可以增加x_forwarded_for信息,用以记录原有客户端的IP地址和原来客户端的请求的服务器地址。 - log_format access '$remote_addr - $remote_user [$time_local] "$request" ' - '$status $body_bytes_sent "$http_referer" ' - '"$http_user_agent" $http_x_forwarded_for'; - - #定义本虚拟主机的访问日志 - access_log /usr/local/nginx/logs/host.access.log main; - access_log /usr/local/nginx/logs/host.access.404.log log404; - - #对 "/" 启用反向代理 - location / { - proxy_pass http://127.0.0.1:88; - proxy_redirect off; - proxy_set_header X-Real-IP $remote_addr; - - #后端的Web服务器可以通过X-Forwarded-For获取用户真实IP - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - - #以下是一些反向代理的配置,可选。 - proxy_set_header Host $host; - - #允许客户端请求的最大单文件字节数 - client_max_body_size 10m; - - #缓冲区代理缓冲用户端请求的最大字节数, - #如果把它设置为比较大的数值,例如256k,那么,无论使用firefox还是IE浏览器,来提交任意小于256k的图片,都很正常。如果注释该指令,使用默认的client_body_buffer_size设置,也就是操作系统页面大小的两倍,8k或者16k,问题就出现了。 - #无论使用firefox4.0还是IE8.0,提交一个比较大,200k左右的图片,都返回500 Internal Server Error错误 - client_body_buffer_size 128k; - - #表示使nginx阻止HTTP应答代码为400或者更高的应答。 - proxy_intercept_errors on; - - #后端服务器连接的超时时间_发起握手等候响应超时时间 - #nginx跟后端服务器连接超时时间(代理连接超时) - proxy_connect_timeout 90; - - #后端服务器数据回传时间(代理发送超时) - #后端服务器数据回传时间_就是在规定时间之内后端服务器必须传完所有的数据 - proxy_send_timeout 90; - - #连接成功后,后端服务器响应时间(代理接收超时) - #连接成功后_等候后端服务器响应时间_其实已经进入后端的排队之中等候处理(也可以说是后端服务器处理请求的时间) - proxy_read_timeout 90; - - #设置代理服务器(nginx)保存用户头信息的缓冲区大小 - #设置从被代理服务器读取的第一部分应答的缓冲区大小,通常情况下这部分应答中包含一个小的应答头,默认情况下这个值的大小为指令proxy_buffers中指定的一个缓冲区的大小,不过可以将其设置为更小 - proxy_buffer_size 4k; - - #proxy_buffers缓冲区,网页平均在32k以下的设置 - #设置用于读取应答(来自被代理服务器)的缓冲区数目和大小,默认情况也为分页大小,根据操作系统的不同可能是4k或者8k - proxy_buffers 4 32k; - - #高负荷下缓冲大小(proxy_buffers*2) - proxy_busy_buffers_size 64k; - - #设置在写入proxy_temp_path时数据的大小,预防一个工作进程在传递文件时阻塞太长 - #设定缓存文件夹大小,大于这个值,将从upstream服务器传 - proxy_temp_file_write_size 64k; - } - - - #设定查看Nginx状态的地址 - location /NginxStatus { - stub_status on; - access_log on; - auth_basic "NginxStatus"; - auth_basic_user_file confpasswd; - #htpasswd文件的内容可以用apache提供的htpasswd工具来产生。 - } - - #本地动静分离反向代理配置 - #所有jsp的页面均交由tomcat或resin处理 - location ~ .(jsp|jspx|do)?$ { - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_pass http://127.0.0.1:8080; - } - - #所有静态文件由nginx直接读取不经过tomcat或resin - location ~ .*.(htm|html|gif|jpg|jpeg|png|bmp|swf|ioc|rar|zip|txt|flv|mid|doc|ppt| - pdf|xls|mp3|wma)$ - { - expires 15d; - } - - location ~ .*.(js|css)?$ - { - expires 1h; - } - } -} -######Nginx配置文件nginx.conf中文详解##### -``` diff --git "a/nginx/\347\254\254\344\272\224\347\253\240\357\274\232nginx\351\235\231\346\200\201+\344\273\243\347\220\206+\347\274\223\345\255\230+\350\264\237\350\275\275\345\235\207\350\241\241\350\257\246\350\247\243.md" "b/nginx/\347\254\254\344\272\224\347\253\240\357\274\232nginx\351\235\231\346\200\201+\344\273\243\347\220\206+\347\274\223\345\255\230+\350\264\237\350\275\275\345\235\207\350\241\241\350\257\246\350\247\243.md" deleted file mode 100644 index c0dfced..0000000 --- "a/nginx/\347\254\254\344\272\224\347\253\240\357\274\232nginx\351\235\231\346\200\201+\344\273\243\347\220\206+\347\274\223\345\255\230+\350\264\237\350\275\275\345\235\207\350\241\241\350\257\246\350\247\243.md" +++ /dev/null @@ -1,267 +0,0 @@ -###一、静态资源WEB服务 ->非服务器动态运行生成的文件 - -配置语法-文件读取 -``` -Synatax: sendfile on| off -Default: sendfile off; -Context: http,server,location,if in location -``` -配置语法-tcp_noposh(sendfile开启时,提高网络传输效率) -``` -Syntax: tcp_nopush on|off; -Default: tcp_nopush off; -Context: http,server,location -简单点说,就是批量收集再push -``` -配置语法-tcp_nodelay -``` -Syntax: tcp_nodelay on|off; -Default: tcp_nodelay off; -Context: http,server,location -及时push,实时性传输 -``` -配置语法-压缩 -``` -Syntax: gzip on|off; -Default: gzip off; -Context: http,server,location,if in location -压缩传输,服务器端进行压缩,浏览器上自动解压 -Syntax: gzip_comp_level level; -Default: gzip_comp_level 1; -Context: http,server,location -调试压缩比例。级别 -Syntax: gzip_http_version 1.0 | 1.1; -Default: gzip_http_version 1.1; -Context: http, server, location -版本号 -``` -扩展Nginx压缩模块 - - 1. http_gzip_static_module -预读gzip功能 -``` -Syntax: gzip_static on | off | always; -Default: -gzip_static off; -Context: http, server, location -先会去磁盘查找是否有已经压缩过的文件,再确定要不要进行压缩操作。如果磁盘有,直接就发送了压缩文件给客户端。 -``` - 2. http_gunzip_module -应用支持gunzip的压缩方式(不多用) - -######实战: -配置文件(注释gzip功能) -``` - location / { - root /usr/share/nginx/html; - index index.html index.htm; - } - - location ~ .*\.(jpg|gif|png) { - #gzip on; - #gzip_http_version 1.1; - #gzip_comp_level 2; - #gzip_types image/jpeg image/gif image/png; - root /opt/app/code/images; - } - location ~ .*\.(txt|xml) { - #gzip on; - #gzip_http_version 1.1; - #gzip_comp_level 2; - #gzip_types image/jpeg image/gif image/png text/javascript text/plain; - root /opt/app/code/doc; - } - location ~ ^/download { - gzip_static on; - tcp_nopush on; - root /opt/app/code; - } - -``` -![没有使用gzip.png](http://upload-images.jianshu.io/upload_images/5786888-f80178e08e93700c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) -然后我们开启gzip,并重启nginx服务 -emmmm我给虚拟机配置了,请求头里有gzip信息,但是size没有压缩。我再找找原因,这里先不放成功后的截图。 - -配置语法 -expires(静态资源过期设置) -``` -添加Cache-Control、Expires头, -优点:可以跟服务器做实时交互 -缺点:每次都会访问服务器看是否有更新 -Syntax: expires [modified] time; - expires epoch | max| off; -Default: expires off; -Context: http,server,location,if in location -``` -![expires.png](http://upload-images.jianshu.io/upload_images/5786888-bab14fb8f1c0736f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) -######跨域访问 -允许跨域访问 -``` -Syntax: add_header Access-Control-Allow-Origin *; #星号代表所有ip都能跨域 - add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,OPTIONS; #可以跨域的方法 -Default: -- -Context: http,server,location,in in location -``` -######防盗链--防止资源被盗用-http_refer -首先,最主要的是区别那些请求是非正常的用户请求 -``` -valid_referers none blocked server_names - *.example.com example.* www.example.org/galleries/ - ~\.google\.; - -if ($invalid_referer) { - return 403; -} -``` -http://nginx.org/en/docs/http/ngx_http_referer_module.html -###二、代理服务 -正向代理 -* 对象是客户端 - -反向代理 -- 对象是服务端 -``` -location / { - proxy_pass http://localhost:8000; #将localhost的8080端口代理到监听的server - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; -} -``` -其他配置语法-缓冲区 -``` -Syntax: proxy_buffering on | off; -Default: proxy_buffering on; -Context: http, server, location -``` -其他配置语法-跳转重定向 -``` -Syntax: proxy_redirect default;proxy_redirect off;proxy_redirect redirect replacement; -Default: proxy_redirect default; -Context: http, server, location -``` -其他配置语法-头信息 -``` -Syntax: proxy_set_header field value; -Default: proxy_set_header Host $proxy_host;proxy_set_header Connection close; -Context: http, server, location -``` -其他配置语法-超时 -``` -Syntax: proxy_connect_timeout time; -Default: proxy_connect_timeout 60s; -Context: http, server, location -``` -实战: -``` -location / { - proxy_pass http://127.0.0.1:8080; - proxy_redirect default; - - proxy_set_header Host $http_host; - proxy_set_header X-Real_IP $remote_addr; - - proxy_connect_timeout 30; - proxy_send_timeout 60; - proxy_read_timeout 60; - - proxy_buffer_size 32k; - proxy_buffering on; - proxy_buffers 4 128k; - proxy_busy_buffers_size 256k; - proxy_max_temp_file_size 256k; -} -``` -###三、负载均衡调度器SLB -``` -upstream dynamic { - zone upstream_dynamic 64k; - hash $request_uri; #采用hash策略,获取uri,确保以下操作在同一服务器 - - server backend1.example.com weight=5; - server backend2.example.com:8080 fail_timeout=5s slow_start=30s; #max_fail失败后服务暂停的时间 - server 192.0.2.1 max_fails=3; #允许请求失败次数 - server backend3.example.com resolve; - server backend4.example.com service=http resolve; - - server backup1.example.com:8080 backup; - server backup2.example.com:8080 backup; #备用服务器 -} - -server { - location / { - proxy_pass http://dynamic; - health_check; - } -} -``` -|策略|介绍| -|:------:|:------:| -|轮询|按时间顺序逐一分配到不通的后端服务器| -|加权轮询|weight值越大,分配到的访问几率越高| -|ip_hash|每个请求按访问IP的hash结果分配,这样癞子同一个IP的固定访问一个沟段服务器| -|least_conn|最少连接数,哪个机器连接数少就分发| -|url_hash|按照访问的URL的hash结果来分配氢气,是每个URL丁香到同一个后端服务器| -|hash关键数值|hash自定义的key| -只有最后一个hash关键数值策略能保证用户访问后,一直停留在该服务器。 -###四、动态缓存 -######客户端缓存 - * 浏览器缓存 -######代理缓存nginx -配置语法-proxy_cache -``` -Syntax: proxy_cache zone | off; -Default: proxy_cache off; -Context: http, server, location -``` -配置语法-缓存过期周期 -``` -Syntax: proxy_cache_valid [code ...] time; -Default: — -Context: http, server, location -``` -配置语法-缓存维度 -``` -Syntax: proxy_cache_key string; -Default: proxy_cache_key $scheme$proxy_host$request_uri; -Context: http, server, location -``` -配置语法-缓存路径 -``` -Syntax: proxy_cache_path path [levels=levels] [use_temp_path=on|off] keys_zone=name:size [inactive=time] [max_size=size] [manager_files=number] [manager_sleep=time] [manager_threshold=time] [loader_files=number] [loader_sleep=time] [loader_threshold=time] [purger=on|off] [purger_files=number] [purger_sleep=time] [purger_threshold=time]; -Default: — -Context: http -``` -配置语法-部分页面不缓存 -``` -Syntax: proxy_no_cache string ...; #string 是 -Default: — -Context: http, server, location -``` -实战: -``` -upstream fantj{ - server 192.168.0.1:8081; - server 192.168.0.1:8082; - server 192.168.0.1:8083; -} -proxy_cache_path /opt/app/cache levels=1:2 keys_zone=fantj_cache:10m max_size=10g inactive=60m use_temp_path=off; - -server { - listen 80; - server_name localhost fantj.com; - ...... - location / { - proxy_cache fantj_cache; - proxy_pass http://fantj; - proxy_cache_valid 200 304 12h; - proxy_cache_valid any 10m; - proxy_cache_key $host$uri$is_args$args; - add_header Nginx-Cache "$upstream_cache_status"; - - proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504; - - include my_proxy_params; #读取自定义的proxy配置文件 - } -} -``` -如何清理指定缓存 -1. rm -rf 缓存目录 -2. 第三方模块`ngx_cache_purge` diff --git "a/nginx/\347\254\254\345\205\253\347\253\240\357\274\232nginx\345\270\270\350\247\201\351\227\256\351\242\230.md" "b/nginx/\347\254\254\345\205\253\347\253\240\357\274\232nginx\345\270\270\350\247\201\351\227\256\351\242\230.md" deleted file mode 100644 index 5c7c38b..0000000 --- "a/nginx/\347\254\254\345\205\253\347\253\240\357\274\232nginx\345\270\270\350\247\201\351\227\256\351\242\230.md" +++ /dev/null @@ -1,33 +0,0 @@ -###相同server_name多个虚拟主机优先级访问 -优先读取第一个conf文件。 -###location匹配优先级 -`=`进行普通字符精确匹配,也就是完全匹配**优先级最高** -`^~`表示普通字符匹配,使用前缀匹配**优先级最高** -`~ \~*`表示执行一个真个则匹配**优先级最低** -###try_files使用 -按顺序检查文件是否存在,存在即访问 -``` -location / { - try_files $uri $uri/ index.html; - #先访问$uri 如果宕机(404)再访问$uri/ 再访问index.html -} -``` -###nginx的alias和root区别 -alias /opt/app/image/; -root /opt/app/image/; -同:指定文件在哪个位置(路径) -异:root会根据uri路径来查找。alias不会 -例子: -我们访问 192.168.0.1/image/dog.jpg -root设置 会在 /opt/app/image/image/dog.jpg目录去找 -alias设置 会在 /opt/app/image/dog.jpg 去找 -###用什么方法传递用户的真实IP -如果客户使用多级代理来隐藏自己的ip,我们该如何获取他的真实ip呢。 -我们应该在第一级代理处做手脚,我们要求一级代理来请求时,带上初始ip请求头信息 -###压测工具ab -建议系统学习下ab工具,不然很多factors都看不懂 -``` -ab -n 2000 -c 2 http://127.0.0.1/index.html #2000次请求,每次2并发 -``` -http_load 也可以,有兴趣者自行百度。 -###系统与nginx性能优化 diff --git "a/nginx/\347\254\254\345\205\255\347\253\240\357\274\232nginx\345\256\236\347\216\260\345\212\250\351\235\231\345\210\206\347\246\273.md" "b/nginx/\347\254\254\345\205\255\347\253\240\357\274\232nginx\345\256\236\347\216\260\345\212\250\351\235\231\345\210\206\347\246\273.md" deleted file mode 100644 index 1849002..0000000 --- "a/nginx/\347\254\254\345\205\255\347\253\240\357\274\232nginx\345\256\236\347\216\260\345\212\250\351\235\231\345\210\206\347\246\273.md" +++ /dev/null @@ -1,27 +0,0 @@ -> 为什么要做动静分离呢? -减少不必要的请求消耗,减少请求延时。 - ->怎么才能做到动静分离呢? -首先我们得想什么是静态东西,什么需要动态获取。我想大家心里肯定都很清楚,图片、影视、音乐等文件一般属于静态文件,带有.jsp .ftl .do 等后缀请求的应该都是动态获取。所以我们在nginx里这样配置: - -* 假设我启动了一个tomcat服务 -``` -upstream tomcat_server{ - server 127.0.0.1:8080' -} - -server{ - listen 80; - server_name localhost; - ...... - location ~ \.jsp$ { - proxy_pass http://tomcat_server; #对.jsp请求做重定向到tomcat服务 - index index.html index.htm; - } - location ~ \.(jpg|png|gif)$ { - expires 1h; #静态缓存过期时间 - gzip on; #压缩开启 - } - ...... -} -``` diff --git "a/nginx/\347\254\254\345\233\233\347\253\240\357\274\232nginx\351\207\215\350\246\201\346\250\241\345\235\227+HTTPS+Lua\350\257\246\350\247\243.md" "b/nginx/\347\254\254\345\233\233\347\253\240\357\274\232nginx\351\207\215\350\246\201\346\250\241\345\235\227+HTTPS+Lua\350\257\246\350\247\243.md" deleted file mode 100644 index 0b3358e..0000000 --- "a/nginx/\347\254\254\345\233\233\347\253\240\357\274\232nginx\351\207\215\350\246\201\346\250\241\345\235\227+HTTPS+Lua\350\257\246\350\247\243.md" +++ /dev/null @@ -1,368 +0,0 @@ -###官方模块* -nginx -V -显示的信息就是加载的模块信息 -* #####Module(1)ngx_http_stub_status_module 本机状态 - >该ngx_http_stub_status_module模块提供对基本状态信息的访问。 -此模块不是默认生成的,应该使用--with-http_stub_status_module 配置参数启用 。 - 配置 - ``` - location / basic_status { - stub_status; - } - ``` - Syntax(句法): stub_status; - Default(默认): - - Context(语境): server, location - 实例: - 1.增加配置 - ![image.png](http://upload-images.jianshu.io/upload_images/5786888-282d5103c53c9739.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - 2.验证语法 - `nginx -tc /etc/nginx/nginx.conf` 检查文件是否有错 - 3.重载服务 - `nginx -s reload -c /etc/nginx/nginx.conf` - 4.效果查看 - 访问59.110.243.88/mystatus - ![image.png](http://upload-images.jianshu.io/upload_images/5786888-19e0f5b89f2f8abe.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - -#####Module(2) ngx_http_random_index_module 主页随机 - >该ngx_http_random_index_module模块处理以斜线字符(' /')结尾的请求,并选取一个目录中的随机文件作为索引文件。该模块在ngx_http_index_module模块之前进行处理 。 -此模块不是默认生成的,应该使用--with-http_random_index_module 配置参数启用 。 - ``` - location / { - random_index on; - } - ``` -* Directives(指令): - Syntax(语法): random_index on | off; - Default(默认): random_index off; - Context(语境): location -#####Module(3) ngx_http_sub_module 字符替换 - >The ngx_http_sub_module module is a filter that modifies a response by replacing one specified string by another. -This module is not built by default, it should be enabled with the --with-http_sub_module configuration parameter. - -* Example Configuration -``` - location / { - sub_filter '该ngx_http_limit_conn_module模块用于**限制**每个定义的密钥的连接数量,特别是来自单个IP地址的连接数量。 -并非所有连接都被计算在内 只有在服务器处理请求并且已经读取了整个请求头时才计算连接。 - * 一次只允许每个IP地址一个连接。 - ``` - limit_conn_zone $binary_remote_addr zone=addr:10m; - server { - location /download/ { - limit_conn addr 1; - } - ``` - * 可能有几个limit_conn指令。例如,以下配置将限制每个客户端IP到服务器的连接数,同时限制连接到虚拟服务器的总数: - ``` - limit_conn_zone $ binary_remote_addr zone = perip:10m; - limit_conn_zone $ server_name zone = perserver:10m; - server{ - ... - limit_conn perip 10; - limit_conn perserver 100; - } - ``` - * Directives - Syntax: limit_conn zone number; -Default: — -Context: http, server, location - - * [详细官方链接 ](http://nginx.org/en/docs/http/ngx_http_limit_conn_module.html) -* #####Module(5) ngx_http_limit_req_module 请求限制 - >所述ngx_http_limit_req_module模块(0.7.21)用于**限制每一个定义的密钥的请求的处理速率**,特别是从一个单一的IP地址的请求的处理速率。限制是使用“泄漏桶”方法完成的。 - - * 平均每秒不超过1个请求,连发数不超过5个请求。 - ``` - limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s; - server { - location /search/ { - limit_req zone=one burst=5; - } - ``` - * 可能有几个limit_req指令。例如,以下配置将限制来自单个IP地址的请求的处理速率,同时限制虚拟服务器的请求处理速率: - ``` - limit_req_zone $binary_remote_addr zone=perip:10m rate=1r/s; - limit_req_zone $server_name zone=perserver:10m rate=10r/s; - server { - ... - limit_req zone=perip burst=5 nodelay; - limit_req zone=perserver burst=10; - } - ``` - * Directives - Syntax: limit_req zone=name [burst=number] [nodelay]; -Default: — -Context: http, server, location - * [详细官方链接 ](http://nginx.org/en/docs/http/ngx_http_limit_req_module.html) - -* #####Module(6) ngx_http_access_module 限制访问某些客户端地址。 - >该ngx_http_access_module模块允许限制访问某些客户端地址。访问也可以通过密码子请求结果或JWT来限制满足控制地址和密码的同时访问限制 - - **具有局限性,因为用户一旦通过代理来访问的话,是阻止不了的。http_x_forwarded_for头信息控制访问 会更好的解决该问题,它要求访问时必须带上所有用到的ip的地址信息** -######局限性解决方法总结: - 方法一: 采用http头信息控制访问,如HTTP_X_FORWARD_FOR - 方法二: 结合geo模块 - 方法三: 通过HTTP自定义变量传递 - * Example Configuration - ``` - location / { - deny 192.168.1.1; - allow 192.168.1.0/24; - allow 10.1.1.0/16; - allow 2001:0db8::/32; - deny all; - } - - 实例: - location ~ ^/admin.html{ - root /usr/share/nginx; - deny 192.168.1.1;#访问admin.html需要限制的ip - allow all; - index index.html index.htm; - } - ``` - * 规则按顺序检查,直到找到第一个匹配。在本例中,只允许IPv4网络访问 `10.1.1.0/16``192.168.1.0/24` 不包括地址`192.168.1.1`和IPv6网络访问`2001:0db8::/32`在很多规则的情况下,使用模块变量是可取的。 - ``` - Syntax: allow address | CIDR | unix: | all; - Default: — - Context: http, server, location, limit_except - ``` - * 允许访问指定的网络或地址。如果unix:指定了特殊值(1.5.1),则允许访问所有UNIX域套接字。 - 句法: deny address | CIDR | unix: | all; - 默认: - - 语境: http,server,location,limit_except - - * [详细官方链接 ](http://nginx.org/en/docs/http/ngx_http_access_module.html) - -* #####Module(7) ngx_http_auth_basic_module - >该ngx_http_auth_basic_module模块允许通过使用“HTTP基本认证”协议验证用户名和密码来限制对资源的访问。访问也可以通过地址,jwt。满足指令控制地址和密码的同时访问限制。 - -在这里首先安装`htpasswd` -######局限性: - 一: 用户信息依赖文件 - 二: 操作管理机械,效率低 -######解决方式: - 一: nginx结合LUA实现高效验证 - 二: nginx配合LDAP打通,利用nginx-auth-ldap模块 - * Example Configuration - ``` - location / { - auth_basic "closed site"; - auth_basic_user_file conf/htpasswd; - } - 实例: - - location ~ ^/admin.html{ - root /usr/share/nginx; - auth_basic "please input your password!"#这里随便写字符串 - auth_basic_user_file /etc/nginx/password; #htpasswd生成的密码所在文件路径 - index index.html index.htm; - } - 然后wq ->语法检查 nginx -t -c /etc/nginx/nginx.conf - ``` - * 使用“HTTP基本验证”协议验证用户名和密码。指定的参数用作a realm。参数值可以包含变量(1.3.10,1.2.7)。特殊值off允许取消auth_basic从先前配置级别继承的指令的效果。 - ``` - Syntax: auth_basic string | off; - Default: - auth_basic off; - Context: http, server, location, limit_except - - ``` - * 指定一个保存用户名和密码的文件,格式如下: -\# comment -name1:password1 -name2:password2:comment -name3:password3 -``` - Syntax: auth_basic_user_file file; - Default: — - Context: http, server, location, limit_except -``` - - * [详细官方链接 ](http://nginx.org/en/docs/http/ngx_http_auth_basic_module.html) - -* #####Module(8) ngx_http_addition_module - >该ngx_http_addition_module模块是一个过滤器,在响应之前和之后添加文本。此模块不是默认生成的,应该使用--with-http_addition_module 配置参数启用 。 - - * Example Configuration - ``` - location / { - add_before_body /before_action; - add_after_body /after_action; - } - ``` - * 在响应主体之前添加处理给定子请求的结果返回的文本。""作为参数的空字符串()会取消从前一个配置级别继承的加法。 - ``` - 句法: add_before_body uri; - 默认: - - 语境: http,server,location - ``` - * 在响应主体之后添加由于处理给定的子请求而返回的文本。""作为参数的空字符串()取消了从以前的配置级别继承的加法。 - 句法: add_after_body uri; - 默认: - - 语境: http,server,location - * 允许在指定的MIME类型的响应中添加文本,除了“ text/html”。特殊值“ *”匹配任何MIME类型(0.8.29)。 - 句法: addition_types mime-type ...; - 默认: addition_types text / html; - 语境: http,server,location - 该指令出现在0.7.9版本中。 - * [详细官方链接 ](http://nginx.org/en/docs/http/ngx_http_addition_module.html) - -* #####Module(9) ngx_http_slice_module-大文件分片请求 -* 优势:每个自请求收到的数据都会形成一个独立文件,一个请求断了,其他请求不受影响。 -* 缺点:当文件很大或者slice很小的时候,可能会导致文件描述符耗尽等情况 -``` -location / { - slice 1m; - proxy_cache cache; - proxy_cache_key $uri$is_args$args$slice_range; - proxy_set_header Range $slice_range; - proxy_cache_valid 200 206 1h; - proxy_pass http://localhost:8000; -} -``` -###高级模块篇 -* #####Module(10) ngx_http_secure_link_module - >该ngx_http_secure_link_module模块(0.7.18)用于检查请求链接的真伪,保护资源免受未经授权的访问,并限制连杆的寿命。 -请求的链接的真实性通过将请求中传递的校验和值与为请求计算的值进行比较来验证。如果链接的使用期限有限且时间已过,则认为该链接已过时。这些检查的状态在$secure_link变量中可用 。 -该模块提供两种替代操作模式。第一种模式由secure_link_secret指令启用,用于检查请求的链接的真实性,并保护资源免受未经授权的访问。第二种模式(0.8.50)由secure_link和secure_link_md5指令启用, 也用于限制链接的生存期。 - -此模块不是默认生成的,应该使用--with-http_secure_link_module 配置参数启用 。 - - * Example Configuration - ``` -location /s/ { - secure_link $arg_md5,$arg_expires; - secure_link_md5 "$secure_link_expires$uri$remote_addr secret"; - - if ($secure_link = "") { - return 403; - } - - if ($secure_link = "0") { - return 410; - } - - ... -} ---------------------------------分割线---------------------------------------------- -location /p/ { - secure_link_secret secret; - - if ($secure_link = "") { - return 403; - } - - rewrite ^ /secure/$secure_link; -} - -location /secure/ { - internal; -} - ``` - * 定义一个带有变量的字符串,从中提取链接的校验值和生存期。 - ``` -句法: secure_link expression; -默认: - -语境: http,server,location - ``` - * 定义word用于检查所请求的链接的真实性的秘密。 -``` -句法: secure_link_secret word; -默认: - -语境: location -``` - * 定义一个计算MD5哈希值的表达式,并将其与请求中传递的值进行比较。 -``` -句法: secure_link_md5 expression; -默认: - -语境: http,server,location -``` - - * [详细官方链接 ](http://nginx.org/en/docs/http/ngx_http_secure_link_module.html) -* #####Module(11) ngx_http_geoip_module - * 使用场景: - 1. 区别国内外作HTTP访问规则 - 2. 区别国内城市地域作HTTP访问规则 - * 该模块需要下载MaxMind库(存放世界的ip信息) -``` -location / { - if ($geoip_country_code != CN){ - return 403; #如果ip不是中国的,就返回403 - } -} -``` - * [详细官方链接 ](http://nginx.org/en/docs/http/ngx_http_geoip_module.html) -* #####Module(12) 配置HTTPS服务器 - * 为什莫要用HTTPS?: - 1. HTTP传输数据可以被中间人盗用 - 2. 数据被劫持篡改 - 3. 同时用到对称加密+非对称加密 - * 该模块需要安装openssl (用于生成密钥和CA证书) -``` -cd /etc/nginx -mkdir ssl_key -openssl genrsa -idea -out fantj.key 1024 - enter pass phrase for fantj.key: ------- -openssl req -new -key fantj.key -out fant.csr ------- -openssl x509 -req -days 3650 -in fantj.csr -signkey fantj.key -out fantj.crt #打包成crt文件 - -server { - listen 443 ssl; - server_name 127.0.0.1:8080; - ssl_certificate /etc/nginx/ssl_key/fantj.crt; - ssl_certificate_key /etc/nginx/ssl_key/fantj.key; - ssl_protocols TLSv1 TLSv1.1 TLSv1.2; - ssl_ciphers HIGH:!aNULL:!MD5; - ... -} -``` - * HTTPS服务优化 - 1. 激活keepalive长连接 - 2. 设置ssl session缓存 -``` -http { - ssl_session_cache shared:SSL:10m; #设置缓存 - ssl_session_timeout 10m; #设置过期时间 - - server { - listen 443 ssl; - server_name www.example.com; - keepalive_timeout 70; #保持连接时间 - - ssl_certificate www.example.com.crt; - ssl_certificate_key www.example.com.key; - ssl_protocols TLSv1 TLSv1.1 TLSv1.2; - ssl_ciphers HIGH:!aNULL:!MD5; - ... -``` - * [详细官方链接 ](http://nginx.org/en/docs/http/configuring_https_servers.html) -* #####Module(13) nginx+Lua 实现灰度发布 - >灰度发布(又名金丝雀发布)是指在黑与白之间,能够平滑过渡的一种发布方式。在其上可以进行A/B testing,即让一部分用户继续用产品特性A,一部分用户开始用产品特性B,如果用户对B没有什么反对意见,那么逐步扩大范围,把所有用户都迁移到B上面来。灰度发布可以保证整体系统的稳定,在初始灰度的时候就可以发现、调整问题,以保证其影响度。 - * 安装lua `yum install lua` - * 运行lua `lua; print("hello");` - * 注释 `--行注释 --[[ --]]块注释` - * 基础语法 http://www.runoob.com/lua/lua-tutorial.html - * nginx&lua wiki https://www.nginx.com/resources/wiki/modules/lua/ -``` -location / { - default_type "text/html"; - content_by_lua_file /opt/app/lua/dep.lua; #将根目录请求交给dep.lua过滤处理 -} -至于dep.lua源码,大家尽可在百度中得到,在这里我不做重点,以后可能会单独作为一章。 -bacause重点是模块讲解,so这里提纲挈领下。 -~sorry -``` diff --git "a/redis/\347\254\254\344\270\203\347\253\240\357\274\232Redis-\344\275\215\345\233\276bitmap&\345\237\272\346\225\260\347\273\237\350\256\241HyperLogLog.md" "b/redis/\347\254\254\344\270\203\347\253\240\357\274\232Redis-\344\275\215\345\233\276bitmap&\345\237\272\346\225\260\347\273\237\350\256\241HyperLogLog.md" deleted file mode 100644 index 190740a..0000000 --- "a/redis/\347\254\254\344\270\203\347\253\240\357\274\232Redis-\344\275\215\345\233\276bitmap&\345\237\272\346\225\260\347\273\237\350\256\241HyperLogLog.md" +++ /dev/null @@ -1,21 +0,0 @@ -###1. 什么是位图 - redis可以直接对数据进行位操作。 -![获取hello二进制的第0位.png](http://upload-images.jianshu.io/upload_images/5786888-2f9d8bff67103715.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - -###2. 实例 -1. setbit key offset value #给位图指定索引设置值 - 上面我们给hello赋值为world,那么我们现在把它的二进制第0位改成1,再进行get hello - ![setbit .png](http://upload-images.jianshu.io/upload_images/5786888-431b1d4f1aea4dbd.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) -2. getbit key offset #获取第offset位的二进制 -3. bitcount key [start end] #获取位图指定范围中(start 到end)1的个数 -4. bitop op destkey key [key...] #做多个Bitmap的and(交集)、or(并集)、not(非)、xor(异或)操作并将结果保存在destkey中 -5. bitpos key targetBit [start] [end] #计算位图指定范围(start)到(end)的位置 - -###1. 什么是Hyperloglog - 极小空间完成独立数量统计。本质是个string。千万级别的存储只会消耗极少的内存(几Mb),但是错误率比较高(0.81%) -###2. 三个命令 -* pfadd key element [element... ] # 向hyperloglog添加元素 -* pfcount key [key...] #计算hyperloglog 的独立总数 -* pfmerge destkey sourcekey [sourcekey...] #合并多个hyperloglog -###3. 实例 -![image.png](http://upload-images.jianshu.io/upload_images/5786888-ebab061df9f77811.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) diff --git "a/redis/\347\254\254\344\270\211\347\253\240\357\274\232jedis\347\232\204\344\275\277\347\224\250.md" "b/redis/\347\254\254\344\270\211\347\253\240\357\274\232jedis\347\232\204\344\275\277\347\224\250.md" deleted file mode 100644 index 7f58f1a..0000000 --- "a/redis/\347\254\254\344\270\211\347\253\240\357\274\232jedis\347\232\204\344\275\277\347\224\250.md" +++ /dev/null @@ -1,135 +0,0 @@ -###Jedis是什么? -那jedis就是集成了redis的一些命令操作,封装了redis的java客户端。提供了连接池管理。一般不直接使用jedis,而是在其上在封装一层,作为业务的使用。如果用spring的话,可以看看spring 封装的 redis [Spring Data Redis](https://link.zhihu.com/?target=http%3A//projects.spring.io/spring-data-redis/) -·###Jedis基本使用 -``` -//生成一个Jedis对象,这个对象负责和指定Redis节点进行通信 -Jedis jedis = new Jedis("127.0.0.1",6379); -//jedis执行set操作 -jedis.set("hello","world"); -//jedis执行get操作 -String value = jedis.get("hello"); -//incr key 自增1,如果key不存在,自增后get(key)=1 -jedis.incr("counter"); -//2.hash -jedis.hset("myhash","f1","v1"); -jedis.hset("myhash","f2","v2"); -//hash输出结果 -jedis.hgetAll("myhash"); -//3.list -jedis.rpush("mylist","1"); -jedis.rpush("mylist","2"); -jedis.rpush("mylist","3"); -//输出结果[1,2,3] -jedis.lrange("mylist",0,-1); -//4.set -jedis.sadd("myset","a"); -jedis.sadd("myset","b"); -jedis.sadd("myset","c"); -//输出结果 -jedis.smembers("myset"); -//5.zset -jedis.zadd("myzset",1,"jiao"); -jedis.zadd("myzset",2,"fant"); -jedis.zadd("myzset",3,"j"); -//输出结果 -jedis.zrangeWithScores("myzset",0,-1); -``` -###Jedis 连接池 -1. 直接连接 - * 优点:简单方便,适用于少量长期连接 - * 缺点:存在每次新建/关闭TCP开销;资源无法控制,存在连接泄露可能;线程不安全 -2. Jedis连接池 - * 优点:Jedis预先生成,降低开销使用;连接池的形式保护和控制资源的使用 - * 缺点:相对于直连,使用相对麻烦,尤其在资源的管理上需要很多参数来保证,一旦规划不合理也会出现问题。 - -Redis连接池工具类 `RedisPool.java` -``` -package com.answer.admin.util.redis; - -import redis.clients.jedis.Jedis; -import redis.clients.jedis.JedisPool; -import redis.clients.jedis.JedisPoolConfig; - -/** - * Redis连接池工具 - * Created by Fant.J. - * 2017/10/24 20:55 - */ -public class RedisPool { - - private static JedisPool jedisPool = null; - private static Jedis jedis; - static { - jedis = getJedisPool().getResource(); - } - /** - * 构建redis连接池 - */ - public static JedisPool getJedisPool(){ - if (jedisPool == null){ - JedisPoolConfig config = new JedisPoolConfig(); - config.setMaxTotal(1024);//可用连接实例的最大数目,如果赋值为-1,表示不限制 - config.setMaxIdle(5);// 控制一个Pool最多有多少个状态为idle(空闲的)jedis实例,默认值也是8 - config.setMaxWaitMillis(1000*100);// 等待可用连接的最大时间,单位毫秒,默认值为-1,表示永不超时/如果超过等待时间,则直接抛出异常 - config.setTestOnBorrow(true);// 在borrow一个jedis实例时,是否提前进行validate操作,如果为true,则得到的jedis实例均是可用的 - jedisPool = new JedisPool(config, "192.168.218.129", 6379); - } - return jedisPool; - } - /** - * 释放redis资源 - */ - public static void returnResource(Jedis jedis){ - if(jedis != null){ - jedis.close(); - } - } -} - -``` -使用 -``` -package com.answer.admin.util.redis; - -import org.junit.Test; -import org.springframework.dao.DataAccessException; -import org.springframework.data.redis.connection.RedisConnection; -import org.springframework.data.redis.core.RedisCallback; -import org.springframework.data.redis.core.RedisTemplate; -import redis.clients.jedis.Jedis; -import redis.clients.jedis.JedisPool; - -/** - * Created by Fant.J. - * 2017/10/24 20:48 - */ - -public class RedisTest { - /** 普通用法 */ - @Test - public void redisLearn(){ - Jedis jedis = new Jedis("192.168.218.129",6379); - String result = jedis.get("hello"); - System.out.println(result); - } - /** 连接池用法,切记操作结束要施放资源 */ - @Test - public void redisPoolLearn(){ - String value = "hello"; - String result = null; - Jedis jedis = null; - try { - JedisPool jedisPool = RedisPool.getJedisPool(); - jedis = jedisPool.getResource(); - result = jedis.get(value); - }catch (Exception e){ - RedisPool.returnResource(jedis);//施放资源 - e.printStackTrace(); - }finally { - RedisPool.returnResource(jedis); - } - System.out.println(result); - } -} - -``` diff --git "a/redis/\347\254\254\344\271\235\347\253\240\357\274\232Redis-replication-\344\270\273\344\273\216\345\244\215\345\210\266.md" "b/redis/\347\254\254\344\271\235\347\253\240\357\274\232Redis-replication-\344\270\273\344\273\216\345\244\215\345\210\266.md" deleted file mode 100644 index ff96809..0000000 --- "a/redis/\347\254\254\344\271\235\347\253\240\357\274\232Redis-replication-\344\270\273\344\273\216\345\244\215\345\210\266.md" +++ /dev/null @@ -1,133 +0,0 @@ -###什么是主从复制 -1. 一个master可以有多个slave -2. 一个slave只能有一个master -3. 数据流是单向的,master到slave - -###全量复制和部分复制 -#####run id 查看复制偏移量(用来比对两边数据同步问题,相差不能太大) -1. 插一个命令`redis-cli -p 6379 info server | grep run`查看redis运行id -![image.png](http://upload-images.jianshu.io/upload_images/5786888-44546be6e6316132.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) -2. 查看复制偏移量 -![image.png](http://upload-images.jianshu.io/upload_images/5786888-47255a9e77985fa4.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) -#####全量复制 -在Redis复制的基础上,使用和配置主从复制非常简单,它允许从属Redis服务器成为主服务器的精确副本。每次链路断开时,从设备将自动重新连接到主设备,无论主设备发生什么情况,都将试图成为主设备的精确副本。 - -这个系统使用三个主要机制: -1. 当主和从实例连接良好时,主设备通过发送命令流来保持从设备更新,以便复制对主数据集中发生的数据集的影响:客户机写入,密钥过期或被驱逐等等。 -2. 当主站和从站之间的链路断开时,对于网络问题或者由于在主站或从站中感测到超时,从站重新连接并尝试进行部分重新同步:这意味着它将尝试只获取部分在断开连接时错过的命令流。 -3. 当部分重新同步不可能时,从机将要求完全重新同步。这将涉及一个更复杂的过程,在这个过程中,主机需要创建所有数据的快照,将数据发送给从机,然后在数据集更改时继续发送命令流。 - -Redis默认使用异步复制,这是高延迟和高性能,是绝大多数Redis用例的自然复制模式。 但是,Redis从站会异步确认主站定期收到的数据量。 - -某些数据的同步复制可以由客户端使用WAIT命令来请求。 但WAIT只能确保在其他Redis实例中具有指定数量的已确认副本:在故障转移期间出于不同原因的故障转移期间,确认写入仍可能丢失,或取决于Redis持久性的确切配置。 您可以检查Sentinel或Redis群集文档以获取有关高可用性和故障转移的更多信息。 本文的其余部分主要描述了Redis基本复制的基本特征。 - -以下是关于Redis复制的一些非常重要的事实: -* Redis使用异步复制,异步从到主机确认处理的数据量。 - -* 主人可以有多个奴隶。 - -* 从站能够接受来自其他从站的连接。除了将多个从站连接到同一个主站之外,从站也可以以层叠状结构连接到其他从站。自从Redis 4.0以来,所有的子从服务器都会收到与主服务器完全相同的复制流。 - -* Redis复制在主端是非阻塞的。这意味着当一个或多个从机执行初始同步或部分重新同步时,主机将继续处理查询。 - -* 复制在很大程度上也是非阻塞的。从服务器执行初始同步时,它可以使用旧版本的数据集处理查询,假定您在redis.conf中配置了Redis。否则,如果复制流已关闭,则可以将Redis从属程序配置为向客户端返回错误。但是,在初始同步之后,必须删除旧数据集,并且必须加载新数据集。从站将在这个简短的窗口中阻塞传入的连接(对于非常大的数据集可能只有很多秒)。自Redis 4.0以来,可以配置Redis,以便旧数据集的删除发生在不同的线程中,但是加载新的初始数据集仍然会在主线程中发生并阻止从属。 - -* 复制既可用于可伸缩性,也可用于只读查询的多个从站(例如,可将低速O(N)操作卸载到从站),或者仅用于数据安全。 - -* 可以使用复制来避免让主服务器将完整数据集写入磁盘的成本:一种典型的技术包括配置主服务器redis.conf以避免永久保存到磁盘,然后连接配置为不时保存的从服务器或启用AOF。然而,这个设置必须小心处理,因为重新启动的主设备将从一个空数据集开始:如果从设备尝试与其同步,则从设备也将被清空。 -#####当master宕机的复制安全 -在使用Redis复制的设置中,强烈建议在主服务器和从服务器中启用持久性。如果这种情况不可行,例如由于磁盘速度非常慢导致的延迟问题,则应配置实例以避免重新启动后自动重新启动。 - -为了更好地理解关闭配置为自动重启的主设备是否危险的原因,请检查以下故障模式,其中数据从主设备及其所有从设备擦除: - -1. 我们有一个设置,节点A作为主节点,持久性关闭,节点B和C从节点A复制。 -2. 节点A崩溃,但它有一些自动重新启动系统,重新启动过程。但是由于持久性被关闭,节点将重新启动一个空的数据集。 -3. 节点B和C将从节点A复制,节点A是空的,所以它们将有效地销毁它们的数据副本。 -当Redis Sentinel用于高可用性时,关闭主服务器上的持久性以及进程的自动重启也是危险的。例如,主机可以很快重启,Sentinel不会检测到故障,以便发生上述故障模式。 - -每次数据安全很重要,复制与配置为无持久性的主站一起使用时,应禁用实例的自动重启。 - -当Redis Sentinel用于高可用性时,关闭主服务器上的持久性以及进程的自动重启也是危险的。 例如,主机可以很快重启,Sentinel不会检测到故障,以便发生上述故障模式。 - -每次数据安全很重要,复制与配置为无持久性的主站一起使用时,应禁用实例的自动重启。 -#####Redis复制如何工作 -每个Redis master都有一个复制ID:它是一个大的伪随机字符串,标记数据集的给定故事。 每个主设备也会获得一个偏移量,该设置为生成的每个复制流字节增加以发送到从设备,以便使用修改数据集的新更改更新从设备的状态。 即使没有从机连接,复制偏移量也会增加,所以基本上每一对给定的: -识别主数据集的确切版本。 - -当从站连接到主站时,它们使用PSYNC命令来发送它们的旧主站复制ID以及到目前为止处理的偏移量。这样主人可以发送所需的增量部分。但是,如果主缓冲区中没有足够的积压,或者从服务器引用了不再知道的历史记录(复制标识),则会发生完全重新同步:在这种情况下,从服务器将获得数据集的完整副本, 从头开始​​。 - -####这是完全同步如何更详细地工作: - -* 主人开始一个后台保存过程,以生成一个RDB文件。同时开始缓冲从客户端收到的所有新的写入命令。当后台保存完成后,主服务器将数据库文件传输给从服务器,将其保存到磁盘上,然后将其加载到内存中。主机会将所有缓冲的命令发送给从机。这是作为命令流完成的,并且与Redis协议本身的格式相同。 - -* 你可以通过telnet自己尝试一下。在服务器正在做一些工作的同时连接到Redis端口并发出SYNC命令。您将看到一个批量传输,然后主机接收到的每个命令都将在远程登录会话中重新发出。实际上,SYNC是一个旧的协议,不再由较新的Redis实例使用,但仍然存在向后兼容性:它不允许部分重新同步,所以现在使用PSYNC。 - -* 如前所述,当主从链路出于某种原因关闭时,从站能够自动重新连接。如果主站收到多个并发的从站同步请求,它将执行一次后台保存,以便为它们提供全部服务。 -####无盘复制 -通常情况下,完全重新同步需要在磁盘上创建一个RDB文件,然后从磁盘重新加载相同的RDB,以便向从属设备提供数据。 - -对于较慢的磁盘,对于主设备来说这可能是一个非常紧张的操作。 Redis 2.8.18版是第一个支持无盘复制的版本。 在此设置中,子进程直接通过线将RDB发送到从服务器,而不使用磁盘作为中间存储。 -####全量复制开销 -* bgsave时间 -* RDB文件网络传输时间 -* 从节点清空数据时间 -* 从节点加载RDB时间 -* 可能的AOF重写时间 -###部分复制 -连接断开时,master会写一个复制缓冲区的命令,slave再连接master时候会把自己的偏量值offset和runid告诉master,如果丢失数据在buffer缓存的一个范围内,则master把缓冲区队列的数据给slave。然后再把部分数据同步给slave -###复制的配置 -#####slaveof命令 -1. 复制命令 -``` -slaveof 127.0.0.1 6380 -``` -丢弃旧数据集,转而开始对新主服务器进行同步。 -2. 取消复制 -``` -slaveof no one -``` -将使得这个从属服务器关闭复制功能,并从从属服务器转变回主服务器,原来同步所得的数据集不会被丢弃。 -#####配置 -主要修改4个参数: -1. port; -2. logfile; -3. slaveof; -4. pidfile; -5. daemonize(是否在后台执行) -``` -然后再添加配置 -slaveof ip port -slave-read-only yes #设置只读 -``` -#####两种方式比较 -|方式|命令|配置| -|:-----:|:-----:|:-----:| -|优点|无需重启|统一配置| -|缺点|不便于管理|需要重启| -#####实战 -1. 我配置了一个端口为6379,和端口为6380的服务,并在后台启动 -``` -#6379配置 -port 6379 -pidfile /var/run/redis_6379.pid -# slaveof -logfile "6379.log" -daemonize yes - - -#6380配置 -port 6380 -pidfile /var/run/redis_6380.pid -slaveof 127.0.0.1 6379 -masterauth xxxxx #如果master有密码,则需要设置 -logfile "6380.log" -daemonize yes -``` -2. ![对6379执行命令 info replication](http://upload-images.jianshu.io/upload_images/5786888-a77e459bd1bab286.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) -![对6380执行命令 info replication](http://upload-images.jianshu.io/upload_images/5786888-c080e69e8d53d2f1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) -3. 然后我在主机上set一个东西,在slave上获取 -![image.png](http://upload-images.jianshu.io/upload_images/5786888-5e07c1d1ce06ce44.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - -4. 同步数据 - -在master的cli中执行`slaveof 127.0.0.1 6380` diff --git "a/redis/\347\254\254\344\272\214\347\253\240\357\274\232redis\345\237\272\347\241\200.md" "b/redis/\347\254\254\344\272\214\347\253\240\357\274\232redis\345\237\272\347\241\200.md" deleted file mode 100644 index 9d4e84a..0000000 --- "a/redis/\347\254\254\344\272\214\347\253\240\357\274\232redis\345\237\272\347\241\200.md" +++ /dev/null @@ -1,233 +0,0 @@ -### 1. Redis可执行文件说明 -1. redis-server redis服务器 -2. redis-cli Redis命令行客户端 - redis-cli -h 10.10.10.10 -p 6384 连接ip为x上的redis -3. redis-benchmark 性能测试工具 -4. redis-check-aof AOF文件修复工具 -5. redis-check-dump RDB文件检查工具 -6. redis-sentinel 高可用 -### 2. 启动方法 -1. 最简启动 - redis-server -2. 带参数启动 - redis-server --port xxxx -3. 配置文件启动(首选) - redis-server configpath -### 3. 常用配置 -1. daemonize 是否守护进程,默认no建议 yes -2. port 默认6379 -3. logfile -4. dir -### 4. 常用命令 - -1. keys * 计算所有的键 -2. dbsize 计算数据库大小 -3. exists key -4. del key [key] -5. expire key seconds 设置key过期时间 ttl key 查看剩余过期时间 persist key 去掉key过期时间 -6. type key -7. info 查看redis基本信息,(查看工作空间详情) -8. select 1 转到1工作空间 -9. save 手动触发备份,该命令将在 redis 安装目录中创建dump.rdb文件。 -10. bgsave 该命令在后台执行save -12. expire a 10 给a设置10秒生存期 -13. ttl a 查看a还有多久过期 -14. rename a b 把a的key 更名为b,b存在的话会被覆盖 -15. renamenx a b 把把a的key 更名为b,如果b存在,则不修改(nx后缀就是具有判断逻辑) - - -### 5. Redis的五大数据结构 -###### 5.1 字符串(String) - -``` -1 SET key value -设置指定 key 的值 -2 GET key -获取指定 key 的值。 -3 GETRANGE key start end -返回 key 中字符串值的子字符 -4 GETSET key value -将给定 key 的值设为 value ,并返回 key 的旧值(old value)。 -5 GETBIT key offset -对 key 所储存的字符串值,获取指定偏移量上的位(bit)。 -6 MGET key1 [key2..] -获取所有(一个或多个)给定 key 的值。 -7 SETBIT key offset value -对 key 所储存的字符串值,设置或清除指定偏移量上的位(bit)。 -8 SETEX key seconds value -将值 value 关联到 key ,并将 key 的过期时间设为 seconds (以秒为单位)。 -9 SETNX key value -只有在 key 不存在时设置 key 的值。 -10 SETRANGE key offset value -用 value 参数覆写给定 key 所储存的字符串值,从偏移量 offset 开始。 -11 STRLEN key -返回 key 所储存的字符串值的长度。 -12 MSET key value [key value ...] -同时设置一个或多个 key-value 对。 -13 MSETNX key value [key value ...] -同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在。 -14 PSETEX key milliseconds value -这个命令和 SETEX 命令相似,但它以毫秒为单位设置 key 的生存时间,而不是像 SETEX 命令那样,以秒为单位。 -15 INCR key -将 key 中储存的数字值增一。 -16 INCRBY key increment -将 key 所储存的值加上给定的增量值(increment) 。 -17 INCRBYFLOAT key increment -将 key 所储存的值加上给定的浮点增量值(increment) 。 -18 DECR key -将 key 中储存的数字值减一。 -19 DECRBY key decrement -key 所储存的值减去给定的减量值(decrement) 。 -20 APPEND key value -如果 key 已经存在并且是一个字符串, APPEND 命令将 指定value 追加到改 key 原来的值(value)的末尾。 -``` - - -###### 5.2 哈希(Hash) ->Redis hash 是一个string类型的field和value的映射表,hash特别适合用于存储对象。 -``` - -1 HDEL key field1 [field2] -删除一个或多个哈希表字段 -2 HEXISTS key field -查看哈希表 key 中,指定的字段是否存在。 -3 HGET key field -获取存储在哈希表中指定字段的值。 -4 HGETALL key -获取在哈希表中指定 key 的所有字段和值 -5 HINCRBY key field increment -为哈希表 key 中的指定字段的整数值加上增量 increment 。 -6 HINCRBYFLOAT key field increment -为哈希表 key 中的指定字段的浮点数值加上增量 increment 。 -7 HKEYS key -获取所有哈希表中的字段 -8 HLEN key -获取哈希表中字段的数量 -9 HMGET key field1 [field2] -获取所有给定字段的值 -10 HMSET key field1 value1 [field2 value2 ] -同时将多个 field-value (域-值)对设置到哈希表 key 中。 -11 HSET key field value -将哈希表 key 中的字段 field 的值设为 value 。 -12 HSETNX key field value -只有在字段 field 不存在时,设置哈希表字段的值。 -13 HVALS key -获取哈希表中所有值 -14 HSCAN key cursor [MATCH pattern] [COUNT count] -迭代哈希表中的键值对。 -``` -###### 5.3 列表(List) -``` -1 BLPOP key1 [key2 ] timeout -移出并获取列表的第一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。 -2 BRPOP key1 [key2 ] timeout -移出并获取列表的最后一个元素, 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。 -3 BRPOPLPUSH source destination timeout -从列表中弹出一个值,将弹出的元素插入到另外一个列表中并返回它; 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止。 -4 LINDEX key index -通过索引获取列表中的元素 -5 LINSERT key BEFORE|AFTER pivot value -在列表的元素前或者后插入元素 -6 LLEN key -获取列表长度 -7 LPOP key -移出并获取列表的第一个元素 -8 LPUSH key value1 [value2] -将一个或多个值插入到列表头部 -9 LPUSHX key value -将一个值插入到已存在的列表头部 -10 LRANGE key start stop -获取列表指定范围内的元素 -11 LREM key count value -移除列表元素 -12 LSET key index value -通过索引设置列表元素的值 -13 LTRIM key start stop -对一个列表进行修剪(trim),就是说,让列表只保留指定区间内的元素,不在指定区间之内的元素都将被删除。 -14 RPOP key -移除并获取列表最后一个元素 -15 RPOPLPUSH source destination -移除列表的最后一个元素,并将该元素添加到另一个列表并返回 -16 RPUSH key value1 [value2] -在列表中添加一个或多个值 -17 RPUSHX key value -为已存在的列表添加值 -``` -###### 5.4 集合(Set) ->Redis 的 Set 是 String 类型的无序集合。集合成员是唯一的,这就意味着集合中不能出现重复的数据。 -Redis 中集合是通过哈希表实现的,所以添加,删除,查找的复杂度都是 O(1)。集合中最大的成员数为 232 - 1 (4294967295, 每个集合可存储40多亿个成员)。 -``` -1 SADD key member1 [member2] -向集合添加一个或多个成员 -2 SCARD key -获取集合的成员数 -3 SDIFF key1 [key2] -返回给定所有集合的差集 -4 SDIFFSTORE destination key1 [key2] -返回给定所有集合的差集并存储在 destination 中 -5 SINTER key1 [key2] -返回给定所有集合的交集 -6 SINTERSTORE destination key1 [key2] -返回给定所有集合的交集并存储在 destination 中 -7 SISMEMBER key member -判断 member 元素是否是集合 key 的成员 -8 SMEMBERS key -返回集合中的所有成员 -9 SMOVE source destination member -将 member 元素从 source 集合移动到 destination 集合 -10 SPOP key -移除并返回集合中的一个随机元素 -11 SRANDMEMBER key [count] -返回集合中一个或多个随机数 -12 SREM key member1 [member2] -移除集合中一个或多个成员 -13 SUNION key1 [key2] -返回所有给定集合的并集 -14 SUNIONSTORE destination key1 [key2] -所有给定集合的并集存储在 destination 集合中 -15 SSCAN key cursor [MATCH pattern] [COUNT count] -迭代集合中的元素 -``` -###### 5.5 有序集合(sorted set) -``` -1 ZADD key score1 member1 [score2 member2] -向有序集合添加一个或多个成员,或者更新已存在成员的分数 -2 ZCARD key -获取有序集合的成员数 -3 ZCOUNT key min max -计算在有序集合中指定区间分数的成员数 -4 ZINCRBY key increment member -有序集合中对指定成员的分数加上增量 increment -5 ZINTERSTORE destination numkeys key [key ...] -计算给定的一个或多个有序集的交集并将结果集存储在新的有序集合 key 中 -6 ZLEXCOUNT key min max -在有序集合中计算指定字典区间内成员数量 -7 ZRANGE key start stop [WITHSCORES] -通过索引区间返回有序集合成指定区间内的成员 -8 ZRANGEBYLEX key min max [LIMIT offset count] -通过字典区间返回有序集合的成员 -9 ZRANGEBYSCORE key min max [WITHSCORES] [LIMIT] -通过分数返回有序集合指定区间内的成员 -10 ZRANK key member -返回有序集合中指定成员的索引 -11 ZREM key member [member ...] -移除有序集合中的一个或多个成员 -12 ZREMRANGEBYLEX key min max -移除有序集合中给定的字典区间的所有成员 -13 ZREMRANGEBYRANK key start stop -移除有序集合中给定的排名区间的所有成员 -14 ZREMRANGEBYSCORE key min max -移除有序集合中给定的分数区间的所有成员 -15 ZREVRANGE key start stop [WITHSCORES] -返回有序集中指定区间内的成员,通过索引,分数从高到底 -16 ZREVRANGEBYSCORE key max min [WITHSCORES] -返回有序集中指定分数区间内的成员,分数从高到低排序 -17 ZREVRANK key member -返回有序集合中指定成员的排名,有序集成员按分数值递减(从大到小)排序 -18 ZSCORE key member -返回有序集中,成员的分数值 -19 ZUNIONSTORE destination numkeys key [key ...] -计算给定的一个或多个有序集的并集,并存储在新的 key 中 -20 ZSCAN key cursor [MATCH pattern] [COUNT count] -迭代有序集合中的元素(包括元素成员和元素分值) -``` diff --git "a/redis/\347\254\254\344\272\224\347\253\240\357\274\232Redis-pipeline\346\265\201\346\260\264\347\272\277.md" "b/redis/\347\254\254\344\272\224\347\253\240\357\274\232Redis-pipeline\346\265\201\346\260\264\347\272\277.md" deleted file mode 100644 index 566ba1b..0000000 --- "a/redis/\347\254\254\344\272\224\347\253\240\357\274\232Redis-pipeline\346\265\201\346\260\264\347\272\277.md" +++ /dev/null @@ -1,20 +0,0 @@ -###1. 什么是流水线 - 其实就是批量查询(来减少网络用时) -###2. 客户端实现 -``` - @Test - /** pipeline 批量操作 */ - public void pipelineLearn(){ - Jedis jedis = new Jedis("192.168.218.129",6379); - for (int i = 0;i<10;i++){ - Pipeline pipeline = jedis.pipelined(); - for (int j = i*10;i<(i+1)*10;j++){ - pipeline.hset("hashkey"+j,"field"+j,"value"+j); - } - pipeline.syncAndReturnAll(); - } - } -``` -###3. 两个注意 -* 注意批量数据量大小要合适 -* pipeline每次只能作用在一个Redis节点 diff --git "a/redis/\347\254\254\345\205\253\347\253\240\357\274\232Redis-\346\214\201\344\271\205\345\214\226-RDB&AOF.md" "b/redis/\347\254\254\345\205\253\347\253\240\357\274\232Redis-\346\214\201\344\271\205\345\214\226-RDB&AOF.md" deleted file mode 100644 index 81eef79..0000000 --- "a/redis/\347\254\254\345\205\253\347\253\240\357\274\232Redis-\346\214\201\344\271\205\345\214\226-RDB&AOF.md" +++ /dev/null @@ -1,97 +0,0 @@ -###1. 什么是RDB - 在第一章有讲过,redis是存储在内存中,并在硬盘里备份一份,硬盘里的数据就是rdb文件。 -* 缺点:耗时,时间复杂度大;fork消耗内存;硬盘io性能影响;宕机save不成功导致数据丢失 -###2. 命令 -######1. 手动生成RDB -* save #生成rdb文件(同步命令,会造成redis阻塞) -* bgsave #生成rdb文件(异步命令,利用系统线程fork来处理) -######2. 自动生成RDB - redis自动会在一个标准内(可根据需求选择更改),自动调用bgsave生成RDB文件dump.rdb。标准图: - |配置|seconds|changes| - |:------:|:------:|:--------:| - |save|900|1| - |save|300|10| - |save|60|10000| -###3. 快照配置 - -将DB保存到磁盘的规则定义(快照) -格式:save -例子:save 900 1 //在900秒(15分钟)内如果至少有1个键值发生变化 就保存 - save 300 10 //在300秒(6分钟)内如果至少有10个键值发生变化 就保存 -save 900 1 //每一条表示一个存盘点 -save 300 10 -save 60 10000 - -**stop-writes-on-bgsave-error yes**:如果启用如上的快照(RDB),在一个存盘点之后,可能磁盘会坏掉或者权限问题,redis将依然能正常工作 -**rdbcompression yes**:是否将字符串用LZF压缩到.rdb 数据库中,如果想节省CPU资源可以将其设置成no,但是字符串存储在磁盘上占用空间会很大,默认是yes - -**rdbchecksum yes**:rdb文件的校验,如果校验将避免文件格式坏掉,如果不校验将在每次操作文件时要付出校验过程的资源新能,将此参数设置为no,将跳过校验 - -**dbfilename dump.rdb**:转储数据的文件名 - -**dir ./data**:redis的工作目录,它会将转储文件存储到这个目录下,并生成一个附加文件 -![image.png](http://upload-images.jianshu.io/upload_images/5786888-ae05a78ebcd8fed7.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - -###1. 什么是AOF - 只要往redis里写入一条命令,它就会把该日志写到aof文件里。 -###2. 配置(三种写入策略和AOF重写) -#####1. AOF写入策略 -1. always 策略: 只要往redis写入一条命令,就会把该命令写入缓冲区,然后慢慢的写入aof文件。(IO开销大) -2. **everysec策略(常用)**: 每秒一次fsync写入aof文件(可能会丢失一秒数据) -3. no策略: 不用管,系统决定什么时候写入aof文件。(不可控) -#####2. AOF重写 - >为什么要重写呢?我们说过AOF是记录每一条命令,那如果我set hello world 之后又set hello fant,AOF文件里还是会有两条记录,但是我们只需要知道最后一条有用记录即可。或者说多条单个命令合成一条批量命令。 -通俗的说:重写是为了对原生aof日志进行精简优化。 - -######重写两种方式 -1. bgrewriteaof 类似bgsave一样的机制。 -2. AOF重写配置 - - |配置名|含义| - |:-----:|:-----:| - |auto-aof-rewrite-percentage 100|AOF文件增长率| - |auto-aof-rewrite-min-size 64mb|AOF文件重写需要的尺寸| -* 增长率和尺寸是什么意思呢? -尺寸:内容打到一定大小才触发aof重写 -增长率:第一次100Mb触发,增长率是2,下次就是200Mb触发 -* 那怎样获取当前配置尺寸(条件大小)呢? -有两个统计名: -1. aof_current_size : 当前尺寸 -2. aof_base_size 上次启动和重写的尺寸 - -#####3. AOF配置详情 -``` -appendonly yes #改为yes,打开AOF -# 只增文件的文件名称。(默认是appendonly.aof) -appendfilename appendonly.aof -#AOF写入策略 -appendfsync always -#目录 -dir ./bigdiskpath -#是否允许丢失数据 -no-appendfsync-on-rewrite yes -# AOF重写配置尺寸和增长率 -auto-aof-rewrite-percentage 100 -auto-aof-rewrite-min-size 64mb -``` -###3. AOF实战 -![image.png](http://upload-images.jianshu.io/upload_images/5786888-26e96212506166ac.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) -然后我们去/data目录看一下 -![image.png](http://upload-images.jianshu.io/upload_images/5786888-fc9d65a7c959f805.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) -cat appendonly.aof -![image.png](http://upload-images.jianshu.io/upload_images/5786888-06cb3a1acec13352.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) -可以看出来,world和fantj都存在,然而只有fantj是有效数据。然后我们手动重写aof(bgrewriteaof) -![image.png](http://upload-images.jianshu.io/upload_images/5786888-7b477cda0a44fda3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) -然后再cat一下 -![image.png](http://upload-images.jianshu.io/upload_images/5786888-1472dfe9ff32a3db.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - -##RDB与AOF对比区别与选择 -|场景|RDB|AOF| -|:-----:|:-----:|:-----:| -|启动优先级|低(相对不全)|高(日志全)| -|体积|小(有压缩)|大(类日志)| -|恢复速度|快|慢| -|数据安全性|丢数据|策略决定| -|重量级|重|轻| - -二者可以同时开启,一般也是同时开启,根据自己项目需求,选择最适合自己的策略。 diff --git "a/redis/\347\254\254\345\205\255\347\253\240\357\274\232Redis-\345\217\221\345\270\203\350\256\242\351\230\205\345\212\237\350\203\275&\345\234\260\351\207\214\344\277\241\346\201\257\345\256\232\344\275\215GEO.md" "b/redis/\347\254\254\345\205\255\347\253\240\357\274\232Redis-\345\217\221\345\270\203\350\256\242\351\230\205\345\212\237\350\203\275&\345\234\260\351\207\214\344\277\241\346\201\257\345\256\232\344\275\215GEO.md" deleted file mode 100644 index 0886aa8..0000000 --- "a/redis/\347\254\254\345\205\255\347\253\240\357\274\232Redis-\345\217\221\345\270\203\350\256\242\351\230\205\345\212\237\350\203\275&\345\234\260\351\207\214\344\277\241\346\201\257\345\256\232\344\275\215GEO.md" +++ /dev/null @@ -1,66 +0,0 @@ -###1. 什么是发布订阅 -Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。 - -Redis 客户端可以订阅任意数量的频道。 - -下图展示了频道 channel1 , 以及订阅这个频道的三个客户端 —— client2 、 client5 和 client1 之间的关系:![image.png](http://upload-images.jianshu.io/upload_images/5786888-0d41cae20e882e26.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) -当有新消息通过 PUBLISH 命令发送给频道 channel1 时, 这个消息就会被发送给订阅它的三个客户端: -![image.png](http://upload-images.jianshu.io/upload_images/5786888-dff24637650291b3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) -###2. 实例 -以下实例演示了发布订阅是如何工作的。在我们实例中我们创建了订阅频道名为 redisChat: -``` -redis 127.0.0.1:6379> SUBSCRIBE redisChat - -Reading messages... (press Ctrl-C to quit) -1) "subscribe" -2) "redisChat" -3) (integer) 1 -``` -现在,我们先重新开启个 redis 客户端,然后在同一个频道 redisChat 发布两次消息,订阅者就能接收到消息。 -``` -redis 127.0.0.1:6379> PUBLISH redisChat "Redis is a great caching technique" - -(integer) 1 - -redis 127.0.0.1:6379> PUBLISH redisChat "Learn redis by runoob.com" - -(integer) 1 - -# 订阅者的客户端会显示如下消息 -1) "message" -2) "redisChat" -3) "Redis is a great caching technique" -1) "message" -2) "redisChat" -3) "Learn redis by runoob.com" -``` - - -### Redis 发布订阅常用命令 - -下表列出了 redis 发布订阅常用命令: - -| 序号 | 命令及描述 | -|:----:|:----:| -| 1 | PSUBSCRIBE pattern订阅一个或多个符合给定模式的频道。 | -| 2 | PUBSUB subcommand 查看订阅与发布系统状态。 | -| 3 | PUBLISH channel message]将信息发送到指定的频道。 | -| 4 | PUNSUBSCRIBE 退订所有给定模式的频道。 | -| 5 | SUBSCRIBE channel [channel ...]订阅给定的一个或多个频道的信息。 | -| 6 | UNSUBSCRIBE [channel [channel ...]]指退订给定的频道。 | - - -本文借鉴 runoob.com,觉得本网站给的思路比较清晰。 - -###1. 什么是GEO - geo就是地理信息定位,存储经纬度,计算两地距离、范围等 -###2. api介绍 -* geo key longitude latitude member [longitude latitude member ...] #增加地里位置信息 -`geoadd cities:locations 111.111.111.11 beijing` -* geopos key member [member ...] #获取地理位置信息 -`geopos cities:location beijing` -* geodist key member1 member2 [unit] #获取两个地里位置的距离 unit:m. km. mi(英里) .ft(尺) -* georadius 命令百度查看详情,这里不做介绍,用到的也很少 -`georadiusbymember cities:locations beijing 150 km`查找距离北京150km以内的城市 - - diff --git "a/redis/\347\254\254\345\215\201\344\270\200\347\253\240\357\274\232Redis-Java\345\256\242\346\210\267\347\253\257\347\256\241\347\220\206sentinel\345\223\250\345\205\265.md" "b/redis/\347\254\254\345\215\201\344\270\200\347\253\240\357\274\232Redis-Java\345\256\242\346\210\267\347\253\257\347\256\241\347\220\206sentinel\345\223\250\345\205\265.md" deleted file mode 100644 index 8b13789..0000000 --- "a/redis/\347\254\254\345\215\201\344\270\200\347\253\240\357\274\232Redis-Java\345\256\242\346\210\267\347\253\257\347\256\241\347\220\206sentinel\345\223\250\345\205\265.md" +++ /dev/null @@ -1 +0,0 @@ - diff --git "a/redis/\347\254\254\345\215\201\347\253\240\357\274\232Redis-Sentinel-\345\223\250\345\205\265\346\234\272\345\210\266.md" "b/redis/\347\254\254\345\215\201\347\253\240\357\274\232Redis-Sentinel-\345\223\250\345\205\265\346\234\272\345\210\266.md" deleted file mode 100644 index 6359382..0000000 --- "a/redis/\347\254\254\345\215\201\347\253\240\357\274\232Redis-Sentinel-\345\223\250\345\205\265\346\234\272\345\210\266.md" +++ /dev/null @@ -1,122 +0,0 @@ ->Redis Sentinel为Redis提供高可用性。实际上,这意味着使用Sentinel可以创建一个Redis部署,在没有人为干预的情况下抵抗某些类型的故障。 - - -### 1. 故障转移步骤 -1. 多个sentinel发现并缺人master有问题。 -2. 选举出一个sentinel作为领导。 -3. 选出一个slave作为master -4. 通知其余slave成为新的master的slave -5. 通知客户端朱从变化 -6. 等待老的master复活成为信master的slave - -### 2. 为什么要用哨兵 -Redis Sentinel是一个分布式系统: - -Sentinel本身被设计成运行在多个Sentinel进程合作的配置中。具有多个Sentinel进程协作的优势如下: - -1. 当多个Sentinels同意给定的主控器不再可用时,执行故障检测。这降低了误报的可能性。 -即使不是所有的Sentinel进程都在工作,Sentinel也能正常工作,从而使系统对故障有效。毕竟,拥有一个本身就是单点故障的故障切换系统是没有意义的。 -2. Sentinel,Redis实例(主服务器和从服务器)以及连接到Sentinel和Redis的客户端的总和也是具有特定属性的更大的分布式系统。在这篇文档中,概念将从为了理解Sentinel的基本属性所需的基本信息,到更复杂的信息(这些是可选的),逐步引入,以便理解Sentinel的工作原理。 - -### 3. 快速开始 - -#### 3.1 配置哨兵 -分别配置`sentinel.conf`和`sentinel-26380.conf` -``` -port 26379 -protected-mode no -sentinel monitor mymaster 47.xx.xxx.xxx 6379 2 # 127.0.0.1 一定要改成服务器 ip -sentinel auth-pass mymaster root++... #如果mamaster节点有密码,则需要设置这项 -sentinel down-after-milliseconds mymaster 60000 -sentinel failover-timeout mymaster 180000 -sentinel parallel-syncs mymaster 1 - - -port 26380 -sentinel monitor myslave 47.xx.xxx.xxx 6380 4 -sentinel auth-pass myslave root++... #如果mamaster节点有密码,则需要设置这项 -sentinel down-after-milliseconds myslave 10000 -sentinel failover-timeout myslave 180000 -sentinel parallel-syncs myslave 5 -``` -#### 3.2 运行哨兵 -两种方法: -1. `redis-sentinel /path/to/sentinel.conf` -2. `redis-server /path/to/sentinel.conf --sentinel` - - -#### 3.3 测试 - -##### mymaster启动 -``` -[root@FantJ redis-4.0.9]# redis-sentinel sentinel.conf & -[1] 17859 -[root@FantJ redis-4.0.9]# 17859:X 07 Sep 16:44:10.344 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo -17859:X 07 Sep 16:44:10.344 # Redis version=4.0.9, bits=64, commit=00000000, modified=0, pid=17859, just started -17859:X 07 Sep 16:44:10.344 # Configuration loaded - _._ - _.-``__ ''-._ - _.-`` `. `_. ''-._ Redis 4.0.9 (00000000/0) 64 bit - .-`` .-```. ```\/ _.,_ ''-._ - ( ' , .-` | `, ) Running in sentinel mode - |`-._`-...-` __...-.``-._|'` _.-'| Port: 26379 - | `-._ `._ / _.-' | PID: 17859 - `-._ `-._ `-./ _.-' _.-' - |`-._`-._ `-.__.-' _.-'_.-'| - | `-._`-._ _.-'_.-' | http://redis.io - `-._ `-._`-.__.-'_.-' _.-' - |`-._`-._ `-.__.-' _.-'_.-'| - | `-._`-._ _.-'_.-' | - `-._ `-._`-.__.-'_.-' _.-' - `-._ `-.__.-' _.-' - `-._ _.-' - `-.__.-' - -17859:X 07 Sep 16:44:10.346 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128. -17859:X 07 Sep 16:44:10.346 # Sentinel ID is e61d1d9c3a7441d2376cb98399fa9dd479076eef -17859:X 07 Sep 16:44:10.346 # +monitor master mymaster xxx.xx.xx.xx 6379 quorum 2 -``` -##### myslave启动 -``` -[root@FantJ sentinel]# redis-sentinel sentinel-26380.conf & -[2] 17873 -[root@FantJ sentinel]# 17873:X 07 Sep 16:46:11.401 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo -17873:X 07 Sep 16:46:11.401 # Redis version=4.0.9, bits=64, commit=00000000, modified=0, pid=17873, just started -17873:X 07 Sep 16:46:11.401 # Configuration loaded - _._ - _.-``__ ''-._ - _.-`` `. `_. ''-._ Redis 4.0.9 (00000000/0) 64 bit - .-`` .-```. ```\/ _.,_ ''-._ - ( ' , .-` | `, ) Running in sentinel mode - |`-._`-...-` __...-.``-._|'` _.-'| Port: 26380 - | `-._ `._ / _.-' | PID: 17873 - `-._ `-._ `-./ _.-' _.-' - |`-._`-._ `-.__.-' _.-'_.-'| - | `-._`-._ _.-'_.-' | http://redis.io - `-._ `-._`-.__.-'_.-' _.-' - |`-._`-._ `-.__.-' _.-'_.-'| - | `-._`-._ _.-'_.-' | - `-._ `-._`-.__.-'_.-' _.-' - `-._ `-.__.-' _.-' - `-._ _.-' - `-.__.-' - -17873:X 07 Sep 16:46:11.403 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128. -17873:X 07 Sep 16:46:11.403 # Sentinel ID is 0f375fb9f45af9c6a5402e88cb9296b24da6f067 -17873:X 07 Sep 16:46:11.403 # +monitor master myslave xxxxx 6380 quorum 4 -``` -##### 查看26379的sentinel信息: -``` -[root@FantJ sentinel]# redis-cli -p 26379 -127.0.0.1:26379> info sentinel -# Sentinel -sentinel_masters:1 -sentinel_tilt:0 -sentinel_running_scripts:0 -sentinel_scripts_queue_length:0 -sentinel_simulate_failure_flags:0 -master0:name=mymaster,status=sdown,address=47.xx.xx.xxx:6379,slaves=0,sentinels=1 -``` - - - diff --git "a/redis/\347\254\254\345\233\233\347\253\240\357\274\232Redis-\346\205\242\346\237\245\350\257\242\346\227\245\345\277\227\347\263\273\347\273\237.md" "b/redis/\347\254\254\345\233\233\347\253\240\357\274\232Redis-\346\205\242\346\237\245\350\257\242\346\227\245\345\277\227\347\263\273\347\273\237.md" deleted file mode 100644 index d1172ad..0000000 --- "a/redis/\347\254\254\345\233\233\347\253\240\357\274\232Redis-\346\205\242\346\237\245\350\257\242\346\227\245\345\277\227\347\263\273\347\273\237.md" +++ /dev/null @@ -1,19 +0,0 @@ -###一、慢查询说明 - 慢查询日志, 是系统记录那些超过指定查询时间的日志,查询时间指的是单个命令占用CPU处理时间。不包括在队列中等待的时间。仅仅指执行该命令需要的时间。 -###二、三个命令 -* slowlog get [n] 获取慢查询队列 -* slowlog len 获取慢查询队列长度 -* slowlog reset 清空慢查询队列 -###三、两个配置 -######1. 两个重要参数 -慢查询日志有两个参数: -* slowlog-log-slower-than: 单位微妙,指定redis执行命令的最大时间,超过将记录到慢查询日志中, -不接受负值,如果设置为0,每条命令都要记录到慢查询日志中.(默认值128) -* slowlog-max-len: 设置慢查询日志长度,如果慢查询日志已经到最大值,如果有新命令需要记录,就将最老那条记录删除.(默认值1000) -![image.png](http://upload-images.jianshu.io/upload_images/5786888-04eb00e95d2af990.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) -######2. 动态配置 - redis作为一个持久化服务,一般开启之后不会在对它进行重启操作。因此redis支持动态修改配置 -命令为`config set slowlog-max-len 1`和`config set slowlog-log-slower-than 1000` -###四、通常配置 - 通常slowlog-max-len不要设置过大 默认10ms ,设置为1ms - 通常slowlog-log-slower-than 不要设置过小,通常设置1000 diff --git "a/spring/Spring\346\272\220\347\240\201\350\247\243\346\236\220\344\271\213\347\216\257\345\242\203\346\220\255\345\273\272\357\274\210\344\270\200\357\274\211.md" "b/spring/Spring\346\272\220\347\240\201\350\247\243\346\236\220\344\271\213\347\216\257\345\242\203\346\220\255\345\273\272\357\274\210\344\270\200\357\274\211.md" deleted file mode 100644 index f510c33..0000000 --- "a/spring/Spring\346\272\220\347\240\201\350\247\243\346\236\220\344\271\213\347\216\257\345\242\203\346\220\255\345\273\272\357\274\210\344\270\200\357\274\211.md" +++ /dev/null @@ -1,9 +0,0 @@ -###1.安装git - 能点开这个文章看的我相信已经有开发经验了,这里不多介绍。 -###2.安装Gradle ->gradle是maven的一个后起之秀,Gradle是一个基于Apache Ant和Apache Maven概念的项目自动化构建工具。它使用一种基于Groovy的特定领域语言(DSL)来声明项目设置,抛弃了基于XML的各种繁琐配置。 比maven语法简单,想详细了解的可以去百度下,这里不过多介绍啦~~ -1. 从www.gradle.org/downloads下载 -2. 安装环境变量 GRADLE_HOME path下添加%GRADLE_HOME%/bin -3. 测试 gradle -version -![image.png](http://upload-images.jianshu.io/upload_images/5786888-644e741874644af3.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) -###3.下载Spring diff --git "a/springboot/2018\346\234\200\346\226\260(\346\234\200\345\205\250)\346\211\213\346\234\272\345\217\267\346\255\243\345\210\231.md" "b/springboot/2018\346\234\200\346\226\260(\346\234\200\345\205\250)\346\211\213\346\234\272\345\217\267\346\255\243\345\210\231.md" deleted file mode 100644 index 56427bd..0000000 --- "a/springboot/2018\346\234\200\346\226\260(\346\234\200\345\205\250)\346\211\213\346\234\272\345\217\267\346\255\243\345\210\231.md" +++ /dev/null @@ -1,21 +0,0 @@ - -``` -/** - * Created by Fant.J. - */ -public class CheckFormat { - public static boolean isEmail(String email){ - String check = "^([a-z0-9A-Z]+[-|_|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$"; - Pattern regex = Pattern.compile(check); - Matcher matcher = regex.matcher(email); - return matcher.matches(); - } - - public static boolean isPhone(String phone){ - String check = "^(((13[0-9])|(14[579])|(15([0-3]|[5-9]))|(16[6])|(17[0135678])|(18[0-9])|(19[89]))\\d{8})$"; - Pattern regex = Pattern.compile(check); - Matcher matcher = regex.matcher(phone); - return matcher.matches(); - } -} -``` diff --git "a/springboot/SpringBoot-\346\225\264\345\220\210\357\274\210\345\205\255\357\274\211Security-&-Oauth2-0\357\274\210\345\256\214\346\225\264\347\257\207\357\274\211.md" "b/springboot/SpringBoot-\346\225\264\345\220\210\357\274\210\345\205\255\357\274\211Security-&-Oauth2-0\357\274\210\345\256\214\346\225\264\347\257\207\357\274\211.md" deleted file mode 100644 index 481910d..0000000 --- "a/springboot/SpringBoot-\346\225\264\345\220\210\357\274\210\345\205\255\357\274\211Security-&-Oauth2-0\357\274\210\345\256\214\346\225\264\347\257\207\357\274\211.md" +++ /dev/null @@ -1,18 +0,0 @@ - - -###### 1. 快速实现篇(实现最基本的登录): -[SpringSecurity 快速实现项目](https://www.jianshu.com/p/79ae54b17670) - -###### 2. 企业级封装篇 -[我的 Spring Security 文集 ](https://www.jianshu.com/nb/23842307) - -[SpringBoot 整合 Security(一)实现用户认证并判断返回json还是view](https://www.jianshu.com/p/18875c2995f1) - -[SpringBoot 整合 Security(二)实现验证码登录](https://www.jianshu.com/p/9d08c767b33e) - -[SpringBoot 整合 oauth2(三)实现 token 认证](https://www.jianshu.com/p/19059060036b) - -[SpringBoot 整合 oauth2(四)实现 token 持久化](https://www.jianshu.com/p/928074740a10) - -[SpringBoot 整合 oauth2(五)实现 jwt 及 扩展](https://www.jianshu.com/p/766cf742e3e8) - diff --git "a/springboot/springboot----\347\224\250\346\233\264\344\274\230\351\233\205\347\232\204\346\226\271\345\274\217\344\275\277\347\224\250\347\274\223\345\255\230\357\274\210Redis\357\274\211.md" "b/springboot/springboot----\347\224\250\346\233\264\344\274\230\351\233\205\347\232\204\346\226\271\345\274\217\344\275\277\347\224\250\347\274\223\345\255\230\357\274\210Redis\357\274\211.md" deleted file mode 100644 index 8b13789..0000000 --- "a/springboot/springboot----\347\224\250\346\233\264\344\274\230\351\233\205\347\232\204\346\226\271\345\274\217\344\275\277\347\224\250\347\274\223\345\255\230\357\274\210Redis\357\274\211.md" +++ /dev/null @@ -1 +0,0 @@ - diff --git "a/\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241/\344\272\213\345\212\241\347\232\204\345\216\237\345\210\231\345\222\214\345\256\236\347\216\260.md" "b/\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241/\344\272\213\345\212\241\347\232\204\345\216\237\345\210\231\345\222\214\345\256\236\347\216\260.md" deleted file mode 100644 index 1938c94..0000000 --- "a/\345\210\206\345\270\203\345\274\217\344\272\213\345\212\241/\344\272\213\345\212\241\347\232\204\345\216\237\345\210\231\345\222\214\345\256\236\347\216\260.md" +++ /dev/null @@ -1,39 +0,0 @@ -事务是什么? - -是一种可靠 一致的方式,访问和操作数据库中数据的程序单元。 - -四大原则/特性 -* A原子性 -* C一致性 -* I隔离性 -* D持久性 - - - -![](https://upload-images.jianshu.io/upload_images/5786888-2ac44edc213e77c1.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - -1. char - - - - - - - - - - - - - - - - - - - - - - - - diff --git "a/React-js/React----Antd\345\256\236\347\216\260\351\241\265\351\235\242\345\270\203\345\261\200.md" "b/\345\211\215\347\253\257\344\270\223\351\242\230/React-js/React----Antd\345\256\236\347\216\260\351\241\265\351\235\242\345\270\203\345\261\200.md" similarity index 100% rename from "React-js/React----Antd\345\256\236\347\216\260\351\241\265\351\235\242\345\270\203\345\261\200.md" rename to "\345\211\215\347\253\257\344\270\223\351\242\230/React-js/React----Antd\345\256\236\347\216\260\351\241\265\351\235\242\345\270\203\345\261\200.md" diff --git a/React-js/React----PropTypes.md "b/\345\211\215\347\253\257\344\270\223\351\242\230/React-js/React----PropTypes.md" similarity index 100% rename from React-js/React----PropTypes.md rename to "\345\211\215\347\253\257\344\270\223\351\242\230/React-js/React----PropTypes.md" diff --git "a/React-js/React----Redux\344\273\216\345\205\245\351\227\250\345\210\260\347\262\276\351\200\232\345\256\236\347\216\260TodoList.md" "b/\345\211\215\347\253\257\344\270\223\351\242\230/React-js/React----Redux\344\273\216\345\205\245\351\227\250\345\210\260\347\262\276\351\200\232\345\256\236\347\216\260TodoList.md" similarity index 100% rename from "React-js/React----Redux\344\273\216\345\205\245\351\227\250\345\210\260\347\262\276\351\200\232\345\256\236\347\216\260TodoList.md" rename to "\345\211\215\347\253\257\344\270\223\351\242\230/React-js/React----Redux\344\273\216\345\205\245\351\227\250\345\210\260\347\262\276\351\200\232\345\256\236\347\216\260TodoList.md" diff --git "a/React-js/React----ref\347\232\204\344\275\277\347\224\250.md" "b/\345\211\215\347\253\257\344\270\223\351\242\230/React-js/React----ref\347\232\204\344\275\277\347\224\250.md" similarity index 100% rename from "React-js/React----ref\347\232\204\344\275\277\347\224\250.md" rename to "\345\211\215\347\253\257\344\270\223\351\242\230/React-js/React----ref\347\232\204\344\275\277\347\224\250.md" diff --git "a/React-js/React----\347\224\237\345\221\275\345\221\250\346\234\237.md" "b/\345\211\215\347\253\257\344\270\223\351\242\230/React-js/React----\347\224\237\345\221\275\345\221\250\346\234\237.md" similarity index 100% rename from "React-js/React----\347\224\237\345\221\275\345\221\250\346\234\237.md" rename to "\345\211\215\347\253\257\344\270\223\351\242\230/React-js/React----\347\224\237\345\221\275\345\221\250\346\234\237.md" diff --git "a/React-js/React----\350\204\232\346\211\213\346\236\266.md" "b/\345\211\215\347\253\257\344\270\223\351\242\230/React-js/React----\350\204\232\346\211\213\346\236\266.md" similarity index 100% rename from "React-js/React----\350\204\232\346\211\213\346\236\266.md" rename to "\345\211\215\347\253\257\344\270\223\351\242\230/React-js/React----\350\204\232\346\211\213\346\236\266.md" diff --git "a/React-js/React----\350\231\232\346\213\237DOM.md" "b/\345\211\215\347\253\257\344\270\223\351\242\230/React-js/React----\350\231\232\346\213\237DOM.md" similarity index 100% rename from "React-js/React----\350\231\232\346\213\237DOM.md" rename to "\345\211\215\347\253\257\344\270\223\351\242\230/React-js/React----\350\231\232\346\213\237DOM.md" diff --git "a/React-js/React----\350\277\207\346\270\241\345\212\250\347\224\273.md" "b/\345\211\215\347\253\257\344\270\223\351\242\230/React-js/React----\350\277\207\346\270\241\345\212\250\347\224\273.md" similarity index 100% rename from "React-js/React----\350\277\207\346\270\241\345\212\250\347\224\273.md" rename to "\345\211\215\347\253\257\344\270\223\351\242\230/React-js/React----\350\277\207\346\270\241\345\212\250\347\224\273.md" diff --git "a/React-js/React--\351\241\271\347\233\256\346\236\266\346\236\204\344\273\213\347\273\215.md" "b/\345\211\215\347\253\257\344\270\223\351\242\230/React-js/React--\351\241\271\347\233\256\346\236\266\346\236\204\344\273\213\347\273\215.md" similarity index 100% rename from "React-js/React--\351\241\271\347\233\256\346\236\266\346\236\204\344\273\213\347\273\215.md" rename to "\345\211\215\347\253\257\344\270\223\351\242\230/React-js/React--\351\241\271\347\233\256\346\236\266\346\236\204\344\273\213\347\273\215.md" diff --git "a/React-js/React\345\255\246\344\271\240----Chrome\346\217\222\344\273\266\346\217\220\345\215\207\345\274\200\345\217\221\346\225\210\347\216\207.md" "b/\345\211\215\347\253\257\344\270\223\351\242\230/React-js/React\345\255\246\344\271\240----Chrome\346\217\222\344\273\266\346\217\220\345\215\207\345\274\200\345\217\221\346\225\210\347\216\207.md" similarity index 100% rename from "React-js/React\345\255\246\344\271\240----Chrome\346\217\222\344\273\266\346\217\220\345\215\207\345\274\200\345\217\221\346\225\210\347\216\207.md" rename to "\345\211\215\347\253\257\344\270\223\351\242\230/React-js/React\345\255\246\344\271\240----Chrome\346\217\222\344\273\266\346\217\220\345\215\207\345\274\200\345\217\221\346\225\210\347\216\207.md" diff --git "a/React-js/React\345\255\246\344\271\240----JSX\350\257\255\346\263\225.md" "b/\345\211\215\347\253\257\344\270\223\351\242\230/React-js/React\345\255\246\344\271\240----JSX\350\257\255\346\263\225.md" similarity index 100% rename from "React-js/React\345\255\246\344\271\240----JSX\350\257\255\346\263\225.md" rename to "\345\211\215\347\253\257\344\270\223\351\242\230/React-js/React\345\255\246\344\271\240----JSX\350\257\255\346\263\225.md" diff --git "a/React-js/React\345\255\246\344\271\240----\345\256\236\347\216\260todolist.md" "b/\345\211\215\347\253\257\344\270\223\351\242\230/React-js/React\345\255\246\344\271\240----\345\256\236\347\216\260todolist.md" similarity index 100% rename from "React-js/React\345\255\246\344\271\240----\345\256\236\347\216\260todolist.md" rename to "\345\211\215\347\253\257\344\270\223\351\242\230/React-js/React\345\255\246\344\271\240----\345\256\236\347\216\260todolist.md" diff --git "a/React-js/React\345\255\246\344\271\240----\347\273\204\344\273\266\346\213\206\345\210\206\345\222\214\344\274\240\345\200\274.md" "b/\345\211\215\347\253\257\344\270\223\351\242\230/React-js/React\345\255\246\344\271\240----\347\273\204\344\273\266\346\213\206\345\210\206\345\222\214\344\274\240\345\200\274.md" similarity index 100% rename from "React-js/React\345\255\246\344\271\240----\347\273\204\344\273\266\346\213\206\345\210\206\345\222\214\344\274\240\345\200\274.md" rename to "\345\211\215\347\253\257\344\270\223\351\242\230/React-js/React\345\255\246\344\271\240----\347\273\204\344\273\266\346\213\206\345\210\206\345\222\214\344\274\240\345\200\274.md" diff --git "a/React-js/React\345\255\246\344\271\240--\347\273\204\344\273\266-\344\273\245\345\217\212-JSX\350\257\255\346\263\225\344\273\213\347\273\215.md" "b/\345\211\215\347\253\257\344\270\223\351\242\230/React-js/React\345\255\246\344\271\240--\347\273\204\344\273\266-\344\273\245\345\217\212-JSX\350\257\255\346\263\225\344\273\213\347\273\215.md" similarity index 100% rename from "React-js/React\345\255\246\344\271\240--\347\273\204\344\273\266-\344\273\245\345\217\212-JSX\350\257\255\346\263\225\344\273\213\347\273\215.md" rename to "\345\211\215\347\253\257\344\270\223\351\242\230/React-js/React\345\255\246\344\271\240--\347\273\204\344\273\266-\344\273\245\345\217\212-JSX\350\257\255\346\263\225\344\273\213\347\273\215.md" diff --git "a/React-js/React\345\255\246\344\271\240--\350\204\232\346\211\213\346\236\266\347\232\204\345\256\211\350\243\205\344\270\216\346\220\255\345\273\272.md" "b/\345\211\215\347\253\257\344\270\223\351\242\230/React-js/React\345\255\246\344\271\240--\350\204\232\346\211\213\346\236\266\347\232\204\345\256\211\350\243\205\344\270\216\346\220\255\345\273\272.md" similarity index 100% rename from "React-js/React\345\255\246\344\271\240--\350\204\232\346\211\213\346\236\266\347\232\204\345\256\211\350\243\205\344\270\216\346\220\255\345\273\272.md" rename to "\345\211\215\347\253\257\344\270\223\351\242\230/React-js/React\345\255\246\344\271\240--\350\204\232\346\211\213\346\236\266\347\232\204\345\256\211\350\243\205\344\270\216\346\220\255\345\273\272.md" diff --git "a/React-js/React\346\211\223\345\214\205\346\263\250\346\204\217\344\272\213\351\241\271\345\217\212\351\235\231\346\200\201\346\226\207\344\273\266\346\234\215\345\212\241\346\220\255\345\273\272.md" "b/\345\211\215\347\253\257\344\270\223\351\242\230/React-js/React\346\211\223\345\214\205\346\263\250\346\204\217\344\272\213\351\241\271\345\217\212\351\235\231\346\200\201\346\226\207\344\273\266\346\234\215\345\212\241\346\220\255\345\273\272.md" similarity index 100% rename from "React-js/React\346\211\223\345\214\205\346\263\250\346\204\217\344\272\213\351\241\271\345\217\212\351\235\231\346\200\201\346\226\207\344\273\266\346\234\215\345\212\241\346\220\255\345\273\272.md" rename to "\345\211\215\347\253\257\344\270\223\351\242\230/React-js/React\346\211\223\345\214\205\346\263\250\346\204\217\344\272\213\351\241\271\345\217\212\351\235\231\346\200\201\346\226\207\344\273\266\346\234\215\345\212\241\346\220\255\345\273\272.md" diff --git "a/React-js/react-360-\345\210\235\346\216\242.md" "b/\345\211\215\347\253\257\344\270\223\351\242\230/React-js/react-360-\345\210\235\346\216\242.md" similarity index 100% rename from "React-js/react-360-\345\210\235\346\216\242.md" rename to "\345\211\215\347\253\257\344\270\223\351\242\230/React-js/react-360-\345\210\235\346\216\242.md" diff --git "a/React-js/\344\275\277\347\224\250Immutable-js\346\235\245\347\256\241\347\220\206store\344\270\255\347\232\204\346\225\260\346\215\256.md" "b/\345\211\215\347\253\257\344\270\223\351\242\230/React-js/\344\275\277\347\224\250Immutable-js\346\235\245\347\256\241\347\220\206store\344\270\255\347\232\204\346\225\260\346\215\256.md" similarity index 100% rename from "React-js/\344\275\277\347\224\250Immutable-js\346\235\245\347\256\241\347\220\206store\344\270\255\347\232\204\346\225\260\346\215\256.md" rename to "\345\211\215\347\253\257\344\270\223\351\242\230/React-js/\344\275\277\347\224\250Immutable-js\346\235\245\347\256\241\347\220\206store\344\270\255\347\232\204\346\225\260\346\215\256.md" diff --git "a/React-js/\344\275\277\347\224\250combineReducers\345\256\214\346\210\220\345\257\271\346\225\260\346\215\256\347\232\204\346\213\206\345\210\206\347\256\241\347\220\206.md" "b/\345\211\215\347\253\257\344\270\223\351\242\230/React-js/\344\275\277\347\224\250combineReducers\345\256\214\346\210\220\345\257\271\346\225\260\346\215\256\347\232\204\346\213\206\345\210\206\347\256\241\347\220\206.md" similarity index 100% rename from "React-js/\344\275\277\347\224\250combineReducers\345\256\214\346\210\220\345\257\271\346\225\260\346\215\256\347\232\204\346\213\206\345\210\206\347\256\241\347\220\206.md" rename to "\345\211\215\347\253\257\344\270\223\351\242\230/React-js/\344\275\277\347\224\250combineReducers\345\256\214\346\210\220\345\257\271\346\225\260\346\215\256\347\232\204\346\213\206\345\210\206\347\256\241\347\220\206.md" diff --git "a/React-js/\344\275\277\347\224\250redux-immutable\347\273\237\344\270\200\346\225\260\346\215\256\346\240\274\345\274\217.md" "b/\345\211\215\347\253\257\344\270\223\351\242\230/React-js/\344\275\277\347\224\250redux-immutable\347\273\237\344\270\200\346\225\260\346\215\256\346\240\274\345\274\217.md" similarity index 100% rename from "React-js/\344\275\277\347\224\250redux-immutable\347\273\237\344\270\200\346\225\260\346\215\256\346\240\274\345\274\217.md" rename to "\345\211\215\347\253\257\344\270\223\351\242\230/React-js/\344\275\277\347\224\250redux-immutable\347\273\237\344\270\200\346\225\260\346\215\256\346\240\274\345\274\217.md" diff --git "a/\345\211\221\346\214\207offer/\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\347\232\204\345\220\216\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227.md" "b/\345\211\221\346\214\207offer/\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\347\232\204\345\220\216\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227.md" deleted file mode 100644 index 2bc6eba..0000000 --- "a/\345\211\221\346\214\207offer/\344\272\214\345\217\211\346\220\234\347\264\242\346\240\221\347\232\204\345\220\216\345\272\217\351\201\215\345\216\206\345\272\217\345\210\227.md" +++ /dev/null @@ -1,47 +0,0 @@ -### Title -输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。如果是则输出Yes,否则输出No。假设输入的数组的任意两个数字都互不相同。 - -### 分析 - -思路: -已知条件:后序序列最后一个值为root;二叉搜索树左子树值都比root小,右子树值都比root大。 -1. 确定root; -2. 遍历序列(除去root结点),找到第一个大于root的位置,则该位置左边为左子树,右边为右子树; -3. 遍历右子树,若发现有小于root的值,则直接返回false; -4. 分别判断左子树和右子树是否仍是二叉搜索树(即递归步骤1、2、3)。 - -### Coding - -``` -public class Solution { - public boolean VerifySquenceOfBST(int [] sequence) { - - if (sequence.length == 0){ - return false; - } - if (sequence.length == 1){ - return true; - } - return ju(sequence, 0, sequence.length-1); - } - - private boolean ju(int[] sequence, int start, int end) { - if (start > end){ - return true; - } - int i = start; - //遍历 找到 左右子树分界, 二叉搜索树:左子树<根<右子树 - // 找到符合条件的i , 则找到了左右子树分界, 数组段的最后一位依旧是根节点 - while (sequence[i]> listAll = new ArrayList<>(); - private ArrayList list = new ArrayList<>(); - public ArrayList> FindPath(TreeNode root, int target) { - // 如果 二叉树是空, 则直接返回空 - if (root == null){ - return listAll; - } - list.add(root.val); - target -= root.val; - if (target == 0 && root.left == null && root.right == null){ - listAll.add(new ArrayList<>(list)); - } - FindPath(root.left, target); - FindPath(root.right, target); - // 如果没有找到符合结果的路径, 则删除上一个add进去的 节点 - list.remove(list.size()-1); - return listAll; - } -} -``` \ No newline at end of file diff --git "a/\345\211\221\346\214\207offer/\344\272\214\345\217\211\346\240\221\347\232\204\351\225\234\345\203\217.md" "b/\345\211\221\346\214\207offer/\344\272\214\345\217\211\346\240\221\347\232\204\351\225\234\345\203\217.md" deleted file mode 100644 index e20c017..0000000 --- "a/\345\211\221\346\214\207offer/\344\272\214\345\217\211\346\240\221\347\232\204\351\225\234\345\203\217.md" +++ /dev/null @@ -1,47 +0,0 @@ -### Title - -``` -题目描述 -操作给定的二叉树,将其变换为源二叉树的镜像。 -输入描述: -二叉树的镜像定义:源二叉树 - 8 - / \ - 6 10 - / \ / \ - 5 7 9 11 - 镜像二叉树 - 8 - / \ - 10 6 - / \ / \ - 11 9 7 5 -``` - -### 思考 - -从root节点最底层左右节点开始递归,如果有左右子节点,则交换,一直递归到root。 - -### Title - -``` -public class Solution { - public void Mirror(TreeNode root) { - TreeNode temp = null; - if (root != null){ - // 交换左右 子节点 - temp = root.left; - root.left = root.right; - root.right = temp; - //交换完成后需要判断 其左右子树 是否是空, 如果不是空 需要遍历交换 - if (root.left != null){ - Mirror(root.left); - } - if (root.right !=null){ - Mirror(root.right); - } - } - - } -} -``` \ No newline at end of file diff --git "a/\345\211\221\346\214\207offer/\344\272\214\347\273\264\346\225\260\347\273\204\344\270\255\347\232\204\346\237\245\346\211\276.md" "b/\345\211\221\346\214\207offer/\344\272\214\347\273\264\346\225\260\347\273\204\344\270\255\347\232\204\346\237\245\346\211\276.md" deleted file mode 100644 index 2af201d..0000000 --- "a/\345\211\221\346\214\207offer/\344\272\214\347\273\264\346\225\260\347\273\204\344\270\255\347\232\204\346\237\245\346\211\276.md" +++ /dev/null @@ -1,34 +0,0 @@ -### 题目描述 - - - -在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。 - - -### Coding - -``` -public class Solution { - public boolean Find(int target, int [][] array) { - for (int i = 0; i< array.length; i++){ - int low = 0; - int height = array[i].length-1; - // 如果 最小值 小于 列最大高度 - while (low<=height){ - // 获取 中间值(二分) - int mid = (low+height)/2; - // 如果目标值 比 中间值大, 则把low赋值为 当前mid+1 - if (target > array[i][mid]){ - low = mid+1; - //如果目标值 比 中间值小, 则把最大值 赋值为 当前height-1 - }else if (target < array[i][mid]){ - height = height-1; - }else if (target == array[i][mid]){ - return true; - } - } - } - return false; - } -} -``` \ No newline at end of file diff --git "a/\345\211\221\346\214\207offer/\344\272\214\350\277\233\345\210\266\344\270\2551\347\232\204\344\270\252\346\225\260.md" "b/\345\211\221\346\214\207offer/\344\272\214\350\277\233\345\210\266\344\270\2551\347\232\204\344\270\252\346\225\260.md" deleted file mode 100644 index ddec2cc..0000000 --- "a/\345\211\221\346\214\207offer/\344\272\214\350\277\233\345\210\266\344\270\2551\347\232\204\344\270\252\346\225\260.md" +++ /dev/null @@ -1,38 +0,0 @@ -### Title - -输入一个整数,输出该数二进制表示中1的个数。其中负数用补码表示。 - - -### 分析 - - - -### Coding -解法一: -``` - public int NumberOf1(int n) { - // 为了防止负数右移 前面补1 造成死循环 ,这里用 1 左移 来和 n取与比较 - int count = 0; - int flag = 1; - while (flag != 0){ - if ((n & flag) != 0){ - count++; - flag = flag<<1; - } - } - return count; - } -``` - -解法二: 时间少,空间占用多 -``` -public static int NumberOf1(int n) { - int count = 0; - while (n != 0) { - ++count; - //把最右边的一个1变成0 - n = (n - 1) & n; - } - return count; -} -``` \ No newline at end of file diff --git "a/\345\211\221\346\214\207offer/\344\273\216\345\260\276\345\210\260\345\244\264\346\211\223\345\215\260\351\223\276\350\241\250.md" "b/\345\211\221\346\214\207offer/\344\273\216\345\260\276\345\210\260\345\244\264\346\211\223\345\215\260\351\223\276\350\241\250.md" deleted file mode 100644 index f925acc..0000000 --- "a/\345\211\221\346\214\207offer/\344\273\216\345\260\276\345\210\260\345\244\264\346\211\223\345\215\260\351\223\276\350\241\250.md" +++ /dev/null @@ -1,36 +0,0 @@ -### Title -输入一个链表,按链表值从尾到头的顺序返回一个ArrayList。 - -### Coding -``` -/** -* public class ListNode { -* int val; -* ListNode next = null; -* -* ListNode(int val) { -* this.val = val; -* } -* } -* -*/ -import java.util.ArrayList; -public class Solution { - public ArrayList printListFromTailToHead(ListNode listNode) { - ArrayList arrayList = new ArrayList(); - ArrayList result= new ArrayList(); - if (listNode == null){ - return result; - } - arrayList.add(listNode.val); - while (listNode.next != null){ - arrayList.add(listNode.next.val); - listNode = listNode.next; - } - for (int i =arrayList.size()-1; i>=0 ; i--){ - result.add(arrayList.get(i)); - } - return result; - } -} -``` \ No newline at end of file diff --git "a/\345\211\221\346\214\207offer/\345\217\215\350\275\254\351\223\276\350\241\250.md" "b/\345\211\221\346\214\207offer/\345\217\215\350\275\254\351\223\276\350\241\250.md" deleted file mode 100644 index d2f4077..0000000 --- "a/\345\211\221\346\214\207offer/\345\217\215\350\275\254\351\223\276\350\241\250.md" +++ /dev/null @@ -1,49 +0,0 @@ -### Title - -输入一个链表,反转链表后,输出新链表的表头。 - - -### 思考 - -难点在于不能让链表的控制中断, - -1. 保存该节点next节点。 -2. 将上一个节点pre赋值给该节点的next节点。 -3. 将当前节点赋值给pre节点。 -4. 将该节点的next节点赋值给head节点。 - -### Coding - -``` -public class Solution { - public ListNode ReverseList(ListNode head) { - - if(head==null) - return null; - //head为当前节点,如果当前节点为空的话,那就什么也不做,直接返回null; - ListNode pre = null; - ListNode next = null; - //当前节点是head,pre为当前节点的前一节点,next为当前节点的下一节点 - //需要pre和next的目的是让当前节点从pre->head->next1->next2变成pre<-head next1->next2 - //即pre让节点可以反转所指方向,但反转之后如果不用next节点保存next1节点的话,此单链表就此断开了 - //所以需要用到pre和next两个节点 - //1->2->3->4->5 - //1<-2<-3 4->5 - while(head!=null){ - //做循环,如果当前节点不为空的话,始终执行此循环,此循环的目的就是让当前节点从指向next到指向pre - //如此就可以做到反转链表的效果 - //先用next保存head的下一个节点的信息,保证单链表不会因为失去head节点的原next节点而就此断裂 - next = head.next; - //保存完next,就可以让head从指向next变成指向pre了,代码如下 - head.next = pre; - //head指向pre后,就继续依次反转下一个节点 - //让pre,head,next依次向后移动一个节点,继续下一次的指针反转 - pre = head; - head = next; - } - //如果head为null的时候,pre就为最后一个节点了,但是链表已经反转完毕,pre就是反转后链表的第一个节点 - //直接输出pre就是我们想要得到的反转后的链表 - return pre; - } -} -``` \ No newline at end of file diff --git "a/\345\211\221\346\214\207offer/\345\217\230\346\200\201\350\267\263\345\217\260\351\230\266.md" "b/\345\211\221\346\214\207offer/\345\217\230\346\200\201\350\267\263\345\217\260\351\230\266.md" deleted file mode 100644 index 8be7b96..0000000 --- "a/\345\211\221\346\214\207offer/\345\217\230\346\200\201\350\267\263\345\217\260\351\230\266.md" +++ /dev/null @@ -1,72 +0,0 @@ -### Title -一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。 -### 分析 - -关于本题,前提是n个台阶会有一次n阶的跳法。分析如下: - -f(1) = 1 - -f(2) = f(2-1) + f(2-2) //f(2-2) 表示2阶一次跳2阶的次数。 - -f(3) = f(3-1) + f(3-2) + f(3-3) - -... - -f(n) = f(n-1) + f(n-2) + f(n-3) + ... + f(n-(n-1)) + f(n-n) - - - -说明: - -1)这里的f(n) 代表的是n个台阶有一次1,2,...n阶的 跳法数。 - -2)n = 1时,只有1种跳法,f(1) = 1 - -3) n = 2时,会有两个跳得方式,一次1阶或者2阶,这回归到了问题(1) ,f(2) = f(2-1) + f(2-2) - -4) n = 3时,会有三种跳得方式,1阶、2阶、3阶, - - 那么就是第一次跳出1阶后面剩下:f(3-1);第一次跳出2阶,剩下f(3-2);第一次3阶,那么剩下f(3-3) - - 因此结论是f(3) = f(3-1)+f(3-2)+f(3-3) - -5) n = n时,会有n中跳的方式,1阶、2阶...n阶,得出结论: - - f(n) = f(n-1)+f(n-2)+...+f(n-(n-1)) + f(n-n) => f(0) + f(1) + f(2) + f(3) + ... + f(n-1) - - - -6) 由以上已经是一种结论,但是为了简单,我们可以继续简化: - - f(n-1) = f(0) + f(1)+f(2)+f(3) + ... + f((n-1)-1) = f(0) + f(1) + f(2) + f(3) + ... + f(n-2) - - f(n) = f(0) + f(1) + f(2) + f(3) + ... + f(n-2) + f(n-1) = f(n-1) + f(n-1) - - 可以得出: - - f(n) = 2*f(n-1) - - - -7) 得出最终结论,在n阶台阶,一次有1、2、...n阶的跳的方式时,总得跳法为: -``` - | 1 ,(n=0 ) - -f(n) = | 1 ,(n=1 ) - - | 2*f(n-1),(n>=2) -``` -### Coding -``` -public class Solution { - public int JumpFloorII(int target) { - if (target == 0){ - return 0 ; - }else if (target == 1){ - return 1; - }else { - return 2*JumpFloorII(target-1); - } - } -} -``` \ No newline at end of file diff --git "a/\345\211\221\346\214\207offer/\345\220\210\345\271\266\344\270\244\344\270\252\346\216\222\345\272\217\347\232\204\351\223\276\350\241\250.md" "b/\345\211\221\346\214\207offer/\345\220\210\345\271\266\344\270\244\344\270\252\346\216\222\345\272\217\347\232\204\351\223\276\350\241\250.md" deleted file mode 100644 index acf84f1..0000000 --- "a/\345\211\221\346\214\207offer/\345\220\210\345\271\266\344\270\244\344\270\252\346\216\222\345\272\217\347\232\204\351\223\276\350\241\250.md" +++ /dev/null @@ -1,38 +0,0 @@ -### Title -输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。 - -### 思考 - -新建一个头节点,然后找出list1和list2的大小关系,然后相应的移动head节点和后推list的子节点。(如果list1.val>list2.var, 则head.next指向list2 ,然后head和list2都往后走个节点) -### Coding - -``` -public class Solution { - - public ListNode Merge(ListNode list1,ListNode list2) { - //新建一个头节点,用来存合并的链表。 - ListNode head=new ListNode(-1); - head.next=null; - ListNode root=head; - while(list1!=null&&list2!=null){ - if(list1.val array[high]: -出现这种情况的array类似[3,4,5,6,0,1,2],此时最小数字一定在mid的右边。 -`low = mid + 1` -2. - array[mid] == array[high]: -出现这种情况的array类似 [1,0,1,1,1] 或者[1,1,1,0,1],此时最小数字不好判断在mid左边 -还是右边,这时只好一个一个试 , -`high = high - 1` -3. array[mid] < array[high]: -出现这种情况的array类似[2,2,3,4,5,6,6],此时最小数字一定就是array[mid]或者在mid的左 -边。因为右边必然都是递增的。 -`high = mid` - -注意这里有个坑:如果待查询的范围最后只剩两个数,那么mid 一定会指向下标靠前的数字 -比如 array = [4,6] -array[low] = 4 ;array[mid] = 4 ; array[high] = 6 ; -如果high = mid - 1,就会产生错误, 因此high = mid -但情形(1)中low = mid + 1就不会错误 - -### Coding -``` -public class Solution { - public int minNumberInRotateArray(int [] array) { - if(array.length == 0){ - return 0; - } - int height = array.length-1; - int low = 0; - while(low< height){ - int mid = (low+height)/2; - if(array[mid]>array[height]){ - low = mid+1; - }else if(array[mid] == array[height]){ - // 1 0 1 1 1 - height--; - }else{ - height = mid; - } - } - return array[low]; - } -} -``` \ No newline at end of file diff --git "a/\345\211\221\346\214\207offer/\346\240\221\347\232\204\345\255\220\347\273\223\346\236\204.md" "b/\345\211\221\346\214\207offer/\346\240\221\347\232\204\345\255\220\347\273\223\346\236\204.md" deleted file mode 100644 index e509cc9..0000000 --- "a/\345\211\221\346\214\207offer/\346\240\221\347\232\204\345\255\220\347\273\223\346\236\204.md" +++ /dev/null @@ -1,47 +0,0 @@ -### Title - -输入两棵二叉树A,B,判断B是不是A的子结构。(ps:我们约定空树不是任意一个树的子结构) - -### 思考 - -先找出root1和root2相等的点,然后开始对比它们的字节点是否相同。 - - - -### Coding -``` -public class Solution { - public boolean HasSubtree(TreeNode root1,TreeNode root2) { - boolean result = false; - if (root1 == null || root2 == null){ - return false; - } - if (root1.val == root2.val){ - //判断是否有 子树关系 - result = isSubTree(root1, root2); - } - if(!result){ - result = HasSubtree(root1.right, root2); - } - if(!result){ - result = HasSubtree(root1.left, root2); - } - - return result; - } - public boolean isSubTree(TreeNode node1,TreeNode node2){ - // 如果node2为空,说明node2 已经为空 - if (node2 == null){ - return true; - } - // 如果node2 不为空, node1 为空,则没有子树 - if (node1 == null){ - return false; - } - if (node1.val != node2.val){ - return false; - } - return isSubTree(node1.left, node2.left)&&isSubTree(node1.right, node2.right); - } -} -``` \ No newline at end of file diff --git "a/\345\211\221\346\214\207offer/\347\224\250\344\270\244\344\270\252\346\240\210\345\256\236\347\216\260\351\230\237\345\210\227.md" "b/\345\211\221\346\214\207offer/\347\224\250\344\270\244\344\270\252\346\240\210\345\256\236\347\216\260\351\230\237\345\210\227.md" deleted file mode 100644 index c10caf7..0000000 --- "a/\345\211\221\346\214\207offer/\347\224\250\344\270\244\344\270\252\346\240\210\345\256\236\347\216\260\351\230\237\345\210\227.md" +++ /dev/null @@ -1,48 +0,0 @@ -### Title -用两个栈来实现一个队列,完成队列的Push和Pop操作。 队列中的元素为int类型。 - -### 分析 -##### 用两个栈实现一个队列的功能?要求给出算法和思路! -``` -入队:将元素进栈A - -出队:判断栈B是否为空,如果为空,则将栈A中所有元素pop,并push进栈B,栈B出栈; - - 如果不为空,栈B直接出栈。 -``` -##### 用两个队列实现一个栈的功能?要求给出算法和思路! -``` -入栈:将元素进队列A - -出栈:判断队列A中元素的个数是否为1,如果等于1,则出队列,否则将队列A中的元素 以此出队列并放入队列B,直到队列A中的元素留下一个,然后队列A出队列,再把 队列B中的元素出队列以此放入队列A中。 -``` -### Coding - -``` -public class Solution { - Stack stack1 = new Stack(); - Stack stack2 = new Stack(); - - /** - * 借助A来调整顺序 - */ - public void push(int node) { - - stack1.push(node); - } - - public int pop() { - - if(stack1.empty() && stack2.empty()){ - throw new RuntimeException("Queue is empty!"); - } - // 如果B为空, 就把 A.pop 放入 B - if (stack2.empty()){ - while (!stack1.empty()) { - stack2.push(stack1.pop()); - } - } - return stack2.pop(); - } -} -``` \ No newline at end of file diff --git "a/\345\211\221\346\214\207offer/\347\237\251\345\275\242\350\246\206\347\233\226.md" "b/\345\211\221\346\214\207offer/\347\237\251\345\275\242\350\246\206\347\233\226.md" deleted file mode 100644 index 8f1b340..0000000 --- "a/\345\211\221\346\214\207offer/\347\237\251\345\275\242\350\246\206\347\233\226.md" +++ /dev/null @@ -1,48 +0,0 @@ -### Title - -我们可以用`2*1` 的小矩形横着或者竖着去覆盖更大的矩形。请问用n个`2*1`的小矩形无重叠地覆盖一个2*n的大矩形,总共有多少种方法? - - -### 分析 -![image](https://uploadfiles.nowcoder.com/images/20160616/716804_1466088939214_DB8DE8E90C58DADF4C1048A7B110E8E5) - - -痛定思痛,还是不能够贪小便宜。用归纳法归纳如下, -1. 当 n < 1时,显然不需要用`2*1`块覆盖,按照题目提示应该返回 0。 -2. 当 n = 1时,只存在一种情况。 - -3. 当 n = 2时,存在两种情况。 - -4. 当 n = 3时,明显感觉到如果没有章法,思维难度比之前提升挺多的。 - -... 尝试归纳,本质上 n 覆盖方法种类都是对 n - 1 时的扩展。 -可以明确,n 时必定有 n-1时原来方式与`2*1`的方块结合。也就是说, `f(n) = f(n-1) + ?(暂时无法判断)。` -5. 如果我们现在归纳 n = 4,应该是什么形式? - * 保持原来n = 3时内容,并扩展一个 2*1 方块,形式分别为 “| | | |”、“= | |”、“| = |” - * 新增加的`2*1 `方块与临近的`2*1`方块组成` 2*2`结构,然后可以变形成 “=”。于是 n = 4在原来n = 3基础上增加了"| | ="、“= =”。 - -所以,自然而然可以得出规律: f(n) = f(n-1) + f(n-2), (n > 2)。 - -#### 拓展 - -如果看了这一套理论还存在疑惑。可以尝试将题目改成`1*3`方块覆盖`3*n`、`1*4`方块覆盖`4*n`。 - -相应的结论应该是: -1. `1 * 3`方块 覆 盖`3*n`区域:`f(n) = f(n-1) + f(n - 3), (n > 3)` -2. `1 *4` 方块 覆 盖`4*n`区域:`f(n) = f(n-1) + f(n - 4),(n > 4)` -更一般的结论,如果用`1*m`的方块覆盖`m*n`区域,递推关系式为`f(n) = f(n-1) + f(n-m),(n > m)`。 -### Coding - -``` -public class Solution { - public int RectCover(int target) { - if (target <= 0){ - return 0; - } else if (target == 1 || target == 2){ - return target; - }else { - return RectCover(target-1)+RectCover(target-2); - } - } -} -``` diff --git "a/\345\211\221\346\214\207offer/\350\260\203\346\225\264\346\225\260\347\273\204\351\241\272\345\272\217\344\275\277\345\245\207\346\225\260\344\275\215\344\272\216\345\201\266\346\225\260\345\211\215\351\235\242.md" "b/\345\211\221\346\214\207offer/\350\260\203\346\225\264\346\225\260\347\273\204\351\241\272\345\272\217\344\275\277\345\245\207\346\225\260\344\275\215\344\272\216\345\201\266\346\225\260\345\211\215\351\235\242.md" deleted file mode 100644 index ebfd711..0000000 --- "a/\345\211\221\346\214\207offer/\350\260\203\346\225\264\346\225\260\347\273\204\351\241\272\345\272\217\344\275\277\345\245\207\346\225\260\344\275\215\344\272\216\345\201\266\346\225\260\345\211\215\351\235\242.md" +++ /dev/null @@ -1,25 +0,0 @@ -### Title - -输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。 - -### Coding - -``` -public class Solution { - public void reOrderArray(int [] array) { - // 复制一个数组出来,并对原数组进行覆盖 - int [] prototype = array.clone(); - int oddCount = 0; - for (int i = 0 ;i< prototype.length; i++){ - if ((prototype[i] & 1 ) == 1){ - array[oddCount++] = prototype[i]; - } - } - for (int i = 0; i< prototype.length; i++){ - if ((prototype[i]&1) == 0){ - array[oddCount++] = prototype[i]; - } - } - } -} -``` \ No newline at end of file diff --git "a/\345\211\221\346\214\207offer/\350\267\263\345\217\260\351\230\266.md" "b/\345\211\221\346\214\207offer/\350\267\263\345\217\260\351\230\266.md" deleted file mode 100644 index 720fd13..0000000 --- "a/\345\211\221\346\214\207offer/\350\267\263\345\217\260\351\230\266.md" +++ /dev/null @@ -1,35 +0,0 @@ -### Title - -一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法(先后次序不同算不同的结果)。 - -### 分析 - - -对于本题,前提只有 一次 1阶或者2阶的跳法。 - -a.如果两种跳法,1阶或者2阶,那么假定第一次跳的是一阶,那么剩下的是n-1个台阶,跳法是f(n-1); - -b.假定第一次跳的是2阶,那么剩下的是n-2个台阶,跳法是f(n-2) - -c.由a\b假设可以得出总跳法为: f(n) = f(n-1) + f(n-2) - -d.然后通过实际的情况可以得出:只有一阶的时候 f(1) = 1 ,只有两阶的时候可以有 f(2) = 2 - -e.可以发现最终得出的是一个斐波那契数列: - -### Coding -``` -public class Solution { - public int JumpFloor(int target) { - if (target <= 0){ - return -1; - }else if (target == 1){ - return 1; - }else if (target == 2){ - return 2; - }else { - return JumpFloor(target-1)+ JumpFloor(target-2); - } - } -} -``` \ No newline at end of file diff --git "a/\345\211\221\346\214\207offer/\351\207\215\345\273\272\344\272\214\345\217\211\346\240\221.md" "b/\345\211\221\346\214\207offer/\351\207\215\345\273\272\344\272\214\345\217\211\346\240\221.md" deleted file mode 100644 index 11f73a4..0000000 --- "a/\345\211\221\346\214\207offer/\351\207\215\345\273\272\344\272\214\345\217\211\346\240\221.md" +++ /dev/null @@ -1,36 +0,0 @@ -### Title -输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。 - -### 分析 -1. pre的第一个数肯定是根结点。 -2. 与pre第一个数相等的in的该结点前都是左子树,同理右边都是右子树。 -3. 拿到根结点左右子树的边界,进行遍历和递归。 - -![](https://upload-images.jianshu.io/upload_images/5786888-28570304603e331e.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1080/q/50) - -### Coding -``` -public class Solution { - - public TreeNode reConstructBinaryTree(int [] pre,int [] in) { - TreeNode root=reConstructBinaryTree(pre,0,pre.length-1,in,0,in.length-1); - return root; - } - //前序遍历{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6} - private TreeNode reConstructBinaryTree(int [] pre,int startPre,int endPre,int [] in,int startIn,int endIn) { - - if(startPre>endPre||startIn>endIn) - return null; - TreeNode root=new TreeNode(pre[startPre]); - - for(int i=startIn;i<=endIn;i++) - if(in[i]==pre[startPre]){ - root.left=reConstructBinaryTree(pre,startPre+1,startPre+i-startIn,in,startIn,i-1); - root.right=reConstructBinaryTree(pre,i-startIn+startPre+1,endPre,in,i+1,endIn); - break; - } - - return root; - } -} -``` \ No newline at end of file diff --git "a/\345\211\221\346\214\207offer/\351\223\276\350\241\250\344\270\255\345\200\222\346\225\260\347\254\254k\344\270\252\347\273\223\347\202\271.md" "b/\345\211\221\346\214\207offer/\351\223\276\350\241\250\344\270\255\345\200\222\346\225\260\347\254\254k\344\270\252\347\273\223\347\202\271.md" deleted file mode 100644 index 18fc63a..0000000 --- "a/\345\211\221\346\214\207offer/\351\223\276\350\241\250\344\270\255\345\200\222\346\225\260\347\254\254k\344\270\252\347\273\223\347\202\271.md" +++ /dev/null @@ -1,55 +0,0 @@ ->总体来说,面试官喜欢问一些题目简洁又清晰明了的经典算法,也不希望你浪费很长时间在上面,速度越快越好。我们甚至可以通过在理解的基础上记忆来达到目的。算法系列我将用心收集做IT要求你滚瓜烂熟的算法。 - - -### Title - -输入一个链表,输出该链表中倒数第k个结点。 - - -### Coding - -``` -public class Solution { - public ListNode FindKthToTail(ListNode head,int k) { - // 倒数第k个就是正数第 length-k+1 个 - if (head == null || k < 0){ - return null; - } - ListNode prototype = head; - int length = 1; - while (prototype.next != null){ - prototype = prototype.next; - length++; - } - if (length < k){ - return null; - } - for (int i = 0; i< length-k; i++){ - head = head.next; - } - return head; - } -} -``` -如果你能想出这个答案,你这一个题只能得至多一半的分。 - - -面试官不要这个答案,那最优解是什么呢? ->思路:当前链表为A,复制一个链表为B,让A先走k个节点,然后B走,当A的next节点为空,则当前的B所指的元素就是倒数第K个节点。 - -``` -public class Solution { - public ListNode FindKthToTail(ListNode head,int k) { - ListNode p, q; - p = q = head; - int i = 0; - for(; p!=null; i++){ - if(i>=k){ - q = q.next; - } - p = p.next; - } - return i printMatrix(int [][] array) { - ArrayList result = new ArrayList (); - if(array.length==0) return result; - int n = array.length,m = array[0].length; - if(m==0) return result; - int layers = (Math.min(n,m)-1)/2+1;//这个是层数 - for(int i=0;i=i)&&(n-i-1!=i);k--) result.add(array[n-i-1][k]); - //左下至左上, 列号不变 i, 行号从 n-i-2 开始--, 别且不能顶到头 - for(int j=n-i-2;(j>i)&&(m-i-1!=i);j--) result.add(array[j][i]); - } - return result; - } -} -``` \ No newline at end of file diff --git "a/\346\210\221\347\232\204\345\274\200\346\272\220\351\241\271\347\233\256/\345\274\200\346\272\220\351\241\271\347\233\256.md" "b/\346\210\221\347\232\204\345\274\200\346\272\220\351\241\271\347\233\256/\345\274\200\346\272\220\351\241\271\347\233\256.md" new file mode 100644 index 0000000..7c13fb6 --- /dev/null +++ "b/\346\210\221\347\232\204\345\274\200\346\272\220\351\241\271\347\233\256/\345\274\200\346\272\220\351\241\271\347\233\256.md" @@ -0,0 +1,45 @@ +# 竞赛网 + +#### 概述 +1. 使用Redis实现数据库减压,Mysql实现数据持久化。 +2. 使用SpringBoot做基础框架,JPA/Mybatis作ORM框架,Spring Security 作权限鉴定框架。 +3. 使用Dubbo做微服务框架,Zookeeper做服务注册中心。 +4. 使用Nginx做反向代理,Nginx+ FTP实现文件服务器。 + +git地址:https://github.com/fantj2016/internet-plus + + +# 博客论坛 +正在开源的路上... + +# Tomcat框架简单实现 +#### 概述 +1. 完成对请求头中url的解析,并返回静态资源。 +2. 完成对配置类`conf.properties`的加载,通过反射完成动态请求的实现。 + +git地址:https://github.com/fantj2016/MyTomcat + +# Jedis框架简单实现 +#### 概述 +redis运行流程:发送命令-命令排队-命令执行-返回结果 + +根据RESP协议, 模拟一个redis的客户端。 + +git地址:https://github.com/fantj2016/easy-jedis + +# MVC框架简单实现 +#### 概述 +1. IOC容器实现 +2. MVC视图解析实现。 + +git地址:https://github.com/fantj2016/easy-springmvc + +# rpc框架简单实现 +#### 概述 +1. 服务提供者向zk注册服务 +2. 消费者去注册中心拿到服务信息 +3. 消费者根据接口信息进行动态代理(代理内容:拿到接口名、方法名、参数类型、参数,然后根据接口名再获取到服务端url信息, 对对应的ip发送Invocation信息),服务端监听到请求,根据Invocation反射调用方法,将结果返回给消费者 + + + +git地址:https://github.com/fantj2016/easy-dubbo \ No newline at end of file diff --git "a/\347\256\227\346\263\225/1--\344\270\244\346\225\260\344\271\213\345\222\214.md" "b/\347\256\227\346\263\225/1--\344\270\244\346\225\260\344\271\213\345\222\214.md" deleted file mode 100644 index 8b6607a..0000000 --- "a/\347\256\227\346\263\225/1--\344\270\244\346\225\260\344\271\213\345\222\214.md" +++ /dev/null @@ -1,44 +0,0 @@ ->给定一个整数数组和一个目标值,找出数组中和为目标值的两个数。 -你可以假设每个输入只对应一种答案,且同样的元素不能被重复利用。 - -示例: -``` -给定 nums = [2, 7, 11, 15], target = 9 - -因为 nums[0] + nums[1] = 2 + 7 = 9 -所以返回 [0, 1] -``` -最初答案: -``` -class Solution { - public int[] twoSum(int[] nums, int target) { - int result[] = new int[2]; - for (int i = 0;i= 2 && transitionTo <= 16) { - String result = Integer.toString(Integer.valueOf(character, transitionFrom), transitionTo); - Integer resultNum = Integer.valueOf(result); - System.out.print(resultNum); - } else { - System.out.println("非法输入"); - } - }catch (Exception e){ - throw new RuntimeException("非法输入"); - } - } -} -``` -来看看Integer.valueOf(String s, int radix)源码 -``` -public static Integer valueOf(String s, int radix) throws NumberFormatException { - return Integer.valueOf(parseInt(s,radix)); - } -``` -parseInt(s,radix)源码 -``` -public static final int MIN_VALUE = 0x80000000; -public static final int MAX_VALUE = 0x7fffffff; -public static final int MIN_RADIX = 2; -public static final int MAX_RADIX = 36; - -public static int parseInt(String s, int radix) - throws NumberFormatException - { - /* - * WARNING: This method may be invoked early during VM initialization - * before IntegerCache is initialized. Care must be taken to not use - * the valueOf method. - */ - - if (s == null) { - throw new NumberFormatException("null"); - } - - if (radix < Character.MIN_RADIX) { - throw new NumberFormatException("radix " + radix + - " less than Character.MIN_RADIX"); - } - - if (radix > Character.MAX_RADIX) { - throw new NumberFormatException("radix " + radix + - " greater than Character.MAX_RADIX"); - } - - int result = 0; - boolean negative = false; - int i = 0, len = s.length(); - int limit = -Integer.MAX_VALUE; - int multmin; - int digit; - - if (len > 0) { - char firstChar = s.charAt(0); - if (firstChar < '0') { // Possible leading "+" or "-" - if (firstChar == '-') { - negative = true; - limit = Integer.MIN_VALUE; - } else if (firstChar != '+') - throw NumberFormatException.forInputString(s); - - if (len == 1) // Cannot have lone "+" or "-" - throw NumberFormatException.forInputString(s); - i++; - } - multmin = limit / radix; - while (i < len) { - // Accumulating negatively avoids surprises near MAX_VALUE - digit = Character.digit(s.charAt(i++),radix); - if (digit < 0) { - throw NumberFormatException.forInputString(s); - } - if (result < multmin) { - throw NumberFormatException.forInputString(s); - } - result *= radix; - if (result < limit + digit) { - throw NumberFormatException.forInputString(s); - } - result -= digit; - } - } else { - throw NumberFormatException.forInputString(s); - } - return negative ? result : -result; - } -``` -然后再分析Character.digit方法 -emmmm java用Unicode在处理。 - -``` -int digit(int ch, int radix) { - int value = -1; - if (radix >= Character.MIN_RADIX && radix <= Character.MAX_RADIX) { - int val = getProperties(ch); - int kind = val & 0x1F; - if (kind == Character.DECIMAL_DIGIT_NUMBER) { - value = ch + ((val & 0x3E0) >> 5) & 0x1F; - } - else if ((val & 0xC00) == 0x00000C00) { - // Java supradecimal digit - value = (ch + ((val & 0x3E0) >> 5) & 0x1F) + 10; - } - } - return (value < radix) ? value : -1; - } -``` -知识补充: - -* 2 to 8:以10010为例,要转成8进制,则从右向左看,每3个为一组,不足的补零,变成010 010,加上权值后为22,即为8进制数! - -* 8 to 2:与上面的相反,以27为例,要转为2进制,则每个位作为一组分开,变成2 7,通过权值变换后为010 111(为1的替换为权值,然后相加等于7,则4+2+1,即每个位都是1,故为111),最后得到的2进制数为010111,去掉左边的0,最终结果是10111。 -* 2 to 16: 以101110为例,要转成16进制,类似,从右向左看,每4个为一组,不足的补零,变成0010 1110,加上权值后为2E,有个规律,8进制的各个位<=7,16进制的各个位<=15,也就是说16进制中的数可以是1、2、3、4……9、A、B、C、D、E、F。 - -* 16 to 2:以EF为例,每个为作为一组分开,变成E F,通过权值变换后为1110 1111,最后得到的2进制是 11101111 -* 8 to 16:以27为例,8进制和16进制之间的转换需要用2进制来作为过渡,先转成2进制为010 111,然后从右向左数,将现在的3个一组变为4个一组,不足的补零,变为0001 0111,然后权值变换后为1 7,也就是16进制数 17 diff --git "a/\347\256\227\346\263\225/C---\344\270\215\345\220\211\345\210\251\346\227\245\346\234\237-.md" "b/\347\256\227\346\263\225/C---\344\270\215\345\220\211\345\210\251\346\227\245\346\234\237-.md" deleted file mode 100644 index 5666138..0000000 --- "a/\347\256\227\346\263\225/C---\344\270\215\345\220\211\345\210\251\346\227\245\346\234\237-.md" +++ /dev/null @@ -1,54 +0,0 @@ ->在国外,每月的13号和每周的星期5都是不吉利的。特别是当13号那天恰好是星期5时,更不吉利。已知某年的一月一日是星期w,并且这一年一定不是闰年,求出这一年所有13号那天是星期5的月份,按从小到大的顺序输出月份数字。(w=1..7) - ->Input -输入有一行,即一月一日星期几(w)。(1 <= w <= 7) - ->Output -输出有一到多行,每行一个月份,表示该月的13日是星期五。 - ->Sample Input -7 - ->Sample Output -1 -10 - ->Hint -1、3、5、7、8、10、12月各有31天 -4、6、9、11月各有30天 -2月有28天 - -``` -package com.fantJ.ACM; - -import java.util.Scanner; - -/** - * Created by Fant.J. - * 2017/12/5 16:02 - */ -public class Main{ - public static void main(String []args) { - - //没有加十二月分,因为十一月+30天就可以判断12月13号是否是星期5了 - int day[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30}; - Scanner scanner = new Scanner(System.in); - int weekday = scanner.nextInt(); - if (weekday>=1 && weekday<=7){ - //先计算一月的13号是否是星期5 - weekday = (weekday+12)%7; - if (weekday == 5){ - System.out.println(1); - } - for (int i = 0;i给定一个整数,请将该数各个位上数字反转得到一个新数。新数也应满足整数的常见形式,即除非给定的原数为零,否则反转后得到的新数的最高位数字不应为零(参见样例 2) 。 - ->Input -输入共 1 行,一个整数 N。 - ->Output -输出共 1 行,一个整数,表示反转后的新数。 - ->Sample Input -样例 #1: -123 ->样例 #2: --380 - ->Sample Output -样例 #1: -321 ->样例 #2: --83 - ->Hint --1,000,000,000 ≤ N ≤1,000,000,000。 - -``` -import java.util.Scanner; - -/** - * Created by Fant.J. - * 2017/12/5 16:39 - */ -public class Main{ - public static void main(String []args){ - Scanner scanner = new Scanner(System.in); - int data = scanner.nextInt(); - int result = 0; - while (true){ - int n = data%10; - result = result*10 + n; - data /= 10; - if (data == 0) {break;} - } - System.out.println(result); - scanner.close(); - } -} -``` diff --git "a/\347\256\227\346\263\225/E---\351\270\241\345\205\224\345\220\214\347\254\274-.md" "b/\347\256\227\346\263\225/E---\351\270\241\345\205\224\345\220\214\347\254\274-.md" deleted file mode 100644 index db1f67b..0000000 --- "a/\347\256\227\346\263\225/E---\351\270\241\345\205\224\345\220\214\347\254\274-.md" +++ /dev/null @@ -1,41 +0,0 @@ ->一个笼子里面关了鸡和兔子(鸡有2只脚,兔子有4只脚,没有例外)。已经知道了笼子里面脚的总数a,问笼子里面至少有多少只动物,至多有多少只动物。 - ->Input -一行,一个正整数a (a < 32768)。 - ->Output -一行,包含两个正整数,第一个是最少的动物数,第二个是最多的动物数,两个正整数用一个空格分开。 -如果没有满足要求的答案,则输出两个0,中间用一个空格分开。 - ->Sample Input -20 - ->Sample Output -5 10 - -``` -import java.util.Scanner; - -/** - * Created by Fant.J. - * 2017/12/5 17:10 - */ -public class Main { - public static void main(String []args){ - Scanner scanner = new Scanner(System.in); - int count = scanner.nextInt(); - //总数肯定是2的倍数 - if (count%2 == 0){ - //如果全是鸡,总数最多 count/2 - int max = count/2; - //鸡尽可能的少,总数就最少 - int tuzi = count/4; - int ji = (count%4)/2; - int min = tuzi+ji; - System.out.print(min+" "+max); - }else { - System.out.println(0+" "+0); - } - } -} -``` diff --git "a/\347\256\227\346\263\225/F---\346\225\260\345\255\227\346\226\271\346\240\274-.md" "b/\347\256\227\346\263\225/F---\346\225\260\345\255\227\346\226\271\346\240\274-.md" deleted file mode 100644 index b15622a..0000000 --- "a/\347\256\227\346\263\225/F---\346\225\260\345\255\227\346\226\271\346\240\274-.md" +++ /dev/null @@ -1,41 +0,0 @@ ->![image](http://upload-images.jianshu.io/upload_images/5786888-5e9b46b1e0eca944?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) -如上图,有3个方格,每个方格里面都有一个整数a1,a2,a3。已知0 <= a1, a2, a3 <= n,而且a1 + a2是2的倍数,a2 + a3是3的倍数, a1 + a2 + a3是5的倍数。你的任务是找到一组a1,a2,a3,使得a1 + a2 + a3最大。 - ->Input -一行,包含一个整数n (0 <= n <= 100)。 - ->Output -一个整数,即a1 + a2 + a3的最大值。 - ->Sample Input -3 - ->Sample Output -5 -``` -import java.util.Scanner; - -/** - * Created by Fant.J. - * 2017/12/5 17:30 - */ -public class Main { - public static void main(String []args){ - Scanner scanner = new Scanner(System.in); - int n = scanner.nextInt(); - int result = 0; - int max = 0; - for (int a1 =n;a1>=0;a1--){ - for (int a2 = n;a2>=0;a2--){ - for (int a3 = n;a3>=0;a3--){ - if ((a1+a2)%2==0 && (a2+a3)%3==0 && (a1+a2+a3)%5==0){ - result = a1+a2+a3; - max = max>result?max:result; - } - } - } - } - System.out.println(max); - } -} -``` diff --git "a/\347\256\227\346\263\225/G---IP\345\234\260\345\235\200\350\275\254\346\215\242-.md" "b/\347\256\227\346\263\225/G---IP\345\234\260\345\235\200\350\275\254\346\215\242-.md" deleted file mode 100644 index 5eb52f7..0000000 --- "a/\347\256\227\346\263\225/G---IP\345\234\260\345\235\200\350\275\254\346\215\242-.md" +++ /dev/null @@ -1,83 +0,0 @@ ->IP地址总是由4个0-255的数字以"."隔开的形式来显示给用户,例如192.168.0.1。在计算机中,一个IP地址用4字节来依次存储其从右到左的4个数字部分,每个字节(8比特)以2进制的形式存储相应的IP地址数字,请你实现一个从IP地址的显示格式到计算机存储格式的转换。 - ->Input -每行输入一个IP地址,如果输入为-1,结束输入 - ->Output -每行输出一个IP地址在计算机存储中以二进制表示的4字节内容 - ->Sample Input -192.168.0.1 -255.255.0.0 -1.0.0.1 --1 - ->Sample Output -11000000101010000000000000000001 -11111111111111110000000000000000 -00000001000000000000000000000001 -``` -import java.util.ArrayList; -import java.util.List; -import java.util.Scanner; - -/** - * Created by Fant.J. - * 2017/12/6 19:20 - */ -public class Main { - private static List list = new ArrayList(); - private static String ipAddr = null; - private static String result = ""; - private static int count = 1; - - public static void main(String []args){ - Scanner scanner = new Scanner(System.in); - ipAddr = scanner.nextLine(); - while (! ipAddr.contains("-1")){ - - String[] datas = ipAddr.split("\\."); - int data1 = Integer.valueOf(datas[0]); - int data2 = Integer.valueOf(datas[1]); - int data3 = Integer.valueOf(datas[2]); - int data4 = Integer.valueOf(datas[3]); - pincou(data4); - pincou(data3); - pincou(data2); - pincou(data1); - // System.out.println(result.length()); - list.add(result); - //初始化 三个参数 - result=""; - count = 1; - ipAddr = scanner.nextLine(); - } - for (Object item:list){ - System.out.println(item); - } - } - public static void pincou(int data){ - while (true){ - int yushu = data%2; - result = yushu+result; - data /= 2; - if (data==0){ - //判断下是否够8个字节 - int judge = result.length(); - if (judge<8*count){ - for (int i = 0;i<8*count-judge;i++){ - result = 0+result; - } - } - count++; - break; - } - } - } - - -} -``` -做的过程中有个逻辑错误,找了好长时间分享给大家。 -就是在获取result长度当作for循环条件的时候。我起初是用result.lenth() 获取。导致字节数不够8(不满足我的要求)就运行下面程序。之后我先把这个长度赋值给变量judge,然后在做循环。希望大家以后用循环,切忌把动态改变的东西当作条件来判断 -![image.png](http://upload-images.jianshu.io/upload_images/5786888-3e69db69a4ad79e2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) diff --git "a/\347\256\227\346\263\225/H---1\347\232\204\344\270\252\346\225\260---(Java).md" "b/\347\256\227\346\263\225/H---1\347\232\204\344\270\252\346\225\260---(Java).md" deleted file mode 100644 index e4441d4..0000000 --- "a/\347\256\227\346\263\225/H---1\347\232\204\344\270\252\346\225\260---(Java).md" +++ /dev/null @@ -1,73 +0,0 @@ ->给定一个十进制整数N,求其对应2进制数中1的个数 - ->Input -第一个整数表示有N组测试数据,其后N行是对应的测试数据,每行为一个整数。 - ->Output -N行,每行输出对应一个输入。 - ->Sample Input -4 -2 -100 -1000 -66 - ->Sample Output -1 -3 -6 -2 -``` -import java.util.ArrayList; -import java.util.List; -import java.util.Scanner; - -/** - * Created by Fant.J. - * 2017/12/7 18:51 - */ -public class Main { - public static void main(String []args){ - List list = new ArrayList(); - String result = ""; - Scanner scanner = new Scanner(System.in); - int n = scanner.nextInt(); - int []data = new int[n]; - for (int i = 0;i有的时候对一种编码信号需要转换成另一种信号以便于发送或达到其他目的。考虑一种字母信号,只有ABCD四个大写字母组成。要求给定一串字母信号,转为数字信号,转化方式为:A转为数值1,B转为数值2,C表示符号$,D表示换行符。给定一串字母信号,求其数字信号。 -例如字母信号: -AABBCBACADAB -转化后数字信号为: -1122$21$1 -12 - ->Input -只有一行数据,由一串字符信号组成,长度小于500。 - ->Output -输出转换后的数字信号 - ->Sample Input -AABBCBACADAB - ->Sample Output -1122$21$1 -12 - -``` -import java.util.Scanner; - -/** - * A转为数值1,B转为数值2,C表示符号$,D表示换行符。给定一串字母信号,求其数字信号。 - * Created by Fant.J. - * 2017/12/7 19:19 - */ -public class Main { - public static void main(String []args){ - Scanner scanner = new Scanner(System.in); - String data = scanner.nextLine(); - char []chars = data.toCharArray(); - String result = ""; - for (int i = 0;i给出两个不大于65535的非负整数,判断其中一个的16位二进制表示形式,是否能由另一个的16位二进制表示形式经过循环左移若干位而得到。 -循环左移和普通左移的区别在于:最左边的那一位经过循环左移一位后就会被移到最右边去。比如: -1011 0000 0000 0001 经过循环左移一位后,变成 0110 0000 0000 0011, 若是循环左移2位,则变成 1100 0000 0000 0110 - ->Input -第一行是个整数n, 0 < n < 300000,表示后面还有n行数据 -后面是n行,每行有两个不大于65535的非负整数 - ->Output -对于每一行的两个整数,输出一行,内容为YES或NO - ->Sample Input -4 -2 4 -9 18 -45057 49158 -7 12 - ->Sample Output -YES -YES -YES -NO diff --git "a/\347\256\227\346\263\225/Java-\351\200\211\346\213\251\346\216\222\345\272\217selection-sort.md" "b/\347\256\227\346\263\225/Java-\351\200\211\346\213\251\346\216\222\345\272\217selection-sort.md" deleted file mode 100644 index 32256fe..0000000 --- "a/\347\256\227\346\263\225/Java-\351\200\211\346\213\251\346\216\222\345\272\217selection-sort.md" +++ /dev/null @@ -1,27 +0,0 @@ -找最小的和第一个位置做交换,递归下去 -``` -#include - -using namespace std; - -void selectionSort(int arr[],int n) { - for (int i = 0; i < n; ++i) { - //寻找【i,n】区间的最小值 - int minIndex = i; - for (int j = i + 1; j < n; ++j) { - if (arr[j] < arr[minIndex]) minIndex = j; - swap(arr[i], arr[minIndex]); - } - } -} -int main() { - int a[10] = {10,9,8,7,6,5,4,3,2,1}; - selectionSort(a,10); - for (int i = 0; i < 10; ++i) { - cout<  Given an array of integers, find two numbers such that they add up to a specific target number. -  The function twoSum should return indices of the two numbers such that they add up to the target, where index1 must be less than index2. Please note that your returned answers (both index1 and index2) are not zero-based. -  You may assume that each input would have exactly one solution. -  Input: numbers={2, 7, 11, 15}, target=9 -  Output: index1=1, index2=2 -specific 具体的 -assume 假定 - -意思是: -给定一个整数数组,找出其中两个数满足相加等于你指定的目标数字。 -这个方法twoSum必须要返回能够相加等于目标数字的两个数的索引,且index1必须要小于index2。请注意一点,你返回的结果(包括index1和index2)都不是基于0开始的。你可以假设每一个输入肯定只有一个结果。 - -``` -public int[] twoSum(int[] numbers, int target) { - int[] result = new int[2]; - Map map = new HashMap(); - for (int i = 0; i < numbers.length; i++) { - if (map.containsKey(target - numbers[i])) { - result[1] = i + 1; - result[0] = map.get(target - numbers[i]); - return result; - } - map.put(numbers[i], i + 1); - } - return result; -} -``` - - - - - - - - diff --git "a/\347\256\227\346\263\225/\346\227\240\346\240\207\351\242\230\346\226\207\347\253\240.md" "b/\347\256\227\346\263\225/\346\227\240\346\240\207\351\242\230\346\226\207\347\253\240.md" deleted file mode 100644 index 8b13789..0000000 --- "a/\347\256\227\346\263\225/\346\227\240\346\240\207\351\242\230\346\226\207\347\253\240.md" +++ /dev/null @@ -1 +0,0 @@ - diff --git "a/\347\275\221\347\273\234/Java-9-\345\222\214Spring-Boot-2-0\347\272\267\347\272\267\345\256\243\345\270\203\346\224\257\346\214\201\347\232\204HTTP-2\345\210\260\345\272\225\346\230\257\344\273\200\344\271\210\357\274\237.md" "b/\347\275\221\347\273\234/Java-9-\345\222\214Spring-Boot-2-0\347\272\267\347\272\267\345\256\243\345\270\203\346\224\257\346\214\201\347\232\204HTTP-2\345\210\260\345\272\225\346\230\257\344\273\200\344\271\210\357\274\237.md" deleted file mode 100644 index dce3240..0000000 --- "a/\347\275\221\347\273\234/Java-9-\345\222\214Spring-Boot-2-0\347\272\267\347\272\267\345\256\243\345\270\203\346\224\257\346\214\201\347\232\204HTTP-2\345\210\260\345\272\225\346\230\257\344\273\200\344\271\210\357\274\237.md" +++ /dev/null @@ -1 +0,0 @@ -[Java 9 和Spring Boot 2.0纷纷宣布支持的HTTP/2到底是什么?](https://juejin.im/post/5ab6f8f06fb9a028ca52edc2?utm_source=gold_browser_extension#heading-0) diff --git "a/\347\275\221\347\273\234/TLV-\346\240\274\345\274\217\345\217\212\347\274\226\350\247\243\347\240\201.md" "b/\347\275\221\347\273\234/TLV-\346\240\274\345\274\217\345\217\212\347\274\226\350\247\243\347\240\201.md" deleted file mode 100644 index 057b3ff..0000000 --- "a/\347\275\221\347\273\234/TLV-\346\240\274\345\274\217\345\217\212\347\274\226\350\247\243\347\240\201.md" +++ /dev/null @@ -1,19 +0,0 @@ -几乎所有的需要在卡片和终端之间传送的数据都是TLV格式的. -我给你举个例子方便快速理解: -`TLV`是`tag`, `length`和`value`的缩写.一个基本的数据元就包括上面三个域. `Tag`唯一标识该数据元, `length`是`value`域的长度. Value就是数据本身了. 举个例子, 下面是一个tlv格式的AID(应用标识符)字节串”`9F0607A0000000031010`”, 其中`9F06`是tag, `07`是长度, `A0000000031010`就是AID本身的值了. - -对于程序编写人员来说,如果有类似上面这样的一串TLV编码的字节串从卡片传过来, 怎么样从中提取我们想要的数据. 这就牵扯出TLV解码的问题了 - -TLV一种可变格式,TLV的意思就是:Type类型, Lenght长度,Value值; -Type和Length的长度固定,一般那是2、4个字节; -Value的长度有Length指定; - -解析方法: - 1.读取type 转换为ntohl、ntohs转换为主机字节序得到类型;指针偏移+2或4 - 2.读取lenght,转换为ntohl、ntohs转换为主机字节序得到长度;指针偏移+2或4 - 3.根据得到的长度读取value,指针偏移+Length; -TLV编码就是指先对Tag编码,再对Length编码,最后对Value编码。 - - - - diff --git "a/\347\275\221\347\273\234/UDP\345\215\217\350\256\256.md" "b/\347\275\221\347\273\234/UDP\345\215\217\350\256\256.md" deleted file mode 100644 index 03ef9e9..0000000 --- "a/\347\275\221\347\273\234/UDP\345\215\217\350\256\256.md" +++ /dev/null @@ -1,32 +0,0 @@ -UDP协议在IP协议上增加了复用、分用和差错检测功能。UDP的特点: - - A)是无连接的。相比于TCP协议,UDP协议在传送数据前不需要建立连接,当然也就没有释放连接。 - - B)是尽最大努力交付的。也就是说UDP协议无法保证数据能够准确的交付到目的主机。也不需要对接收到的UDP报文进行确认。 - - C)是面向报文的。也就是说UDP协议将应用层传输下来的数据封装在一个UDP包中,不进行拆分或合并。因此,运输层在收到对方的UDP包后,会去掉首部后,将数据原封不动的交给应用进程。 - - D)没有拥塞控制。因此UDP协议的发送速率不送网络的拥塞度影响。 - - E)UDP支持一对一、一对多、多对一和多对多的交互通信。 - - F)UDP的头部占用较小,只占用8个字节。 - -### UDP报文格式 - UDP协议分为首部字段和数据字段,其中首部字段只占用8个字节,分别是个占用两个字节的源端口、目的端口、长度和检验和。 -![](http://upload-images.jianshu.io/upload_images/5786888-6d3608d3dff36744.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - -* 长度:UDP报文的整个大小,最小为8个字节(仅为首部)。 - -* 检验和:在进行检验和计算时,会添加一个伪首部一起进行运算。伪首部(占用12个字节)为:4个字节的源IP地址、4个字节的目的IP地址、1个字节的0、一个字节的数字17、以及占用2个字节UDP长度。这个伪首部不是报文的真正首部,只是引入为了计算校验和。相对于IP协议的只计算首部,UDP检验和会把首部和数据一起进行校验。接收端进行的校验和与UDP报文中的校验和相与,如果无差错应该全为1。如果有误,则将报文丢弃或者发给应用层、并附上差错警告。 - -![](http://upload-images.jianshu.io/upload_images/5786888-daea4d5261b00900.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) - - - - - - - - - diff --git "a/\347\275\221\347\273\234/\345\257\271CSRF\344\275\240\344\272\206\350\247\243\345\244\232\345\260\221.md" "b/\347\275\221\347\273\234/\345\257\271CSRF\344\275\240\344\272\206\350\247\243\345\244\232\345\260\221.md" deleted file mode 100644 index b43237e..0000000 --- "a/\347\275\221\347\273\234/\345\257\271CSRF\344\275\240\344\272\206\350\247\243\345\244\232\345\260\221.md" +++ /dev/null @@ -1,22 +0,0 @@ ->CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。尽管听起来像跨站脚本([XSS](https://baike.baidu.com/item/XSS)),但它与XSS非常不同,XSS利用站点内的信任用户,而CSRF则通过伪装来自受信任用户的请求来利用受信任的网站。与[XSS](https://baike.baidu.com/item/XSS)攻击相比,CSRF攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比[XSS](https://baike.baidu.com/item/XSS) -更具危险性。 --摘自百度百科 - -我在这里给大家举个形象的例子来帮助大家理解。 - -攻击通过在授权用户访问的页面中包含链接或者脚本的方式工作。例如:一个网站用户Bob可能正在浏览聊天论坛,而同时另一个用户Alice也在此论坛中,并且后者刚刚发布了一个具有Bob银行链接的图片消息。设想一下,Alice编写了一个在Bob的银行站点上进行取款的**form提交的链接**,并将此链接作为**图片src(获取图片就是GET请求,对方就能抓取到你的浏览器cookie和更多的请求头)**。如果Bob的银行在cookie中保存他的授权信息,并且此**cookie没有过期**,那么当Bob的浏览器尝试装载图片时将提交这个取款form和他的**cookie**,这样在没经Bob同意的情况下便授权了这次事务。 - -CSRF是一种依赖web浏览器的、被混淆过的代理人攻击(deputy attack)。在上面银行示例中的代理人是Bob的web浏览器,它被混淆后误将Bob的授权直接交给了Alice使用。 - -下面是CSRF的常见特性: -依靠用户标识危害网站 -利用网站对用户标识的信任 -欺骗用户的浏览器发送HTTP请求给目标站点 -另外可以通过IMG标签会触发一个GET请求,可以利用它来实现CSRF攻击。 - -防范: -* 使用图片的CSRF攻击常常出现在网络论坛中,因为那里允许用户发布图片而不能使用JavaScript。 -* 当我们用鼠标在Blog/BBS/WebMail点击别人留下的链接的时候,说不定一场精心准备的CSRF攻击正等着我们。 - -Springsecurity 和shiro都有对csrf的验证模块,帮你更好的解决项目开发的这种风险。 - - diff --git "a/\350\256\276\350\256\241\346\250\241\345\274\217/2018-11-23.md" "b/\350\256\276\350\256\241\346\250\241\345\274\217/2018-11-23.md" deleted file mode 100644 index bade62b..0000000 --- "a/\350\256\276\350\256\241\346\250\241\345\274\217/2018-11-23.md" +++ /dev/null @@ -1 +0,0 @@ ->在UML的静态机制中类图是一个重点,它不但是设计人员关心的核心,更是实现人员关注的核心。建模工具也主要根据类图来产生代码。类图在UML的9个图中占据了一个相当重要的地位。