converter = something::startsWith;
+String converted = converter.convert("Java");
+System.out.println(converted); // "J"
+```
+
+接下来看看构造函数是如何使用`::`关键字来引用的,首先我们定义一个包含多个构造函数的简单类:
+
+```java
+class Person {
+ String firstName;
+ String lastName;
+
+ Person() {}
+
+ Person(String firstName, String lastName) {
+ this.firstName = firstName;
+ this.lastName = lastName;
+ }
+}
+```
+接下来我们指定一个用来创建Person对象的对象工厂接口:
+
+```java
+interface PersonFactory {
+ P create(String firstName, String lastName);
+}
+```
+
+这里我们使用构造函数引用来将他们关联起来,而不是手动实现一个完整的工厂:
+
+```java
+PersonFactory personFactory = Person::new;
+Person person = personFactory.create("Peter", "Parker");
+```
+我们只需要使用 `Person::new` 来获取Person类构造函数的引用,Java编译器会自动根据`PersonFactory.create`方法的参数类型来选择合适的构造函数。
+
+### Lamda 表达式作用域(Lambda Scopes)
+
+#### 访问局部变量
+
+我们可以直接在 lambda 表达式中访问外部的局部变量:
+
+```java
+final int num = 1;
+Converter stringConverter =
+ (from) -> String.valueOf(from + num);
+
+stringConverter.convert(2); // 3
+```
+
+但是和匿名对象不同的是,这里的变量num可以不用声明为final,该代码同样正确:
+
+```java
+int num = 1;
+Converter stringConverter =
+ (from) -> String.valueOf(from + num);
+
+stringConverter.convert(2); // 3
+```
+
+不过这里的 num 必须不可被后面的代码修改(即隐性的具有final的语义),例如下面的就无法编译:
+
+```java
+int num = 1;
+Converter stringConverter =
+ (from) -> String.valueOf(from + num);
+num = 3;//在lambda表达式中试图修改num同样是不允许的。
+```
+
+#### 访问字段和静态变量
+
+与局部变量相比,我们对lambda表达式中的实例字段和静态变量都有读写访问权限。 该行为和匿名对象是一致的。
+
+```java
+class Lambda4 {
+ static int outerStaticNum;
+ int outerNum;
+
+ void testScopes() {
+ Converter stringConverter1 = (from) -> {
+ outerNum = 23;
+ return String.valueOf(from);
+ };
+
+ Converter stringConverter2 = (from) -> {
+ outerStaticNum = 72;
+ return String.valueOf(from);
+ };
+ }
+}
+```
+
+#### 访问默认接口方法
+
+还记得第一节中的 formula 示例吗? `Formula` 接口定义了一个默认方法`sqrt`,可以从包含匿名对象的每个 formula 实例访问该方法。 这不适用于lambda表达式。
+
+无法从 lambda 表达式中访问默认方法,故以下代码无法编译:
+
+```java
+Formula formula = (a) -> sqrt(a * 100);
+```
+
+### 内置函数式接口(Built-in Functional Interfaces)
+
+JDK 1.8 API包含许多内置函数式接口。 其中一些借口在老版本的 Java 中是比较常见的比如: `Comparator` 或`Runnable`,这些接口都增加了`@FunctionalInterface`注解以便能用在 lambda 表达式上。
+
+但是 Java 8 API 同样还提供了很多全新的函数式接口来让你的编程工作更加方便,有一些接口是来自 [Google Guava](https://code.google.com/p/guava-libraries/) 库里的,即便你对这些很熟悉了,还是有必要看看这些是如何扩展到lambda上使用的。
+
+#### Predicates
+
+Predicate 接口是只有一个参数的返回布尔类型值的 **断言型** 接口。该接口包含多种默认方法来将 Predicate 组合成其他复杂的逻辑(比如:与,或,非):
+
+**译者注:** Predicate 接口源码如下
+
+```java
+package java.util.function;
+import java.util.Objects;
+
+@FunctionalInterface
+public interface Predicate {
+
+ // 该方法是接受一个传入类型,返回一个布尔值.此方法应用于判断.
+ boolean test(T t);
+
+ //and方法与关系型运算符"&&"相似,两边都成立才返回true
+ default Predicate and(Predicate super T> other) {
+ Objects.requireNonNull(other);
+ return (t) -> test(t) && other.test(t);
+ }
+ // 与关系运算符"!"相似,对判断进行取反
+ default Predicate negate() {
+ return (t) -> !test(t);
+ }
+ //or方法与关系型运算符"||"相似,两边只要有一个成立就返回true
+ default Predicate or(Predicate super T> other) {
+ Objects.requireNonNull(other);
+ return (t) -> test(t) || other.test(t);
+ }
+ // 该方法接收一个Object对象,返回一个Predicate类型.此方法用于判断第一个test的方法与第二个test方法相同(equal).
+ static Predicate isEqual(Object targetRef) {
+ return (null == targetRef)
+ ? Objects::isNull
+ : object -> targetRef.equals(object);
+ }
+```
+
+示例:
+
+```java
+Predicate predicate = (s) -> s.length() > 0;
+
+predicate.test("foo"); // true
+predicate.negate().test("foo"); // false
+
+Predicate nonNull = Objects::nonNull;
+Predicate isNull = Objects::isNull;
+
+Predicate isEmpty = String::isEmpty;
+Predicate isNotEmpty = isEmpty.negate();
+```
+
+#### Functions
+
+Function 接口接受一个参数并生成结果。默认方法可用于将多个函数链接在一起(compose, andThen):
+
+**译者注:** Function 接口源码如下
+
+```java
+
+package java.util.function;
+
+import java.util.Objects;
+
+@FunctionalInterface
+public interface Function {
+
+ //将Function对象应用到输入的参数上,然后返回计算结果。
+ R apply(T t);
+ //将两个Function整合,并返回一个能够执行两个Function对象功能的Function对象。
+ default Function compose(Function super V, ? extends T> before) {
+ Objects.requireNonNull(before);
+ return (V v) -> apply(before.apply(v));
+ }
+ //
+ default Function andThen(Function super R, ? extends V> after) {
+ Objects.requireNonNull(after);
+ return (T t) -> after.apply(apply(t));
+ }
+
+ static Function identity() {
+ return t -> t;
+ }
+}
+```
+
+
+
+```java
+Function toInteger = Integer::valueOf;
+Function backToString = toInteger.andThen(String::valueOf);
+backToString.apply("123"); // "123"
+```
+
+#### Suppliers
+
+Supplier 接口产生给定泛型类型的结果。 与 Function 接口不同,Supplier 接口不接受参数。
+
+```java
+Supplier personSupplier = Person::new;
+personSupplier.get(); // new Person
+```
+
+#### Consumers
+
+Consumer 接口表示要对单个输入参数执行的操作。
+
+```java
+Consumer greeter = (p) -> System.out.println("Hello, " + p.firstName);
+greeter.accept(new Person("Luke", "Skywalker"));
+```
+
+#### Comparators
+
+Comparator 是老Java中的经典接口, Java 8在此之上添加了多种默认方法:
+
+```java
+Comparator comparator = (p1, p2) -> p1.firstName.compareTo(p2.firstName);
+
+Person p1 = new Person("John", "Doe");
+Person p2 = new Person("Alice", "Wonderland");
+
+comparator.compare(p1, p2); // > 0
+comparator.reversed().compare(p1, p2); // < 0
+```
+
+## Optionals
+
+Optionals不是函数式接口,而是用于防止 NullPointerException 的漂亮工具。这是下一节的一个重要概念,让我们快速了解一下Optionals的工作原理。
+
+Optional 是一个简单的容器,其值可能是null或者不是null。在Java 8之前一般某个函数应该返回非空对象但是有时却什么也没有返回,而在Java 8中,你应该返回 Optional 而不是 null。
+
+译者注:示例中每个方法的作用已经添加。
+
+```java
+//of():为非null的值创建一个Optional
+Optional optional = Optional.of("bam");
+// isPresent(): 如果值存在返回true,否则返回false
+optional.isPresent(); // true
+//get():如果Optional有值则将其返回,否则抛出NoSuchElementException
+optional.get(); // "bam"
+//orElse():如果有值则将其返回,否则返回指定的其它值
+optional.orElse("fallback"); // "bam"
+//ifPresent():如果Optional实例有值则为其调用consumer,否则不做处理
+optional.ifPresent((s) -> System.out.println(s.charAt(0))); // "b"
+```
+
+推荐阅读:[[Java8]如何正确使用Optional](https://blog.kaaass.net/archives/764)
+
+## Streams(流)
+
+`java.util.Stream` 表示能应用在一组元素上一次执行的操作序列。Stream 操作分为中间操作或者最终操作两种,最终操作返回一特定类型的计算结果,而中间操作返回Stream本身,这样你就可以将多个操作依次串起来。Stream 的创建需要指定一个数据源,比如` java.util.Collection` 的子类,List 或者 Set, Map 不支持。Stream 的操作可以串行执行或者并行执行。
+
+首先看看Stream是怎么用,首先创建实例代码的用到的数据List:
+
+```java
+List stringCollection = new ArrayList<>();
+stringCollection.add("ddd2");
+stringCollection.add("aaa2");
+stringCollection.add("bbb1");
+stringCollection.add("aaa1");
+stringCollection.add("bbb3");
+stringCollection.add("ccc");
+stringCollection.add("bbb2");
+stringCollection.add("ddd1");
+```
+
+Java 8扩展了集合类,可以通过 Collection.stream() 或者 Collection.parallelStream() 来创建一个Stream。下面几节将详细解释常用的Stream操作:
+
+### Filter(过滤)
+
+过滤通过一个predicate接口来过滤并只保留符合条件的元素,该操作属于**中间操作**,所以我们可以在过滤后的结果来应用其他Stream操作(比如forEach)。forEach需要一个函数来对过滤后的元素依次执行。forEach是一个最终操作,所以我们不能在forEach之后来执行其他Stream操作。
+
+```java
+ // 测试 Filter(过滤)
+ stringList
+ .stream()
+ .filter((s) -> s.startsWith("a"))
+ .forEach(System.out::println);//aaa2 aaa1
+```
+
+forEach 是为 Lambda 而设计的,保持了最紧凑的风格。而且 Lambda 表达式本身是可以重用的,非常方便。
+
+### Sorted(排序)
+
+排序是一个 **中间操作**,返回的是排序好后的 Stream。**如果你不指定一个自定义的 Comparator 则会使用默认排序。**
+
+```java
+ // 测试 Sort (排序)
+ stringList
+ .stream()
+ .sorted()
+ .filter((s) -> s.startsWith("a"))
+ .forEach(System.out::println);// aaa1 aaa2
+```
+
+需要注意的是,排序只创建了一个排列好后的Stream,而不会影响原有的数据源,排序之后原数据stringCollection是不会被修改的:
+
+```java
+ System.out.println(stringList);// ddd2, aaa2, bbb1, aaa1, bbb3, ccc, bbb2, ddd1
+```
+
+### Map(映射)
+
+中间操作 map 会将元素根据指定的 Function 接口来依次将元素转成另外的对象。
+
+下面的示例展示了将字符串转换为大写字符串。你也可以通过map来讲对象转换成其他类型,map返回的Stream类型是根据你map传递进去的函数的返回值决定的。
+
+```java
+ // 测试 Map 操作
+ stringList
+ .stream()
+ .map(String::toUpperCase)
+ .sorted((a, b) -> b.compareTo(a))
+ .forEach(System.out::println);// "DDD2", "DDD1", "CCC", "BBB3", "BBB2", "AAA2", "AAA1"
+```
+
+
+
+### Match(匹配)
+
+Stream提供了多种匹配操作,允许检测指定的Predicate是否匹配整个Stream。所有的匹配操作都是 **最终操作** ,并返回一个 boolean 类型的值。
+
+```java
+ // 测试 Match (匹配)操作
+ boolean anyStartsWithA =
+ stringList
+ .stream()
+ .anyMatch((s) -> s.startsWith("a"));
+ System.out.println(anyStartsWithA); // true
+
+ boolean allStartsWithA =
+ stringList
+ .stream()
+ .allMatch((s) -> s.startsWith("a"));
+
+ System.out.println(allStartsWithA); // false
+
+ boolean noneStartsWithZ =
+ stringList
+ .stream()
+ .noneMatch((s) -> s.startsWith("z"));
+
+ System.out.println(noneStartsWithZ); // true
+```
+
+
+
+### Count(计数)
+
+计数是一个 **最终操作**,返回Stream中元素的个数,**返回值类型是 long**。
+
+```java
+ //测试 Count (计数)操作
+ long startsWithB =
+ stringList
+ .stream()
+ .filter((s) -> s.startsWith("b"))
+ .count();
+ System.out.println(startsWithB); // 3
+```
+
+### Reduce(规约)
+
+这是一个 **最终操作** ,允许通过指定的函数来讲stream中的多个元素规约为一个元素,规约后的结果是通过Optional 接口表示的:
+
+```java
+ //测试 Reduce (规约)操作
+ Optional reduced =
+ stringList
+ .stream()
+ .sorted()
+ .reduce((s1, s2) -> s1 + "#" + s2);
+
+ reduced.ifPresent(System.out::println);//aaa1#aaa2#bbb1#bbb2#bbb3#ccc#ddd1#ddd2
+```
+
+
+
+**译者注:** 这个方法的主要作用是把 Stream 元素组合起来。它提供一个起始值(种子),然后依照运算规则(BinaryOperator),和前面 Stream 的第一个、第二个、第 n 个元素组合。从这个意义上说,字符串拼接、数值的 sum、min、max、average 都是特殊的 reduce。例如 Stream 的 sum 就相当于`Integer sum = integers.reduce(0, (a, b) -> a+b);`也有没有起始值的情况,这时会把 Stream 的前面两个元素组合起来,返回的是 Optional。
+
+```java
+// 字符串连接,concat = "ABCD"
+String concat = Stream.of("A", "B", "C", "D").reduce("", String::concat);
+// 求最小值,minValue = -3.0
+double minValue = Stream.of(-1.5, 1.0, -3.0, -2.0).reduce(Double.MAX_VALUE, Double::min);
+// 求和,sumValue = 10, 有起始值
+int sumValue = Stream.of(1, 2, 3, 4).reduce(0, Integer::sum);
+// 求和,sumValue = 10, 无起始值
+sumValue = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get();
+// 过滤,字符串连接,concat = "ace"
+concat = Stream.of("a", "B", "c", "D", "e", "F").
+ filter(x -> x.compareTo("Z") > 0).
+ reduce("", String::concat);
+```
+
+上面代码例如第一个示例的 reduce(),第一个参数(空白字符)即为起始值,第二个参数(String::concat)为 BinaryOperator。这类有起始值的 reduce() 都返回具体的对象。而对于第四个示例没有起始值的 reduce(),由于可能没有足够的元素,返回的是 Optional,请留意这个区别。更多内容查看: [IBM:Java 8 中的 Streams API 详解](https://www.ibm.com/developerworks/cn/java/j-lo-java8streamapi/index.html)
+
+## Parallel Streams(并行流)
+
+前面提到过Stream有串行和并行两种,串行Stream上的操作是在一个线程中依次完成,而并行Stream则是在多个线程上同时执行。
+
+下面的例子展示了是如何通过并行Stream来提升性能:
+
+首先我们创建一个没有重复元素的大表:
+
+```java
+int max = 1000000;
+List values = new ArrayList<>(max);
+for (int i = 0; i < max; i++) {
+ UUID uuid = UUID.randomUUID();
+ values.add(uuid.toString());
+}
+```
+
+我们分别用串行和并行两种方式对其进行排序,最后看看所用时间的对比。
+
+### Sequential Sort(串行排序)
+
+```java
+//串行排序
+long t0 = System.nanoTime();
+long count = values.stream().sorted().count();
+System.out.println(count);
+
+long t1 = System.nanoTime();
+
+long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);
+System.out.println(String.format("sequential sort took: %d ms", millis));
+```
+
+```
+1000000
+sequential sort took: 709 ms//串行排序所用的时间
+```
+
+### Parallel Sort(并行排序)
+
+```java
+//并行排序
+long t0 = System.nanoTime();
+
+long count = values.parallelStream().sorted().count();
+System.out.println(count);
+
+long t1 = System.nanoTime();
+
+long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0);
+System.out.println(String.format("parallel sort took: %d ms", millis));
+
+```
+
+```java
+1000000
+parallel sort took: 475 ms//串行排序所用的时间
+```
+
+上面两个代码几乎是一样的,但是并行版的快了 50% 左右,唯一需要做的改动就是将 `stream()` 改为`parallelStream()`。
+
+## Maps
+
+前面提到过,Map 类型不支持 streams,不过Map提供了一些新的有用的方法来处理一些日常任务。Map接口本身没有可用的 `stream()`方法,但是你可以在键,值上创建专门的流或者通过 `map.keySet().stream()`,`map.values().stream()`和`map.entrySet().stream()`。
+
+此外,Maps 支持各种新的和有用的方法来执行常见任务。
+
+```java
+Map map = new HashMap<>();
+
+for (int i = 0; i < 10; i++) {
+ map.putIfAbsent(i, "val" + i);
+}
+
+map.forEach((id, val) -> System.out.println(val));//val0 val1 val2 val3 val4 val5 val6 val7 val8 val9
+```
+
+`putIfAbsent` 阻止我们在null检查时写入额外的代码;`forEach`接受一个 consumer 来对 map 中的每个元素操作。
+
+此示例显示如何使用函数在 map 上计算代码:
+
+```java
+map.computeIfPresent(3, (num, val) -> val + num);
+map.get(3); // val33
+
+map.computeIfPresent(9, (num, val) -> null);
+map.containsKey(9); // false
+
+map.computeIfAbsent(23, num -> "val" + num);
+map.containsKey(23); // true
+
+map.computeIfAbsent(3, num -> "bam");
+map.get(3); // val33
+```
+
+接下来展示如何在Map里删除一个键值全都匹配的项:
+
+```java
+map.remove(3, "val3");
+map.get(3); // val33
+map.remove(3, "val33");
+map.get(3); // null
+```
+
+另外一个有用的方法:
+
+```java
+map.getOrDefault(42, "not found"); // not found
+```
+
+对Map的元素做合并也变得很容易了:
+
+```java
+map.merge(9, "val9", (value, newValue) -> value.concat(newValue));
+map.get(9); // val9
+map.merge(9, "concat", (value, newValue) -> value.concat(newValue));
+map.get(9); // val9concat
+```
+
+Merge 做的事情是如果键名不存在则插入,否则则对原键对应的值做合并操作并重新插入到map中。
+
+## Data API(日期相关API)
+
+Java 8在 `java.time` 包下包含一个全新的日期和时间API。新的Date API与Joda-Time库相似,但它们不一样。以下示例涵盖了此新 API 的最重要部分。译者对这部分内容参考相关书籍做了大部分修改。
+
+**译者注(总结):**
+
+- Clock 类提供了访问当前日期和时间的方法,Clock 是时区敏感的,可以用来取代 `System.currentTimeMillis()` 来获取当前的微秒数。某一个特定的时间点也可以使用 `Instant` 类来表示,`Instant` 类也可以用来创建旧版本的`java.util.Date` 对象。
+
+- 在新API中时区使用 ZoneId 来表示。时区可以很方便的使用静态方法of来获取到。 抽象类`ZoneId`(在`java.time`包中)表示一个区域标识符。 它有一个名为`getAvailableZoneIds`的静态方法,它返回所有区域标识符。
+
+- jdk1.8中新增了 LocalDate 与 LocalDateTime等类来解决日期处理方法,同时引入了一个新的类DateTimeFormatter 来解决日期格式化问题。可以使用Instant代替 Date,LocalDateTime代替 Calendar,DateTimeFormatter 代替 SimpleDateFormat。
+
+
+
+### Clock
+
+Clock 类提供了访问当前日期和时间的方法,Clock 是时区敏感的,可以用来取代 `System.currentTimeMillis()` 来获取当前的微秒数。某一个特定的时间点也可以使用 `Instant` 类来表示,`Instant` 类也可以用来创建旧版本的`java.util.Date` 对象。
+
+```java
+Clock clock = Clock.systemDefaultZone();
+long millis = clock.millis();
+System.out.println(millis);//1552379579043
+Instant instant = clock.instant();
+System.out.println(instant);
+Date legacyDate = Date.from(instant); //2019-03-12T08:46:42.588Z
+System.out.println(legacyDate);//Tue Mar 12 16:32:59 CST 2019
+```
+
+### Timezones(时区)
+
+在新API中时区使用 ZoneId 来表示。时区可以很方便的使用静态方法of来获取到。 抽象类`ZoneId`(在`java.time`包中)表示一个区域标识符。 它有一个名为`getAvailableZoneIds`的静态方法,它返回所有区域标识符。
+
+```java
+//输出所有区域标识符
+System.out.println(ZoneId.getAvailableZoneIds());
+
+ZoneId zone1 = ZoneId.of("Europe/Berlin");
+ZoneId zone2 = ZoneId.of("Brazil/East");
+System.out.println(zone1.getRules());// ZoneRules[currentStandardOffset=+01:00]
+System.out.println(zone2.getRules());// ZoneRules[currentStandardOffset=-03:00]
+```
+
+### LocalTime(本地时间)
+
+LocalTime 定义了一个没有时区信息的时间,例如 晚上10点或者 17:30:15。下面的例子使用前面代码创建的时区创建了两个本地时间。之后比较时间并以小时和分钟为单位计算两个时间的时间差:
+
+```java
+LocalTime now1 = LocalTime.now(zone1);
+LocalTime now2 = LocalTime.now(zone2);
+System.out.println(now1.isBefore(now2)); // false
+
+long hoursBetween = ChronoUnit.HOURS.between(now1, now2);
+long minutesBetween = ChronoUnit.MINUTES.between(now1, now2);
+
+System.out.println(hoursBetween); // -3
+System.out.println(minutesBetween); // -239
+```
+
+LocalTime 提供了多种工厂方法来简化对象的创建,包括解析时间字符串.
+
+```java
+LocalTime late = LocalTime.of(23, 59, 59);
+System.out.println(late); // 23:59:59
+DateTimeFormatter germanFormatter =
+ DateTimeFormatter
+ .ofLocalizedTime(FormatStyle.SHORT)
+ .withLocale(Locale.GERMAN);
+
+LocalTime leetTime = LocalTime.parse("13:37", germanFormatter);
+System.out.println(leetTime); // 13:37
+```
+
+### LocalDate(本地日期)
+
+LocalDate 表示了一个确切的日期,比如 2014-03-11。该对象值是不可变的,用起来和LocalTime基本一致。下面的例子展示了如何给Date对象加减天/月/年。另外要注意的是这些对象是不可变的,操作返回的总是一个新实例。
+
+```java
+LocalDate today = LocalDate.now();//获取现在的日期
+System.out.println("今天的日期: "+today);//2019-03-12
+LocalDate tomorrow = today.plus(1, ChronoUnit.DAYS);
+System.out.println("明天的日期: "+tomorrow);//2019-03-13
+LocalDate yesterday = tomorrow.minusDays(2);
+System.out.println("昨天的日期: "+yesterday);//2019-03-11
+LocalDate independenceDay = LocalDate.of(2019, Month.MARCH, 12);
+DayOfWeek dayOfWeek = independenceDay.getDayOfWeek();
+System.out.println("今天是周几:"+dayOfWeek);//TUESDAY
+```
+
+从字符串解析一个 LocalDate 类型和解析 LocalTime 一样简单,下面是使用 `DateTimeFormatter` 解析字符串的例子:
+
+```java
+ String str1 = "2014==04==12 01时06分09秒";
+ // 根据需要解析的日期、时间字符串定义解析所用的格式器
+ DateTimeFormatter fomatter1 = DateTimeFormatter
+ .ofPattern("yyyy==MM==dd HH时mm分ss秒");
+
+ LocalDateTime dt1 = LocalDateTime.parse(str1, fomatter1);
+ System.out.println(dt1); // 输出 2014-04-12T01:06:09
+
+ String str2 = "2014$$$四月$$$13 20小时";
+ DateTimeFormatter fomatter2 = DateTimeFormatter
+ .ofPattern("yyy$$$MMM$$$dd HH小时");
+ LocalDateTime dt2 = LocalDateTime.parse(str2, fomatter2);
+ System.out.println(dt2); // 输出 2014-04-13T20:00
+
+```
+
+再来看一个使用 `DateTimeFormatter` 格式化日期的示例
+
+```java
+LocalDateTime rightNow=LocalDateTime.now();
+String date=DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(rightNow);
+System.out.println(date);//2019-03-12T16:26:48.29
+DateTimeFormatter formatter=DateTimeFormatter.ofPattern("YYYY-MM-dd HH:mm:ss");
+System.out.println(formatter.format(rightNow));//2019-03-12 16:26:48
+```
+
+### LocalDateTime(本地日期时间)
+
+LocalDateTime 同时表示了时间和日期,相当于前两节内容合并到一个对象上了。LocalDateTime 和 LocalTime还有 LocalDate 一样,都是不可变的。LocalDateTime 提供了一些能访问具体字段的方法。
+
+```java
+LocalDateTime sylvester = LocalDateTime.of(2014, Month.DECEMBER, 31, 23, 59, 59);
+
+DayOfWeek dayOfWeek = sylvester.getDayOfWeek();
+System.out.println(dayOfWeek); // WEDNESDAY
+
+Month month = sylvester.getMonth();
+System.out.println(month); // DECEMBER
+
+long minuteOfDay = sylvester.getLong(ChronoField.MINUTE_OF_DAY);
+System.out.println(minuteOfDay); // 1439
+```
+
+只要附加上时区信息,就可以将其转换为一个时间点Instant对象,Instant时间点对象可以很容易的转换为老式的`java.util.Date`。
+
+```java
+Instant instant = sylvester
+ .atZone(ZoneId.systemDefault())
+ .toInstant();
+
+Date legacyDate = Date.from(instant);
+System.out.println(legacyDate); // Wed Dec 31 23:59:59 CET 2014
+```
+
+格式化LocalDateTime和格式化时间和日期一样的,除了使用预定义好的格式外,我们也可以自己定义格式:
+
+```java
+DateTimeFormatter formatter =
+ DateTimeFormatter
+ .ofPattern("MMM dd, yyyy - HH:mm");
+LocalDateTime parsed = LocalDateTime.parse("Nov 03, 2014 - 07:13", formatter);
+String string = formatter.format(parsed);
+System.out.println(string); // Nov 03, 2014 - 07:13
+```
+
+和java.text.NumberFormat不一样的是新版的DateTimeFormatter是不可变的,所以它是线程安全的。
+关于时间日期格式的详细信息在[这里](https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html)。
+
+## Annotations(注解)
+
+在Java 8中支持多重注解了,先看个例子来理解一下是什么意思。
+首先定义一个包装类Hints注解用来放置一组具体的Hint注解:
+
+```java
+@interface Hints {
+ Hint[] value();
+}
+@Repeatable(Hints.class)
+@interface Hint {
+ String value();
+}
+```
+
+Java 8允许我们把同一个类型的注解使用多次,只需要给该注解标注一下`@Repeatable`即可。
+
+例 1: 使用包装类当容器来存多个注解(老方法)
+
+```java
+@Hints({@Hint("hint1"), @Hint("hint2")})
+class Person {}
+```
+
+例 2:使用多重注解(新方法)
+
+```java
+@Hint("hint1")
+@Hint("hint2")
+class Person {}
+```
+
+第二个例子里java编译器会隐性的帮你定义好@Hints注解,了解这一点有助于你用反射来获取这些信息:
+
+```java
+Hint hint = Person.class.getAnnotation(Hint.class);
+System.out.println(hint); // null
+Hints hints1 = Person.class.getAnnotation(Hints.class);
+System.out.println(hints1.value().length); // 2
+
+Hint[] hints2 = Person.class.getAnnotationsByType(Hint.class);
+System.out.println(hints2.length); // 2
+```
+
+即便我们没有在 `Person`类上定义 `@Hints`注解,我们还是可以通过 `getAnnotation(Hints.class) `来获取 `@Hints`注解,更加方便的方法是使用 `getAnnotationsByType` 可以直接获取到所有的`@Hint`注解。
+另外Java 8的注解还增加到两种新的target上了:
+
+```java
+@Target({ElementType.TYPE_PARAMETER, ElementType.TYPE_USE})
+@interface MyAnnotation {}
+```
+
+
+
+## Whete to go from here?
+
+关于Java 8的新特性就写到这了,肯定还有更多的特性等待发掘。JDK 1.8里还有很多很有用的东西,比如`Arrays.parallelSort`, `StampedLock`和`CompletableFuture`等等。
+
diff --git "a/docs/java/What's New in JDK8/Java8\346\225\231\347\250\213\346\216\250\350\215\220.md" "b/docs/java/What's New in JDK8/Java8\346\225\231\347\250\213\346\216\250\350\215\220.md"
new file mode 100644
index 00000000000..43e4539ccac
--- /dev/null
+++ "b/docs/java/What's New in JDK8/Java8\346\225\231\347\250\213\346\216\250\350\215\220.md"
@@ -0,0 +1,20 @@
+
+
+### 书籍
+
+- **《Java8 In Action》**
+- **《写给大忙人看的Java SE 8》**
+
+上述书籍的PDF版本见 https://shimo.im/docs/CPB0PK05rP4CFmI2/ 中的 “Java 书籍推荐”。
+
+### 开源文档
+
+- **【译】Java 8 简明教程**:
+- **30 seconds of java8:**
+
+### 视频
+
+- **尚硅谷 Java 8 新特性**
+
+视频资源见: https://shimo.im/docs/CPB0PK05rP4CFmI2/ 。
+
diff --git "a/docs/java/What's New in JDK8/Lambda\350\241\250\350\276\276\345\274\217.md" "b/docs/java/What's New in JDK8/Lambda\350\241\250\350\276\276\345\274\217.md"
new file mode 100644
index 00000000000..359c4714473
--- /dev/null
+++ "b/docs/java/What's New in JDK8/Lambda\350\241\250\350\276\276\345\274\217.md"
@@ -0,0 +1,235 @@
+JDK8--Lambda表达式
+===
+## 1.什么是Lambda表达式
+**Lambda表达式实质上是一个可传递的代码块,Lambda又称为闭包或者匿名函数,是函数式编程语法,让方法可以像普通参数一样传递**
+
+## 2.Lambda表达式语法
+```(参数列表) -> {执行代码块}```
+
参数列表可以为空```()->{}```
+
可以加类型声明比如```(String para1, int para2) -> {return para1 + para2;}```我们可以看到,lambda同样可以有返回值.
+
在编译器可以推断出类型的时候,可以将类型声明省略,比如```(para1, para2) -> {return para1 + para2;}```
+
(lambda有点像动态类型语言语法。lambda在字节码层面是用invokedynamic实现的,而这条指令就是为了让JVM更好的支持运行在其上的动态类型语言)
+
+## 3.函数式接口
+在了解Lambda表达式之前,有必要先了解什么是函数式接口```(@FunctionalInterface)```
+**函数式接口指的是有且只有一个抽象(abstract)方法的接口**
+当需要一个函数式接口的对象时,就可以用Lambda表达式来实现,举个常用的例子:
+
+```java
+ Thread thread = new Thread(() -> {
+ System.out.println("This is JDK8's Lambda!");
+ });
+```
+这段代码和函数式接口有啥关系?我们回忆一下,Thread类的构造函数里是不是有一个以Runnable接口为参数的?
+```java
+public Thread(Runnable target) {...}
+
+/**
+ * Runnable Interface
+ */
+@FunctionalInterface
+public interface Runnable {
+ public abstract void run();
+}
+```
+到这里大家可能已经明白了,**Lambda表达式相当于一个匿名类或者说是一个匿名方法**。上面Thread的例子相当于
+```java
+ Thread thread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ System.out.println("Anonymous class");
+ }
+ });
+```
+也就是说,上面的lambda表达式相当于实现了这个run()方法,然后当做参数传入(个人感觉可以这么理解,lambda表达式就是一个函数,只不过它的返回值、参数列表都
+由编译器帮我们推断,因此可以减少很多代码量)。
+
Lambda也可以这样用 :
+```java
+ Runnable runnable = () -> {...};
+```
+其实这和上面的用法没有什么本质上的区别。
+
至此大家应该明白什么是函数式接口以及函数式接口和lambda表达式之间的关系了。在JDK8中修改了接口的规范,
+目的是为了在给接口添加新的功能时保持向前兼容(个人理解),比如一个已经定义了的函数式接口,某天我们想给它添加新功能,那么就不能保持向前兼容了,
+因为在旧的接口规范下,添加新功能必定会破坏这个函数式接口[(JDK8中接口规范)]()
+
+除了上面说的Runnable接口之外,JDK中已经存在了很多函数式接口
+比如(当然不止这些):
+- ```java.util.concurrent.Callable```
+- ```java.util.Comparator```
+- ```java.io.FileFilter```
+
**关于JDK中的预定义的函数式接口**
+
+- JDK在```java.util.function```下预定义了很多函数式接口
+ - ```Function {R apply(T t);}``` 接受一个T对象,然后返回一个R对象,就像普通的函数。
+ - ```Consumer {void accept(T t);}``` 消费者 接受一个T对象,没有返回值。
+ - ```Predicate {boolean test(T t);}``` 判断,接受一个T对象,返回一个布尔值。
+ - ```Supplier {T get();} 提供者(工厂)``` 返回一个T对象。
+ - 其他的跟上面的相似,大家可以看一下function包下的具体接口。
+## 4.变量作用域
+```java
+public class VaraibleHide {
+ @FunctionalInterface
+ interface IInner {
+ void printInt(int x);
+ }
+ public static void main(String[] args) {
+ int x = 20;
+ IInner inner = new IInner() {
+ int x = 10;
+ @Override
+ public void printInt(int x) {
+ System.out.println(x);
+ }
+ };
+ inner.printInt(30);
+
+ inner = (s) -> {
+ //Variable used in lambda expression should be final or effectively final
+ //!int x = 10;
+ //!x= 50; error
+ System.out.print(x);
+ };
+ inner.printInt(30);
+ }
+}
+输出 :
+30
+20
+```
+对于lambda表达式```java inner = (s) -> {System.out.print(x);};```,变量x并不是在lambda表达式中定义的,像这样并不是在lambda中定义或者通过lambda的参数列表()获取的变量成为自由变量,它是被lambda表达式捕获的。
+
lambda表达式和内部类一样,对外部自由变量捕获时,外部自由变量必须为final或者是最终变量(effectively final)的,也就是说这个变量初始化后就不能为它赋新值,
+同时lambda不像内部类/匿名类,lambda表达式与外围嵌套块有着相同的作用域,因此对变量命名的有关规则对lambda同样适用。大家阅读上面的代码对这些概念应该
+不难理解。
+## 5.方法引用
+**只需要提供方法的名字,具体的调用过程由Lambda和函数式接口来确定,这样的方法调用成为方法引用。**
+
下面的例子会打印list中的每个元素:
+```java
+List list = new ArrayList<>();
+ for (int i = 0; i < 10; ++i) {
+ list.add(i);
+ }
+ list.forEach(System.out::println);
+```
+其中```System.out::println```这个就是一个方法引用,等价于Lambda表达式 ```(para)->{System.out.println(para);}```
+
我们看一下List#forEach方法 ```default void forEach(Consumer super T> action)```可以看到它的参数是一个Consumer接口,该接口是一个函数式接口
+```java
+@FunctionalInterface
+public interface Consumer {
+ void accept(T t);
+```
+大家能发现这个函数接口的方法和```System.out::println```有什么相似的么?没错,它们有着相似的参数列表和返回值。
+
我们自己定义一个方法,看看能不能像标准输出的打印函数一样被调用
+```java
+public class MethodReference {
+ public static void main(String[] args) {
+ List list = new ArrayList<>();
+ for (int i = 0; i < 10; ++i) {
+ list.add(i);
+ }
+ list.forEach(MethodReference::myPrint);
+ }
+
+ static void myPrint(int i) {
+ System.out.print(i + ", ");
+ }
+}
+
+输出: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+```
+可以看到,我们自己定义的方法也可以当做方法引用。
+
到这里大家多少对方法引用有了一定的了解,我们再来说一下方法引用的形式。
+- 方法引用
+ - 类名::静态方法名
+ - 类名::实例方法名
+ - 类名::new (构造方法引用)
+ - 实例名::实例方法名
+可以看出,方法引用是通过(方法归属名)::(方法名)来调用的。通过上面的例子已经讲解了一个`类名::静态方法名`的使用方法了,下面再依次介绍其余的几种
+方法引用的使用方法。
+**类名::实例方法名**
+先来看一段代码
+```java
+ String[] strings = new String[10];
+ Arrays.sort(strings, String::compareToIgnoreCase);
+```
+**上面的String::compareToIgnoreCase等价于(x, y) -> {return x.compareToIgnoreCase(y);}**
+我们看一下`Arrays#sort`方法`public static void sort(T[] a, Comparator super T> c)`,
+可以看到第二个参数是一个Comparator接口,该接口也是一个函数式接口,其中的抽象方法是`int compare(T o1, T o2);`,再看一下
+`String#compareToIgnoreCase`方法,`public int compareToIgnoreCase(String str)`,这个方法好像和上面讲方法引用中`类名::静态方法名`不大一样啊,它
+的参数列表和函数式接口的参数列表不一样啊,虽然它的返回值一样?
+
是的,确实不一样但是别忘了,String类的这个方法是个实例方法,而不是静态方法,也就是说,这个方法是需要有一个接收者的。所谓接收者就是
+instance.method(x)中的instance,
+它是某个类的实例,有的朋友可能已经明白了。上面函数式接口的`compare(T o1, T o2)`中的第一个参数作为了实例方法的接收者,而第二个参数作为了实例方法的
+参数。我们再举一个自己实现的例子:
+```java
+public class MethodReference {
+ static Random random = new Random(47);
+ public static void main(String[] args) {
+ MethodReference[] methodReferences = new MethodReference[10];
+ Arrays.sort(methodReferences, MethodReference::myCompare);
+ }
+ int myCompare(MethodReference o) {
+ return random.nextInt(2) - 1;
+ }
+}
+```
+上面的例子可以在IDE里通过编译,大家有兴趣的可以模仿上面的例子自己写一个程序,打印出排序后的结果。
+
**构造器引用**
+构造器引用仍然需要与特定的函数式接口配合使用,并不能像下面这样直接使用。IDE会提示String不是一个函数式接口
+```java
+ //compile error : String is not a functional interface
+ String str = String::new;
+```
+下面是一个使用构造器引用的例子,可以看出构造器引用可以和这种工厂型的函数式接口一起使用的。
+```java
+ interface IFunctional {
+ T func();
+}
+
+public class ConstructorReference {
+
+ public ConstructorReference() {
+ }
+
+ public static void main(String[] args) {
+ Supplier supplier0 = () -> new ConstructorReference();
+ Supplier supplier1 = ConstructorReference::new;
+ IFunctional functional = () -> new ConstructorReference();
+ IFunctional functional1 = ConstructorReference::new;
+ }
+}
+```
+下面是一个JDK官方的例子
+```java
+ public static , DEST extends Collection>
+ DEST transferElements(
+ SOURCE sourceCollection,
+ Supplier collectionFactory) {
+
+ DEST result = collectionFactory.get();
+ for (T t : sourceCollection) {
+ result.add(t);
+ }
+ return result;
+ }
+
+ ...
+
+ Set rosterSet = transferElements(
+ roster, HashSet::new);
+```
+
+**实例::实例方法**
+
+其实开始那个例子就是一个实例::实例方法的引用
+```java
+List list = new ArrayList<>();
+ for (int i = 0; i < 10; ++i) {
+ list.add(i);
+ }
+ list.forEach(System.out::println);
+```
+其中System.out就是一个实例,println是一个实例方法。相信不用再给大家做解释了。
+## 总结
+Lambda表达式是JDK8引入Java的函数式编程语法,使用Lambda需要直接或者间接的与函数式接口配合,在开发中使用Lambda可以减少代码量,
+但是并不是说必须要使用Lambda(虽然它是一个很酷的东西)。有些情况下使用Lambda会使代码的可读性急剧下降,并且也节省不了多少代码,
+所以在实际开发中还是需要仔细斟酌是否要使用Lambda。和Lambda相似的还有JDK10中加入的var类型推断,同样对于这个特性需要斟酌使用。
diff --git a/docs/java/What's New in JDK8/README.md b/docs/java/What's New in JDK8/README.md
new file mode 100644
index 00000000000..fa71e907410
--- /dev/null
+++ b/docs/java/What's New in JDK8/README.md
@@ -0,0 +1,556 @@
+JDK8新特性总结
+======
+总结了部分JDK8新特性,另外一些新特性可以通过Oracle的官方文档查看,毕竟是官方文档,各种新特性都会介绍,有兴趣的可以去看。
+[Oracle官方文档:What's New in JDK8](https://www.oracle.com/technetwork/java/javase/8-whats-new-2157071.html)
+-----
+- [Java语言特性](#JavaProgrammingLanguage)
+ - [Lambda表达式是一个新的语言特性,已经在JDK8中加入。它是一个可以传递的代码块,你也可以把它们当做方法参数。
+ Lambda表达式允许您更紧凑地创建单虚方法接口(称为功能接口)的实例。](#LambdaExpressions)
+
+ - [方法引用为已经存在的具名方法提供易于阅读的Lambda表达式](#MethodReferences)
+
+ - [默认方法允许将新功能添加到库的接口,并确保与为这些接口的旧版本编写的代码的二进制兼容性。](#DefaultMethods)
+
+ - [改进的类型推断。](#ImprovedTypeInference)
+
+ - [方法参数反射(通过反射获得方法参数信息)](#MethodParameterReflection)
+
+- [流(stream)](#stream)
+ - [新java.util.stream包中的类提供Stream API以支持对元素流的功能样式操作。流(stream)和I/O里的流不是同一个概念
+ ,使用stream API可以更方便的操作集合。]()
+
+- [国际化]()
+ - 待办
+- 待办
+___
+
+
+
+
+
+
+
+## Lambda表达式
+### 1.什么是Lambda表达式
+**Lambda表达式实质上是一个可传递的代码块,Lambda又称为闭包或者匿名函数,是函数式编程语法,让方法可以像普通参数一样传递**
+
+### 2.Lambda表达式语法
+```(参数列表) -> {执行代码块}```
+
参数列表可以为空```()->{}```
+
可以加类型声明比如```(String para1, int para2) -> {return para1 + para2;}```我们可以看到,lambda同样可以有返回值.
+
在编译器可以推断出类型的时候,可以将类型声明省略,比如```(para1, para2) -> {return para1 + para2;}```
+
(lambda有点像动态类型语言语法。lambda在字节码层面是用invokedynamic实现的,而这条指令就是为了让JVM更好的支持运行在其上的动态类型语言)
+
+### 3.函数式接口
+在了解Lambda表达式之前,有必要先了解什么是函数式接口```(@FunctionalInterface)```
+**函数式接口指的是有且只有一个抽象(abstract)方法的接口**
+当需要一个函数式接口的对象时,就可以用Lambda表达式来实现,举个常用的例子:
+
+```java
+ Thread thread = new Thread(() -> {
+ System.out.println("This is JDK8's Lambda!");
+ });
+```
+这段代码和函数式接口有啥关系?我们回忆一下,Thread类的构造函数里是不是有一个以Runnable接口为参数的?
+```java
+public Thread(Runnable target) {...}
+
+/**
+ * Runnable Interface
+ */
+@FunctionalInterface
+public interface Runnable {
+ public abstract void run();
+}
+```
+到这里大家可能已经明白了,**Lambda表达式相当于一个匿名类或者说是一个匿名方法**。上面Thread的例子相当于
+```java
+ Thread thread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ System.out.println("Anonymous class");
+ }
+ });
+```
+也就是说,上面的lambda表达式相当于实现了这个run()方法,然后当做参数传入(个人感觉可以这么理解,lambda表达式就是一个函数,只不过它的返回值、参数列表都
+由编译器帮我们推断,因此可以减少很多代码量)。
+
Lambda也可以这样用 :
+```java
+ Runnable runnable = () -> {...};
+```
+其实这和上面的用法没有什么本质上的区别。
+
至此大家应该明白什么是函数式接口以及函数式接口和lambda表达式之间的关系了。在JDK8中修改了接口的规范,
+目的是为了在给接口添加新的功能时保持向前兼容(个人理解),比如一个已经定义了的函数式接口,某天我们想给它添加新功能,那么就不能保持向前兼容了,
+因为在旧的接口规范下,添加新功能必定会破坏这个函数式接口[(JDK8中接口规范)]()
+
+除了上面说的Runnable接口之外,JDK中已经存在了很多函数式接口
+比如(当然不止这些):
+- ```java.util.concurrent.Callable```
+- ```java.util.Comparator```
+- ```java.io.FileFilter```
+
**关于JDK中的预定义的函数式接口**
+
+- JDK在```java.util.function```下预定义了很多函数式接口
+ - ```Function {R apply(T t);}``` 接受一个T对象,然后返回一个R对象,就像普通的函数。
+ - ```Consumer {void accept(T t);}``` 消费者 接受一个T对象,没有返回值。
+ - ```Predicate {boolean test(T t);}``` 判断,接受一个T对象,返回一个布尔值。
+ - ```Supplier {T get();} 提供者(工厂)``` 返回一个T对象。
+ - 其他的跟上面的相似,大家可以看一下function包下的具体接口。
+### 4.变量作用域
+```java
+public class VaraibleHide {
+ @FunctionalInterface
+ interface IInner {
+ void printInt(int x);
+ }
+ public static void main(String[] args) {
+ int x = 20;
+ IInner inner = new IInner() {
+ int x = 10;
+ @Override
+ public void printInt(int x) {
+ System.out.println(x);
+ }
+ };
+ inner.printInt(30);
+
+ inner = (s) -> {
+ //Variable used in lambda expression should be final or effectively final
+ //!int x = 10;
+ //!x= 50; error
+ System.out.print(x);
+ };
+ inner.printInt(30);
+ }
+}
+输出 :
+30
+20
+```
+对于lambda表达式```java inner = (s) -> {System.out.print(x);};```,变量x并不是在lambda表达式中定义的,像这样并不是在lambda中定义或者通过lambda
+的参数列表()获取的变量成为自由变量,它是被lambda表达式捕获的。
+
lambda表达式和内部类一样,对外部自由变量捕获时,外部自由变量必须为final或者是最终变量(effectively final)的,也就是说这个变量初始化后就不能为它赋新值,同时lambda不像内部类/匿名类,lambda表达式与外围嵌套块有着相同的作用域,因此对变量命名的有关规则对lambda同样适用。大家阅读上面的代码对这些概念应该不难理解。
+
+### 5.方法引用
+**只需要提供方法的名字,具体的调用过程由Lambda和函数式接口来确定,这样的方法调用成为方法引用。**
+
下面的例子会打印list中的每个元素:
+```java
+List list = new ArrayList<>();
+ for (int i = 0; i < 10; ++i) {
+ list.add(i);
+ }
+ list.forEach(System.out::println);
+```
+其中```System.out::println```这个就是一个方法引用,等价于Lambda表达式 ```(para)->{System.out.println(para);}```
+
我们看一下List#forEach方法 ```default void forEach(Consumer super T> action)```可以看到它的参数是一个Consumer接口,该接口是一个函数式接口
+```java
+@FunctionalInterface
+public interface Consumer {
+ void accept(T t);
+```
+大家能发现这个函数接口的方法和```System.out::println```有什么相似的么?没错,它们有着相似的参数列表和返回值。
+
我们自己定义一个方法,看看能不能像标准输出的打印函数一样被调用
+```java
+public class MethodReference {
+ public static void main(String[] args) {
+ List list = new ArrayList<>();
+ for (int i = 0; i < 10; ++i) {
+ list.add(i);
+ }
+ list.forEach(MethodReference::myPrint);
+ }
+
+ static void myPrint(int i) {
+ System.out.print(i + ", ");
+ }
+}
+
+输出: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+```
+可以看到,我们自己定义的方法也可以当做方法引用。
+
到这里大家多少对方法引用有了一定的了解,我们再来说一下方法引用的形式。
+- 方法引用
+ - 类名::静态方法名
+ - 类名::实例方法名
+ - 类名::new (构造方法引用)
+ - 实例名::实例方法名
+可以看出,方法引用是通过(方法归属名)::(方法名)来调用的。通过上面的例子已经讲解了一个`类名::静态方法名`的使用方法了,下面再依次介绍其余的几种
+方法引用的使用方法。
+**类名::实例方法名**
+先来看一段代码
+```java
+ String[] strings = new String[10];
+ Arrays.sort(strings, String::compareToIgnoreCase);
+```
+**上面的String::compareToIgnoreCase等价于(x, y) -> {return x.compareToIgnoreCase(y);}**
+我们看一下`Arrays#sort`方法`public static void sort(T[] a, Comparator super T> c)`,
+可以看到第二个参数是一个Comparator接口,该接口也是一个函数式接口,其中的抽象方法是`int compare(T o1, T o2);`,再看一下
+`String#compareToIgnoreCase`方法,`public int compareToIgnoreCase(String str)`,这个方法好像和上面讲方法引用中`类名::静态方法名`不大一样啊,它
+的参数列表和函数式接口的参数列表不一样啊,虽然它的返回值一样?
+
是的,确实不一样但是别忘了,String类的这个方法是个实例方法,而不是静态方法,也就是说,这个方法是需要有一个接收者的。所谓接收者就是
+instance.method(x)中的instance,
+它是某个类的实例,有的朋友可能已经明白了。上面函数式接口的`compare(T o1, T o2)`中的第一个参数作为了实例方法的接收者,而第二个参数作为了实例方法的
+参数。我们再举一个自己实现的例子:
+```java
+public class MethodReference {
+ static Random random = new Random(47);
+ public static void main(String[] args) {
+ MethodReference[] methodReferences = new MethodReference[10];
+ Arrays.sort(methodReferences, MethodReference::myCompare);
+ }
+ int myCompare(MethodReference o) {
+ return random.nextInt(2) - 1;
+ }
+}
+```
+上面的例子可以在IDE里通过编译,大家有兴趣的可以模仿上面的例子自己写一个程序,打印出排序后的结果。
+
**构造器引用**
+构造器引用仍然需要与特定的函数式接口配合使用,并不能像下面这样直接使用。IDE会提示String不是一个函数式接口
+```java
+ //compile error : String is not a functional interface
+ String str = String::new;
+```
+下面是一个使用构造器引用的例子,可以看出构造器引用可以和这种工厂型的函数式接口一起使用的。
+```java
+ interface IFunctional {
+ T func();
+}
+
+public class ConstructorReference {
+
+ public ConstructorReference() {
+ }
+
+ public static void main(String[] args) {
+ Supplier supplier0 = () -> new ConstructorReference();
+ Supplier supplier1 = ConstructorReference::new;
+ IFunctional functional = () -> new ConstructorReference();
+ IFunctional functional1 = ConstructorReference::new;
+ }
+}
+```
+下面是一个JDK官方的例子
+```java
+ public static , DEST extends Collection>
+ DEST transferElements(
+ SOURCE sourceCollection,
+ Supplier collectionFactory) {
+
+ DEST result = collectionFactory.get();
+ for (T t : sourceCollection) {
+ result.add(t);
+ }
+ return result;
+ }
+
+ ...
+
+ Set rosterSet = transferElements(
+ roster, HashSet::new);
+```
+
+**实例::实例方法**
+
+其实开始那个例子就是一个实例::实例方法的引用
+```java
+List list = new ArrayList<>();
+ for (int i = 0; i < 10; ++i) {
+ list.add(i);
+ }
+ list.forEach(System.out::println);
+```
+其中System.out就是一个实例,println是一个实例方法。相信不用再给大家做解释了。
+### 总结
+Lambda表达式是JDK8引入Java的函数式编程语法,使用Lambda需要直接或者间接的与函数式接口配合,在开发中使用Lambda可以减少代码量,
+但是并不是说必须要使用Lambda(虽然它是一个很酷的东西)。有些情况下使用Lambda会使代码的可读性急剧下降,并且也节省不了多少代码,
+所以在实际开发中还是需要仔细斟酌是否要使用Lambda。和Lambda相似的还有JDK10中加入的var类型推断,同样对于这个特性需要斟酌使用。
+
+
+___
+
+
+## JDK8接口规范
+### 在JDK8中引入了lambda表达式,出现了函数式接口的概念,为了在扩展接口时保持向前兼容性(JDK8之前扩展接口会使得实现了该接口的类必须实现添加的方法,否则会报错。为了保持兼容性而做出妥协的特性还有泛型,泛型也是为了保持兼容性而失去了在一些别的语言泛型拥有的功能),Java接口规范发生了一些改变。
+### 1.JDK8以前的接口规范
+- JDK8以前接口可以定义的变量和方法
+ - 所有变量(Field)不论是否显式 的声明为```public static final```,它实际上都是```public static final```的。
+ - 所有方法(Method)不论是否显示 的声明为```public abstract```,它实际上都是```public abstract```的。
+```java
+public interface AInterfaceBeforeJDK8 {
+ int FIELD = 0;
+ void simpleMethod();
+}
+```
+以上接口信息反编译以后可以看到字节码信息里Filed是public static final的,而方法是public abstract的,即是你没有显示的去声明它。
+```java
+{
+ public static final int FIELD;
+ descriptor: I
+ flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+ ConstantValue: int 0
+
+ public abstract void simpleMethod();
+ descriptor: ()V
+ flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
+}
+```
+### 2.JDK8之后的接口规范
+- JDK8之后接口可以定义的变量和方法
+ - 变量(Field)仍然必须是 ```java public static final```的
+ - 方法(Method)除了可以是public abstract之外,还可以是public static或者是default(相当于仅public修饰的实例方法)的。
+从以上改变不难看出,修改接口的规范主要是为了能在扩展接口时保持向前兼容。
+
下面是一个JDK8之后的接口例子
+```java
+public interface AInterfaceInJDK8 {
+ int simpleFiled = 0;
+ static int staticField = 1;
+
+ public static void main(String[] args) {
+ }
+ static void staticMethod(){}
+
+ default void defaultMethod(){}
+
+ void simpleMethod() throws IOException;
+
+}
+```
+进行反编译(去除了一些没用信息)
+```java
+{
+ public static final int simpleFiled;
+ flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+
+ public static final int staticField;
+ flags: (0x0019) ACC_PUBLIC, ACC_STATIC, ACC_FINAL
+
+ public static void main(java.lang.String[]);
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+
+ public static void staticMethod();
+ flags: (0x0009) ACC_PUBLIC, ACC_STATIC
+
+ public void defaultMethod();
+ flags: (0x0001) ACC_PUBLIC
+
+ public abstract void simpleMethod() throws java.io.IOException;
+ flags: (0x0401) ACC_PUBLIC, ACC_ABSTRACT
+ Exceptions:
+ throws java.io.IOException
+}
+```
+可以看到 default关键字修饰的方法是像实例方法(就是普通类中定义的普通方法)一样定义的,所以我们来定义一个只有default方法的接口并且实现一下这个接口试一
+试。
+```java
+interface Default {
+ default int defaultMethod() {
+ return 4396;
+ }
+}
+
+public class DefaultMethod implements Default {
+ public static void main(String[] args) {
+ DefaultMethod defaultMethod = new DefaultMethod();
+ System.out.println(defaultMethod.defaultMethod());
+ //compile error : Non-static method 'defaultMethod()' cannot be referenced from a static context
+ //! DefaultMethod.defaultMethod();
+ }
+}
+```
+可以看到default方法确实像实例方法一样,必须有实例对象才能调用,并且子类在实现接口时,可以不用实现default方法,也可以选择覆盖该方法。
+这有点像子类继承父类实例方法。
+
+接口静态方法就像是类静态方法,唯一的区别是**接口静态方法只能通过接口名调用,而类静态方法既可以通过类名调用也可以通过实例调用**
+```java
+interface Static {
+ static int staticMethod() {
+ return 4396;
+ }
+}
+ ... main(String...args)
+ //!compile error: Static method may be invoked on containing interface class only
+ //!aInstanceOfStatic.staticMethod();
+ ...
+```
+另一个问题是多继承问题,大家知道Java中类是不支持多继承的,但是接口是多继承和多实现(implements后跟多个接口)的,
+那么如果一个接口继承另一个接口,两个接口都有同名的default方法会怎么样呢?答案是会像类继承一样覆写(@Override),以下代码在IDE中可以顺利编译
+```java
+interface Default {
+ default int defaultMethod() {
+ return 4396;
+ }
+}
+interface Default2 extends Default {
+ @Override
+ default int defaultMethod() {
+ return 9527;
+ }
+}
+public class DefaultMethod implements Default,Default2 {
+ public static void main(String[] args) {
+ DefaultMethod defaultMethod = new DefaultMethod();
+ System.out.println(defaultMethod.defaultMethod());
+ }
+}
+
+输出 : 9527
+```
+出现上面的情况时,会优先找继承树上近的方法,类似于“短路优先”。
+
+那么如果一个类实现了两个没有继承关系的接口,且这两个接口有同名方法的话会怎么样呢?IDE会要求你重写这个冲突的方法,让你自己选择去执行哪个方法,因为IDE它还没智能到你不告诉它,它就知道你想执行哪个方法。可以通过```java 接口名.super```指针来访问接口中定义的实例(default)方法。
+```java
+interface Default {
+ default int defaultMethod() {
+ return 4396;
+ }
+}
+
+interface Default2 {
+ default int defaultMethod() {
+ return 9527;
+ }
+}
+//如果不重写
+//compile error : defaults.DefaultMethod inherits unrelated defaults for defaultMethod() from types defaults.Default and defaults.Default2
+public class DefaultMethod implements Default,Default2 {
+@Override
+ public int defaultMethod() {
+ System.out.println(Default.super.defaultMethod());
+ System.out.println(Default2.super.defaultMethod());
+ return 996;
+ }
+ public static void main(String[] args) {
+ DefaultMethod defaultMethod = new DefaultMethod();
+ System.out.println(defaultMethod.defaultMethod());
+ }
+}
+
+运行输出 :
+4396
+9527
+996
+```
+
+
+___
+
+
+## 改进的类型推断
+### 1.什么是类型推断
+类型推断就像它的字面意思一样,编译器根据你显示声明的已知的信息 推断出你没有显示声明的类型,这就是类型推断。
+看过《Java编程思想 第四版》的朋友可能还记得里面讲解泛型一章的时候,里面很多例子是下面这样的:
+```java
+ Map map = new Map();
+```
+而我们平常写的都是这样的:
+```java
+ Map map = new Map<>();
+```
+这就是类型推断,《Java编程思想 第四版》这本书出书的时候最新的JDK只有1.6(JDK7推出的类型推断),在Java编程思想里Bruce Eckel大叔还提到过这个问题
+(可能JDK的官方人员看了Bruce Eckel大叔的Thinking in Java才加的类型推断,☺),在JDK7中推出了上面这样的类型推断,可以减少一些无用的代码。
+(Java编程思想到现在还只有第四版,是不是因为Bruce Eckel大叔觉得Java新推出的语言特性“然并卵”呢?/滑稽)
+
+在JDK7中,类型推断只有上面例子的那样的能力,即只有在使用**赋值语句**时才能自动推断出泛型参数信息(即<>里的信息),下面的官方文档里的例子在JDK7里会编译
+错误
+```java
+ List stringList = new ArrayList<>();
+ stringList.add("A");
+ //error : addAll(java.util.Collection extends java.lang.String>)in List cannot be applied to (java.util.List)
+ stringList.addAll(Arrays.asList());
+```
+但是上面的代码在JDK8里可以通过,也就说,JDK8里,类型推断不仅可以用于赋值语句,而且可以根据代码中上下文里的信息推断出更多的信息,因此我们需要些的代码
+会更少。加强的类型推断还有一个就是用于Lambda表达式了。
+
+大家其实不必细究类型推断,在日常使用中IDE会自动判断,当IDE自己无法推断出足够的信息时,就需要我们额外做一下工作,比如在<>里添加更多的类型信息,
+相信随着Java的进化,这些便利的功能会越来越强大。
+
+
+____
+
+
+## 通过反射获得方法的参数信息
+JDK8之前 .class文件是不会存储方法参数信息的,因此也就无法通过反射获取该信息(想想反射获取类信息的入口是什么?当然就是Class类了)。即是是在JDK11里
+也不会默认生成这些信息,可以通过在javac加上-parameters参数来让javac生成这些信息(javac就是java编译器,可以把java文件编译成.class文件)。生成额外
+的信息(运行时非必须信息)会消耗内存并且有可能公布敏感信息(某些方法参数比如password,JDK文档里这么说的),并且确实很多信息javac并不会为我们生成,比如
+LocalVariableTable,javac就不会默认生成,需要你加上 -g:vars来强制让编译器生成,同样的,方法参数信息也需要加上
+-parameters来让javac为你在.class文件中生成这些信息,否则运行时反射是无法获取到这些信息的。在讲解Java语言层面的方法之前,先看一下javac加上该
+参数和不加生成的信息有什么区别(不感兴趣想直接看运行代码的可以跳过这段)。下面是随便写的一个类。
+```java
+public class ByteCodeParameters {
+ public String simpleMethod(String canUGetMyName, Object yesICan) {
+ return "9527";
+ }
+}
+```
+先来不加参数编译和反编译一下这个类javac ByteCodeParameters.java , javap -v ByteCodeParameters:
+```java
+ //只截取了部分信息
+ public java.lang.String simpleMethod(java.lang.String, java.lang.Object);
+ descriptor: (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=3, args_size=3
+ 0: ldc #2 // String 9527
+ 2: areturn
+ LineNumberTable:
+ line 5: 0
+ //这个方法的描述到这里就结束了
+```
+接下来我们加上参数javac -parameters ByteCodeParameters.java 再来看反编译的信息:
+```java
+ public java.lang.String simpleMethod(java.lang.String, java.lang.Object);
+ descriptor: (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=3, args_size=3
+ 0: ldc #2 // String 9527
+ 2: areturn
+ LineNumberTable:
+ line 8: 0
+ MethodParameters:
+ Name Flags
+ canUGetMyName
+ yesICan
+```
+可以看到.class文件里多了一个MethodParameters信息,这就是参数的名字,可以看到默认是不保存的。
+
下面看一下在Intelj Idea里运行的这个例子,我们试一下通过反射获取方法名 :
+```java
+public class ByteCodeParameters {
+ public String simpleMethod(String canUGetMyName, Object yesICan) {
+ return "9527";
+ }
+
+ public static void main(String[] args) throws NoSuchMethodException {
+ Class> clazz = ByteCodeParameters.class;
+ Method simple = clazz.getDeclaredMethod("simpleMethod", String.class, Object.class);
+ Parameter[] parameters = simple.getParameters();
+ for (Parameter p : parameters) {
+ System.out.println(p.getName());
+ }
+ }
+}
+输出 :
+arg0
+arg1
+```
+???说好的方法名呢????别急,哈哈。前面说了,默认是不生成参数名信息的,因此我们需要做一些配置,我们找到IDEA的settings里的Java Compiler选项,在
+Additional command line parameters:一行加上-parameters(Eclipse 也是找到Java Compiler选中Stoer information about method parameters),或者自
+己编译一个.class文件放在IDEA的out下,然后再来运行 :
+```java
+输出 :
+canUGetMyName
+yesICan
+```
+这样我们就通过反射获取到参数信息了。想要了解更多的同学可以自己研究一下 [官方文档]
+(https://docs.oracle.com/javase/tutorial/reflect/member/methodparameterreflection.html)
+
+## 总结与补充
+在JDK8之后,可以通过-parameters参数来让编译器生成参数信息然后在运行时通过反射获取方法参数信息,其实在SpringFramework
+里面也有一个LocalVariableTableParameterNameDiscoverer对象可以获取方法参数名信息,有兴趣的同学可以自行百度(这个类在打印日志时可能会比较有用吧,个人感觉)。
+
+____
+
+
+
+
+___
diff --git a/docs/java/What's New in JDK8/Stream.md b/docs/java/What's New in JDK8/Stream.md
new file mode 100644
index 00000000000..de7c86e3f2e
--- /dev/null
+++ b/docs/java/What's New in JDK8/Stream.md
@@ -0,0 +1,75 @@
+Stream API 旨在让编码更高效率、干净、简洁。
+
+### 从迭代器到Stream操作
+
+当使用 `Stream` 时,我们一般会通过三个阶段建立一个流水线:
+
+1. 创建一个 `Stream`;
+2. 进行一个或多个中间操作;
+3. 使用终止操作产生一个结果,`Stream` 就不会再被使用了。
+
+**案例1:统计 List 中的单词长度大于6的个数**
+
+```java
+/**
+* 案例1:统计 List 中的单词长度大于6的个数
+*/
+ArrayList wordsList = new ArrayList();
+wordsList.add("Charles");
+wordsList.add("Vincent");
+wordsList.add("William");
+wordsList.add("Joseph");
+wordsList.add("Henry");
+wordsList.add("Bill");
+wordsList.add("Joan");
+wordsList.add("Linda");
+int count = 0;
+```
+Java8之前我们通常用迭代方法来完成上面的需求:
+
+```java
+//迭代(Java8之前的常用方法)
+//迭代不好的地方:1. 代码多;2 很难被并行运算。
+for (String word : wordsList) {
+ if (word.length() > 6) {
+ count++;
+ }
+}
+System.out.println(count);//3
+```
+Java8之前我们使用 `Stream` 一行代码就能解决了,而且可以瞬间转换为并行执行的效果:
+
+```java
+//Stream
+//将stream()改为parallelStream()就可以瞬间将代码编程并行执行的效果
+long count2=wordsList.stream()
+ .filter(w->w.length()>6)
+ .count();
+long count3=wordsList.parallelStream()
+ .filter(w->w.length()>6)
+ .count();
+System.out.println(count2);
+System.out.println(count3);
+```
+
+### `distinct()`
+
+去除 List 中重复的 String
+
+```java
+List list = list.stream()
+ .distinct()
+ .collect(Collectors.toList());
+```
+
+### `map`
+
+map 方法用于映射每个元素到对应的结果,以下代码片段使用 map 输出了元素对应的平方数:
+
+```java
+List numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
+// 获取 List 中每个元素对应的平方数并去重
+List squaresList = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());
+System.out.println(squaresList.toString());//[9, 4, 49, 25]
+```
+
diff --git "a/docs/java/What's New in JDK8/\346\224\271\350\277\233\347\232\204\347\261\273\345\236\213\346\216\250\346\226\255.md" "b/docs/java/What's New in JDK8/\346\224\271\350\277\233\347\232\204\347\261\273\345\236\213\346\216\250\346\226\255.md"
new file mode 100644
index 00000000000..b5cff7bb0c0
--- /dev/null
+++ "b/docs/java/What's New in JDK8/\346\224\271\350\277\233\347\232\204\347\261\273\345\236\213\346\216\250\346\226\255.md"
@@ -0,0 +1,30 @@
+## 改进的类型推断
+### 1.什么是类型推断
+类型推断就像它的字面意思一样,编译器根据你显示声明的已知的信息 推断出你没有显示声明的类型,这就是类型推断。
+看过《Java编程思想 第四版》的朋友可能还记得里面讲解泛型一章的时候,里面很多例子是下面这样的:
+```java
+ Map map = new Map();
+```
+而我们平常写的都是这样的:
+```java
+ Map map = new Map<>();
+```
+这就是类型推断,《Java编程思想 第四版》这本书出书的时候最新的JDK只有1.6(JDK7推出的类型推断),在Java编程思想里Bruce Eckel大叔还提到过这个问题
+(可能JDK的官方人员看了Bruce Eckel大叔的Thinking in Java才加的类型推断,☺),在JDK7中推出了上面这样的类型推断,可以减少一些无用的代码。
+(Java编程思想到现在还只有第四版,是不是因为Bruce Eckel大叔觉得Java新推出的语言特性“然并卵”呢?/滑稽)
+
+在JDK7中,类型推断只有上面例子的那样的能力,即只有在使用**赋值语句**时才能自动推断出泛型参数信息(即<>里的信息),下面的官方文档里的例子在JDK7里会编译
+错误
+```java
+ List stringList = new ArrayList<>();
+ stringList.add("A");
+ //error : addAll(java.util.Collection extends java.lang.String>)in List cannot be applied to (java.util.List)
+ stringList.addAll(Arrays.asList());
+```
+但是上面的代码在JDK8里可以通过,也就说,JDK8里,类型推断不仅可以用于赋值语句,而且可以根据代码中上下文里的信息推断出更多的信息,因此我们需要些的代码
+会更少。加强的类型推断还有一个就是用于Lambda表达式了。
+
+大家其实不必细究类型推断,在日常使用中IDE会自动判断,当IDE自己无法推断出足够的信息时,就需要我们额外做一下工作,比如在<>里添加更多的类型信息,
+相信随着Java的进化,这些便利的功能会越来越强大。
+
+
diff --git "a/docs/java/What's New in JDK8/\351\200\232\350\277\207\345\217\215\345\260\204\350\216\267\345\276\227\346\226\271\346\263\225\347\232\204\345\217\202\346\225\260\344\277\241\346\201\257.md" "b/docs/java/What's New in JDK8/\351\200\232\350\277\207\345\217\215\345\260\204\350\216\267\345\276\227\346\226\271\346\263\225\347\232\204\345\217\202\346\225\260\344\277\241\346\201\257.md"
new file mode 100644
index 00000000000..a1d91c4b2fe
--- /dev/null
+++ "b/docs/java/What's New in JDK8/\351\200\232\350\277\207\345\217\215\345\260\204\350\216\267\345\276\227\346\226\271\346\263\225\347\232\204\345\217\202\346\225\260\344\277\241\346\201\257.md"
@@ -0,0 +1,79 @@
+## 通过反射获得方法的参数信息
+JDK8之前 .class文件是不会存储方法参数信息的,因此也就无法通过反射获取该信息(想想反射获取类信息的入口是什么?当然就是Class类了)。即是是在JDK11里
+也不会默认生成这些信息,可以通过在javac加上-parameters参数来让javac生成这些信息(javac就是java编译器,可以把java文件编译成.class文件)。生成额外
+的信息(运行时非必须信息)会消耗内存并且有可能公布敏感信息(某些方法参数比如password,JDK文档里这么说的),并且确实很多信息javac并不会为我们生成,比如
+LocalVariableTable,javac就不会默认生成,需要你加上 -g:vars来强制让编译器生成,同样的,方法参数信息也需要加上
+-parameters来让javac为你在.class文件中生成这些信息,否则运行时反射是无法获取到这些信息的。在讲解Java语言层面的方法之前,先看一下javac加上该
+参数和不加生成的信息有什么区别(不感兴趣想直接看运行代码的可以跳过这段)。下面是随便写的一个类。
+```java
+public class ByteCodeParameters {
+ public String simpleMethod(String canUGetMyName, Object yesICan) {
+ return "9527";
+ }
+}
+```
+先来不加参数编译和反编译一下这个类javac ByteCodeParameters.java , javap -v ByteCodeParameters:
+```java
+ //只截取了部分信息
+ public java.lang.String simpleMethod(java.lang.String, java.lang.Object);
+ descriptor: (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=3, args_size=3
+ 0: ldc #2 // String 9527
+ 2: areturn
+ LineNumberTable:
+ line 5: 0
+ //这个方法的描述到这里就结束了
+```
+接下来我们加上参数javac -parameters ByteCodeParameters.java 再来看反编译的信息:
+```java
+ public java.lang.String simpleMethod(java.lang.String, java.lang.Object);
+ descriptor: (Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/String;
+ flags: (0x0001) ACC_PUBLIC
+ Code:
+ stack=1, locals=3, args_size=3
+ 0: ldc #2 // String 9527
+ 2: areturn
+ LineNumberTable:
+ line 8: 0
+ MethodParameters:
+ Name Flags
+ canUGetMyName
+ yesICan
+```
+可以看到.class文件里多了一个MethodParameters信息,这就是参数的名字,可以看到默认是不保存的。
+
下面看一下在Intelj Idea里运行的这个例子,我们试一下通过反射获取方法名 :
+```java
+public class ByteCodeParameters {
+ public String simpleMethod(String canUGetMyName, Object yesICan) {
+ return "9527";
+ }
+
+ public static void main(String[] args) throws NoSuchMethodException {
+ Class> clazz = ByteCodeParameters.class;
+ Method simple = clazz.getDeclaredMethod("simpleMethod", String.class, Object.class);
+ Parameter[] parameters = simple.getParameters();
+ for (Parameter p : parameters) {
+ System.out.println(p.getName());
+ }
+ }
+}
+输出 :
+arg0
+arg1
+```
+???说好的方法名呢????别急,哈哈。前面说了,默认是不生成参数名信息的,因此我们需要做一些配置,我们找到IDEA的settings里的Java Compiler选项,在
+Additional command line parameters:一行加上-parameters(Eclipse 也是找到Java Compiler选中Stoer information about method parameters),或者自
+己编译一个.class文件放在IDEA的out下,然后再来运行 :
+```java
+输出 :
+canUGetMyName
+yesICan
+```
+这样我们就通过反射获取到参数信息了。想要了解更多的同学可以自己研究一下 [官方文档]
+(https://docs.oracle.com/javase/tutorial/reflect/member/methodparameterreflection.html)
+
+## 总结与补充
+在JDK8之后,可以通过-parameters参数来让编译器生成参数信息然后在运行时通过反射获取方法参数信息,其实在SpringFramework
+里面也有一个LocalVariableTableParameterNameDiscoverer对象可以获取方法参数名信息,有兴趣的同学可以自行百度(这个类在打印日志时可能会比较有用吧,个人感觉)。
diff --git "a/Java\347\233\270\345\205\263/synchronized.md" b/docs/java/synchronized.md
similarity index 94%
rename from "Java\347\233\270\345\205\263/synchronized.md"
rename to docs/java/synchronized.md
index dfca675f14a..0a1f4f2b073 100644
--- "a/Java\347\233\270\345\205\263/synchronized.md"
+++ b/docs/java/synchronized.md
@@ -1,6 +1,4 @@
-以下内容摘自我的 Gitchat :[Java 程序员必备:并发知识系统总结](https://gitbook.cn/gitchat/activity/5bc2b6af56f0425673d299bb),欢迎订阅!
-Github 地址:[https://github.com/Snailclimb/JavaGuide/edit/master/Java相关/synchronized.md](https://github.com/Snailclimb/JavaGuide/edit/master/Java相关/synchronized.md)

@@ -12,7 +10,7 @@ Github 地址:[https://github.com/Snailclimb/JavaGuide/edit/master/Java相关/
下面我已一个常见的面试题为例讲解一下 synchronized 关键字的具体使用。
-面试中面试官经常会说:“单例模式了解吗?来给我手写一下!给我解释一下双重检验锁方式实现单利模式的原理呗!”
+面试中面试官经常会说:“单例模式了解吗?来给我手写一下!给我解释一下双重检验锁方式实现单例模式的原理呗!”
@@ -48,7 +46,7 @@ uniqueInstance 采用 volatile 关键字修饰也是很有必要的, uniqueIns
2. 初始化 uniqueInstance
3. 将 uniqueInstance 指向分配的内存地址
-但是由于 JVM 具有指令重排的特性,执行顺序有可能变成 1->3->2。指令重排在单线程环境下不会出先问题,但是在多线程环境下会导致一个线程获得还没有初始化的实例。例如,线程 T1 执行了 1 和 3,此时 T2 调用 getUniqueInstance() 后发现 uniqueInstance 不为空,因此返回 uniqueInstance,但此时 uniqueInstance 还未被初始化。
+但是由于 JVM 具有指令重排的特性,执行顺序有可能变成 1->3->2。指令重排在单线程环境下不会出现问题,但是在多线程环境下会导致一个线程获得还没有初始化的实例。例如,线程 T1 执行了 1 和 3,此时 T2 调用 getUniqueInstance() 后发现 uniqueInstance 不为空,因此返回 uniqueInstance,但此时 uniqueInstance 还未被初始化。
使用 volatile 可以禁止 JVM 的指令重排,保证在多线程环境下也能正常运行。
@@ -141,7 +139,7 @@ JDK1.6 对锁的实现引入了大量的优化,如偏向锁、轻量级锁、
**⑤ 锁粗化**
-原则上,我们再编写代码的时候,总是推荐将同步快的作用范围限制得尽量小——只在共享数据的实际作用域才进行同步,这样是为了使得需要同步的操作数量尽可能变小,如果存在锁竞争,那等待线程也能尽快拿到锁。
+原则上,我们在编写代码的时候,总是推荐将同步块的作用范围限制得尽量小,——直在共享数据的实际作用域才进行同步,这样是为了使得需要同步的操作数量尽可能变小,如果存在锁竞争,那等待线程也能尽快拿到锁。
大部分情况下,上面的原则都是没有问题的,但是如果一系列的连续操作都对同一个对象反复加锁和解锁,那么会带来很多不必要的性能消耗。
@@ -168,4 +166,4 @@ synchronized 是依赖于 JVM 实现的,前面我们也讲到了 虚拟机团
**④ 性能已不是选择标准**
-在JDK1.6之前,synchronized 的性能是比 ReenTrantLock 差很多。具体表示为:synchronized 关键字吞吐量岁线程数的增加,下降得非常严重。而ReenTrantLock 基本保持一个比较稳定的水平。我觉得这也侧面反映了, synchronized 关键字还有非常大的优化余地。后续的技术发展也证明了这一点,我们上面也讲了在 JDK1.6 之后 JVM 团队对 synchronized 关键字做了很多优化。**JDK1.6 之后,synchronized 和 ReenTrantLock 的性能基本是持平了。所以网上那些说因为性能才选择 ReenTrantLock 的文章都是错的!JDK1.6之后,性能已经不是选择synchronized和ReenTrantLock的影响因素了!而且虚拟机在未来的性能改进中会更偏向于原生的synchronized,所以还是提倡在synchronized能满足你的需求的情况下,优先考虑使用synchronized关键字来进行同步!优化后的synchronized和ReenTrantLock一样,在很多地方都是用到了CAS操作**。
+在JDK1.6之前,synchronized 的性能是比 ReenTrantLock 差很多。具体表示为:synchronized 关键字吞吐量随线程数的增加,下降得非常严重。而ReenTrantLock 基本保持一个比较稳定的水平。我觉得这也侧面反映了, synchronized 关键字还有非常大的优化余地。后续的技术发展也证明了这一点,我们上面也讲了在 JDK1.6 之后 JVM 团队对 synchronized 关键字做了很多优化。**JDK1.6 之后,synchronized 和 ReenTrantLock 的性能基本是持平了。所以网上那些说因为性能才选择 ReenTrantLock 的文章都是错的!JDK1.6之后,性能已经不是选择synchronized和ReenTrantLock的影响因素了!而且虚拟机在未来的性能改进中会更偏向于原生的synchronized,所以还是提倡在synchronized能满足你的需求的情况下,优先考虑使用synchronized关键字来进行同步!优化后的synchronized和ReenTrantLock一样,在很多地方都是用到了CAS操作**。
diff --git "a/Java\347\233\270\345\205\263/\345\217\257\350\203\275\346\230\257\346\212\212Java\345\206\205\345\255\230\345\214\272\345\237\237\350\256\262\347\232\204\346\234\200\346\270\205\346\245\232\347\232\204\344\270\200\347\257\207\346\226\207\347\253\240.md" "b/docs/java/\345\217\257\350\203\275\346\230\257\346\212\212Java\345\206\205\345\255\230\345\214\272\345\237\237\350\256\262\347\232\204\346\234\200\346\270\205\346\245\232\347\232\204\344\270\200\347\257\207\346\226\207\347\253\240.md"
similarity index 81%
rename from "Java\347\233\270\345\205\263/\345\217\257\350\203\275\346\230\257\346\212\212Java\345\206\205\345\255\230\345\214\272\345\237\237\350\256\262\347\232\204\346\234\200\346\270\205\346\245\232\347\232\204\344\270\200\347\257\207\346\226\207\347\253\240.md"
rename to "docs/java/\345\217\257\350\203\275\346\230\257\346\212\212Java\345\206\205\345\255\230\345\214\272\345\237\237\350\256\262\347\232\204\346\234\200\346\270\205\346\245\232\347\232\204\344\270\200\347\257\207\346\226\207\347\253\240.md"
index 4f38f3a1261..01b59724850 100644
--- "a/Java\347\233\270\345\205\263/\345\217\257\350\203\275\346\230\257\346\212\212Java\345\206\205\345\255\230\345\214\272\345\237\237\350\256\262\347\232\204\346\234\200\346\270\205\346\245\232\347\232\204\344\270\200\347\257\207\346\226\207\347\253\240.md"
+++ "b/docs/java/\345\217\257\350\203\275\346\230\257\346\212\212Java\345\206\205\345\255\230\345\214\272\345\237\237\350\256\262\347\232\204\346\234\200\346\270\205\346\245\232\347\232\204\344\270\200\347\257\207\346\226\207\347\253\240.md"
@@ -1,28 +1,62 @@
-
-## 写在前面(常见面试题)
-
-### 基本问题:
+
+
+- [写在前面(常见面试题)](#写在前面常见面试题)
+ - [基本问题](#基本问题)
+ - [拓展问题](#拓展问题)
+- [一 概述](#一-概述)
+- [二 运行时数据区域](#二-运行时数据区域)
+ - [2.1 程序计数器](#21-程序计数器)
+ - [2.2 Java 虚拟机栈](#22-java-虚拟机栈)
+ - [2.3 本地方法栈](#23-本地方法栈)
+ - [2.4 堆](#24-堆)
+ - [2.5 方法区](#25-方法区)
+ - [2.6 运行时常量池](#26-运行时常量池)
+ - [2.7 直接内存](#27-直接内存)
+- [三 HotSpot 虚拟机对象探秘](#三-hotspot-虚拟机对象探秘)
+ - [3.1 对象的创建](#31-对象的创建)
+ - [3.2 对象的内存布局](#32-对象的内存布局)
+ - [3.3 对象的访问定位](#33-对象的访问定位)
+- [四 重点补充内容](#四--重点补充内容)
+ - [String 类和常量池](#string-类和常量池)
+ - [String s1 = new String("abc");这句话创建了几个对象?](#string-s1--new-stringabc这句话创建了几个对象)
+ - [8种基本类型的包装类和常量池](#8种基本类型的包装类和常量池)
+- [参考](#参考)
+
+
+## 写在前面(常见面试题)
+
+### 基本问题
- **介绍下 Java 内存区域(运行时数据区)**
- **Java 对象的创建过程(五步,建议能默写出来并且要知道每一步虚拟机做了什么)**
- **对象的访问定位的两种方式(句柄和直接指针两种方式)**
-### 拓展问题:
+### 拓展问题
- **String类和常量池**
- **8种基本类型的包装类和常量池**
-## 1 概述
+## 一 概述
对于 Java 程序员来说,在虚拟机自动内存管理机制下,不再需要像C/C++程序开发程序员这样为内一个 new 操作去写对应的 delete/free 操作,不容易出现内存泄漏和内存溢出问题。正是因为 Java 程序员把内存控制权利交给 Java 虚拟机,一旦出现内存泄漏和溢出方面的问题,如果不了解虚拟机是怎样使用内存的,那么排查错误将会是一个非常艰巨的任务。
-## 2 运行时数据区域
-Java 虚拟机在执行 Java 程序的过程中会把它管理的内存划分成若干个不同的数据区域。
-
-这些组成部分一些是线程私有的,其他的则是线程共享的。
+## 二 运行时数据区域
+Java 虚拟机在执行 Java 程序的过程中会把它管理的内存划分成若干个不同的数据区域。JDK. 1.8 和之前的版本略有不同,下面会介绍到。
+
+**JDK 1.8之前:**
+
+
+

+
+
+**JDK 1.8 :**
+
+
+

+
**线程私有的:**
@@ -34,7 +68,7 @@ Java 虚拟机在执行 Java 程序的过程中会把它管理的内存划分成
- 堆
- 方法区
-- 直接内存
+- 直接内存(非运行时数据区的一部分)
### 2.1 程序计数器
@@ -47,11 +81,11 @@ Java 虚拟机在执行 Java 程序的过程中会把它管理的内存划分成
1. 字节码解释器通过改变程序计数器来依次读取指令,从而实现代码的流程控制,如:顺序执行、选择、循环、异常处理。
2. 在多线程的情况下,程序计数器用于记录当前线程执行的位置,从而当线程被切换回来的时候能够知道该线程上次运行到哪儿了。
-**注意:程序计数器是唯一一个不会出现OutOfMemoryError的内存区域,它的生命周期随着线程的创建而创建,随着线程的结束而死亡。**
+**注意:程序计数器是唯一一个不会出现 OutOfMemoryError 的内存区域,它的生命周期随着线程的创建而创建,随着线程的结束而死亡。**
### 2.2 Java 虚拟机栈
-**与程序计数器一样,Java虚拟机栈也是线程私有的,它的生命周期和线程相同,描述的是 Java 方法执行的内存模型。**
+**与程序计数器一样,Java虚拟机栈也是线程私有的,它的生命周期和线程相同,描述的是 Java 方法执行的内存模型,每次方法调用的数据都是通过栈传递的。**
**Java 内存可以粗糙的区分为堆内存(Heap)和栈内存(Stack),其中栈就是现在说的虚拟机栈,或者说是虚拟机栈中局部变量表部分。** (实际上,Java虚拟机栈是由一个个栈帧组成,而每个栈帧中都拥有:局部变量表、操作数栈、动态链接、方法出口信息。)
@@ -64,6 +98,17 @@ Java 虚拟机在执行 Java 程序的过程中会把它管理的内存划分成
Java 虚拟机栈也是线程私有的,每个线程都有各自的Java虚拟机栈,而且随着线程的创建而创建,随着线程的死亡而死亡。
+**扩展:那么方法/函数如何调用?**
+
+Java 栈可用类比数据结构中栈,Java 栈中保存的主要内容是栈帧,每一次函数调用都会有一个对应的栈帧被压入Java栈,每一个函数调用结束后,都会有一个栈帧被弹出。
+
+Java方法有两种返回方式:
+
+1. return 语句。
+2. 抛出异常。
+
+不管哪种返回方式都会导致栈帧被弹出。
+
### 2.3 本地方法栈
和虚拟机栈所发挥的作用非常相似,区别是: **虚拟机栈为虚拟机执行 Java 方法 (也就是字节码)服务,而本地方法栈则为虚拟机使用到的 Native 方法服务。** 在 HotSpot 虚拟机中和 Java 虚拟机栈合二为一。
@@ -77,13 +122,11 @@ Java 虚拟机所管理的内存中最大的一块,Java 堆是所有线程共
Java 堆是垃圾收集器管理的主要区域,因此也被称作**GC堆(Garbage Collected Heap)**.从垃圾回收的角度,由于现在收集器基本都采用分代垃圾收集算法,所以Java堆还可以细分为:新生代和老年代:再细致一点有:Eden空间、From Survivor、To Survivor空间等。**进一步划分的目的是更好地回收内存,或者更快地分配内存。**
-
+
+

+
-**在 JDK 1.8中移除整个永久代,取而代之的是一个叫元空间(Metaspace)的区域(永久代使用的是JVM的堆内存空间,而元空间使用的是物理内存,直接受到本机的物理内存限制)。**
-
-推荐阅读:
-
-- 《Java8内存模型—永久代(PermGen)和元空间(Metaspace)》:[http://www.cnblogs.com/paddix/p/5309550.html](http://www.cnblogs.com/paddix/p/5309550.html)
+上图所示的 eden区、s0区、s1区都属于新生代,tentired 区属于老年代。大部分情况,对象都会首先在 Eden 区域分配,在一次新生代垃圾回收后,如果对象还存活,则会进入 s0 或者 s1,并且对象的年龄还会加 1(Eden区->Survivor 区后对象的初始年龄变为1),当它的年龄增加到一定程度(默认为15岁),就会被晋升到老年代中。对象晋升到老年代的年龄阈值,可以通过参数 `-XX:MaxTenuringThreshold` 来设置。
### 2.5 方法区
@@ -91,9 +134,11 @@ Java 堆是垃圾收集器管理的主要区域,因此也被称作**GC堆(Ga
HotSpot 虚拟机中方法区也常被称为 **“永久代”**,本质上两者并不等价。仅仅是因为 HotSpot 虚拟机设计团队用永久代来实现方法区而已,这样 HotSpot 虚拟机的垃圾收集器就可以像管理 Java 堆一样管理这部分内存了。但是这并不是一个好主意,因为这样更容易遇到内存溢出问题。
+**相对而言,垃圾收集行为在这个区域是比较少出现的,但并非数据进入方法区后就“永久存在”了。**
+JDK 1.8 的时候,方法区被彻底移除了(JDK1.7就已经开始了),取而代之是元空间,元空间使用的是直接内存。
-**相对而言,垃圾收集行为在这个区域是比较少出现的,但并非数据进入方法区后就“永久存在”了。**
+我们可以使用参数: `-XX:MetaspaceSize ` 来指定元数据区的大小。与永久区很大的不同就是,如果不指定大小的话,随着更多类的创建,虚拟机会耗尽所有可用的系统内存。
### 2.6 运行时常量池
@@ -107,22 +152,16 @@ HotSpot 虚拟机中方法区也常被称为 **“永久代”**,本质上两
——图片来源:https://blog.csdn.net/wangbiao007/article/details/78545189
-
-推荐阅读:
-
-- 《Java 中几种常量池的区分》: [https://blog.csdn.net/qq_26222859/article/details/73135660](https://blog.csdn.net/qq_26222859/article/details/73135660)
-
-
### 2.7 直接内存
-直接内存并不是虚拟机运行时数据区的一部分,也不是虚拟机规范中定义的内存区域,但是这部分内存也被频繁地使用。而且也可能导致OutOfMemoryError异常出现。
+**直接内存并不是虚拟机运行时数据区的一部分,也不是虚拟机规范中定义的内存区域,但是这部分内存也被频繁地使用。而且也可能导致 OutOfMemoryError 异常出现。**
-JDK1.4中新加入的 **NIO(New Input/Output) 类**,引入了一种基于**通道(Channel)** 与**缓存区(Buffer)** 的 I/O 方式,它可以直接使用Native函数库直接分配堆外内存,然后通过一个存储在 Java 堆中的 DirectByteBuffer 对象作为这块内存的引用进行操作。这样就能在一些场景中显著提高性能,因为**避免了在 Java 堆和 Native 堆之间来回复制数据**。
+JDK1.4 中新加入的 **NIO(New Input/Output) 类**,引入了一种基于**通道(Channel)** 与**缓存区(Buffer)** 的 I/O 方式,它可以直接使用 Native 函数库直接分配堆外内存,然后通过一个存储在 Java 堆中的 DirectByteBuffer 对象作为这块内存的引用进行操作。这样就能在一些场景中显著提高性能,因为**避免了在 Java 堆和 Native 堆之间来回复制数据**。
本机直接内存的分配不会收到 Java 堆的限制,但是,既然是内存就会受到本机总内存大小以及处理器寻址空间的限制。
-## 3 HotSpot 虚拟机对象探秘
+## 三 HotSpot 虚拟机对象探秘
通过上面的介绍我们大概知道了虚拟机的内存情况,下面我们来详细的了解一下 HotSpot 虚拟机在 Java 堆中对象分配、布局和访问的全过程。
### 3.1 对象的创建
@@ -139,7 +178,7 @@ JDK1.4中新加入的 **NIO(New Input/Output) 类**,引入了一种基于**通
选择以上两种方式中的哪一种,取决于 Java 堆内存是否规整。而 Java 堆内存是否规整,取决于 GC 收集器的算法是"标记-清除",还是"标记-整理"(也称作"标记-压缩"),值得注意的是,复制算法内存也是规整的

-
+
**内存分配并发问题(补充内容,需要掌握)**
在创建对象的时候有一个很重要的问题,就是线程安全,因为在实际开发过程中,创建对象是很频繁的事情,作为虚拟机来说,必须要保证线程是安全的,通常来讲,虚拟机采用两种方式来保证线程安全:
@@ -235,7 +274,7 @@ JDK1.4中新加入的 **NIO(New Input/Output) 类**,引入了一种基于**通
**验证:**
```java
- String s1 = new String("abc");// 堆内存的地值值
+ String s1 = new String("abc");// 堆内存的地址值
String s2 = "abc";
System.out.println(s1 == s2);// 输出false,因为一个是堆内存,一个是常量池的内存,故两者是不同的。
System.out.println(s1.equals(s2));// 输出true
@@ -325,8 +364,7 @@ i4=i5+i6 true
语句i4 == i5 + i6,因为+这个操作符不适用于Integer对象,首先i5和i6进行自动拆箱操作,进行数值相加,即i4 == 40。然后Integer对象无法与数值进行直接比较,所以i4自动拆箱转为int值40,最终这条语句转为40 == 40进行数值比较。
-
-**参考:**
+## 参考
- 《深入理解Java虚拟机:JVM高级特性与最佳实践(第二版》
- 《实战java虚拟机》
diff --git "a/Java\347\233\270\345\205\263/\345\244\232\347\272\277\347\250\213\347\263\273\345\210\227.md" "b/docs/java/\345\244\232\347\272\277\347\250\213\347\263\273\345\210\227.md"
similarity index 100%
rename from "Java\347\233\270\345\205\263/\345\244\232\347\272\277\347\250\213\347\263\273\345\210\227.md"
rename to "docs/java/\345\244\232\347\272\277\347\250\213\347\263\273\345\210\227.md"
diff --git "a/Java\347\233\270\345\205\263/\346\220\236\345\256\232JVM\345\236\203\345\234\276\345\233\236\346\224\266\345\260\261\346\230\257\350\277\231\344\271\210\347\256\200\345\215\225.md" "b/docs/java/\346\220\236\345\256\232JVM\345\236\203\345\234\276\345\233\236\346\224\266\345\260\261\346\230\257\350\277\231\344\271\210\347\256\200\345\215\225.md"
similarity index 95%
rename from "Java\347\233\270\345\205\263/\346\220\236\345\256\232JVM\345\236\203\345\234\276\345\233\236\346\224\266\345\260\261\346\230\257\350\277\231\344\271\210\347\256\200\345\215\225.md"
rename to "docs/java/\346\220\236\345\256\232JVM\345\236\203\345\234\276\345\233\236\346\224\266\345\260\261\346\230\257\350\277\231\344\271\210\347\256\200\345\215\225.md"
index 819d37faca2..4530f3d3b0e 100644
--- "a/Java\347\233\270\345\205\263/\346\220\236\345\256\232JVM\345\236\203\345\234\276\345\233\236\346\224\266\345\260\261\346\230\257\350\277\231\344\271\210\347\256\200\345\215\225.md"
+++ "b/docs/java/\346\220\236\345\256\232JVM\345\236\203\345\234\276\345\233\236\346\224\266\345\260\261\346\230\257\350\277\231\344\271\210\347\256\200\345\215\225.md"
@@ -28,11 +28,17 @@
Java 的自动内存管理主要是针对对象内存的回收和对象内存的分配。同时,Java 自动内存管理最核心的功能是 **堆** 内存中对象的分配与回收。
-**JDK1.8之前的堆内存示意图:**
+Java 堆是垃圾收集器管理的主要区域,因此也被称作**GC堆(Garbage Collected Heap)**.从垃圾回收的角度,由于现在收集器基本都采用分代垃圾收集算法,所以 Java 堆还可以细分为:新生代和老年代:再细致一点有:Eden空间、From Survivor、To Survivor空间等。**进一步划分的目的是更好地回收内存,或者更快地分配内存。**
+
+**堆空间的基本结构:**
+
+
+

+
+
+上图所示的 eden区、s0区、s1区都属于新生代,tentired 区属于老年代。大部分情况,对象都会首先在 Eden 区域分配,在一次新生代垃圾回收后,如果对象还存活,则会进入 s0 或者 s1,并且对象的年龄还会加 1(Eden区->Survivor 区后对象的初始年龄变为1),当它的年龄增加到一定程度(默认为15岁),就会被晋升到老年代中。对象晋升到老年代的年龄阈值,可以通过参数 `-XX:MaxTenuringThreshold` 来设置。
-
-从上图可以看出堆内存分为新生代、老年代和永久代。新生代又被进一步分为:Eden 区+Survivor1 区+Survivor2 区。值得注意的是,在 JDK 1.8中移除整个永久代,取而代之的是一个叫元空间(Metaspace)的区域(永久代使用的是JVM的堆内存空间,而元空间使用的是物理内存,直接受到本机的物理内存限制)。

@@ -306,7 +312,7 @@ Parallel Scavenge 收集器类似于ParNew 收集器。 **那么它有什么特
### 4.5 Parallel Old收集器
**Parallel Scavenge收集器的老年代版本**。使用多线程和“标记-整理”算法。在注重吞吐量以及CPU资源的场合,都可以优先考虑 Parallel Scavenge收集器和Parallel Old收集器。
-
+
### 4.6 CMS收集器
**CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。它而非常符合在注重用户体验的应用上使用。**
diff --git "a/Java\347\233\270\345\205\263/\350\277\231\345\207\240\351\201\223Java\351\233\206\345\220\210\346\241\206\346\236\266\351\235\242\350\257\225\351\242\230\345\207\240\344\271\216\345\277\205\351\227\256.md" "b/docs/java/\350\277\231\345\207\240\351\201\223Java\351\233\206\345\220\210\346\241\206\346\236\266\351\235\242\350\257\225\351\242\230\345\207\240\344\271\216\345\277\205\351\227\256.md"
similarity index 98%
rename from "Java\347\233\270\345\205\263/\350\277\231\345\207\240\351\201\223Java\351\233\206\345\220\210\346\241\206\346\236\266\351\235\242\350\257\225\351\242\230\345\207\240\344\271\216\345\277\205\351\227\256.md"
rename to "docs/java/\350\277\231\345\207\240\351\201\223Java\351\233\206\345\220\210\346\241\206\346\236\266\351\235\242\350\257\225\351\242\230\345\207\240\344\271\216\345\277\205\351\227\256.md"
index eb86fc2e285..18d276c4e22 100644
--- "a/Java\347\233\270\345\205\263/\350\277\231\345\207\240\351\201\223Java\351\233\206\345\220\210\346\241\206\346\236\266\351\235\242\350\257\225\351\242\230\345\207\240\344\271\216\345\277\205\351\227\256.md"
+++ "b/docs/java/\350\277\231\345\207\240\351\201\223Java\351\233\206\345\220\210\346\241\206\346\236\266\351\235\242\350\257\225\351\242\230\345\207\240\344\271\216\345\277\205\351\227\256.md"
@@ -32,8 +32,7 @@
- **3. 插入和删除是否受元素位置的影响:** ① **ArrayList 采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响。** 比如:执行`add(E e) `方法的时候, ArrayList 会默认在将指定的元素追加到此列表的末尾,这种情况时间复杂度就是O(1)。但是如果要在指定位置 i 插入和删除元素的话(`add(int index, E element) `)时间复杂度就为 O(n-i)。因为在进行上述操作的时候集合中第 i 和第 i 个元素之后的(n-i)个元素都要执行向后位/向前移一位的操作。 ② **LinkedList 采用链表存储,所以插入,删除元素时间复杂度不受元素位置的影响,都是近似 O(1)而数组为近似 O(n)。**
- **4. 是否支持快速随机访问:** LinkedList 不支持高效的随机元素访问,而 ArrayList 支持。快速随机访问就是通过元素的序号快速获取元素对象(对应于`get(int index) `方法)。
- **5. 内存空间占用:** ArrayList的空 间浪费主要体现在在list列表的结尾会预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗比ArrayList更多的空间(因为要存放直接后继和直接前驱以及数据)。
--**6. 发
-**补充内容:RandomAccess接口**
+- **6.补充内容:RandomAccess接口**
```java
public interface RandomAccess {
@@ -203,7 +202,7 @@ static int hash(int h) {
这个过程为,先将 A 复制到新的 hash 表中,然后接着复制 B 到链头(A 的前边:B.next=A),本来 B.next=null,到此也就结束了(跟线程二一样的过程),但是,由于线程二扩容的原因,将 B.next=A,所以,这里继续复制A,让 A.next=B,由此,环形链表出现:B.next=A; A.next=B
-**注意:jdk1.8已经解决了死循环的问题。**
+**注意:jdk1.8已经解决了死循环的问题。**详细信息请阅读[jdk1.8 hashmap多线程put不会造成死循环](https://blog.csdn.net/qq_27007251/article/details/71403647)
## HashSet 和 HashMap 区别
diff --git "a/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\344\270\216\346\225\260\346\215\256\351\200\232\344\277\241/HTTPS\344\270\255\347\232\204TLS.md" "b/docs/network/HTTPS\344\270\255\347\232\204TLS.md"
similarity index 100%
rename from "\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\344\270\216\346\225\260\346\215\256\351\200\232\344\277\241/HTTPS\344\270\255\347\232\204TLS.md"
rename to "docs/network/HTTPS\344\270\255\347\232\204TLS.md"
diff --git "a/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\344\270\216\346\225\260\346\215\256\351\200\232\344\277\241/\345\271\262\350\264\247\357\274\232\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\347\237\245\350\257\206\346\200\273\347\273\223.md" "b/docs/network/\345\271\262\350\264\247\357\274\232\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\347\237\245\350\257\206\346\200\273\347\273\223.md"
similarity index 100%
rename from "\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\344\270\216\346\225\260\346\215\256\351\200\232\344\277\241/\345\271\262\350\264\247\357\274\232\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\347\237\245\350\257\206\346\200\273\347\273\223.md"
rename to "docs/network/\345\271\262\350\264\247\357\274\232\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\347\237\245\350\257\206\346\200\273\347\273\223.md"
diff --git "a/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\344\270\216\346\225\260\346\215\256\351\200\232\344\277\241/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234.md" "b/docs/network/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234.md"
similarity index 98%
rename from "\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\344\270\216\346\225\260\346\215\256\351\200\232\344\277\241/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234.md"
rename to "docs/network/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234.md"
index ba5fb91ecc9..813ba89c983 100644
--- "a/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\344\270\216\346\225\260\346\215\256\351\200\232\344\277\241/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234.md"
+++ "b/docs/network/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234.md"
@@ -69,7 +69,7 @@
#### 运输层主要使用以下两种协议
-1. **传输控制协议 TCP**(Transmisson Control Protocol)--提供**面向连接**的,**可靠的**数据传输服务。
+1. **传输控制协议 TCP**(Transmission Control Protocol)--提供**面向连接**的,**可靠的**数据传输服务。
2. **用户数据协议 UDP**(User Datagram Protocol)--提供**无连接**的,尽最大努力的数据传输服务(**不保证数据传输的可靠性**)。
#### UDP 的主要特点
@@ -97,7 +97,7 @@
这里强调指出,网络层中的“网络”二字已经不是我们通常谈到的具体网络,而是指计算机网络体系结构模型中第三层的名称.
-互联网是由大量的异构(heterogeneous)网络通过路由器(router)相互连接起来的。互联网使用的网络层协议是无连接的网际协议(Intert Prococol)和许多路由选择协议,因此互联网的网络层也叫做**网际层**或**IP层**。
+互联网是由大量的异构(heterogeneous)网络通过路由器(router)相互连接起来的。互联网使用的网络层协议是无连接的网际协议(Intert Protocol)和许多路由选择协议,因此互联网的网络层也叫做**网际层**或**IP层**。
### 4 数据链路层
**数据链路层(data link layer)通常简称为链路层。两台主机之间的数据传输,总是在一段一段的链路上传送的,这就需要使用专门的链路层的协议。** 在两个相邻节点之间传送数据时,**数据链路层将网络层交下来的 IP 数据报组装程帧**,在两个相邻节点间的链路上传送帧。每一帧包括数据和必要的控制信息(如同步信息,地址信息,差错控制等)。
@@ -187,7 +187,8 @@ TCP 提供面向连接的服务。在传送数据之前必须先建立连接,
4. TCP 的接收端会丢弃重复的数据。
5. **流量控制:** TCP 连接的每一方都有固定大小的缓冲空间,TCP的接收端只允许发送端发送接收端缓冲区能接纳的数据。当接收方来不及处理发送方的数据,能提示发送方降低发送的速率,防止包丢失。TCP 使用的流量控制协议是可变大小的滑动窗口协议。 (TCP 利用滑动窗口实现流量控制)
6. **拥塞控制:** 当网络拥塞时,减少数据的发送。
-7. **停止等待协议** 也是为了实现可靠传输的,它的基本原理就是每发完一个分组就停止发送,等待对方确认。在收到确认后再发下一个分组。 **超时重传:** 当 TCP 发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段。
+7. **停止等待协议** 也是为了实现可靠传输的,它的基本原理就是每发完一个分组就停止发送,等待对方确认。在收到确认后再发下一个分组。
+8. **超时重传:** 当 TCP 发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文段。
@@ -221,7 +222,7 @@ TCP 提供面向连接的服务。在传送数据之前必须先建立连接,
2. B收到重复的M1后,也直接丢弃重复的M1。
### 自动重传请求 ARQ 协议
-停止等待协议中超时重传是指只要超过一段时间仍然没有收到确认,就重传前面发送过的分组(认为刚才发送过的分组丢失了)。因此每发送完一个分组需要设置一个超时计时器,其重转时间应比数据在分组传输的平均往返时间更长一些。这种自动重传方式常称为自动重传请求ARQ。
+停止等待协议中超时重传是指只要超过一段时间仍然没有收到确认,就重传前面发送过的分组(认为刚才发送过的分组丢失了)。因此每发送完一个分组需要设置一个超时计时器,其重传时间应比数据在分组传输的平均往返时间更长一些。这种自动重传方式常称为自动重传请求ARQ。
**优点:** 简单
diff --git "a/\346\223\215\344\275\234\347\263\273\347\273\237/Shell.md" b/docs/operating-system/Shell.md
similarity index 99%
rename from "\346\223\215\344\275\234\347\263\273\347\273\237/Shell.md"
rename to docs/operating-system/Shell.md
index 4179f2702bb..9f3ae871ee4 100644
--- "a/\346\223\215\344\275\234\347\263\273\347\273\237/Shell.md"
+++ b/docs/operating-system/Shell.md
@@ -210,7 +210,7 @@ expr length "$name";
expr 5+6 // 直接输出 5+6
expr 5 + 6 // 输出 11
```
-对于某些运算符,还需要我们使用符号"\"进行转义,否则就会提示语法错误。
+对于某些运算符,还需要我们使用符号`\`进行转义,否则就会提示语法错误。
```shell
expr 5 * 6 // 输出错误
diff --git "a/\346\223\215\344\275\234\347\263\273\347\273\237/\345\220\216\347\253\257\347\250\213\345\272\217\345\221\230\345\277\205\345\244\207\347\232\204Linux\345\237\272\347\241\200\347\237\245\350\257\206.md" "b/docs/operating-system/\345\220\216\347\253\257\347\250\213\345\272\217\345\221\230\345\277\205\345\244\207\347\232\204Linux\345\237\272\347\241\200\347\237\245\350\257\206.md"
similarity index 100%
rename from "\346\223\215\344\275\234\347\263\273\347\273\237/\345\220\216\347\253\257\347\250\213\345\272\217\345\221\230\345\277\205\345\244\207\347\232\204Linux\345\237\272\347\241\200\347\237\245\350\257\206.md"
rename to "docs/operating-system/\345\220\216\347\253\257\347\250\213\345\272\217\345\221\230\345\277\205\345\244\207\347\232\204Linux\345\237\272\347\241\200\347\237\245\350\257\206.md"
diff --git "a/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\344\270\216\346\225\260\346\215\256\351\200\232\344\277\241/dubbo.md" b/docs/system-design/data-communication/dubbo.md
similarity index 100%
rename from "\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\344\270\216\346\225\260\346\215\256\351\200\232\344\277\241/dubbo.md"
rename to docs/system-design/data-communication/dubbo.md
diff --git "a/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\344\270\216\346\225\260\346\215\256\351\200\232\344\277\241/message-queue.md" b/docs/system-design/data-communication/message-queue.md
similarity index 100%
rename from "\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\344\270\216\346\225\260\346\215\256\351\200\232\344\277\241/message-queue.md"
rename to docs/system-design/data-communication/message-queue.md
diff --git "a/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\344\270\216\346\225\260\346\215\256\351\200\232\344\277\241/rabbitmq.md" b/docs/system-design/data-communication/rabbitmq.md
similarity index 97%
rename from "\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\344\270\216\346\225\260\346\215\256\351\200\232\344\277\241/rabbitmq.md"
rename to docs/system-design/data-communication/rabbitmq.md
index 7b1508f456c..825f71239c6 100644
--- "a/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\344\270\216\346\225\260\346\215\256\351\200\232\344\277\241/rabbitmq.md"
+++ b/docs/system-design/data-communication/rabbitmq.md
@@ -123,7 +123,7 @@ direct 类型常用在处理有优先级的任务,根据任务的优先级把
- RoutingKey 为一个点号“.”分隔的字符串(被点号“.”分隔开的每一段独立的字符串称为一个单词),如 “com.rabbitmq.client”、“java.util.concurrent”、“com.hidden.client”;
- BindingKey 和 RoutingKey 一样也是点号“.”分隔的字符串;
-- BindingKey 中可以存在两种特殊字符串“*”和“#”,用于做模糊匹配,其中“#”用于匹配一个单词,“#”用于匹配多规格单词(可以是零个)。
+- BindingKey 中可以存在两种特殊字符串“*”和“#”,用于做模糊匹配,其中“.”用于匹配一个单词,“#”用于匹配多个单词(可以是零个)。

@@ -145,6 +145,10 @@ headers 类型的交换器不依赖于路由键的匹配规则来路由消息,
前面提到了 RabbitMQ 是由 Erlang语言编写的,也正因如此,在安装RabbitMQ 之前需要安装 Erlang。
+注意:在安装 RabbitMQ 的时候需要注意 RabbitMQ 和 Erlang 的版本关系,如果不注意的话会导致出错,两者对应关系如下:
+
+
+
### 2.1 安装 erlang
**1 下载 erlang 安装包**
diff --git "a/\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\344\270\216\346\225\260\346\215\256\351\200\232\344\277\241/\346\225\260\346\215\256\351\200\232\344\277\241(RESTful\343\200\201RPC\343\200\201\346\266\210\346\201\257\351\230\237\345\210\227).md" "b/docs/system-design/data-communication/\346\225\260\346\215\256\351\200\232\344\277\241(RESTful\343\200\201RPC\343\200\201\346\266\210\346\201\257\351\230\237\345\210\227).md"
similarity index 100%
rename from "\350\256\241\347\256\227\346\234\272\347\275\221\347\273\234\344\270\216\346\225\260\346\215\256\351\200\232\344\277\241/\346\225\260\346\215\256\351\200\232\344\277\241(RESTful\343\200\201RPC\343\200\201\346\266\210\346\201\257\351\230\237\345\210\227).md"
rename to "docs/system-design/data-communication/\346\225\260\346\215\256\351\200\232\344\277\241(RESTful\343\200\201RPC\343\200\201\346\266\210\346\201\257\351\230\237\345\210\227).md"
diff --git "a/\344\270\273\346\265\201\346\241\206\346\236\266/SpringBean.md" b/docs/system-design/framework/SpringBean.md
similarity index 100%
rename from "\344\270\273\346\265\201\346\241\206\346\236\266/SpringBean.md"
rename to docs/system-design/framework/SpringBean.md
diff --git "a/\344\270\273\346\265\201\346\241\206\346\236\266/SpringMVC \345\267\245\344\275\234\345\216\237\347\220\206\350\257\246\350\247\243.md" "b/docs/system-design/framework/SpringMVC \345\267\245\344\275\234\345\216\237\347\220\206\350\257\246\350\247\243.md"
similarity index 100%
rename from "\344\270\273\346\265\201\346\241\206\346\236\266/SpringMVC \345\267\245\344\275\234\345\216\237\347\220\206\350\257\246\350\247\243.md"
rename to "docs/system-design/framework/SpringMVC \345\267\245\344\275\234\345\216\237\347\220\206\350\257\246\350\247\243.md"
diff --git "a/\344\270\273\346\265\201\346\241\206\346\236\266/Spring\345\255\246\344\271\240\344\270\216\351\235\242\350\257\225.md" "b/docs/system-design/framework/Spring\345\255\246\344\271\240\344\270\216\351\235\242\350\257\225.md"
similarity index 100%
rename from "\344\270\273\346\265\201\346\241\206\346\236\266/Spring\345\255\246\344\271\240\344\270\216\351\235\242\350\257\225.md"
rename to "docs/system-design/framework/Spring\345\255\246\344\271\240\344\270\216\351\235\242\350\257\225.md"
diff --git "a/\344\270\273\346\265\201\346\241\206\346\236\266/ZooKeeper.md" b/docs/system-design/framework/ZooKeeper.md
similarity index 100%
rename from "\344\270\273\346\265\201\346\241\206\346\236\266/ZooKeeper.md"
rename to docs/system-design/framework/ZooKeeper.md
diff --git "a/\344\270\273\346\265\201\346\241\206\346\236\266/ZooKeeper\346\225\260\346\215\256\346\250\241\345\236\213\345\222\214\345\270\270\350\247\201\345\221\275\344\273\244.md" "b/docs/system-design/framework/ZooKeeper\346\225\260\346\215\256\346\250\241\345\236\213\345\222\214\345\270\270\350\247\201\345\221\275\344\273\244.md"
similarity index 100%
rename from "\344\270\273\346\265\201\346\241\206\346\236\266/ZooKeeper\346\225\260\346\215\256\346\250\241\345\236\213\345\222\214\345\270\270\350\247\201\345\221\275\344\273\244.md"
rename to "docs/system-design/framework/ZooKeeper\346\225\260\346\215\256\346\250\241\345\236\213\345\222\214\345\270\270\350\247\201\345\221\275\344\273\244.md"
diff --git "a/\346\236\266\346\236\204/8 \345\274\240\345\233\276\350\257\273\346\207\202\345\244\247\345\236\213\347\275\221\347\253\231\346\212\200\346\234\257\346\236\266\346\236\204.md" "b/docs/system-design/website-architecture/8 \345\274\240\345\233\276\350\257\273\346\207\202\345\244\247\345\236\213\347\275\221\347\253\231\346\212\200\346\234\257\346\236\266\346\236\204.md"
similarity index 100%
rename from "\346\236\266\346\236\204/8 \345\274\240\345\233\276\350\257\273\346\207\202\345\244\247\345\236\213\347\275\221\347\253\231\346\212\200\346\234\257\346\236\266\346\236\204.md"
rename to "docs/system-design/website-architecture/8 \345\274\240\345\233\276\350\257\273\346\207\202\345\244\247\345\236\213\347\275\221\347\253\231\346\212\200\346\234\257\346\236\266\346\236\204.md"
diff --git "a/\346\236\266\346\236\204/\343\200\220\351\235\242\350\257\225\347\262\276\351\200\211\343\200\221\345\205\263\344\272\216\345\244\247\345\236\213\347\275\221\347\253\231\347\263\273\347\273\237\346\236\266\346\236\204\344\275\240\344\270\215\345\276\227\344\270\215\346\207\202\347\232\20410\344\270\252\351\227\256\351\242\230.md" "b/docs/system-design/website-architecture/\343\200\220\351\235\242\350\257\225\347\262\276\351\200\211\343\200\221\345\205\263\344\272\216\345\244\247\345\236\213\347\275\221\347\253\231\347\263\273\347\273\237\346\236\266\346\236\204\344\275\240\344\270\215\345\276\227\344\270\215\346\207\202\347\232\20410\344\270\252\351\227\256\351\242\230.md"
similarity index 99%
rename from "\346\236\266\346\236\204/\343\200\220\351\235\242\350\257\225\347\262\276\351\200\211\343\200\221\345\205\263\344\272\216\345\244\247\345\236\213\347\275\221\347\253\231\347\263\273\347\273\237\346\236\266\346\236\204\344\275\240\344\270\215\345\276\227\344\270\215\346\207\202\347\232\20410\344\270\252\351\227\256\351\242\230.md"
rename to "docs/system-design/website-architecture/\343\200\220\351\235\242\350\257\225\347\262\276\351\200\211\343\200\221\345\205\263\344\272\216\345\244\247\345\236\213\347\275\221\347\253\231\347\263\273\347\273\237\346\236\266\346\236\204\344\275\240\344\270\215\345\276\227\344\270\215\346\207\202\347\232\20410\344\270\252\351\227\256\351\242\230.md"
index 415661fdeaa..47ba541fb4d 100644
--- "a/\346\236\266\346\236\204/\343\200\220\351\235\242\350\257\225\347\262\276\351\200\211\343\200\221\345\205\263\344\272\216\345\244\247\345\236\213\347\275\221\347\253\231\347\263\273\347\273\237\346\236\266\346\236\204\344\275\240\344\270\215\345\276\227\344\270\215\346\207\202\347\232\20410\344\270\252\351\227\256\351\242\230.md"
+++ "b/docs/system-design/website-architecture/\343\200\220\351\235\242\350\257\225\347\262\276\351\200\211\343\200\221\345\205\263\344\272\216\345\244\247\345\236\213\347\275\221\347\253\231\347\263\273\347\273\237\346\236\266\346\236\204\344\275\240\344\270\215\345\276\227\344\270\215\346\207\202\347\232\20410\344\270\252\351\227\256\351\242\230.md"
@@ -24,7 +24,7 @@
### 1. 你使用过哪些组件或者方法来提升网站性能,可用性以及并发量
1. **提高硬件能力、增加系统服务器**。(当服务器增加到某个程度的时候系统所能提供的并发访问量几乎不变,所以不能根本解决问题)
-2. **使用缓存**(本地缓存:本地可以使用JDK自带的 Map、Guava Cache.分布式缓存:Redis、Memcache.本地缓存不适用于提高系统并发量,一般是用处用在程序中。比如Spring是如何实现单例的呢?大家如果看过源码的话,应该知道,Spiring把已经初始过的变量放在一个Map中,下次再要使用这个变量的时候,先判断Map中有没有,这也就是系统中常见的单例模式的实现。)
+2. **使用缓存**(本地缓存:本地可以使用JDK自带的 Map、Guava Cache.分布式缓存:Redis、Memcache.本地缓存不适用于提高系统并发量,一般是用处用在程序中。比如Spring是如何实现单例的呢?大家如果看过源码的话,应该知道,S把已经初始过的变量放在一个Map中,下次再要使用这个变量的时候,先判断Map中有没有,这也就是系统中常见的单例模式的实现。)
3. **消息队列** (解耦+削峰+异步)
4. **采用分布式开发** (不同的服务部署在不同的机器节点上,并且一个服务也可以部署在多台机器上,然后利用 Nginx 负载均衡访问。这样就解决了单点部署(All In)的缺点,大大提高的系统并发量)
5. **数据库分库(读写分离)、分表(水平分表、垂直分表)**
diff --git "a/\346\236\266\346\236\204/\345\210\206\345\270\203\345\274\217.md" "b/docs/system-design/website-architecture/\345\210\206\345\270\203\345\274\217.md"
similarity index 100%
rename from "\346\236\266\346\236\204/\345\210\206\345\270\203\345\274\217.md"
rename to "docs/system-design/website-architecture/\345\210\206\345\270\203\345\274\217.md"
diff --git "a/Java\347\233\270\345\205\263/\350\256\276\350\256\241\346\250\241\345\274\217.md" "b/docs/system-design/\350\256\276\350\256\241\346\250\241\345\274\217.md"
similarity index 51%
rename from "Java\347\233\270\345\205\263/\350\256\276\350\256\241\346\250\241\345\274\217.md"
rename to "docs/system-design/\350\256\276\350\256\241\346\250\241\345\274\217.md"
index c36bb237ee5..e3e95860529 100644
--- "a/Java\347\233\270\345\205\263/\350\256\276\350\256\241\346\250\241\345\274\217.md"
+++ "b/docs/system-design/\350\256\276\350\256\241\346\250\241\345\274\217.md"
@@ -1,36 +1,28 @@
+# Java 设计模式
+
下面是自己学习设计模式的时候做的总结,有些是自己的原创文章,有些是网上写的比较好的文章,保存下来细细消化吧!
-## 创建型模式:
+**系列文章推荐:**
+
+## 创建型模式
-> ### 创建型模式概述:
+### 创建型模式概述
- 创建型模式(Creational Pattern)对类的实例化过程进行了抽象,能够将软件模块中对象的创建和对象的使用分离。为了使软件的结构更加清晰,外界对于这些对象只需要知道它们共同的接口,而不清楚其具体的实现细节,使整个系统的设计更加符合单一职责原则。
- 创建型模式在创建什么(What),由谁创建(Who),何时创建(When)等方面都为软件设计者提供了尽可能大的灵活性。创建型模式隐藏了类的实例的创建细节,通过隐藏对象如何被创建和组合在一起达到使整个系统独立的目的。

-> ### 创建型模式系列文章推荐:
-
-- **单例模式:**
-
-[深入理解单例模式——只有一个实例](https://blog.csdn.net/qq_34337272/article/details/80455972)
-
-- **工厂模式:**
-
-[深入理解工厂模式——由对象工厂生成对象](https://blog.csdn.net/qq_34337272/article/details/80472071)
-
-- **建造者模式:**
-
-[深入理解建造者模式 ——组装复杂的实例](http://blog.csdn.net/qq_34337272/article/details/80540059)
-
-- **原型模式:**
+### 常见创建型模式详解
-[深入理解原型模式 ——通过复制生成实例](https://blog.csdn.net/qq_34337272/article/details/80706444)
+- **单例模式:** [深入理解单例模式——只有一个实例](https://blog.csdn.net/qq_34337272/article/details/80455972)
+- **工厂模式:** [深入理解工厂模式——由对象工厂生成对象](https://blog.csdn.net/qq_34337272/article/details/80472071)
+- **建造者模式:** [深入理解建造者模式 ——组装复杂的实例](http://blog.csdn.net/qq_34337272/article/details/80540059)
+- **原型模式:** [深入理解原型模式 ——通过复制生成实例](https://blog.csdn.net/qq_34337272/article/details/80706444)
+## 结构型模式
-## 结构型模式:
-
-> ### 结构型模式概述:
+### 结构型模式概述
- **结构型模式(Structural Pattern):** 描述如何将类或者对象结合在一起形成更大的结构,就像搭积木,可以通过简单积木的组合形成复杂的、功能更为强大的结构

@@ -40,48 +32,25 @@

-> ### 结构型模式系列文章推荐:
+### 常见结构型模式详解
- **适配器模式:**
-
-[深入理解适配器模式——加个“适配器”以便于复用](https://segmentfault.com/a/1190000011856448)
-
-[适配器模式原理及实例介绍-IBM](https://www.ibm.com/developerworks/cn/java/j-lo-adapter-pattern/index.html)
-
-- **桥接模式:**
-
-[设计模式笔记16:桥接模式(Bridge Pattern)](https://blog.csdn.net/yangzl2008/article/details/7670996)
-
-- **组合模式:**
-
-[大话设计模式—组合模式](https://blog.csdn.net/lmb55/article/details/51039781)
-
-- **装饰模式:**
-
-[java模式—装饰者模式](https://www.cnblogs.com/chenxing818/p/4705919.html)
-
-[Java设计模式-装饰者模式](https://blog.csdn.net/cauchyweierstrass/article/details/48240147)
-
-- **外观模式:**
-
-[java设计模式之外观模式(门面模式)](https://www.cnblogs.com/lthIU/p/5860607.html)
-
-- **享元模式:**
-
-[享元模式](http://www.jasongj.com/design_pattern/flyweight/)
-
+ - [深入理解适配器模式——加个“适配器”以便于复用](https://segmentfault.com/a/1190000011856448)
+ - [适配器模式原理及实例介绍-IBM](https://www.ibm.com/developerworks/cn/java/j-lo-adapter-pattern/index.html)
+- **桥接模式:** [设计模式笔记16:桥接模式(Bridge Pattern)](https://blog.csdn.net/yangzl2008/article/details/7670996)
+- **组合模式:** [大话设计模式—组合模式](https://blog.csdn.net/lmb55/article/details/51039781)
+- **装饰模式:** [java模式—装饰者模式](https://www.cnblogs.com/chenxing818/p/4705919.html)、[Java设计模式-装饰者模式](https://blog.csdn.net/cauchyweierstrass/article/details/48240147)
+- **外观模式:** [java设计模式之外观模式(门面模式)](https://www.cnblogs.com/lthIU/p/5860607.html)
+- **享元模式:** [享元模式](http://www.jasongj.com/design_pattern/flyweight/)
- **代理模式:**
-
-[代理模式原理及实例讲解 (IBM出品,很不错)](https://www.ibm.com/developerworks/cn/java/j-lo-proxy-pattern/index.html)
-
-[轻松学,Java 中的代理模式及动态代理](https://blog.csdn.net/briblue/article/details/73928350)
-
-[Java代理模式及其应用](https://blog.csdn.net/justloveyou_/article/details/74203025)
+ - [代理模式原理及实例讲解 (IBM出品,很不错)](https://www.ibm.com/developerworks/cn/java/j-lo-proxy-pattern/index.html)
+ - [轻松学,Java 中的代理模式及动态代理](https://blog.csdn.net/briblue/article/details/73928350)
+ - [Java代理模式及其应用](https://blog.csdn.net/justloveyou_/article/details/74203025)
## 行为型模式
-> ### 行为型模式概述:
+### 行为型模式概述
- 行为型模式(Behavioral Pattern)是对在不同的对象之间划分责任和算法的抽象化。
- 行为型模式不仅仅关注类和对象的结构,而且重点关注它们之间的相互作用。
@@ -95,27 +64,16 @@

- **职责链模式:**
-
-[Java设计模式之责任链模式、职责链模式](https://blog.csdn.net/jason0539/article/details/45091639)
-
-[责任链模式实现的三种方式](https://www.cnblogs.com/lizo/p/7503862.html)
-
-- **命令模式:**
-
-
-
-- **解释器模式:**
+- [Java设计模式之责任链模式、职责链模式](https://blog.csdn.net/jason0539/article/details/45091639)
+- [责任链模式实现的三种方式](https://www.cnblogs.com/lizo/p/7503862.html)
+- **命令模式:** 在软件设计中,我们经常需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是哪个,我们只需在程序运行时指定具体的请求接收者即可,此时,可以使用命令模式来进行设计,使得请求发送者与请求接收者消除彼此之间的耦合,让对象之间的调用关系更加灵活。命令模式可以对发送者和接收者完全解耦,发送者与接收者之间没有直接引用关系,发送请求的对象只需要知道如何发送请求,而不必知道如何完成请求。这就是命令模式的模式动机。
+- **解释器模式:**
- **迭代器模式:**
- **中介者模式:**
- **备忘录模式:**
-- **观察者模式:**
-
-观察者模式也是非常常用的设计模式,下面这个博客简单介绍了观察者模式的简单定义、解决了一个什么问题,用一个气象站和气象看板的例子去描述一对多的关系中观察者模式的应用,并且还介绍了jdk内置的观察者模式接口。
-
-[Java设计模式之观察者模式](https://zhanglijun1217.github.io/blog/2018/12/24/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E2%80%94%E2%80%94%E8%A7%82%E5%AF%9F%E8%80%85%E6%A8%A1%E5%BC%8F-md/)
-
-- **状态模式:**
-- **策略模式:**
+- **观察者模式:**
+- **状态模式:**
+- **策略模式:**
策略模式作为设计原则中开闭原则最典型的体现,也是经常使用的。下面这篇博客介绍了策略模式一般的组成部分和概念,并用了一个小demo去说明了策略模式的应用。
diff --git a/docs/tools/Docker.md b/docs/tools/Docker.md
new file mode 100644
index 00000000000..b7dd4f50ffd
--- /dev/null
+++ b/docs/tools/Docker.md
@@ -0,0 +1,220 @@
+**本文只是对Docker的概念做了较为详细的介绍,并不涉及一些像Docker环境的安装以及Docker的一些常见操作和命令。**
+
+
+
+- [一 先从认识容器开始](#一-先从认识容器开始)
+ - [1.1 什么是容器?](#11-什么是容器)
+ - [先来看看容器较为官方的解释](#先来看看容器较为官方的解释)
+ - [再来看看容器较为通俗的解释](#再来看看容器较为通俗的解释)
+ - [1.2 图解物理机,虚拟机与容器](#12-图解物理机虚拟机与容器)
+- [二 再来谈谈 Docker 的一些概念](#二-再来谈谈-docker-的一些概念)
+ - [2.1 什么是 Docker?](#21-什么是-docker)
+ - [2.2 Docker 思想](#22-docker-思想)
+ - [2.3 Docker 容器的特点](#23-docker-容器的特点)
+ - [2.4 为什么要用 Docker ?](#24-为什么要用-docker-)
+- [三 容器 VS 虚拟机](#三-容器-vs-虚拟机)
+ - [3.1 两者对比图](#31-两者对比图)
+ - [3.2 容器与虚拟机总结](#32-容器与虚拟机总结)
+ - [3.3 容器与虚拟机两者是可以共存的](#33-容器与虚拟机两者是可以共存的)
+- [四 Docker基本概念](#四-docker基本概念)
+ - [4.1 镜像(Image):一个特殊的文件系统](#41-镜像image一个特殊的文件系统)
+ - [4.2 容器(Container):镜像运行时的实体](#42-容器container镜像运行时的实体)
+ - [4.3仓库(Repository):集中存放镜像文件的地方](#43仓库repository集中存放镜像文件的地方)
+- [五 最后谈谈:Build Ship and Run](#五-最后谈谈build-ship-and-run)
+- [六 总结](#六-总结)
+
+
+
+> **Docker 是世界领先的软件容器平台**,所以想要搞懂Docker的概念我们必须先从容器开始说起。
+
+## 一 先从认识容器开始
+
+### 1.1 什么是容器?
+
+#### 先来看看容器较为官方的解释
+
+**一句话概括容器:容器就是将软件打包成标准化单元,以用于开发、交付和部署。**
+
+- **容器镜像是轻量的、可执行的独立软件包** ,包含软件运行所需的所有内容:代码、运行时环境、系统工具、系统库和设置。
+- **容器化软件适用于基于Linux和Windows的应用,在任何环境中都能够始终如一地运行。**
+- **容器赋予了软件独立性** ,使其免受外在环境差异(例如,开发和预演环境的差异)的影响,从而有助于减少团队间在相同基础设施上运行不同软件时的冲突。
+
+#### 再来看看容器较为通俗的解释
+
+**如果需要通俗的描述容器的话,我觉得容器就是一个存放东西的地方,就像书包可以装各种文具、衣柜可以放各种衣服、鞋架可以放各种鞋子一样。我们现在所说的容器存放的东西可能更偏向于应用比如网站、程序甚至是系统环境。**
+
+
+
+### 1.2 图解物理机,虚拟机与容器
+关于虚拟机与容器的对比在后面会详细介绍到,这里只是通过网上的图片加深大家对于物理机、虚拟机与容器这三者的理解。
+
+**物理机**
+
+
+**虚拟机:**
+
+
+
+**容器:**
+
+
+
+通过上面这三张抽象图,我们可以大概可以通过类比概括出: **容器虚拟化的是操作系统而不是硬件,容器之间是共享同一套操作系统资源的。虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统。因此容器的隔离级别会稍低一些。**
+
+---
+
+> 相信通过上面的解释大家对于容器这个既陌生又熟悉的概念有了一个初步的认识,下面我们就来谈谈Docker的一些概念。
+
+## 二 再来谈谈 Docker 的一些概念
+
+
+
+### 2.1 什么是 Docker?
+
+说实话关于Docker是什么并太好说,下面我通过四点向你说明Docker到底是个什么东西。
+
+- **Docker 是世界领先的软件容器平台。**
+- **Docker** 使用 Google 公司推出的 **Go 语言** 进行开发实现,基于 **Linux 内核** 的cgroup,namespace,以及AUFS类的**UnionFS**等技术,**对进程进行封装隔离,属于操作系统层面的虚拟化技术。** 由于隔离的进程独立于宿主和其它的隔离的进
+程,因此也称其为容器。**Docke最初实现是基于 LXC.**
+- **Docker 能够自动执行重复性任务,例如搭建和配置开发环境,从而解放了开发人员以便他们专注在真正重要的事情上:构建杰出的软件。**
+- **用户可以方便地创建和使用容器,把自己的应用放入容器。容器还可以进行版本管理、复制、分享、修改,就像管理普通的代码一样。**
+
+
+
+### 2.2 Docker 思想
+
+- **集装箱**
+- **标准化:** ①运输方式 ② 存储方式 ③ API接口
+- **隔离**
+
+### 2.3 Docker 容器的特点
+
+- #### 轻量
+
+ 在一台机器上运行的多个 Docker 容器可以共享这台机器的操作系统内核;它们能够迅速启动,只需占用很少的计算和内存资源。镜像是通过文件系统层进行构造的,并共享一些公共文件。这样就能尽量降低磁盘用量,并能更快地下载镜像。
+- #### 标准
+
+ Docker 容器基于开放式标准,能够在所有主流 Linux 版本、Microsoft Windows 以及包括 VM、裸机服务器和云在内的任何基础设施上运行。
+- #### 安全
+
+ Docker 赋予应用的隔离性不仅限于彼此隔离,还独立于底层的基础设施。Docker 默认提供最强的隔离,因此应用出现问题,也只是单个容器的问题,而不会波及到整台机器。
+
+### 2.4 为什么要用 Docker ?
+
+- **Docker 的镜像提供了除内核外完整的运行时环境,确保了应用运行环境一致性,从而不会再出现 “这段代码在我机器上没问题啊” 这类问题;——一致的运行环境**
+- **可以做到秒级、甚至毫秒级的启动时间。大大的节约了开发、测试、部署的时间。——更快速的启动时间**
+- **避免公用的服务器,资源会容易受到其他用户的影响。——隔离性**
+- **善于处理集中爆发的服务器使用压力;——弹性伸缩,快速扩展**
+- **可以很轻易的将在一个平台上运行的应用,迁移到另一个平台上,而不用担心运行环境的变化导致应用无法正常运行的情况。——迁移方便**
+- **使用 Docker 可以通过定制应用镜像来实现持续集成、持续交付、部署。——持续交付和部署**
+
+---
+
+> 每当说起容器,我们不得不将其与虚拟机做一个比较。就我而言,对于两者无所谓谁会取代谁,而是两者可以和谐共存。
+
+## 三 容器 VS 虚拟机
+
+ 简单来说: **容器和虚拟机具有相似的资源隔离和分配优势,但功能有所不同,因为容器虚拟化的是操作系统,而不是硬件,因此容器更容易移植,效率也更高。**
+
+### 3.1 两者对比图
+
+ 传统虚拟机技术是虚拟出一套硬件后,在其上运行一个完整操作系统,在该系统上再运行所需应用进程;而容器内的应用进程直接运行于宿主的内核,容器内没有自己的内核,而且也没有进行硬件虚拟。因此容器要比传统虚拟机更为轻便.
+
+
+
+### 3.2 容器与虚拟机总结
+
+
+
+- **容器是一个应用层抽象,用于将代码和依赖资源打包在一起。** **多个容器可以在同一台机器上运行,共享操作系统内核,但各自作为独立的进程在用户空间中运行** 。与虚拟机相比, **容器占用的空间较少**(容器镜像大小通常只有几十兆),**瞬间就能完成启动** 。
+
+- **虚拟机 (VM) 是一个物理硬件层抽象,用于将一台服务器变成多台服务器。** 管理程序允许多个 VM 在一台机器上运行。每个VM都包含一整套操作系统、一个或多个应用、必要的二进制文件和库资源,因此 **占用大量空间** 。而且 VM **启动也十分缓慢** 。
+
+ 通过Docker官网,我们知道了这么多Docker的优势,但是大家也没有必要完全否定虚拟机技术,因为两者有不同的使用场景。**虚拟机更擅长于彻底隔离整个运行环境**。例如,云服务提供商通常采用虚拟机技术隔离不同的用户。而 **Docker通常用于隔离不同的应用** ,例如前端,后端以及数据库。
+
+### 3.3 容器与虚拟机两者是可以共存的
+
+就我而言,对于两者无所谓谁会取代谁,而是两者可以和谐共存。
+
+
+
+---
+
+> Docker中非常重要的三个基本概念,理解了这三个概念,就理解了 Docker 的整个生命周期。
+
+## 四 Docker基本概念
+
+Docker 包括三个基本概念
+
+- **镜像(Image)**
+- **容器(Container)**
+- **仓库(Repository)**
+
+理解了这三个概念,就理解了 Docker 的整个生命周期
+
+
+
+### 4.1 镜像(Image):一个特殊的文件系统
+
+ **操作系统分为内核和用户空间**。对于 Linux 而言,内核启动后,会挂载 root 文件系统为其提供用户空间支持。而Docker 镜像(Image),就相当于是一个 root 文件系统。
+
+ **Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。** 镜像不包含任何动态数据,其内容在构建之后也不会被改变。
+
+ Docker 设计时,就充分利用 **Union FS**的技术,将其设计为 **分层存储的架构** 。 镜像实际是由多层文件系统联合组成。
+
+ **镜像构建时,会一层层构建,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。** 比如,删除前一层文件的操作,实际不是真的删除前一层的文件,而是仅在当前层标记为该文件已删除。在最终容器运行的时候,虽然不会看到这个文件,但是实际上该文件会一直跟随镜像。因此,在构建镜像的时候,需要额外小心,每一层尽量只包含该层需要添加的东西,任何额外的东西应该在该层构建结束前清理掉。
+
+ 分层存储的特征还使得镜像的复用、定制变的更为容易。甚至可以用之前构建好的镜像作为基础层,然后进一步添加新的层,以定制自己所需的内容,构建新的镜像。
+
+### 4.2 容器(Container):镜像运行时的实体
+
+ 镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的 类 和 实例 一样,镜像是静态的定义,**容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等** 。
+
+ **容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的 命名空间。前面讲过镜像使用的是分层存储,容器也是如此。**
+
+ **容器存储层的生存周期和容器一样,容器消亡时,容器存储层也随之消亡。因此,任何保存于容器存储层的信息都会随容器删除而丢失。**
+
+ 按照 Docker 最佳实践的要求,**容器不应该向其存储层内写入任何数据** ,容器存储层要保持无状态化。**所有的文件写入操作,都应该使用数据卷(Volume)、或者绑定宿主目录**,在这些位置的读写会跳过容器存储层,直接对宿主(或网络存储)发生读写,其性能和稳定性更高。数据卷的生存周期独立于容器,容器消亡,数据卷不会消亡。因此, **使用数据卷后,容器可以随意删除、重新 run ,数据却不会丢失。**
+
+
+### 4.3仓库(Repository):集中存放镜像文件的地方
+
+ 镜像构建完成后,可以很容易的在当前宿主上运行,但是, **如果需要在其它服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,Docker Registry就是这样的服务。**
+
+ 一个 Docker Registry中可以包含多个仓库(Repository);每个仓库可以包含多个标签(Tag);每个标签对应一个镜像。所以说:**镜像仓库是Docker用来集中存放镜像文件的地方类似于我们之前常用的代码仓库。**
+
+ 通常,**一个仓库会包含同一个软件不同版本的镜像**,而**标签就常用于对应该软件的各个版本** 。我们可以通过```<仓库名>:<标签>```的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 latest 作为默认标签.。
+
+**这里补充一下Docker Registry 公开服务和私有 Docker Registry的概念:**
+
+ **Docker Registry 公开服务** 是开放给用户使用、允许用户管理镜像的 Registry 服务。一般这类公开服务允许用户免费上传、下载公开的镜像,并可能提供收费服务供用户管理私有镜像。
+
+ 最常使用的 Registry 公开服务是官方的 **Docker Hub** ,这也是默认的 Registry,并拥有大量的高质量的官方镜像,网址为:[https://hub.docker.com/](https://hub.docker.com/) 。在国内访问**Docker Hub** 可能会比较慢国内也有一些云服务商提供类似于 Docker Hub 的公开服务。比如 [时速云镜像库](https://hub.tenxcloud.com/)、[网易云镜像服务](https://www.163yun.com/product/repo)、[DaoCloud 镜像市场](https://www.daocloud.io/)、[阿里云镜像库](https://www.aliyun.com/product/containerservice?utm_content=se_1292836)等。
+
+ 除了使用公开服务外,用户还可以在 **本地搭建私有 Docker Registry** 。Docker 官方提供了 Docker Registry 镜像,可以直接使用做为私有 Registry 服务。开源的 Docker Registry 镜像只提供了 Docker Registry API 的服务端实现,足以支持 docker 命令,不影响使用。但不包含图形界面,以及镜像维护、用户管理、访问控制等高级功能。
+
+---
+
+> Docker的概念基本上已经讲完,最后我们谈谈:Build, Ship, and Run。
+
+## 五 最后谈谈:Build Ship and Run
+如果你搜索Docker官网,会发现如下的字样:**“Docker - Build, Ship, and Run Any App, Anywhere”**。那么Build, Ship, and Run到底是在干什么呢?
+
+
+
+- **Build(构建镜像)** : 镜像就像是集装箱包括文件以及运行环境等等资源。
+- **Ship(运输镜像)** :主机和仓库间运输,这里的仓库就像是超级码头一样。
+- **Run (运行镜像)** :运行的镜像就是一个容器,容器就是运行程序的地方。
+
+**Docker 运行过程也就是去仓库把镜像拉到本地,然后用一条命令把镜像运行起来变成容器。所以,我们也常常将Docker称为码头工人或码头装卸工,这和Docker的中文翻译搬运工人如出一辙。**
+
+## 六 总结
+
+本文主要把Docker中的一些常见概念做了详细的阐述,但是并不涉及Docker的安装、镜像的使用、容器的操作等内容。这部分东西,希望读者自己可以通过阅读书籍与官方文档的形式掌握。如果觉得官方文档阅读起来很费力的话,这里推荐一本书籍《Docker技术入门与实战第二版》。
+
+
+
+
+
+
+
diff --git a/docs/tools/Git.md b/docs/tools/Git.md
new file mode 100644
index 00000000000..e58f13b63aa
--- /dev/null
+++ b/docs/tools/Git.md
@@ -0,0 +1,258 @@
+
+
+- [版本控制](#版本控制)
+ - [什么是版本控制](#什么是版本控制)
+ - [为什么要版本控制](#为什么要版本控制)
+ - [本地版本控制系统](#本地版本控制系统)
+ - [集中化的版本控制系统](#集中化的版本控制系统)
+ - [分布式版本控制系统](#分布式版本控制系统)
+- [认识 Git](#认识-git)
+ - [Git 简史](#git-简史)
+ - [Git 与其他版本管理系统的主要区别](#git-与其他版本管理系统的主要区别)
+ - [Git 的三种状态](#git-的三种状态)
+- [Git 使用快速入门](#git-使用快速入门)
+ - [获取 Git 仓库](#获取-git-仓库)
+ - [记录每次更新到仓库](#记录每次更新到仓库)
+ - [推送改动到远程仓库](#推送改动到远程仓库)
+ - [远程仓库的移除与重命名](#远程仓库的移除与重命名)
+ - [查看提交历史](#查看提交历史)
+ - [撤销操作](#撤销操作)
+ - [分支](#分支)
+- [推荐阅读](#推荐阅读)
+
+
+
+## 版本控制
+
+### 什么是版本控制
+
+版本控制是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统。 除了项目源代码,你可以对任何类型的文件进行版本控制。
+
+### 为什么要版本控制
+
+有了它你就可以将某个文件回溯到之前的状态,甚至将整个项目都回退到过去某个时间点的状态,你可以比较文件的变化细节,查出最后是谁修改了哪个地方,从而找出导致怪异问题出现的原因,又是谁在何时报告了某个功能缺陷等等。
+
+### 本地版本控制系统
+
+许多人习惯用复制整个项目目录的方式来保存不同的版本,或许还会改名加上备份时间以示区别。 这么做唯一的好处就是简单,但是特别容易犯错。 有时候会混淆所在的工作目录,一不小心会写错文件或者覆盖意想外的文件。
+
+为了解决这个问题,人们很久以前就开发了许多种本地版本控制系统,大多都是采用某种简单的数据库来记录文件的历次更新差异。
+
+
+
+### 集中化的版本控制系统
+
+接下来人们又遇到一个问题,如何让在不同系统上的开发者协同工作? 于是,集中化的版本控制系统(Centralized Version Control Systems,简称 CVCS)应运而生。
+
+集中化的版本控制系统都有一个单一的集中管理的服务器,保存所有文件的修订版本,而协同工作的人们都通过客户端连到这台服务器,取出最新的文件或者提交更新。
+
+
+
+这么做虽然解决了本地版本控制系统无法让在不同系统上的开发者协同工作的诟病,但也还是存在下面的问题:
+
+- **单点故障:** 中央服务器宕机,则其他人无法使用;如果中心数据库磁盘损坏有没有进行备份,你将丢失所有数据。本地版本控制系统也存在类似问题,只要整个项目的历史记录被保存在单一位置,就有丢失所有历史更新记录的风险。
+- **必须联网才能工作:** 受网络状况、带宽影响。
+
+### 分布式版本控制系统
+
+于是分布式版本控制系统(Distributed Version Control System,简称 DVCS)面世了。 Git 就是一个典型的分布式版本控制系统。
+
+这类系统,客户端并不只提取最新版本的文件快照,而是把代码仓库完整地镜像下来。 这么一来,任何一处协同工作用的服务器发生故障,事后都可以用任何一个镜像出来的本地仓库恢复。 因为每一次的克隆操作,实际上都是一次对代码仓库的完整备份。
+
+
+
+分布式版本控制系统可以不用联网就可以工作,因为每个人的电脑上都是完整的版本库,当你修改了某个文件后,你只需要将自己的修改推送给别人就可以了。但是,在实际使用分布式版本控制系统的时候,很少会直接进行推送修改,而是使用一台充当“中央服务器”的东西。这个服务器的作用仅仅是用来方便“交换”大家的修改,没有它大家也一样干活,只是交换修改不方便而已。
+
+分布式版本控制系统的优势不单是不必联网这么简单,后面我们还会看到 Git 极其强大的分支管理等功能。
+
+## 认识 Git
+
+### Git 简史
+
+Linux 内核项目组当时使用分布式版本控制系统 BitKeeper 来管理和维护代码。但是,后来开发 BitKeeper 的商业公司同 Linux 内核开源社区的合作关系结束,他们收回了 Linux 内核社区免费使用 BitKeeper 的权力。 Linux 开源社区(特别是 Linux 的缔造者 Linus Torvalds)基于使用 BitKeeper 时的经验教训,开发出自己的版本系统,而且对新的版本控制系统做了很多改进。
+
+### Git 与其他版本管理系统的主要区别
+
+ Git 在保存和对待各种信息的时候与其它版本控制系统有很大差异,尽管操作起来的命令形式非常相近,理解这些差异将有助于防止你使用中的困惑。
+
+下面我们主要说一个关于 Git 其他版本管理系统的主要差别:**对待数据的方式**。
+
+**Git采用的是直接记录快照的方式,而非差异比较。我后面会详细介绍这两种方式的差别。**
+
+大部分版本控制系统(CVS、Subversion、Perforce、Bazaar 等等)都是以文件变更列表的方式存储信息,这类系统**将它们保存的信息看作是一组基本文件和每个文件随时间逐步累积的差异。**
+
+具体原理如下图所示,理解起来其实很简单,每个我们对提交更新一个文件之后,系统记录都会记录这个文件做了哪些更新,以增量符号Δ(Delta)表示。
+
+
+

+
+
+
+**我们怎样才能得到一个文件的最终版本呢?**
+
+很简单,高中数学的基本知识,我们只需要将这些原文件和这些增加进行相加就行了。
+
+**这种方式有什么问题呢?**
+
+比如我们的增量特别特别多的话,如果我们要得到最终的文件是不是会耗费时间和性能。
+
+Git 不按照以上方式对待或保存数据。 反之,Git 更像是把数据看作是对小型文件系统的一组快照。 每次你提交更新,或在 Git 中保存项目状态时,它主要对当时的全部文件制作一个快照并保存这个快照的索引。 为了高效,如果文件没有修改,Git 不再重新存储该文件,而是只保留一个链接指向之前存储的文件。 Git 对待数据更像是一个 **快照流**。
+
+
+

+
+
+
+
+### Git 的三种状态
+
+Git 有三种状态,你的文件可能处于其中之一:
+
+1. **已提交(committed)**:数据已经安全的保存在本地数据库中。
+2. **已修改(modified)**:已修改表示修改了文件,但还没保存到数据库中。
+3. **已暂存(staged)**:表示对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中。
+
+由此引入 Git 项目的三个工作区域的概念:**Git 仓库(.git directoty) **、**工作目录(Working Directory)** 以及 **暂存区域(Staging Area)** 。
+
+
+

+
+
+**基本的 Git 工作流程如下:**
+
+1. 在工作目录中修改文件。
+2. 暂存文件,将文件的快照放入暂存区域。
+3. 提交更新,找到暂存区域的文件,将快照永久性存储到 Git 仓库目录。
+
+## Git 使用快速入门
+
+### 获取 Git 仓库
+
+有两种取得 Git 项目仓库的方法。
+
+1. 在现有目录中初始化仓库: 进入项目目录运行 `git init` 命令,该命令将创建一个名为 `.git` 的子目录。
+2. 从一个服务器克隆一个现有的 Git 仓库: `git clone [url]` 自定义本地仓库的名字: `git clone [url]` directoryname
+
+### 记录每次更新到仓库
+
+1. **检测当前文件状态** : `git status`
+2. **提出更改(把它们添加到暂存区**):`git add filename ` (针对特定文件)、`git add *`(所有文件)、`git add *.txt`(支持通配符,所有 .txt 文件)
+3. **忽略文件**:`.gitignore` 文件
+4. **提交更新:** `git commit -m "代码提交信息"` (每次准备提交前,先用 `git status` 看下,是不是都已暂存起来了, 然后再运行提交命令 `git commit`)
+5. **跳过使用暂存区域更新的方式** : `git commit -a -m "代码提交信息"`。 `git commit` 加上 `-a` 选项,Git 就会自动把所有已经跟踪过的文件暂存起来一并提交,从而跳过 `git add` 步骤。
+6. **移除文件** :`git rm filename` (从暂存区域移除,然后提交。)
+7. **对文件重命名** :`git mv README.md README`(这个命令相当于`mv README.md README`、`git rm README.md`、`git add README` 这三条命令的集合)
+
+### 推送改动到远程仓库
+
+- 如果你还没有克隆现有仓库,并欲将你的仓库连接到某个远程服务器,你可以使用如下命令添加:·`git remote add origin ` ,比如我们要让本地的一个仓库和 Github 上创建的一个仓库关联可以这样`git remote add origin https://github.com/Snailclimb/test.git`
+- 将这些改动提交到远端仓库:`git push origin master` (可以把 *master* 换成你想要推送的任何分支)
+
+ 如此你就能够将你的改动推送到所添加的服务器上去了。
+
+### 远程仓库的移除与重命名
+
+- 将 test 重命名位 test1:`git remote rename test test1`
+- 移除远程仓库 test1:`git remote rm test1`
+
+### 查看提交历史
+
+在提交了若干更新,又或者克隆了某个项目之后,你也许想回顾下提交历史。 完成这个任务最简单而又有效的工具是 `git log` 命令。`git log` 会按提交时间列出所有的更新,最近的更新排在最上面。
+
+**可以添加一些参数来查看自己希望看到的内容:**
+
+只看某个人的提交记录:
+
+```shell
+git log --author=bob
+```
+
+### 撤销操作
+
+有时候我们提交完了才发现漏掉了几个文件没有添加,或者提交信息写错了。 此时,可以运行带有 `--amend` 选项的提交命令尝试重新提交:
+
+```console
+git commit --amend
+```
+
+取消暂存的文件
+
+```console
+git reset filename
+```
+
+撤消对文件的修改:
+
+```
+git checkout -- filename
+```
+
+假如你想丢弃你在本地的所有改动与提交,可以到服务器上获取最新的版本历史,并将你本地主分支指向它:
+
+```
+git fetch origin
+git reset --hard origin/master
+```
+
+
+
+### 分支
+
+分支是用来将特性开发绝缘开来的。在你创建仓库的时候,*master* 是“默认的”分支。在其他分支上进行开发,完成后再将它们合并到主分支上。
+
+我们通常在开发新功能、修复一个紧急 bug 等等时候会选择创建分支。单分支开发好还是多分支开发好,还是要看具体场景来说。
+
+创建一个名字叫做 test 的分支
+
+```console
+git branch test
+```
+
+切换当前分支到 test(当你切换分支的时候,Git 会重置你的工作目录,使其看起来像回到了你在那个分支上最后一次提交的样子。 Git 会自动添加、删除、修改文件以确保此时你的工作目录和这个分支最后一次提交时的样子一模一样)
+
+```console
+git checkout test
+```
+
+
+

+
+
+你也可以直接这样创建分支并切换过去(上面两条命令的合写)
+
+```console
+git checkout -b feature_x
+```
+
+切换到主分支
+
+```
+git checkout master
+```
+
+合并分支(可能会有冲突)
+
+```java
+ git merge test
+```
+
+把新建的分支删掉
+
+```
+git branch -d feature_x
+```
+
+将分支推送到远端仓库(推送成功后其他人可见):
+
+```
+git push origin
+```
+
+
+
+## 推荐阅读
+
+- [Git - 简明指南](http://rogerdudler.github.io/git-guide/index.zh.html)
+- [图解Git](http://marklodato.github.io/visual-git-guide/index-zh-cn.html)
+- [猴子都能懂得Git入门](https://backlog.com/git-tutorial/cn/intro/intro1_1.html)
+- https://git-scm.com/book/en/v2
diff --git "a/\345\205\266\344\273\226/2018 summary.md" "b/\345\205\266\344\273\226/2018 summary.md"
deleted file mode 100644
index 8d82e40b876..00000000000
--- "a/\345\205\266\344\273\226/2018 summary.md"
+++ /dev/null
@@ -1,174 +0,0 @@
-# 【2018总结】即使平凡,也要热爱自己的生活
-
-2018 年于我而讲,虽然平凡,但是自己就是在这平凡的一年也收货了很多东西。不光是自己学到的知识,我觉得 2018 年最大的幸运有三:其一是自己拥有了一份爱情,一份甜蜜的初恋,我真的很幸运遇到我现在的女朋友,愿以后的日子都能有她;其一是在 2018 年,我拥有了一份自己还算满意的 offer,马上就要毕业了,自己也要正式进去社会了;其一是自己在 2018 年的实现了自己的经济独立,这是一件让我很高兴的事情,我觉得大在学生时代实现经济独立还算是一件很不错的事情,花了这么多年父母的辛苦钱,自己也终于能替他们分担一点了。2018 年,感恩父母,感恩老师,感恩朋友,感恩遇到的每个善良的人,同时感恩2018年那个还算努力的自己。2019 继续加油!
-
-## 一份甜蜜的初恋(分手)
-
-先说说爱情。我和我的女朋友在一起已经半年多了,准确的来说截止到今天也就是 2018-12-30 号已经 190 天了。
-
-
-
-我俩是异地,我在荆州,她在三亚。相见一面不管是时间上还是经济上对于还是学生的我们来说都甚是不易。见过很多人议论异地恋的种种不好,但是,于我而言,一份好的感情是值得被等待的。“待每一天如初恋,互相尊重彼此生活,共同努力,等老了就退隐山林养老......”,这应该是我和她之间最好的承诺了。
-
-## 还算不错的学习收获
-
-再来说说学习。这一年还算是让人满意,虽然我也不知道这一年自己到底学到了什么。如果你要问我这一年在学习上做的最满意的事情是什么,我还真不好回答,下面就从下面几个维度来简单谈一谈。
-
-### 开源
-
-这一年自己在Github上还是挺活跃的,提交了很多的代码和文档,同时也收获了很多的star、follower、pr、issue以及fork。
-
-
-
-
-
-开源的Java学习/面试指南—JavaGuide 某种程度上让我挺满意的,3月份开源 ,到现在的18k+ star 也算是可以用厚积薄发来形容了。但是,JavaGuide 也有很多让我不满意的,在2019年以及以后我也会继续完善。JavaGuide 地址:[https://github.com/Snailclimb/JavaGuide](https://github.com/Snailclimb/JavaGuide)
-
-
-
-### 技术博客
-
-我更新的博客主要都是关于Java方面的,也更新了几篇Python的,有一篇Python的文章竟然在我的CSDN上面阅读霸榜。
-
-
-在这一年,我更新了挺多技术文章,这里就不一一列举了,我贴一下自己觉得不错的文章吧!
-
-#### 最常见面试题系列
-
-- [最最最常见的Java面试题总结——第一周](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484252&idx=1&sn=cb160d67fc1c0a95babc464b703df5e7&chksm=fd98553dcaefdc2b18f934957dd950aeaf04e90136099fa2817fffbd1e1df452b581e1caee17&token=1398134989&lang=zh_CN#rd)
-- [最最最常见的Java面试题总结——第二周](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484282&idx=1&sn=7f986dc3263b6ca0f9e182145fdd40a1&chksm=fd98551bcaefdc0d5aff9577692881dc79765a339ce97e55958e23e1956aa7092dfac44b68f1&token=1398134989&lang=zh_CN#rd)
-- [这几道Java集合框架面试题在面试中几乎必问](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484308&idx=1&sn=e3607919aed604be629617f867f46844&chksm=fd9855f5caefdce3f1ee72cb33b9b3bf9899fa2b64bbb92f1e820c0ef3985245b1f7dfc05358&token=1398134989&lang=zh_CN#rd)
-- [如果不会这几道多线程基础题,请自觉面壁!](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484337&idx=1&sn=d5e953d4b2da7ed37a7f843bfb437ed8&chksm=fd9855d0caefdcc65cb2e5cc0c69d27f785fc41477bcf55fff2cdff3268b0b078eb1a5107726&token=1398134989&lang=zh_CN#rd)
-- [值得立马保存的 synchronized 关键字总结](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484355&idx=1&sn=6da29974b6dd1a4aa0d032f44d5fa8de&chksm=fd9855a2caefdcb4c370814baafd4baca27dfccaf609c9edf82370637ba4856176ab143a375e&token=1398134989&lang=zh_CN#rd)
-- [【面试必备】源码角度一步一步分析 ArrayList 扩容机制](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484400&idx=1&sn=1b6155015fedfc9f78fabecc18da7b18&chksm=fd985591caefdc870cb018d27f92e1908b6c6e22816a77ead03c4e44b2f53caec00871172b1f&token=1398134989&lang=zh_CN#rd)
-- [Synchronized 关键字使用、底层原理、JDK1.6 之后的底层优化以及 和ReenTrantLock 的对比](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484539&idx=1&sn=3500cdcd5188bdc253fb19a1bfa805e6&chksm=fd98521acaefdb0c5167247a1fa903a1a53bb4e050b558da574f894f9feda5378ec9d0fa1ac7&token=1398134989&lang=zh_CN#rd)
-
-#### Github
-
-- [近几个月Github上最热门的Java项目一览](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484188&idx=1&sn=40037de4844f62316465bbe4e910c69c&chksm=fd98557dcaefdc6bedcaeb275aae7c340d46cf6ab0dc96e49c51982f9c53d6a44de283efc9a8&token=1398134989&lang=zh_CN#rd)
-- [推荐10个Java方向最热门的开源项目(8月)](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484333&idx=1&sn=8c97b029692877a537d55175a8c82977&chksm=fd9855cccaefdcdaffe0558ba5e8dca415495935b0ad1181e6b148b08e1c86ce5d841e9df901&token=1398134989&lang=zh_CN#rd)
-- [Github上 Star 数相加超过 7w+ 的三个面试相关的仓库推荐](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484644&idx=1&sn=5016caaf97e498b76de2189e3f55e9dc&chksm=fd985285caefdb93f4e3c7545d30edac6ad31b99f1fcc4503350101f0b20bba9a9705ed7d124&token=1398134989&lang=zh_CN#rd)
-- [11月 Github Trending 榜最热门的 10 个 Java 项目](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484730&idx=1&sn=86e35dfea1478221b6d14a263e88ac89&chksm=fd98535bcaefda4d4f03bf0cd2e0a8fd9f44b1a2b118457a0c8b3de2ff8a1f4c4b7cd083f40e&token=1398134989&lang=zh_CN#rd)
-- [盘点一下Github上开源的Java面试/学习相关的仓库,看完弄懂薪资至少增加10k](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484817&idx=1&sn=12f0c254a240c40c2ccab8314653216b&chksm=fd9853f0caefdae6d191e6bf085d44ab9c73f165e3323aa0362d830e420ccbfad93aa5901021&token=1398134989&lang=zh_CN#rd)
-
-
-#### 备战面试系列
-
-- [可能是一份最适合你的后端面试指南(部分内容前端同样适用)](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484529&idx=1&sn=9c7a3d6ad124affcadc19b0ff49bf68a&chksm=fd985210caefdb0615a9643fa698cb6267e89562730423841d942cde17ec9c1280dfc3a2b933&token=1398134989&lang=zh_CN#rd)
-- [【备战春招/秋招系列1】程序员的简历就该这样写](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484573&idx=1&sn=8c5965d4a3710d405d8e8cc10c7b0ce5&chksm=fd9852fccaefdbea8dfe0bc40188b7579f1cddb1e8905dc981669a3f21d2a04cadceafa9023f&token=1990180468&lang=zh_CN#rd)
-- [【备战春招/秋招系列2】初出茅庐的程序员该如何准备面试?](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484578&idx=1&sn=eea72d80a2325257f00aaed21d5b226f&chksm=fd9852c3caefdbd52dd8a537cc723ed1509314401b3a669a253ef5bc0360b6fddef48b9c2e94&token=1990180468&lang=zh_CN#rd)
-- [【备战春招/秋招系列3】Java程序员必备书单](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484592&idx=1&sn=6d9731ce7401be49e97c1af6ed384ecc&chksm=fd9852d1caefdbc720a361ae65a8ad9d53cfb4800b15a7c68cbdc630b313215c6c52e0934ec2&token=1990180468&lang=zh_CN#rd)
-- [【备战春招/秋招系列】美团面经总结基础篇 (附详解答案)](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484601&idx=1&sn=4907b7fef0856791c565d49d788ba8cc&chksm=fd9852d8caefdbce88e51c0a10a4ec77c97f382fd2af4a840ea47cffc828bfd0f993f50d5f0d&token=1895808268&lang=zh_CN#rd)
-- [【备战春招/秋招系列】美团面经总结进阶篇 (附详解答案)](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484625&idx=1&sn=9c4fa1f7d4291a5fbd7daa44bac2b012&chksm=fd9852b0caefdba6edcf9a827aa4a17ddc97bf6ad2e5ee6f7e1aa1b443b54444d05d2b76732b&token=1895808268&lang=zh_CN#rd)
-- [【备战春招/秋招系列】美团Java面经总结终结篇 (附详解答案)](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484668&idx=1&sn=9d4631588393075d9c453f307410f0cd&chksm=fd98529dcaefdb8b5497d1f161834af6917c33ea3d305eb41872e522707fa94218769ca60101&token=1398134989&lang=zh_CN#rd)
-- [GitHub 上四万 Star 大佬的求职回忆](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484739&idx=1&sn=25cf5b36090f69299150663bdccfeec2&chksm=fd985322caefda34df0734efa607114704d1937f083aee2230b797d1f5aa04f7d13bf2f81dc5&token=1398134989&lang=zh_CN#rd)(非原创)
-
-
-#### 并发编程面试必备
-
-- [并发编程面试必备:synchronized 关键字使用、底层原理、JDK1.6 之后的底层优化以及 和ReenTrantLock 的对比](http://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484539&idx=1&sn=3500cdcd5188bdc253fb19a1bfa805e6&chksm=fd98521acaefdb0c5167247a1fa903a1a53bb4e050b558da574f894f9feda5378ec9d0fa1ac7&scene=21#wechat_redirect)
-- [并发编程面试必备:JUC 中的 Atomic 原子类总结](http://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484553&idx=1&sn=aca9fa19f723206eff7e33a10973a887&chksm=fd9852e8caefdbfe7180c34f83bbb422a1a0bef1ed44b1e84f56924244ea3fd2da720f25c6dd#rd)
-- [并发编程面试必备:AQS 原理以及 AQS 同步组件总结](http://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484559&idx=1&sn=28dae85c38c4c500201c39234d25d731&chksm=fd9852eecaefdbf80cc54a25204e7c7d81170ce659acf92b7fa4151799ca3d0d7df2225d4ff1#rd)
-- [并发编程面试必备:BATJ都爱问的多线程面试题](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484564&idx=1&sn=d8467fdc5c1b3883e9b99485f7b0fb9a&chksm=fd9852f5caefdbe364d1c438865cff84acd8f40c1c9e2f9f5c8fef673b30f905b4c5f5255368&token=1398134989&lang=zh_CN#rd)
-
-#### 虚拟机
-
-- [可能是把Java内存区域讲的最清楚的一篇文章](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484303&idx=1&sn=af0fd436cef755463f59ee4dd0720cbd&chksm=fd9855eecaefdcf8d94ac581cfda4e16c8a730bda60c3b50bc55c124b92f23b6217f7f8e58d5&token=1398134989&lang=zh_CN#rd)
-- [搞定 JVM 垃圾回收就是这么简单](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484328&idx=1&sn=214f5e18a6afa096eb552fd8627e0cea&chksm=fd9855c9caefdcdf70c746c74d31f65bbb109eedaea0cfe311a1e10af666047df59ff04c873b&token=1398134989&lang=zh_CN#rd)
-
-#### Spring Boot
-
-- [超详细,新手都能看懂 !使用SpringBoot+Dubbo 搭建一个简单的分布式服务](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484706&idx=1&sn=d413fc17023482f67ca17cb6756b9ff8&chksm=fd985343caefda555969568fdf4734536e0a1745f9de337d434a7dbd04e893bd2d75f3641aab&token=1398134989&lang=zh_CN#rd)
-- [基于 SpringBoot2.0+优雅整合 SpringBoot+Mybatis](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484730&idx=2&sn=9be4636dd9a416b46f9029df68fad232&chksm=fd98535bcaefda4dccf14a286a24fcd2b3d4ab0d0e4d89dfbc955df99d2b06a1e17392b3c10b&token=1398134989&lang=zh_CN#rd)
-- [新手也能实现,基于SpirngBoot2.0+ 的 SpringBoot+Mybatis 多数据源配置](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484737&idx=1&sn=e39693d845f022d689437ee58948ef6a&chksm=fd985320caefda36d5ab8abd52f5516c11cc5d1104608695bcea5909602b28dc40c132d6d46c&token=1398134989&lang=zh_CN#rd)
-- [SpringBoot 整合 阿里云OSS 存储服务,快来免费搭建一个自己的图床](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484745&idx=1&sn=dbeec694916d204605929244d48a6b1c&chksm=fd985328caefda3e793170d81433c7c0b7dc1c4a4ae99395cce23b1d1a239482fd5bf1d89bc6&token=1398134989&lang=zh_CN#rd)
-
-#### 成长
-
-- [结束了我短暂的秋招,说点自己的感受](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484516&idx=1&sn=4e2320613e76dd73a130c63beebbc3ca&chksm=fd985205caefdb13b4b611ed3c604d95314d28d567ec0c3b44585b89a7dc3142bcd52bc2d4cb&token=1398134989&lang=zh_CN#rd)
-- [保研之路:从双非到南大](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484477&idx=1&sn=3b597e2431611aacca2b5d671a309d85&chksm=fd98525ccaefdb4a7e3742b5958244d453efe26f61f42f9f108190a0c18313f083189f10944e&token=1398134989&lang=zh_CN#rd)(非原创)
-- [【周日闲谈】最近想说的几件小事](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484650&idx=1&sn=e97ea1eeebdb5def58bae1949bec9448&chksm=fd98528bcaefdb9d76ac62fd10544f058b1fee4a40ffd06ab9312b7eb3a62f86d67ea653b88a&token=1398134989&lang=zh_CN#rd)
-- [这7个问题,可能大部分Java程序员都比较关心吧!](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484836&idx=1&sn=a6ada99c9506af01dc3bb472f66c57be&chksm=fd9853c5caefdad3034dbed00cf04412ea990fc05b6168720e6828ae6c90c9a885793acd7a14&token=1398134989&lang=zh_CN#rd)
-
-#### Docker
-
-- [可能是把Docker的概念讲的最清楚的一篇文章](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484127&idx=1&sn=70ee95619ec761da884c4f9af3e83194&chksm=fd9854becaefdda81a02bf6cf9bd07a2fc879efa7cefc79691a0d319b501d8572e8bad981d87&token=1398134989&lang=zh_CN#rd)
-
-#### Linux
-
-- [后端程序员必备的Linux基础知识](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484157&idx=1&sn=8b47e623e83fb3666bce7c680e4649b8&chksm=fd98549ccaefdd8ad815f3d8eaca86cc7e7245b4f8de1d23897af3017f5fdb3f152734c40f5e&token=1398134989&lang=zh_CN#rd)
-- [快速入门大厂后端面试必备的 Shell 编程](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484696&idx=1&sn=d3c1ba5abc10c10ff844cae2109a2628&chksm=fd985379caefda6faff8e050b7dfa1e92fbfe2912e44150cb4ae349aea807836166355062970&token=1398134989&lang=zh_CN#rd)
-
-#### ZooKeeper
-
-- [可能是全网把 ZooKeeper 概念讲的最清楚的一篇文章](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484379&idx=1&sn=036f5d3defa8a6979afb77acc82a9517&chksm=fd9855bacaefdcacc1462f781b634e5599f2ee9e806bd24297dae4af0e4196a70ca6bbd8c354&token=1398134989&lang=zh_CN#rd)
-
-#### Redis
-
-- [redis 总结——重构版](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484450&idx=1&sn=7ee03fa67aecd05c5becd2a8259d3631&chksm=fd985243caefdb554ebab9149e750ac0c819074c57bd208f2d7f097fbc461ed58223e71c05f1&token=1398134989&lang=zh_CN#rd)
-- [史上最全Redis高可用技术解决方案大全](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484478&idx=1&sn=a1250d9b8025cd7cb6fc6a58238ab51e&chksm=fd98525fcaefdb499a027df0138c98d4b02d828f27bd6144a4d40a1c088d340c29dd53d4a026&token=1398134989&lang=zh_CN#rd)(非原创)
-
-#### 计算机网络
-
-- [搞定计算机网络面试,看这篇就够了(补充版)](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484289&idx=1&sn=6b556843c60aac9a17b0e7c2e3cd6bca&chksm=fd9855e0caefdcf6af4123b719448c81d90c5442d4052ae01a4698047e226c0c18c14b2cc54a&token=1398134989&lang=zh_CN#rd)
-
-#### 数据库
-
-- [【思维导图-索引篇】搞定数据库索引就是这么简单](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484486&idx=1&sn=215450f11e042bca8a58eac9f4a97686&chksm=fd985227caefdb3117b8375f150676f5824aa20d1ebfdbcfb93ff06e23e26efbafae6cf6b48e&token=1398134989&lang=zh_CN#rd)
-
-#### 消息队列
-
-- [新手也能看懂,消息队列其实很简单](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484789&idx=1&sn=ba972f0aac39e9a28b29ddf92fc15c18&chksm=fd985314caefda0278235427d43846b6374ff32f4149352dec063287cbf9733b888acbb79923&token=1398134989&lang=zh_CN#rd)
-- [一文搞懂 RabbitMQ 的重要概念以及安装](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484796&idx=1&sn=bc56fecb634732669cfe7db8d1820ded&chksm=fd98531dcaefda0b07b8a9c13429ef225d36a6e287c96c53d7aa3dfd65c62ccd60d13b22ebbf&token=1398134989&lang=zh_CN#rd)
-
-### 读书
-
-推荐一下 2018 年看过的书籍,小部分可能2017年也看过一些。
-
-#### 已看完
-
-- [《图解HTTP》](https://book.douban.com/subject/25863515/)(推荐,豆瓣评分 8.1 , 1.6K+人评价): 讲漫画一样的讲HTTP,很有意思,不会觉得枯燥,大概也涵盖也HTTP常见的知识点。因为篇幅问题,内容可能不太全面。不过,如果不是专门做网络方向研究的小伙伴想研究HTTP相关知识的话,读这本书的话应该来说就差不多了。
-- [《大话数据结构》](https://book.douban.com/subject/6424904/)(推荐,豆瓣评分 7.9 , 1K+人评价):入门类型的书籍,读起来比较浅显易懂,适合没有数据结构基础或者说数据结构没学好的小伙伴用来入门数据结构。
-- [《算法图解》](https://book.douban.com/subject/26979890/)(推荐,豆瓣评分 8.4,0.6K+人评价):入门类型的书籍,读起来比较浅显易懂,适合没有算法基础或者说算法没学好的小伙伴用来入门。示例丰富,图文并茂,以让人容易理解的方式阐释了算法.读起来比较快,内容不枯燥!
-- [《Java并发编程的艺术》](https://book.douban.com/subject/26591326/)(推荐,豆瓣评分 7.2,0.2K+人评价): 这本书不是很适合作为Java并发入门书籍,需要具备一定的JVM基础。我感觉有些东西讲的还是挺深入的,推荐阅读。
-- [《实战Java高并发程序设计》](https://book.douban.com/subject/26663605/)(推荐):豆瓣评分 8.3 ,书的质量没的说,推荐大家好好看一下。
-- [《深入理解Java虚拟机(第2版)周志明》](https://book.douban.com/subject/24722612/)(推荐,豆瓣评分 8.9,1.0K+人评价):建议多刷几遍,书中的所有知识点可以通过JAVA运行时区域和JAVA的内存模型与线程两个大模块罗列完全。
-- [《Netty实战》](https://book.douban.com/subject/27038538/)(推荐,豆瓣评分 7.8,92人评价):内容很细,如果想学Netty的话,推荐阅读这本书!
-- [《从Paxos到Zookeeper》](https://book.douban.com/subject/26292004/)(推荐,豆瓣评分 7.8,0.3K人评价):简要介绍几种典型的分布式一致性协议,以及解决分布式一致性问题的思路,其中重点讲解了Paxos和ZAB协议。同时,本书深入介绍了分布式一致性问题的工业解决方案——ZooKeeper,并着重向读者展示这一分布式协调框架的使用方法、内部实现及运维技巧,旨在帮助读者全面了解ZooKeeper,并更好地使用和运维ZooKeeper。
-- [《Redis实战》](https://book.douban.com/subject/26612779/):如果你想了解Redis的一些概念性知识的话,这本书真的非常不错。
-- [《Redis设计与实现》](https://book.douban.com/subject/25900156/)(推荐,豆瓣评分 8.5,0.5K+人评价)
-- [《大型网站技术架构:核心原理与案例分析+李智慧》](https://book.douban.com/subject/25723064/)(推荐):这本书我读过,基本不需要你有什么基础啊~读起来特别轻松,但是却可以学到很多东西,非常推荐了。另外我写过这本书的思维导图,关注我的微信公众号:“Java面试通关手册”回复“大型网站技术架构”即可领取思维导图。
-
-
-#### 未看完
-
-
-- [《鸟哥的Linux私房菜》](https://book.douban.com/subject/4889838/)(推荐,,豆瓣评分 9.1,0.3K+人评价):本书是最具知名度的Linux入门书《鸟哥的Linux私房菜基础学习篇》的最新版,全面而详细地介绍了Linux操作系统。全书分为5个部分:第一部分着重说明Linux的起源及功能,如何规划和安装Linux主机;第二部分介绍Linux的文件系统、文件、目录与磁盘的管理;第三部分介绍文字模式接口 shell和管理系统的好帮手shell脚本,另外还介绍了文字编辑器vi和vim的使用方法;第四部分介绍了对于系统安全非常重要的Linux账号的管理,以及主机系统与程序的管理,如查看进程、任务分配和作业管理;第五部分介绍了系统管理员(root)的管理事项,如了解系统运行状况、系统服务,针对登录文件进行解析,对系统进行备份以及核心的管理等。
-- [《亿级流量网站架构核心技术》](https://book.douban.com/subject/26999243/)(推荐):一书总结并梳理了亿级流量网站高可用和高并发原则,通过实例详细介绍了如何落地这些原则。本书分为四部分:概述、高可用原则、高并发原则、案例实战。从负载均衡、限流、降级、隔离、超时与重试、回滚机制、压测与预案、缓存、池化、异步化、扩容、队列等多方面详细介绍了亿级流量网站的架构核心技术,让读者看后能快速运用到实践项目中。
-- [《重构_改善既有代码的设计》](https://book.douban.com/subject/4262627/)(推荐):豆瓣 9.1 分,重构书籍的开山鼻祖。
-- [《深入剖析Tomcat》](https://book.douban.com/subject/10426640/)(推荐,豆瓣评分 8.4,0.2K+人评价):本书深入剖析Tomcat 4和Tomcat 5中的每个组件,并揭示其内部工作原理。通过学习本书,你将可以自行开发Tomcat组件,或者扩展已有的组件。 读完这本书,基本可以摆脱背诵面试题的尴尬。
-- [《高性能MySQL》](https://book.douban.com/subject/23008813/)(推荐,豆瓣评分 9.3,0.4K+人评价):mysql 领域的经典之作,拥有广泛的影响力。不但适合数据库管理员(dba)阅读,也适合开发人员参考学习。不管是数据库新手还是专家,相信都能从本书有所收获。
-- [深入理解Nginx(第2版)](https://book.douban.com/subject/26745255/):作者讲的非常细致,注释都写的都很工整,对于 Nginx 的开发人员非常有帮助。优点是细致,缺点是过于细致,到处都是代码片段,缺少一些抽象。
-- [《RabbitMQ实战指南》](https://book.douban.com/subject/27591386/):《RabbitMQ实战指南》从消息中间件的概念和RabbitMQ的历史切入,主要阐述RabbitMQ的安装、使用、配置、管理、运维、原理、扩展等方面的细节。如果你想浅尝RabbitMQ的使用,这本书是你最好的选择;如果你想深入RabbitMQ的原理,这本书也是你最好的选择;总之,如果你想玩转RabbitMQ,这本书一定是最值得看的书之一
-- [《Spring Cloud微服务实战》](https://book.douban.com/subject/27025912/):从时下流行的微服务架构概念出发,详细介绍了Spring Cloud针对微服务架构中几大核心要素的解决方案和基础组件。对于各个组件的介绍,《Spring Cloud微服务实战》主要以示例与源码结合的方式来帮助读者更好地理解这些组件的使用方法以及运行原理。同时,在介绍的过程中,还包含了作者在实践中所遇到的一些问题和解决思路,可供读者在实践中作为参考。
-- [《第一本Docker书》](https://book.douban.com/subject/26780404/):Docker入门书籍!
-- [《Effective java 》](https://book.douban.com/subject/3360807/)(推荐,豆瓣评分 9.0,1.4K+人评价):本书介绍了在Java编程中78条极具实用价值的经验规则,这些经验规则涵盖了大多数开发人员每天所面临的问题的解决方案。通过对Java平台设计专家所使用的技术的全面描述,揭示了应该做什么,不应该做什么才能产生清晰、健壮和高效的代码。本书中的每条规则都以简短、独立的小文章形式出现,并通过例子代码加以进一步说明。本书内容全面,结构清晰,讲解详细。可作为技术人员的参考用书。
-- [《算法 第四版》](https://book.douban.com/subject/10432347/)(推荐,豆瓣评分 9.3,0.4K+人评价):Java语言描述,算法领域经典的参考书,全面介绍了关于算法和数据结构的必备知识,并特别针对排序、搜索、图处理和字符串处理进行了论述。书的内容非常多,可以说是Java程序员的必备书籍之一了。
-
-## 一些个人愚见
-
-### 关于读书
-
-不知道大家收藏栏是不是和我一样收藏了很多文章,但是有多少篇是你真真认真看的呢?或者那几篇你认真看之后,经过一个月之后还记得这篇文章的大概内容。现在这个社会真是一个信息爆炸的社会,我个人真的深有感触,就在刚刚我还取消关注了好多微信公众号,很多公众号给我推的文章都有好几十篇了,但是我一篇都没有看,所以索性取消关注,省个位置。我个人觉得遇到好的文章,我们不光要读,而且要记录下来。就拿我本人来说,我平时喜欢用 OneNote 来记录学习笔记以及其他我感觉重要的事情比如重要人的生日啦这些。每当遇到自己喜欢的文章的时候,我都先会把文章的地址保存到我分好类的笔记本上,我会先进行第一遍阅读,第一遍我会读的很仔细,如果晦涩难懂的话我会先快速把总体看一遍,然后在细细品读。一般第二遍的时候我就会在笔记本上记录这篇文章的一些要点,以便我日后看到这些要点可以快速回忆起整篇文章的内容。如果某篇文章的知识点太过庞大的话,我会去选择采用思维导图的方式展示要点。看视频一样,看教学视频的话,如果觉得老师讲的不错,我们不妨记录下来,Onenote 或者有道云笔记都行,记录大概,够我们日后回忆就好。
-
-### 关于学习
-
-做事不要有功利性,我最早在掘金写文章,其实也只是为了记录自己的学习,没想到会有人喜欢自己的文章,另外我课外学的很多东西,我自己也不清楚以后工作会不会用到,反正我自己感觉即然自己感兴趣,那么就去学吧。我相信,很多东西可能暂时带给你不了带多实质性的帮助,但是总有一天它会对你有帮助。如果感到迷茫的话,就做好眼前的事(拿我们班主任的话说,如果你感到迷茫,你就学好现在的专业知识就好了),我觉得没毛病。
-
-### 关于个人
-
-在生活中一定要保持谦虚,保持谦虚,保持谦虚,时刻都要有反省的准备,你要记住学无止境,永远不要满足现在的现状。另外,就是一定要掌控好自己的时间,多留点时间给父母亲人,以及那些自己在乎的人。如果对别人很在乎的话,不要去装作不在乎,因为这样真的不是太好,虽然我之前也会这样,很多时候撰写的消息,最后没发出去。
-
-## 最后分享一句话
-
-分享给大家,我笔记本里一直保存的杨绛老先生的一句话:“我们曾如此渴望命运的波澜,到最后才发现:人生最曼妙的风景,竟是内心的淡定与从容……我们曾如此期盼外界的认可,到最后才知道:世界是自己的,与他人毫无关系!”。
diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/.classpath" "b/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/.classpath"
deleted file mode 100644
index 0a1daddd3e8..00000000000
--- "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/.classpath"
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/.gitignore" "b/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/.gitignore"
deleted file mode 100644
index b83d22266ac..00000000000
--- "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/.gitignore"
+++ /dev/null
@@ -1 +0,0 @@
-/target/
diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/.project" "b/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/.project"
deleted file mode 100644
index 7b9c539d09c..00000000000
--- "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/.project"
+++ /dev/null
@@ -1,23 +0,0 @@
-
-
- securityAlgorithm
-
-
-
-
-
- org.eclipse.jdt.core.javabuilder
-
-
-
-
- org.eclipse.m2e.core.maven2Builder
-
-
-
-
-
- org.eclipse.jdt.core.javanature
- org.eclipse.m2e.core.maven2Nature
-
-
diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/.settings/org.eclipse.core.resources.prefs" "b/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/.settings/org.eclipse.core.resources.prefs"
deleted file mode 100644
index f9fe34593fc..00000000000
--- "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/.settings/org.eclipse.core.resources.prefs"
+++ /dev/null
@@ -1,4 +0,0 @@
-eclipse.preferences.version=1
-encoding//src/main/java=UTF-8
-encoding//src/test/java=UTF-8
-encoding/=UTF-8
diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/.settings/org.eclipse.jdt.core.prefs" "b/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/.settings/org.eclipse.jdt.core.prefs"
deleted file mode 100644
index abec6ca389a..00000000000
--- "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/.settings/org.eclipse.jdt.core.prefs"
+++ /dev/null
@@ -1,5 +0,0 @@
-eclipse.preferences.version=1
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
-org.eclipse.jdt.core.compiler.compliance=1.5
-org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
-org.eclipse.jdt.core.compiler.source=1.5
diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/.settings/org.eclipse.m2e.core.prefs" "b/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/.settings/org.eclipse.m2e.core.prefs"
deleted file mode 100644
index f897a7f1cb2..00000000000
--- "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/.settings/org.eclipse.m2e.core.prefs"
+++ /dev/null
@@ -1,4 +0,0 @@
-activeProfiles=
-eclipse.preferences.version=1
-resolveWorkspaceProjects=true
-version=1
diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/pom.xml" "b/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/pom.xml"
deleted file mode 100644
index 39d693b84b0..00000000000
--- "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/pom.xml"
+++ /dev/null
@@ -1,37 +0,0 @@
-
- 4.0.0
-
- com.snailclimb.ks
- securityAlgorithm
- 0.0.1-SNAPSHOT
- jar
-
- securityAlgorithm
- http://maven.apache.org
-
-
- UTF-8
-
-
-
-
- junit
- junit
- 4.12
- test
-
-
-
- commons-codec
- commons-codec
- 1.8
-
-
- org.bouncycastle
- bcprov-jdk15on
- 1.56
-
-
-
-
diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/src/main/java/com/snailclimb/ks/securityAlgorithm/Base64Demo.java" "b/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/src/main/java/com/snailclimb/ks/securityAlgorithm/Base64Demo.java"
deleted file mode 100644
index 1c6fd6df0ea..00000000000
--- "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/src/main/java/com/snailclimb/ks/securityAlgorithm/Base64Demo.java"
+++ /dev/null
@@ -1,49 +0,0 @@
-package com.snailclimb.ks.securityAlgorithm;
-
-import java.io.UnsupportedEncodingException;
-import java.util.Base64;
-
-public class Base64Demo {
-
- public static void main(String[] args) throws UnsupportedEncodingException {
- // TODO Auto-generated method stub
- CommonsCodecDemo();
- bouncyCastleDemo();
- jdkDemo();
- }
-
- static String str = "你若安好,便是晴天";
-
- /**
- * commons codec实现Base64加密解密
- */
- public static void CommonsCodecDemo() {
- // 加密:
- byte[] encodeBytes = org.apache.commons.codec.binary.Base64.encodeBase64(str.getBytes());
- System.out.println("commons codec实现base64加密: " + new String(encodeBytes));
- // 解密:
- byte[] decodeBytes = org.apache.commons.codec.binary.Base64.decodeBase64(encodeBytes);
- System.out.println("commons codec实现base64解密: " + new String(decodeBytes));
- }
-
- /**
- * bouncy castle实现Base64加密解密
- */
- public static void bouncyCastleDemo() {
- // 加密
- byte[] encodeBytes = org.bouncycastle.util.encoders.Base64.encode(str.getBytes());
- System.out.println("bouncy castle实现base64加密: " + new String(encodeBytes));
- // 解密
- byte[] decodeBytes = org.bouncycastle.util.encoders.Base64.decode(encodeBytes);
- System.out.println("bouncy castle实现base64解密:" + new String(decodeBytes));
- }
-
- public static void jdkDemo() throws UnsupportedEncodingException {
- // 加密
- String encodeBytes = Base64.getEncoder().encodeToString(str.getBytes("UTF-8"));
- System.out.println("JDK实现的base64加密: " + encodeBytes);
- //解密
- byte[] decodeBytes = Base64.getDecoder().decode(encodeBytes.getBytes("UTF-8"));
- System.out.println("JDK实现的base64解密: "+new String(decodeBytes));
- }
-}
diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/src/main/java/com/snailclimb/ks/securityAlgorithm/DesDemo.java" "b/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/src/main/java/com/snailclimb/ks/securityAlgorithm/DesDemo.java"
deleted file mode 100644
index ce8b09ee1da..00000000000
--- "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/src/main/java/com/snailclimb/ks/securityAlgorithm/DesDemo.java"
+++ /dev/null
@@ -1,100 +0,0 @@
-package com.snailclimb.ks.securityAlgorithm;
-
-import java.io.UnsupportedEncodingException;
-import java.security.SecureRandom;
-import javax.crypto.spec.DESKeySpec;
-import javax.crypto.SecretKeyFactory;
-import javax.crypto.SecretKey;
-import javax.crypto.Cipher;
-
-/**
- * DES加密介绍 DES是一种对称加密算法,所谓对称加密算法即:加密和解密使用相同密钥的算法。DES加密算法出自IBM的研究,
- * 后来被美国政府正式采用,之后开始广泛流传,但是近些年使用越来越少,因为DES使用56位密钥,以现代计算能力,
- * 24小时内即可被破解。虽然如此,在某些简单应用中,我们还是可以使用DES加密算法,本文简单讲解DES的JAVA实现 。
- * 注意:DES加密和解密过程中,密钥长度都必须是8的倍数
- */
-public class DesDemo {
- public DesDemo() {
- }
-
- // 测试
- public static void main(String args[]) {
- // 待加密内容
- String str = "cryptology";
- // 密码,长度要是8的倍数
- String password = "95880288";
-
- byte[] result;
- try {
- result = DesDemo.encrypt(str.getBytes(), password);
- System.out.println("加密后:" + result);
- byte[] decryResult = DesDemo.decrypt(result, password);
- System.out.println("解密后:" + decryResult);
- } catch (UnsupportedEncodingException e2) {
- // TODO Auto-generated catch block
- e2.printStackTrace();
- } catch (Exception e1) {
- e1.printStackTrace();
- }
- }
-
- // 直接将如上内容解密
-
- /**
- * 加密
- *
- * @param datasource
- * byte[]
- * @param password
- * String
- * @return byte[]
- */
- public static byte[] encrypt(byte[] datasource, String password) {
- try {
- SecureRandom random = new SecureRandom();
- DESKeySpec desKey = new DESKeySpec(password.getBytes());
- // 创建一个密匙工厂,然后用它把DESKeySpec转换成
- SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
- SecretKey securekey = keyFactory.generateSecret(desKey);
- // Cipher对象实际完成加密操作
- Cipher cipher = Cipher.getInstance("DES");
- // 用密匙初始化Cipher对象,ENCRYPT_MODE用于将 Cipher 初始化为加密模式的常量
- cipher.init(Cipher.ENCRYPT_MODE, securekey, random);
- // 现在,获取数据并加密
- // 正式执行加密操作
- return cipher.doFinal(datasource); // 按单部分操作加密或解密数据,或者结束一个多部分操作
- } catch (Throwable e) {
- e.printStackTrace();
- }
- return null;
- }
-
- /**
- * 解密
- *
- * @param src
- * byte[]
- * @param password
- * String
- * @return byte[]
- * @throws Exception
- */
- public static byte[] decrypt(byte[] src, String password) throws Exception {
- // DES算法要求有一个可信任的随机数源
- SecureRandom random = new SecureRandom();
- // 创建一个DESKeySpec对象
- DESKeySpec desKey = new DESKeySpec(password.getBytes());
- // 创建一个密匙工厂
- SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");// 返回实现指定转换的
- // Cipher
- // 对象
- // 将DESKeySpec对象转换成SecretKey对象
- SecretKey securekey = keyFactory.generateSecret(desKey);
- // Cipher对象实际完成解密操作
- Cipher cipher = Cipher.getInstance("DES");
- // 用密匙初始化Cipher对象
- cipher.init(Cipher.DECRYPT_MODE, securekey, random);
- // 真正开始解密操作
- return cipher.doFinal(src);
- }
-}
\ No newline at end of file
diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/src/main/java/com/snailclimb/ks/securityAlgorithm/IDEADemo.java" "b/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/src/main/java/com/snailclimb/ks/securityAlgorithm/IDEADemo.java"
deleted file mode 100644
index 5ce251df0fa..00000000000
--- "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/src/main/java/com/snailclimb/ks/securityAlgorithm/IDEADemo.java"
+++ /dev/null
@@ -1,46 +0,0 @@
-package com.snailclimb.ks.securityAlgorithm;
-
-import java.security.Key;
-import java.security.Security;
-
-import javax.crypto.Cipher;
-import javax.crypto.KeyGenerator;
-import javax.crypto.SecretKey;
-import javax.crypto.spec.SecretKeySpec;
-
-import org.apache.commons.codec.binary.Base64;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-
-public class IDEADemo {
- public static void main(String args[]) {
- bcIDEA();
- }
- public static void bcIDEA() {
- String src = "www.xttblog.com security idea";
- try {
- Security.addProvider(new BouncyCastleProvider());
-
- //生成key
- KeyGenerator keyGenerator = KeyGenerator.getInstance("IDEA");
- keyGenerator.init(128);
- SecretKey secretKey = keyGenerator.generateKey();
- byte[] keyBytes = secretKey.getEncoded();
-
- //转换密钥
- Key key = new SecretKeySpec(keyBytes, "IDEA");
-
- //加密
- Cipher cipher = Cipher.getInstance("IDEA/ECB/ISO10126Padding");
- cipher.init(Cipher.ENCRYPT_MODE, key);
- byte[] result = cipher.doFinal(src.getBytes());
- System.out.println("bc idea encrypt : " + Base64.encodeBase64String(result));
-
- //解密
- cipher.init(Cipher.DECRYPT_MODE, key);
- result = cipher.doFinal(result);
- System.out.println("bc idea decrypt : " + new String(result));
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-}
diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/src/main/java/com/snailclimb/ks/securityAlgorithm/MD5.java" "b/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/src/main/java/com/snailclimb/ks/securityAlgorithm/MD5.java"
deleted file mode 100644
index 2b8e31f83f5..00000000000
--- "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/src/main/java/com/snailclimb/ks/securityAlgorithm/MD5.java"
+++ /dev/null
@@ -1,160 +0,0 @@
-package com.snailclimb.ks.securityAlgorithm;
-
-public class MD5{
- /*
- *四个链接变量
- */
- private final int A=0x67452301;
- private final int B=0xefcdab89;
- private final int C=0x98badcfe;
- private final int D=0x10325476;
- /*
- *ABCD的临时变量
- */
- private int Atemp,Btemp,Ctemp,Dtemp;
-
- /*
- *常量ti
- *公式:floor(abs(sin(i+1))×(2pow32)
- */
- private final int K[]={
- 0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee,
- 0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501,0x698098d8,
- 0x8b44f7af,0xffff5bb1,0x895cd7be,0x6b901122,0xfd987193,
- 0xa679438e,0x49b40821,0xf61e2562,0xc040b340,0x265e5a51,
- 0xe9b6c7aa,0xd62f105d,0x02441453,0xd8a1e681,0xe7d3fbc8,
- 0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed,0xa9e3e905,
- 0xfcefa3f8,0x676f02d9,0x8d2a4c8a,0xfffa3942,0x8771f681,
- 0x6d9d6122,0xfde5380c,0xa4beea44,0x4bdecfa9,0xf6bb4b60,
- 0xbebfbc70,0x289b7ec6,0xeaa127fa,0xd4ef3085,0x04881d05,
- 0xd9d4d039,0xe6db99e5,0x1fa27cf8,0xc4ac5665,0xf4292244,
- 0x432aff97,0xab9423a7,0xfc93a039,0x655b59c3,0x8f0ccc92,
- 0xffeff47d,0x85845dd1,0x6fa87e4f,0xfe2ce6e0,0xa3014314,
- 0x4e0811a1,0xf7537e82,0xbd3af235,0x2ad7d2bb,0xeb86d391};
- /*
- *向左位移数,计算方法未知
- */
- private final int s[]={7,12,17,22,7,12,17,22,7,12,17,22,7,
- 12,17,22,5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,
- 4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,6,10,
- 15,21,6,10,15,21,6,10,15,21,6,10,15,21};
-
-
- /*
- *初始化函数
- */
- private void init(){
- Atemp=A;
- Btemp=B;
- Ctemp=C;
- Dtemp=D;
- }
- /*
- *移动一定位数
- */
- private int shift(int a,int s){
- return(a<>>(32-s));//右移的时候,高位一定要补零,而不是补充符号位
- }
- /*
- *主循环
- */
- private void MainLoop(int M[]){
- int F,g;
- int a=Atemp;
- int b=Btemp;
- int c=Ctemp;
- int d=Dtemp;
- for(int i = 0; i < 64; i ++){
- if(i<16){
- F=(b&c)|((~b)&d);
- g=i;
- }else if(i<32){
- F=(d&b)|((~d)&c);
- g=(5*i+1)%16;
- }else if(i<48){
- F=b^c^d;
- g=(3*i+5)%16;
- }else{
- F=c^(b|(~d));
- g=(7*i)%16;
- }
- int tmp=d;
- d=c;
- c=b;
- b=b+shift(a+F+K[i]+M[g],s[i]);
- a=tmp;
- }
- Atemp=a+Atemp;
- Btemp=b+Btemp;
- Ctemp=c+Ctemp;
- Dtemp=d+Dtemp;
-
- }
- /*
- *填充函数
- *处理后应满足bits≡448(mod512),字节就是bytes≡56(mode64)
- *填充方式为先加一个0,其它位补零
- *最后加上64位的原来长度
- */
- private int[] add(String str){
- int num=((str.length()+8)/64)+1;//以512位,64个字节为一组
- int strByte[]=new int[num*16];//64/4=16,所以有16个整数
- for(int i=0;i>2]|=str.charAt(i)<<((i%4)*8);//一个整数存储四个字节,小端序
- }
- strByte[i>>2]|=0x80<<((i%4)*8);//尾部添加1
- /*
- *添加原长度,长度指位的长度,所以要乘8,然后是小端序,所以放在倒数第二个,这里长度只用了32位
- */
- strByte[num*16-2]=str.length()*8;
- return strByte;
- }
- /*
- *调用函数
- */
- public String getMD5(String source){
- init();
- int strByte[]=add(source);
- for(int i=0;i>i*8)%(1<<8))&0xff)).replace(' ', '0');
-
- }
- return str;
- }
- /*
- *单例
- */
- private static MD5 instance;
- public static MD5 getInstance(){
- if(instance==null){
- instance=new MD5();
- }
- return instance;
- }
-
- private MD5(){};
-
- public static void main(String[] args){
- String str=MD5.getInstance().getMD5("你若安好,便是晴天");
- System.out.println(str);
- }
-}
\ No newline at end of file
diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/src/main/java/com/snailclimb/ks/securityAlgorithm/MD5Demo.java" "b/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/src/main/java/com/snailclimb/ks/securityAlgorithm/MD5Demo.java"
deleted file mode 100644
index 3a8635d1e69..00000000000
--- "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/src/main/java/com/snailclimb/ks/securityAlgorithm/MD5Demo.java"
+++ /dev/null
@@ -1,50 +0,0 @@
-package com.snailclimb.ks.securityAlgorithm;
-
-import java.security.MessageDigest;
-
-public class MD5Demo {
-
- // test
- public static void main(String[] args) {
- System.out.println(getMD5Code("你若安好,便是晴天"));
- }
-
- private MD5Demo() {
- }
-
- // md5加密
- public static String getMD5Code(String message) {
- String md5Str = "";
- try {
- //创建MD5算法消息摘要
- MessageDigest md = MessageDigest.getInstance("MD5");
- //生成的哈希值的字节数组
- byte[] md5Bytes = md.digest(message.getBytes());
- md5Str = bytes2Hex(md5Bytes);
- }catch(Exception e) {
- e.printStackTrace();
- }
- return md5Str;
- }
-
- // 2进制转16进制
- public static String bytes2Hex(byte[] bytes) {
- StringBuffer result = new StringBuffer();
- int temp;
- try {
- for (int i = 0; i < bytes.length; i++) {
- temp = bytes[i];
- if(temp < 0) {
- temp += 256;
- }
- if (temp < 16) {
- result.append("0");
- }
- result.append(Integer.toHexString(temp));
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- return result.toString();
- }
-}
diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/src/main/java/com/snailclimb/ks/securityAlgorithm/RSADemo.java" "b/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/src/main/java/com/snailclimb/ks/securityAlgorithm/RSADemo.java"
deleted file mode 100644
index 5234028ece4..00000000000
--- "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/src/main/java/com/snailclimb/ks/securityAlgorithm/RSADemo.java"
+++ /dev/null
@@ -1,249 +0,0 @@
-package com.snailclimb.ks.securityAlgorithm;
-
-import org.apache.commons.codec.binary.Base64;
-
-import java.security.*;
-import java.security.spec.PKCS8EncodedKeySpec;
-import java.security.spec.X509EncodedKeySpec;
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.crypto.Cipher;
-
-/**
- * Created by humf.需要依赖 commons-codec 包
- */
-public class RSADemo {
-
- public static void main(String[] args) throws Exception {
- Map keyMap = initKey();
- String publicKey = getPublicKey(keyMap);
- String privateKey = getPrivateKey(keyMap);
-
- System.out.println(keyMap);
- System.out.println("-----------------------------------");
- System.out.println(publicKey);
- System.out.println("-----------------------------------");
- System.out.println(privateKey);
- System.out.println("-----------------------------------");
- byte[] encryptByPrivateKey = encryptByPrivateKey("123456".getBytes(), privateKey);
- byte[] encryptByPublicKey = encryptByPublicKey("123456", publicKey);
- System.out.println(encryptByPrivateKey);
- System.out.println("-----------------------------------");
- System.out.println(encryptByPublicKey);
- System.out.println("-----------------------------------");
- String sign = sign(encryptByPrivateKey, privateKey);
- System.out.println(sign);
- System.out.println("-----------------------------------");
- boolean verify = verify(encryptByPrivateKey, publicKey, sign);
- System.out.println(verify);
- System.out.println("-----------------------------------");
- byte[] decryptByPublicKey = decryptByPublicKey(encryptByPrivateKey, publicKey);
- byte[] decryptByPrivateKey = decryptByPrivateKey(encryptByPublicKey, privateKey);
- System.out.println(decryptByPublicKey);
- System.out.println("-----------------------------------");
- System.out.println(decryptByPrivateKey);
-
- }
-
- public static final String KEY_ALGORITHM = "RSA";
- public static final String SIGNATURE_ALGORITHM = "MD5withRSA";
-
- private static final String PUBLIC_KEY = "RSAPublicKey";
- private static final String PRIVATE_KEY = "RSAPrivateKey";
-
- public static byte[] decryptBASE64(String key) {
- return Base64.decodeBase64(key);
- }
-
- public static String encryptBASE64(byte[] bytes) {
- return Base64.encodeBase64String(bytes);
- }
-
- /**
- * 用私钥对信息生成数字签名
- *
- * @param data
- * 加密数据
- * @param privateKey
- * 私钥
- * @return
- * @throws Exception
- */
- public static String sign(byte[] data, String privateKey) throws Exception {
- // 解密由base64编码的私钥
- byte[] keyBytes = decryptBASE64(privateKey);
- // 构造PKCS8EncodedKeySpec对象
- PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
- // KEY_ALGORITHM 指定的加密算法
- KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
- // 取私钥匙对象
- PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);
- // 用私钥对信息生成数字签名
- Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
- signature.initSign(priKey);
- signature.update(data);
- return encryptBASE64(signature.sign());
- }
-
- /**
- * 校验数字签名
- *
- * @param data
- * 加密数据
- * @param publicKey
- * 公钥
- * @param sign
- * 数字签名
- * @return 校验成功返回true 失败返回false
- * @throws Exception
- */
- public static boolean verify(byte[] data, String publicKey, String sign) throws Exception {
- // 解密由base64编码的公钥
- byte[] keyBytes = decryptBASE64(publicKey);
- // 构造X509EncodedKeySpec对象
- X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
- // KEY_ALGORITHM 指定的加密算法
- KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
- // 取公钥匙对象
- PublicKey pubKey = keyFactory.generatePublic(keySpec);
- Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
- signature.initVerify(pubKey);
- signature.update(data);
- // 验证签名是否正常
- return signature.verify(decryptBASE64(sign));
- }
-
- public static byte[] decryptByPrivateKey(byte[] data, String key) throws Exception {
- // 对密钥解密
- byte[] keyBytes = decryptBASE64(key);
- // 取得私钥
- PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
- KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
- Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
- // 对数据解密
- Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
- cipher.init(Cipher.DECRYPT_MODE, privateKey);
- return cipher.doFinal(data);
- }
-
- /**
- * 解密
- * 用私钥解密
- *
- * @param data
- * @param key
- * @return
- * @throws Exception
- */
- public static byte[] decryptByPrivateKey(String data, String key) throws Exception {
- return decryptByPrivateKey(decryptBASE64(data), key);
- }
-
- /**
- * 解密
- * 用公钥解密
- *
- * @param data
- * @param key
- * @return
- * @throws Exception
- */
- public static byte[] decryptByPublicKey(byte[] data, String key) throws Exception {
- // 对密钥解密
- byte[] keyBytes = decryptBASE64(key);
- // 取得公钥
- X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
- KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
- Key publicKey = keyFactory.generatePublic(x509KeySpec);
- // 对数据解密
- Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
- cipher.init(Cipher.DECRYPT_MODE, publicKey);
- return cipher.doFinal(data);
- }
-
- /**
- * 加密
- * 用公钥加密
- *
- * @param data
- * @param key
- * @return
- * @throws Exception
- */
- public static byte[] encryptByPublicKey(String data, String key) throws Exception {
- // 对公钥解密
- byte[] keyBytes = decryptBASE64(key);
- // 取得公钥
- X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
- KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
- Key publicKey = keyFactory.generatePublic(x509KeySpec);
- // 对数据加密
- Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
- cipher.init(Cipher.ENCRYPT_MODE, publicKey);
- return cipher.doFinal(data.getBytes());
- }
-
- /**
- * 加密
- * 用私钥加密
- *
- * @param data
- * @param key
- * @return
- * @throws Exception
- */
- public static byte[] encryptByPrivateKey(byte[] data, String key) throws Exception {
- // 对密钥解密
- byte[] keyBytes = decryptBASE64(key);
- // 取得私钥
- PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
- KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
- Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
- // 对数据加密
- Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
- cipher.init(Cipher.ENCRYPT_MODE, privateKey);
- return cipher.doFinal(data);
- }
-
- /**
- * 取得私钥
- *
- * @param keyMap
- * @return
- * @throws Exception
- */
- public static String getPrivateKey(Map keyMap) throws Exception {
- Key key = (Key) keyMap.get(PRIVATE_KEY);
- return encryptBASE64(key.getEncoded());
- }
-
- /**
- * 取得公钥
- *
- * @param keyMap
- * @return
- * @throws Exception
- */
- public static String getPublicKey(Map keyMap) throws Exception {
- Key key = keyMap.get(PUBLIC_KEY);
- return encryptBASE64(key.getEncoded());
- }
-
- /**
- * 初始化密钥
- *
- * @return
- * @throws Exception
- */
- public static Map initKey() throws Exception {
- KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
- keyPairGen.initialize(1024);
- KeyPair keyPair = keyPairGen.generateKeyPair();
- Map keyMap = new HashMap(2);
- keyMap.put(PUBLIC_KEY, keyPair.getPublic());// 公钥
- keyMap.put(PRIVATE_KEY, keyPair.getPrivate());// 私钥
- return keyMap;
- }
-
-}
\ No newline at end of file
diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/src/main/java/com/snailclimb/ks/securityAlgorithm/SHA1Demo.java" "b/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/src/main/java/com/snailclimb/ks/securityAlgorithm/SHA1Demo.java"
deleted file mode 100644
index ab19e3d0cf9..00000000000
--- "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/src/main/java/com/snailclimb/ks/securityAlgorithm/SHA1Demo.java"
+++ /dev/null
@@ -1,45 +0,0 @@
-package com.snailclimb.ks.securityAlgorithm;
-
-import java.io.UnsupportedEncodingException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-
-public class SHA1Demo {
-
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- System.out.println(getSha1("你若安好,便是晴天"));
-
- }
-
- public static String getSha1(String str) {
- if (null == str || 0 == str.length()) {
- return null;
- }
- char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
- try {
- //创建SHA1算法消息摘要对象
- MessageDigest mdTemp = MessageDigest.getInstance("SHA1");
- //使用指定的字节数组更新摘要。
- mdTemp.update(str.getBytes("UTF-8"));
- //生成的哈希值的字节数组
- byte[] md = mdTemp.digest();
- //SHA1算法生成信息摘要关键过程
- int j = md.length;
- char[] buf = new char[j * 2];
- int k = 0;
- for (int i = 0; i < j; i++) {
- byte byte0 = md[i];
- buf[k++] = hexDigits[byte0 >>> 4 & 0xf];
- buf[k++] = hexDigits[byte0 & 0xf];
- }
- return new String(buf);
- } catch (NoSuchAlgorithmException e) {
- e.printStackTrace();
- } catch (UnsupportedEncodingException e) {
- e.printStackTrace();
- }
- return "0";
-
- }
-}
diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/src/main/java/com/snailclimb/ks/securityAlgorithm/readme" "b/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/src/main/java/com/snailclimb/ks/securityAlgorithm/readme"
deleted file mode 100644
index 5c2d452dd23..00000000000
--- "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/src/main/java/com/snailclimb/ks/securityAlgorithm/readme"
+++ /dev/null
@@ -1,3 +0,0 @@
-Des算法参考:http://blog.csdn.net/super_cui/article/details/70820983
-IDEA算法参考:https://www.xttblog.com/?p=1121
-RSA算法实现参考:https://www.cnblogs.com/xlhan/p/7120488.html
\ No newline at end of file
diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/src/test/java/com/snailclimb/ks/securityAlgorithm/AppTest.java" "b/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/src/test/java/com/snailclimb/ks/securityAlgorithm/AppTest.java"
deleted file mode 100644
index 932254515a0..00000000000
--- "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/source code/securityAlgorithm/src/test/java/com/snailclimb/ks/securityAlgorithm/AppTest.java"
+++ /dev/null
@@ -1,38 +0,0 @@
-package com.snailclimb.ks.securityAlgorithm;
-
-import junit.framework.Test;
-import junit.framework.TestCase;
-import junit.framework.TestSuite;
-
-/**
- * Unit test for simple App.
- */
-public class AppTest
- extends TestCase
-{
- /**
- * Create the test case
- *
- * @param testName name of the test case
- */
- public AppTest( String testName )
- {
- super( testName );
- }
-
- /**
- * @return the suite of tests being tested
- */
- public static Test suite()
- {
- return new TestSuite( AppTest.class );
- }
-
- /**
- * Rigourous Test :-)
- */
- public void testApp()
- {
- assertTrue( true );
- }
-}
diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\345\270\270\350\247\201\345\256\211\345\205\250\347\256\227\346\263\225\357\274\210MD5\343\200\201SHA1\343\200\201Base64\347\255\211\347\255\211\357\274\211\346\200\273\347\273\223.md" "b/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\345\270\270\350\247\201\345\256\211\345\205\250\347\256\227\346\263\225\357\274\210MD5\343\200\201SHA1\343\200\201Base64\347\255\211\347\255\211\357\274\211\346\200\273\347\273\223.md"
deleted file mode 100644
index 79a03d391d8..00000000000
--- "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\345\270\270\350\247\201\345\256\211\345\205\250\347\256\227\346\263\225\357\274\210MD5\343\200\201SHA1\343\200\201Base64\347\255\211\347\255\211\357\274\211\346\200\273\347\273\223.md"
+++ /dev/null
@@ -1,849 +0,0 @@
-本文主要对消息摘要算法和加密算法做了整理,包括MD5、SHA、DES、AES、RSA等,并且提供了相应算法的Java实现和测试。
-
-# 一 消息摘要算法
-
-## **1. 简介:**
-
-- **消息摘要算法的主要特征是加密过程不需要密钥,并且经过加密的数据无法被解密**
-- **只有输入相同的明文数据经过相同的消息摘要算法才能得到相同的密文。**
-- **消息摘要算法主要应用在“数字签名”领域,作为对明文的摘要算法。**
-- **著名的摘要算法有RSA公司的MD5算法和SHA-1算法及其大量的变体**。
-
-## **2. 特点:**
-
-1. **无论输入的消息有多长,计算出来的消息摘要的长度总是固定的。**
-2. **消息摘要看起来是“伪随机的”。也就是说对相同的信息求摘要结果相同。**
-3. **消息轻微改变生成的摘要变化会很大**
-4. **只能进行正向的信息摘要,而无法从摘要中恢复出任何的消息,甚至根本就找不到任何与原信息相关的信息**
-
-## **3. 应用:**
-
-消息摘要算法最常用的场景就是数字签名以及数据(密码)加密了。(一般平时做项目用的比较多的就是使用MD5对用户密码进行加密)
-
-## **4. 何谓数字签名:**
-
-数字签名主要用到了非对称密钥加密技术与数字摘要技术。数字签名技术是将摘要信息用发送者的私钥加密,与原文一起传送给接收者。接收者只有用发送者的公钥才能解密被加密的摘要信息,然后用HASH函数对收到的原文产生一个摘要信息,与解密的摘要信息对比。
-如果相同,则说明收到的信息是完整的,在传输过程中没有被修改,否则说明信息被修改过.
-
-因此数字签名能够验证信息的完整性。
-数字签名是个加密的过程,数字签名验证是个解密的过程。
-
-## **5. 常见消息/数字摘要算法:**
-
-### [**MD5:**](https://baike.baidu.com/item/MD5/212708?fr=aladdin)
-
-#### 简介:
-
-MD5的作用是让大容量信息在用数字签名软件签署私人密钥前被"压缩"成一种保密的格式
- (也就是把一个任意长度的字节串变换成一定长的十六进制数字串)。
-
-#### 特点:
-
-1. **压缩性:** 任意长度的数据,算出的MD5值长度都是固定的。
-2. **容易计算:** 从原数据计算出MD5值很容易。
-3. **抗修改性:** 对原数据进行任何改动,哪怕只修改1个字节,所得到的MD5值都有很大区别。
-4. **强抗碰撞:** 已知原数据和其MD5值,想找到一个具有相同MD5值的数据(即伪造数据)是非常困难的。
-
-#### 代码实现:
-
-**利用JDK提供java.security.MessageDigest类实现MD5算法:**
-
-```java
-package com.snailclimb.ks.securityAlgorithm;
-
-import java.security.MessageDigest;
-
-public class MD5Demo {
-
- // test
- public static void main(String[] args) {
- System.out.println(getMD5Code("你若安好,便是晴天"));
- }
-
- private MD5Demo() {
- }
-
- // md5加密
- public static String getMD5Code(String message) {
- String md5Str = "";
- try {
- //创建MD5算法消息摘要
- MessageDigest md = MessageDigest.getInstance("MD5");
- //生成的哈希值的字节数组
- byte[] md5Bytes = md.digest(message.getBytes());
- md5Str = bytes2Hex(md5Bytes);
- }catch(Exception e) {
- e.printStackTrace();
- }
- return md5Str;
- }
-
- // 2进制转16进制
- public static String bytes2Hex(byte[] bytes) {
- StringBuffer result = new StringBuffer();
- int temp;
- try {
- for (int i = 0; i < bytes.length; i++) {
- temp = bytes[i];
- if(temp < 0) {
- temp += 256;
- }
- if (temp < 16) {
- result.append("0");
- }
- result.append(Integer.toHexString(temp));
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- return result.toString();
- }
-}
-
-```
-
-**结果:**
-```
-6bab82679914f7cb480a120b532ffa80
-
-```
-
-**注意MessageDigest类的几个方法:**
-
-```java
-static MessageDigest getInstance(String algorithm)//返回实现指定摘要算法的MessageDigest对象
-```
-```java
-byte[] digest(byte[] input)//使用指定的字节数组对摘要执行最终更新,然后完成摘要计算。
-```
-
-#### 不利用Java提供的java.security.MessageDigest类实现MD5算法:
-
-```java
-package com.snailclimb.ks.securityAlgorithm;
-
-public class MD5{
- /*
- *四个链接变量
- */
- private final int A=0x67452301;
- private final int B=0xefcdab89;
- private final int C=0x98badcfe;
- private final int D=0x10325476;
- /*
- *ABCD的临时变量
- */
- private int Atemp,Btemp,Ctemp,Dtemp;
-
- /*
- *常量ti
- *公式:floor(abs(sin(i+1))×(2pow32)
- */
- private final int K[]={
- 0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee,
- 0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501,0x698098d8,
- 0x8b44f7af,0xffff5bb1,0x895cd7be,0x6b901122,0xfd987193,
- 0xa679438e,0x49b40821,0xf61e2562,0xc040b340,0x265e5a51,
- 0xe9b6c7aa,0xd62f105d,0x02441453,0xd8a1e681,0xe7d3fbc8,
- 0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed,0xa9e3e905,
- 0xfcefa3f8,0x676f02d9,0x8d2a4c8a,0xfffa3942,0x8771f681,
- 0x6d9d6122,0xfde5380c,0xa4beea44,0x4bdecfa9,0xf6bb4b60,
- 0xbebfbc70,0x289b7ec6,0xeaa127fa,0xd4ef3085,0x04881d05,
- 0xd9d4d039,0xe6db99e5,0x1fa27cf8,0xc4ac5665,0xf4292244,
- 0x432aff97,0xab9423a7,0xfc93a039,0x655b59c3,0x8f0ccc92,
- 0xffeff47d,0x85845dd1,0x6fa87e4f,0xfe2ce6e0,0xa3014314,
- 0x4e0811a1,0xf7537e82,0xbd3af235,0x2ad7d2bb,0xeb86d391};
- /*
- *向左位移数,计算方法未知
- */
- private final int s[]={7,12,17,22,7,12,17,22,7,12,17,22,7,
- 12,17,22,5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,
- 4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,6,10,
- 15,21,6,10,15,21,6,10,15,21,6,10,15,21};
-
-
- /*
- *初始化函数
- */
- private void init(){
- Atemp=A;
- Btemp=B;
- Ctemp=C;
- Dtemp=D;
- }
- /*
- *移动一定位数
- */
- private int shift(int a,int s){
- return(a<>>(32-s));//右移的时候,高位一定要补零,而不是补充符号位
- }
- /*
- *主循环
- */
- private void MainLoop(int M[]){
- int F,g;
- int a=Atemp;
- int b=Btemp;
- int c=Ctemp;
- int d=Dtemp;
- for(int i = 0; i < 64; i ++){
- if(i<16){
- F=(b&c)|((~b)&d);
- g=i;
- }else if(i<32){
- F=(d&b)|((~d)&c);
- g=(5*i+1)%16;
- }else if(i<48){
- F=b^c^d;
- g=(3*i+5)%16;
- }else{
- F=c^(b|(~d));
- g=(7*i)%16;
- }
- int tmp=d;
- d=c;
- c=b;
- b=b+shift(a+F+K[i]+M[g],s[i]);
- a=tmp;
- }
- Atemp=a+Atemp;
- Btemp=b+Btemp;
- Ctemp=c+Ctemp;
- Dtemp=d+Dtemp;
-
- }
- /*
- *填充函数
- *处理后应满足bits≡448(mod512),字节就是bytes≡56(mode64)
- *填充方式为先加一个0,其它位补零
- *最后加上64位的原来长度
- */
- private int[] add(String str){
- int num=((str.length()+8)/64)+1;//以512位,64个字节为一组
- int strByte[]=new int[num*16];//64/4=16,所以有16个整数
- for(int i=0;i>2]|=str.charAt(i)<<((i%4)*8);//一个整数存储四个字节,小端序
- }
- strByte[i>>2]|=0x80<<((i%4)*8);//尾部添加1
- /*
- *添加原长度,长度指位的长度,所以要乘8,然后是小端序,所以放在倒数第二个,这里长度只用了32位
- */
- strByte[num*16-2]=str.length()*8;
- return strByte;
- }
- /*
- *调用函数
- */
- public String getMD5(String source){
- init();
- int strByte[]=add(source);
- for(int i=0;i>i*8)%(1<<8))&0xff)).replace(' ', '0');
-
- }
- return str;
- }
- /*
- *单例
- */
- private static MD5 instance;
- public static MD5 getInstance(){
- if(instance==null){
- instance=new MD5();
- }
- return instance;
- }
-
- private MD5(){};
-
- public static void main(String[] args){
- String str=MD5.getInstance().getMD5("你若安好,便是晴天");
- System.out.println(str);
- }
-}
-```
-
-### [**SHA1:**](https://baike.baidu.com/item/MD5/212708?fr=aladdin)
-对于长度小于2^64位的消息,SHA1会产生一个160位(40个字符)的消息摘要。当接收到消息的时候,这个消息摘要可以用来验证数据的完整性。在传输的过程中,数据很可能会发生变化,那么这时候就会产生不同的消息摘要。
-
-SHA1有如下特性:
-
-- 不可以从消息摘要中复原信息;
-- 两个不同的消息不会产生同样的消息摘要,(但会有1x10 ^ 48分之一的机率出现相同的消息摘要,一般使用时忽略)。
-
-#### 代码实现:
-
-**利用JDK提供java.security.MessageDigest类实现SHA1算法:*
-
-```java
-package com.snailclimb.ks.securityAlgorithm;
-
-import java.io.UnsupportedEncodingException;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-
-public class SHA1Demo {
-
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- System.out.println(getSha1("你若安好,便是晴天"));
-
- }
-
- public static String getSha1(String str) {
- if (null == str || 0 == str.length()) {
- return null;
- }
- char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
- try {
- //创建SHA1算法消息摘要对象
- MessageDigest mdTemp = MessageDigest.getInstance("SHA1");
- //使用指定的字节数组更新摘要。
- mdTemp.update(str.getBytes("UTF-8"));
- //生成的哈希值的字节数组
- byte[] md = mdTemp.digest();
- //SHA1算法生成信息摘要关键过程
- int j = md.length;
- char[] buf = new char[j * 2];
- int k = 0;
- for (int i = 0; i < j; i++) {
- byte byte0 = md[i];
- buf[k++] = hexDigits[byte0 >>> 4 & 0xf];
- buf[k++] = hexDigits[byte0 & 0xf];
- }
- return new String(buf);
- } catch (NoSuchAlgorithmException e) {
- e.printStackTrace();
- } catch (UnsupportedEncodingException e) {
- e.printStackTrace();
- }
- return "0";
-
- }
-}
-
-```
-
-**结果:**
-
-```
-8ce764110a42da9b08504b20e26b19c9e3382414
-```
-
-
-
-# 二 加密算法
-
-## **1. 简介:**
-
-- **加密技术包括两个元素:加密算法和密钥。**
-- **加密算法是将普通的文本(或者可以理解的信息)与一串数字(密钥)的结合,产生不可理解的密文的步骤。**
-- **密钥是用来对数据进行编码和解码的一种算法。**
-- **在安全保密中,可通过适当的密钥加密技术和管理机制来保证网络的信息通讯安全。**
-
-## **2. 分类:**
-
-**密钥加密技术的密码体制分为对称密钥体制和非对称密钥体制两种。相应地,对数据加密的技术分为两类,即对称加密(私人密钥加密)和非对称加密(公开密钥加密)。**
-
-**对称加密以数据加密标准(DES,Data Encryption Standard)算法为典型代表,非对称加密通常以RSA(Rivest Shamir Adleman)算法为代表。**
-
-**对称加密的加密密钥和解密密钥相同。非对称加密的加密密钥和解密密钥不同,加密密钥可以公开而解密密钥需要保密**
-## **3. 应用:**
-
-常被用在电子商务或者其他需要保证网络传输安全的范围。
-
-## **4. 对称加密:**
-
-加密密钥和解密密钥相同的加密算法。
-
-对称加密算法使用起来简单快捷,密钥较短,且破译困难,除了数据加密标准(DES),
-另一个对称密钥加密系统是国际数据加密算法(IDEA),它比DES的加密性好,而且对计算机功能要求也没有那么高。IDEA加密标准由PGP(Pretty Good Privacy)系统使用。
-### [**DES:**](https://baike.baidu.com/item/DES)
-
-DES全称为Data Encryption Standard,即数据加密标准,是一种使用密钥加密的块算法,现在已经过时。
-
-#### 代码实现:
-
-DES算法实现 :
-
-```java
-package com.snailclimb.ks.securityAlgorithm;
-
-import java.io.UnsupportedEncodingException;
-import java.security.SecureRandom;
-import javax.crypto.spec.DESKeySpec;
-import javax.crypto.SecretKeyFactory;
-import javax.crypto.SecretKey;
-import javax.crypto.Cipher;
-
-/**
- * DES加密介绍 DES是一种对称加密算法,所谓对称加密算法即:加密和解密使用相同密钥的算法。DES加密算法出自IBM的研究,
- * 后来被美国政府正式采用,之后开始广泛流传,但是近些年使用越来越少,因为DES使用56位密钥,以现代计算能力,
- * 24小时内即可被破解。虽然如此,在某些简单应用中,我们还是可以使用DES加密算法,本文简单讲解DES的JAVA实现 。
- * 注意:DES加密和解密过程中,密钥长度都必须是8的倍数
- */
-public class DesDemo {
- public DesDemo() {
- }
-
- // 测试
- public static void main(String args[]) {
- // 待加密内容
- String str = "cryptology";
- // 密码,长度要是8的倍数
- String password = "95880288";
-
- byte[] result;
- try {
- result = DesDemo.encrypt(str.getBytes(), password);
- System.out.println("加密后:" + result);
- byte[] decryResult = DesDemo.decrypt(result, password);
- System.out.println("解密后:" + new String(decryResult));
- } catch (UnsupportedEncodingException e2) {
- // TODO Auto-generated catch block
- e2.printStackTrace();
- } catch (Exception e1) {
- e1.printStackTrace();
- }
- }
-
- // 直接将如上内容解密
-
- /**
- * 加密
- *
- * @param datasource
- * byte[]
- * @param password
- * String
- * @return byte[]
- */
- public static byte[] encrypt(byte[] datasource, String password) {
- try {
- SecureRandom random = new SecureRandom();
- DESKeySpec desKey = new DESKeySpec(password.getBytes());
- // 创建一个密匙工厂,然后用它把DESKeySpec转换成
- SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
- SecretKey securekey = keyFactory.generateSecret(desKey);
- // Cipher对象实际完成加密操作
- Cipher cipher = Cipher.getInstance("DES");
- // 用密匙初始化Cipher对象,ENCRYPT_MODE用于将 Cipher 初始化为加密模式的常量
- cipher.init(Cipher.ENCRYPT_MODE, securekey, random);
- // 现在,获取数据并加密
- // 正式执行加密操作
- return cipher.doFinal(datasource); // 按单部分操作加密或解密数据,或者结束一个多部分操作
- } catch (Throwable e) {
- e.printStackTrace();
- }
- return null;
- }
-
- /**
- * 解密
- *
- * @param src
- * byte[]
- * @param password
- * String
- * @return byte[]
- * @throws Exception
- */
- public static byte[] decrypt(byte[] src, String password) throws Exception {
- // DES算法要求有一个可信任的随机数源
- SecureRandom random = new SecureRandom();
- // 创建一个DESKeySpec对象
- DESKeySpec desKey = new DESKeySpec(password.getBytes());
- // 创建一个密匙工厂
- SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");// 返回实现指定转换的
- // Cipher
- // 对象
- // 将DESKeySpec对象转换成SecretKey对象
- SecretKey securekey = keyFactory.generateSecret(desKey);
- // Cipher对象实际完成解密操作
- Cipher cipher = Cipher.getInstance("DES");
- // 用密匙初始化Cipher对象
- cipher.init(Cipher.DECRYPT_MODE, securekey, random);
- // 真正开始解密操作
- return cipher.doFinal(src);
- }
-}
-```
-
-结果:
-
-```
-加密后:[B@50cbc42f
-解密后:cryptology
-```
-
-### [**IDEA:**](https://baike.baidu.com/item/%E5%9B%BD%E9%99%85%E6%95%B0%E6%8D%AE%E5%8A%A0%E5%AF%86%E7%AE%97%E6%B3%95/11048972?fr=aladdin)
-
-- **这种算法是在DES算法的基础上发展出来的,类似于三重DES。**
-- **发展IDEA也是因为感到DES具有密钥太短等缺点。**
-- **DEA的密钥为128位,这么长的密钥在今后若干年内应该是安全的。**
-- **在实际项目中用到的很少了解即可。**
-
-#### 代码实现:
-
-IDEA算法实现
-
-```java
-package com.snailclimb.ks.securityAlgorithm;
-
-import java.security.Key;
-import java.security.Security;
-
-import javax.crypto.Cipher;
-import javax.crypto.KeyGenerator;
-import javax.crypto.SecretKey;
-import javax.crypto.spec.SecretKeySpec;
-
-import org.apache.commons.codec.binary.Base64;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-
-public class IDEADemo {
- public static void main(String args[]) {
- bcIDEA();
- }
- public static void bcIDEA() {
- String src = "www.xttblog.com security idea";
- try {
- Security.addProvider(new BouncyCastleProvider());
-
- //生成key
- KeyGenerator keyGenerator = KeyGenerator.getInstance("IDEA");
- keyGenerator.init(128);
- SecretKey secretKey = keyGenerator.generateKey();
- byte[] keyBytes = secretKey.getEncoded();
-
- //转换密钥
- Key key = new SecretKeySpec(keyBytes, "IDEA");
-
- //加密
- Cipher cipher = Cipher.getInstance("IDEA/ECB/ISO10126Padding");
- cipher.init(Cipher.ENCRYPT_MODE, key);
- byte[] result = cipher.doFinal(src.getBytes());
- System.out.println("bc idea encrypt : " + Base64.encodeBase64String(result));
-
- //解密
- cipher.init(Cipher.DECRYPT_MODE, key);
- result = cipher.doFinal(result);
- System.out.println("bc idea decrypt : " + new String(result));
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
-}
-
-```
-
-## **5. 非对称加密:**
-
-- 与对称加密算法不同,非对称加密算法需要两个密钥:公开密钥(publickey)和私有密钥 (privatekey)。
-- 公开密钥与私有密钥是一对,如果用公开密钥对数据进行加密,只有用对应的私有密钥才能解密;
-- 如果用私有密钥对数据进行加密,那么只有用对应的公开密钥才能解密。
-- 因为加密和解密使用的是两个不同的密钥,所以这种算法叫作非对称加密算法。
-### [**RAS:**](https://baike.baidu.com/item/DES)
-
-RSA是目前最有影响力和最常用的公钥加密算法。它能够抵抗到目前为止已知的绝大多数密码攻击,已被ISO推荐为公钥数据加密标准。
-
-#### 代码实现:
-
-RAS算法实现:
-
-```java
-package com.snailclimb.ks.securityAlgorithm;
-
-import org.apache.commons.codec.binary.Base64;
-
-import java.security.*;
-import java.security.spec.PKCS8EncodedKeySpec;
-import java.security.spec.X509EncodedKeySpec;
-import java.util.HashMap;
-import java.util.Map;
-
-import javax.crypto.Cipher;
-
-/**
- * Created by humf.需要依赖 commons-codec 包
- */
-public class RSADemo {
-
- public static void main(String[] args) throws Exception {
- Map keyMap = initKey();
- String publicKey = getPublicKey(keyMap);
- String privateKey = getPrivateKey(keyMap);
-
- System.out.println(keyMap);
- System.out.println("-----------------------------------");
- System.out.println(publicKey);
- System.out.println("-----------------------------------");
- System.out.println(privateKey);
- System.out.println("-----------------------------------");
- byte[] encryptByPrivateKey = encryptByPrivateKey("123456".getBytes(), privateKey);
- byte[] encryptByPublicKey = encryptByPublicKey("123456", publicKey);
- System.out.println(encryptByPrivateKey);
- System.out.println("-----------------------------------");
- System.out.println(encryptByPublicKey);
- System.out.println("-----------------------------------");
- String sign = sign(encryptByPrivateKey, privateKey);
- System.out.println(sign);
- System.out.println("-----------------------------------");
- boolean verify = verify(encryptByPrivateKey, publicKey, sign);
- System.out.println(verify);
- System.out.println("-----------------------------------");
- byte[] decryptByPublicKey = decryptByPublicKey(encryptByPrivateKey, publicKey);
- byte[] decryptByPrivateKey = decryptByPrivateKey(encryptByPublicKey, privateKey);
- System.out.println(decryptByPublicKey);
- System.out.println("-----------------------------------");
- System.out.println(decryptByPrivateKey);
-
- }
-
- public static final String KEY_ALGORITHM = "RSA";
- public static final String SIGNATURE_ALGORITHM = "MD5withRSA";
-
- private static final String PUBLIC_KEY = "RSAPublicKey";
- private static final String PRIVATE_KEY = "RSAPrivateKey";
-
- public static byte[] decryptBASE64(String key) {
- return Base64.decodeBase64(key);
- }
-
- public static String encryptBASE64(byte[] bytes) {
- return Base64.encodeBase64String(bytes);
- }
-
- /**
- * 用私钥对信息生成数字签名
- *
- * @param data
- * 加密数据
- * @param privateKey
- * 私钥
- * @return
- * @throws Exception
- */
- public static String sign(byte[] data, String privateKey) throws Exception {
- // 解密由base64编码的私钥
- byte[] keyBytes = decryptBASE64(privateKey);
- // 构造PKCS8EncodedKeySpec对象
- PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
- // KEY_ALGORITHM 指定的加密算法
- KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
- // 取私钥匙对象
- PrivateKey priKey = keyFactory.generatePrivate(pkcs8KeySpec);
- // 用私钥对信息生成数字签名
- Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
- signature.initSign(priKey);
- signature.update(data);
- return encryptBASE64(signature.sign());
- }
-
- /**
- * 校验数字签名
- *
- * @param data
- * 加密数据
- * @param publicKey
- * 公钥
- * @param sign
- * 数字签名
- * @return 校验成功返回true 失败返回false
- * @throws Exception
- */
- public static boolean verify(byte[] data, String publicKey, String sign) throws Exception {
- // 解密由base64编码的公钥
- byte[] keyBytes = decryptBASE64(publicKey);
- // 构造X509EncodedKeySpec对象
- X509EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes);
- // KEY_ALGORITHM 指定的加密算法
- KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
- // 取公钥匙对象
- PublicKey pubKey = keyFactory.generatePublic(keySpec);
- Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
- signature.initVerify(pubKey);
- signature.update(data);
- // 验证签名是否正常
- return signature.verify(decryptBASE64(sign));
- }
-
- public static byte[] decryptByPrivateKey(byte[] data, String key) throws Exception {
- // 对密钥解密
- byte[] keyBytes = decryptBASE64(key);
- // 取得私钥
- PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
- KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
- Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
- // 对数据解密
- Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
- cipher.init(Cipher.DECRYPT_MODE, privateKey);
- return cipher.doFinal(data);
- }
-
- /**
- * 解密
- * 用私钥解密
- *
- * @param data
- * @param key
- * @return
- * @throws Exception
- */
- public static byte[] decryptByPrivateKey(String data, String key) throws Exception {
- return decryptByPrivateKey(decryptBASE64(data), key);
- }
-
- /**
- * 解密
- * 用公钥解密
- *
- * @param data
- * @param key
- * @return
- * @throws Exception
- */
- public static byte[] decryptByPublicKey(byte[] data, String key) throws Exception {
- // 对密钥解密
- byte[] keyBytes = decryptBASE64(key);
- // 取得公钥
- X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
- KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
- Key publicKey = keyFactory.generatePublic(x509KeySpec);
- // 对数据解密
- Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
- cipher.init(Cipher.DECRYPT_MODE, publicKey);
- return cipher.doFinal(data);
- }
-
- /**
- * 加密
- * 用公钥加密
- *
- * @param data
- * @param key
- * @return
- * @throws Exception
- */
- public static byte[] encryptByPublicKey(String data, String key) throws Exception {
- // 对公钥解密
- byte[] keyBytes = decryptBASE64(key);
- // 取得公钥
- X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
- KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
- Key publicKey = keyFactory.generatePublic(x509KeySpec);
- // 对数据加密
- Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
- cipher.init(Cipher.ENCRYPT_MODE, publicKey);
- return cipher.doFinal(data.getBytes());
- }
-
- /**
- * 加密
- * 用私钥加密
- *
- * @param data
- * @param key
- * @return
- * @throws Exception
- */
- public static byte[] encryptByPrivateKey(byte[] data, String key) throws Exception {
- // 对密钥解密
- byte[] keyBytes = decryptBASE64(key);
- // 取得私钥
- PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(keyBytes);
- KeyFactory keyFactory = KeyFactory.getInstance(KEY_ALGORITHM);
- Key privateKey = keyFactory.generatePrivate(pkcs8KeySpec);
- // 对数据加密
- Cipher cipher = Cipher.getInstance(keyFactory.getAlgorithm());
- cipher.init(Cipher.ENCRYPT_MODE, privateKey);
- return cipher.doFinal(data);
- }
-
- /**
- * 取得私钥
- *
- * @param keyMap
- * @return
- * @throws Exception
- */
- public static String getPrivateKey(Map keyMap) throws Exception {
- Key key = (Key) keyMap.get(PRIVATE_KEY);
- return encryptBASE64(key.getEncoded());
- }
-
- /**
- * 取得公钥
- *
- * @param keyMap
- * @return
- * @throws Exception
- */
- public static String getPublicKey(Map keyMap) throws Exception {
- Key key = keyMap.get(PUBLIC_KEY);
- return encryptBASE64(key.getEncoded());
- }
-
- /**
- * 初始化密钥
- *
- * @return
- * @throws Exception
- */
- public static Map initKey() throws Exception {
- KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance(KEY_ALGORITHM);
- keyPairGen.initialize(1024);
- KeyPair keyPair = keyPairGen.generateKeyPair();
- Map keyMap = new HashMap(2);
- keyMap.put(PUBLIC_KEY, keyPair.getPublic());// 公钥
- keyMap.put(PRIVATE_KEY, keyPair.getPrivate());// 私钥
- return keyMap;
- }
-
-}
-```
-
-结果:
-
-```
-{RSAPublicKey=Sun RSA public key, 1024 bits
- modulus: 115328826086047873902606456571034976538836553998745367981848911677968062571831626674499650854318207280419960767020601253071739555161388135589487284843845439403614883967713749605268831336418001722701924537624573180276356615050309809260289965219855862692230362893996010057188170525719351126759886050891484226169
- public exponent: 65537, RSAPrivateKey=sun.security.rsa.RSAPrivateCrtKeyImpl@93479}
------------------------------------
-MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCkO9PBTOFJQTkzznALN62PU7ixd9YFjXrt2dPOGj3wwhymbOU8HLoCztjwpLXHgbpBUJlGmbURV955M1BkZ1kr5dkZYR5x1gO4xOnu8rEipy4AAMcpFttfiarIZrtzL9pKEvEOxABltVN4yzFDr3IjBqY46aHna7YjwhXI0xHieQIDAQAB
------------------------------------
-MIICdQIBADANBgkqhkiG9w0BAQEFAASCAl8wggJbAgEAAoGBAKQ708FM4UlBOTPOcAs3rY9TuLF31gWNeu3Z084aPfDCHKZs5TwcugLO2PCktceBukFQmUaZtRFX3nkzUGRnWSvl2RlhHnHWA7jE6e7ysSKnLgAAxykW21+Jqshmu3Mv2koS8Q7EAGW1U3jLMUOvciMGpjjpoedrtiPCFcjTEeJ5AgMBAAECgYAK4sxOa8IjEOexv2U92Rrv/SSo3sCY7Z/QVDft2V9xrewoO9+V9HF/7iYDDWffKYInAiimvVl7JM/iSLxza0ZFv29VMpyDcr4TigYmWwBlk7ZbxSTkqLdNwxxldMmEoTn1py53MUm+1V1K3rzNvJjuZaZFAevU7vUnwQwD+JGQYQJBAM9HBaC+dF3PJ2mkXekHpDS1ZPaSFdrdzd/GvHFi/cJAMM+Uz6PmpkosNXRtOpSYWwlOMRamLZtrHhfQoqSk3S8CQQDK1qL1jGvVdqw5OjqxktR7MmOsWUVZdWiBN+6ojxBgA0yVn0n7vkdAAgEZBj89WG0VHPEu3hd4AgXFZHDfXeDXAkBvSn7nE9t/Et7ihfI2UHgGJO8UxNMfNMB5Skebyb7eMYEDs67ZHdpjMOFypcMyTatzj5wjwQ3zyMvblZX+ONbZAkAX4ysRy9WvL+icXLUo0Gfhkk+WrnSyUldaUGH0y9Rb2kecn0OxN/lgGlxSvB+ac910zRHCOTl+Uo6nbmq0g3PFAkAyqA4eT7G9GXfncakgW1Kdkn72w/ODpozgfhTLNX0SGw1ITML3c4THTtH5h3zLi3AF9zJO2O+K6ajRbV0szHHI
------------------------------------
-[B@387c703b
------------------------------------
-[B@224aed64
------------------------------------
-la4Hc4n/UbeBu0z9iLRuwKVv014SiOJMXkO5qdJvKBsw0MlnsrM+89a3p73yMrb1dAnCU/2kgO0PtFpvmG8pzxTe1u/5nX/25iIyUXALlwVRptJyjzFE83g2IX0XEv/Dxqr1RCRcrMHOLQM0oBoxZCaChmyw1Ub4wsSs6Ndxb9M=
------------------------------------
-true
------------------------------------
-[B@c39f790
------------------------------------
-[B@71e7a66b
-
-```
-
diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\347\256\227\346\263\225.md" "b/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\347\256\227\346\263\225.md"
deleted file mode 100644
index ec53e7002f9..00000000000
--- "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\347\256\227\346\263\225.md"
+++ /dev/null
@@ -1,90 +0,0 @@
-
-## LeetCode
-[LeetCode(中国)官网](https://leetcode-cn.com/)
-
-[如何高效地使用 LeetCode](https://leetcode-cn.com/articles/%E5%A6%82%E4%BD%95%E9%AB%98%E6%95%88%E5%9C%B0%E4%BD%BF%E7%94%A8-leetcode/)
-
-
-## 牛客网:
-
-[牛客网首页](https://www.nowcoder.com)
-
-
-> ### **[剑指offer编程题](https://www.nowcoder.com/ta/coding-interviews)**
-
-**分类解析:**
-- [(1)斐波那契数列问题和跳台阶问题](https://github.com/Snailclimb/Java-Guide/tree/master/数据结构与算法/算法题解析/剑指offer/(1)斐波那契数列问题和跳台阶问题.md)
-- [(2)二维数组查找和替换空格问题](https://github.com/Snailclimb/Java-Guide/tree/master/数据结构与算法/算法题解析/剑指offer/(2)二维数组查找和替换空格问题.md)
-- [(3)数值的整数次方和调整数组元素顺序](https://github.com/Snailclimb/Java-Guide/tree/master/数据结构与算法/算法题解析/剑指offer/(3)数值的整数次方和调整数组元素顺序.md)
-- [(4)链表相关编程题](https://github.com/Snailclimb/Java-Guide/tree/master/数据结构与算法/算法题解析/剑指offer/(4)链表相关编程题.md)
-- [(5)栈变队列和栈的压入、弹出序列](https://github.com/Snailclimb/Java-Guide/tree/master/数据结构与算法/算法题解析/剑指offer/(5)栈变队列和栈的压入、弹出序列.md)
-
-> ### [2017校招真题](https://www.nowcoder.com/ta/2017test)
-
-> ### [华为机试题](https://www.nowcoder.com/ta/huawei)
-
-
-## 公司真题
-
-> [ 网易2018校园招聘编程题真题集合](https://www.nowcoder.com/test/6910869/summary)
-
-**解析:**
-- [ 网易2018校招编程题1-3](https://github.com/Snailclimb/Java-Guide/tree/master/数据结构与算法/算法题解析/公司真题/网易2018校招编程题1-3.md)
-
-> [ 网易2018校招内推编程题集合](https://www.nowcoder.com/test/6291726/summary)
-
-> [2017年校招全国统一模拟笔试(第五场)编程题集合](https://www.nowcoder.com/test/5986669/summary)
-
- > [2017年校招全国统一模拟笔试(第四场)编程题集合](https://www.nowcoder.com/test/5507925/summary)
-
-> [2017年校招全国统一模拟笔试(第三场)编程题集合](https://www.nowcoder.com/test/5217106/summary)
-
-> [2017年校招全国统一模拟笔试(第二场)编程题集合](https://www.nowcoder.com/test/4546329/summary)
-
-> [ 2017年校招全国统一模拟笔试(第一场)编程题集合](https://www.nowcoder.com/test/4236887/summary)
-
-
-> [百度2017春招笔试真题编程题集合](https://www.nowcoder.com/test/4998655/summary)
-
-> [网易2017春招笔试真题编程题集合](https://www.nowcoder.com/test/4575457/summary)
-
- > [网易2017秋招编程题集合](https://www.nowcoder.com/test/2811407/summary)
-
- > [网易有道2017内推编程题](https://www.nowcoder.com/test/2385858/summary)
-
- > [ 滴滴出行2017秋招笔试真题-编程题汇总](https://www.nowcoder.com/test/3701760/summary)
-
-> [腾讯2017暑期实习生编程题](https://www.nowcoder.com/test/1725829/summary)
-
- > [今日头条2017客户端工程师实习生笔试题](https://www.nowcoder.com/test/1649301/summary)
-
- > [今日头条2017后端工程师实习生笔试题](https://www.nowcoder.com/test/1649268/summary)
-
-
-
-## 排序算法:
-[图解排序算法(一)之3种简单排序(选择,冒泡,直接插入)](http://www.cnblogs.com/chengxiao/p/6103002.html)
-
-[图解排序算法(二)之希尔排序](https://www.cnblogs.com/chengxiao/p/6104371.html)
-
-[图解排序算法(三)之堆排序](http://www.cnblogs.com/chengxiao/p/6129630.html)
-
-[图解排序算法(四)之归并排序](http://www.cnblogs.com/chengxiao/p/6194356.html)
-
-[图解排序算法(五)之快速排序——三数取中法](http://www.cnblogs.com/chengxiao/p/6262208.html)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\347\256\227\346\263\225\351\242\230\350\247\243\346\236\220/\345\211\221\346\214\207offer/\357\274\2101\357\274\211\346\226\220\346\263\242\351\202\243\345\245\221\346\225\260\345\210\227\351\227\256\351\242\230\345\222\214\350\267\263\345\217\260\351\230\266\351\227\256\351\242\230.md" "b/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\347\256\227\346\263\225\351\242\230\350\247\243\346\236\220/\345\211\221\346\214\207offer/\357\274\2101\357\274\211\346\226\220\346\263\242\351\202\243\345\245\221\346\225\260\345\210\227\351\227\256\351\242\230\345\222\214\350\267\263\345\217\260\351\230\266\351\227\256\351\242\230.md"
deleted file mode 100644
index 87a51d9083e..00000000000
--- "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\347\256\227\346\263\225\351\242\230\350\247\243\346\236\220/\345\211\221\346\214\207offer/\357\274\2101\357\274\211\346\226\220\346\263\242\351\202\243\345\245\221\346\225\260\345\210\227\351\227\256\351\242\230\345\222\214\350\267\263\345\217\260\351\230\266\351\227\256\351\242\230.md"
+++ /dev/null
@@ -1,127 +0,0 @@
-### 一 斐波那契数列
-#### **题目描述:**
-大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项。
-n<=39
-
-#### **问题分析:**
-可以肯定的是这一题通过递归的方式是肯定能做出来,但是这样会有一个很大的问题,那就是递归大量的重复计算会导致内存溢出。另外可以使用迭代法,用fn1和fn2保存计算过程中的结果,并复用起来。下面我会把两个方法示例代码都给出来并给出两个方法的运行时间对比。
-
-#### **示例代码:**
-**采用迭代法:**
-
-```java
-
- int Fibonacci(int number) {
- if (number <= 0) {
- return 0;
- }
- if (number == 1 || number == 2) {
- return 1;
- }
- int first = 1, second = 1, third = 0;
- for (int i = 3; i <= number; i++) {
- third = first + second;
- first = second;
- second = third;
- }
- return third;
- }
-```
-**采用递归:**
-```java
- public int Fibonacci(int n) {
-
- if (n <= 0) {
- return 0;
- }
- if (n == 1||n==2) {
- return 1;
- }
-
- return Fibonacci(n - 2) + Fibonacci(n - 1);
-
- }
-```
-
-#### **运行时间对比:**
-假设n为40我们分别使用迭代法和递归法计算,计算结果如下:
-1. 迭代法
-
-2. 递归法
- 
-
-
-### 二 跳台阶问题
-#### **题目描述:**
-一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
-#### **问题分析:**
-**正常分析法:**
-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
-**找规律分析法:**
-f(1) = 1, f(2) = 2, f(3) = 3, f(4) = 5, 可以总结出f(n) = f(n-1) + f(n-2)的规律。
-但是为什么会出现这样的规律呢?假设现在6个台阶,我们可以从第5跳一步到6,这样的话有多少种方案跳到5就有多少种方案跳到6,另外我们也可以从4跳两步跳到6,跳到4有多少种方案的话,就有多少种方案跳到6,其他的不能从3跳到6什么的啦,所以最后就是f(6) = f(5) + f(4);这样子也很好理解变态跳台阶的问题了。
-
-**所以这道题其实就是斐波那契数列的问题。**
-代码只需要在上一题的代码稍做修改即可。和上一题唯一不同的就是这一题的初始元素变为 1 2 3 5 8.....而上一题为1 1 2 3 5 .......。另外这一题也可以用递归做,但是递归效率太低,所以我这里只给出了迭代方式的代码。
-#### **示例代码:**
-```java
-
- int jumpFloor(int number) {
- if (number <= 0) {
- return 0;
- }
- if (number == 1) {
- return 1;
- }
- if (number == 2) {
- return 2;
- }
- int first = 1, second = 2, third = 0;
- for (int i = 3; i <= number; i++) {
- third = first + second;
- first = second;
- second = third;
- }
- return third;
- }
-```
-
-
-### 三 变态跳台阶问题
-#### **题目描述:**
-一只青蛙一次可以跳上1级台阶,也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。
-
-#### **问题分析:**
-假设n>=2,第一步有n种跳法:跳1级、跳2级、到跳n级
-跳1级,剩下n-1级,则剩下跳法是f(n-1)
-跳2级,剩下n-2级,则剩下跳法是f(n-2)
-......
-跳n-1级,剩下1级,则剩下跳法是f(1)
-跳n级,剩下0级,则剩下跳法是f(0)
-所以在n>=2的情况下:
-f(n)=f(n-1)+f(n-2)+...+f(1)
-因为f(n-1)=f(n-2)+f(n-3)+...+f(1)
-所以f(n)=2*f(n-1) 又f(1)=1,所以可得**f(n)=2^(number-1)**
-
-#### **示例代码:**
-
-```java
- int JumpFloorII(int number) {
- return 1 << --number;//2^(number-1)用位移操作进行,更快
- }
-```
-#### **补充:**
-**java中有三种移位运算符:**
-
-1. “<<” : **左移运算符**,等同于乘2的n次方
-2. “>>”: **右移运算符**,等同于除2的n次方
-3. “>>>” **无符号右移运算符**,不管移动前最高位是0还是1,右移后左侧产生的空位部分都以0来填充。与>>类似。
-例:
- int a = 16;
- int b = a << 2;//左移2,等同于16 * 2的2次方,也就是16 * 4
- int c = a >> 2;//右移2,等同于16 / 2的2次方,也就是16 / 4
-
-
diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\347\256\227\346\263\225\351\242\230\350\247\243\346\236\220/\345\211\221\346\214\207offer/\357\274\2102\357\274\211\344\272\214\347\273\264\346\225\260\347\273\204\346\237\245\346\211\276\345\222\214\346\233\277\346\215\242\347\251\272\346\240\274\351\227\256\351\242\230.md" "b/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\347\256\227\346\263\225\351\242\230\350\247\243\346\236\220/\345\211\221\346\214\207offer/\357\274\2102\357\274\211\344\272\214\347\273\264\346\225\260\347\273\204\346\237\245\346\211\276\345\222\214\346\233\277\346\215\242\347\251\272\346\240\274\351\227\256\351\242\230.md"
deleted file mode 100644
index 964ed6c9892..00000000000
--- "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\347\256\227\346\263\225\351\242\230\350\247\243\346\236\220/\345\211\221\346\214\207offer/\357\274\2102\357\274\211\344\272\214\347\273\264\346\225\260\347\273\204\346\237\245\346\211\276\345\222\214\346\233\277\346\215\242\347\251\272\346\240\274\351\227\256\351\242\230.md"
+++ /dev/null
@@ -1,64 +0,0 @@
-### 一 二维数组查找
-#### **题目描述:**
-在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
-#### **问题解析:**
-这一道题还是比较简单的,我们需要考虑的是如何做,效率最快。这里有一种很好理解的思路:
-
-> 矩阵是有序的,从左下角来看,向上数字递减,向右数字递增,
- 因此从左下角开始查找,当要查找数字比左下角数字大时。右移
- 要查找数字比左下角数字小时,上移。这样找的速度最快。
-
-#### **示例代码:**
-```java
- public boolean Find(int target, int [][] array) {
- //基本思路从左下角开始找,这样速度最快
- int row = array.length-1;//行
- int column = 0;//列
- //当行数大于0,当前列数小于总列数时循环条件成立
- while((row >= 0)&& (column< array[0].length)){
- if(array[row][column] > target){
- row--;
- }else if(array[row][column] < target){
- column++;
- }else{
- return true;
- }
- }
- return false;
- }
-```
-### 二 替换空格
-#### **题目描述:**
-请实现一个函数,将一个字符串中的空格替换成“%20”。例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy。
-#### **问题分析:**
-这道题不难,我们可以通过循环判断字符串的字符是否为空格,是的话就利用append()方法添加追加“%20”,否则还是追加原字符。
-
-或者最简单的方法就是利用: replaceAll(String regex,String replacement)方法了,一行代码就可以解决。
-
-#### **示例代码:**
-**常规做法:**
-```java
- public String replaceSpace(StringBuffer str) {
- StringBuffer out=new StringBuffer();
- for (int i = 0; i < str.toString().length(); i++) {
- char b=str.charAt(i);
- if(String.valueOf(b).equals(" ")){
- out.append("%20");
- }else{
- out.append(b);
- }
- }
- return out.toString();
- }
-```
-**一行代码解决:**
-```java
- public String replaceSpace(StringBuffer str) {
- //return str.toString().replaceAll(" ", "%20");
- //public String replaceAll(String regex,String replacement)
- //用给定的替换替换与给定的regular expression匹配的此字符串的每个子字符串。
- //\ 转义字符. 如果你要使用 "\" 本身, 则应该使用 "\\". String类型中的空格用“\s”表示,所以我这里猜测"\\s"就是代表空格的意思
- return str.toString().replaceAll("\\s", "%20");
- }
-
-```
diff --git "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\347\256\227\346\263\225\351\242\230\350\247\243\346\236\220/\345\211\221\346\214\207offer/\357\274\2103\357\274\211\346\225\260\345\200\274\347\232\204\346\225\264\346\225\260\346\254\241\346\226\271\345\222\214\350\260\203\346\225\264\346\225\260\347\273\204\345\205\203\347\264\240\351\241\272\345\272\217.md" "b/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\347\256\227\346\263\225\351\242\230\350\247\243\346\236\220/\345\211\221\346\214\207offer/\357\274\2103\357\274\211\346\225\260\345\200\274\347\232\204\346\225\264\346\225\260\346\254\241\346\226\271\345\222\214\350\260\203\346\225\264\346\225\260\347\273\204\345\205\203\347\264\240\351\241\272\345\272\217.md"
deleted file mode 100644
index 291569c0375..00000000000
--- "a/\346\225\260\346\215\256\347\273\223\346\236\204\344\270\216\347\256\227\346\263\225/\347\256\227\346\263\225\351\242\230\350\247\243\346\236\220/\345\211\221\346\214\207offer/\357\274\2103\357\274\211\346\225\260\345\200\274\347\232\204\346\225\264\346\225\260\346\254\241\346\226\271\345\222\214\350\260\203\346\225\264\346\225\260\347\273\204\345\205\203\347\264\240\351\241\272\345\272\217.md"
+++ /dev/null
@@ -1,113 +0,0 @@
-### 一 数值的整数次方
-#### **题目描述:**
-给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。
-#### **问题解析:**
-这道题算是比较麻烦和难一点的一个了。我这里采用的是**二分幂**思想,当然也可以采用**快速幂**。
-更具剑指offer书中细节,该题的解题思路如下:
-1.当底数为0且指数<0时,会出现对0求倒数的情况,需进行错误处理,设置一个全局变量;
-2.判断底数是否等于0,由于base为double型,所以不能直接用==判断
-3.优化求幂函数(二分幂)。
-当n为偶数,a^n =(a^n/2)*(a^n/2);
-当n为奇数,a^n = a^[(n-1)/2] * a^[(n-1)/2] * a。时间复杂度O(logn)
-
-**时间复杂度**:O(logn)
-#### **示例代码:**
-```java
-public class Solution {
- boolean invalidInput=false;
- public double Power(double base, int exponent) {
- //如果底数等于0并且指数小于0
- //由于base为double型,不能直接用==判断
- if(equal(base,0.0)&&exponent<0){
- invalidInput=true;
- return 0.0;
- }
- int absexponent=exponent;
- //如果指数小于0,将指数转正
- if(exponent<0)
- absexponent=-exponent;
- //getPower方法求出base的exponent次方。
- double res=getPower(base,absexponent);
- //如果指数小于0,所得结果为上面求的结果的倒数
- if(exponent<0)
- res=1.0/res;
- return res;
- }
- //比较两个double型变量是否相等的方法
- boolean equal(double num1,double num2){
- if(num1-num2>-0.000001&&num1-num2<0.000001)
- return true;
- else
- return false;
- }
- //求出b的e次方的方法
- double getPower(double b,int e){
- //如果指数为0,返回1
- if(e==0)
- return 1.0;
- //如果指数为1,返回b
- if(e==1)
- return b;
- //e>>1相等于e/2,这里就是求a^n =(a^n/2)*(a^n/2)
- double result=getPower(b,e>>1);
- result*=result;
- //如果指数n为奇数,则要再乘一次底数base
- if((e&1)==1)
- result*=b;
- return result;
- }
-}
-```
-
-当然这一题也可以采用笨方法:累乘。不过这种方法的时间复杂度为O(n),这样没有前一种方法效率高。
-```java
- // 使用累乘
- public double powerAnother(double base, int exponent) {
- double result = 1.0;
- for (int i = 0; i < Math.abs(exponent); i++) {
- result *= base;
- }
- if (exponent >= 0)
- return result;
- else
- return 1 / result;
- }
-```
-### 二 调整数组顺序使奇数位于偶数前面
-#### **题目描述:**
-输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变。
-
-#### **问题解析:**
-这道题有挺多种解法的,给大家介绍一种我觉得挺好理解的方法:
-我们首先统计奇数的个数假设为n,然后新建一个等长数组,然后通过循环判断原数组中的元素为偶数还是奇数。如果是则从数组下标0的元素开始,把该奇数添加到新数组;如果是偶数则从数组下标为n的元素开始把该偶数添加到新数组中。
-
-#### **示例代码:**
-时间复杂度为O(n),空间复杂度为O(n)的算法
-```java
-public class Solution {
- public void reOrderArray(int [] array) {
- //如果数组长度等于0或者等于1,什么都不做直接返回
- if(array.length==0||array.length==1)
- return;
- //oddCount:保存奇数个数
- //oddBegin:奇数从数组头部开始添加
- int oddCount=0,oddBegin=0;
- //新建一个数组
- int[] newArray=new int[array.length];
- //计算出(数组中的奇数个数)开始添加元素
- for(int i=0;i stack1 = new Stack();
- Stack stack2 = new Stack();
-
- //当执行push操作时,将元素添加到stack1
- public void push(int node) {
- stack1.push(node);
- }
-
- public int pop() {
- //如果两个队列都为空则抛出异常,说明用户没有push进任何元素
- if(stack1.empty()&&stack2.empty()){
- throw new RuntimeException("Queue is empty!");
- }
- //如果stack2不为空直接对stack2执行pop操作,
- if(stack2.empty()){
- while(!stack1.empty()){
- //将stack1的元素按后进先出push进stack2里面
- stack2.push(stack1.pop());
- }
- }
- return stack2.pop();
- }
-}
-```
-
-### 二 栈的压入、弹出序列
-#### **题目描述:**
-输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。(注意:这两个序列的长度是相等的)
-#### **题目分析:**
-这道题想了半天没有思路,参考了Alias的答案,他的思路写的也很详细应该很容易看懂。
-作者:Alias
-https://www.nowcoder.com/questionTerminal/d77d11405cc7470d82554cb392585106
-来源:牛客网
-
-【思路】借用一个辅助的栈,遍历压栈顺序,先讲第一个放入栈中,这里是1,然后判断栈顶元素是不是出栈顺序的第一个元素,这里是4,很显然1≠4,所以我们继续压栈,直到相等以后开始出栈,出栈一个元素,则将出栈顺序向后移动一位,直到不相等,这样循环等压栈顺序遍历完成,如果辅助栈还不为空,说明弹出序列不是该栈的弹出顺序。
-
-举例:
-
-入栈1,2,3,4,5
-
-出栈4,5,3,2,1
-
-首先1入辅助栈,此时栈顶1≠4,继续入栈2
-
-此时栈顶2≠4,继续入栈3
-
-此时栈顶3≠4,继续入栈4
-
-此时栈顶4=4,出栈4,弹出序列向后一位,此时为5,,辅助栈里面是1,2,3
-
-此时栈顶3≠5,继续入栈5
-
-此时栈顶5=5,出栈5,弹出序列向后一位,此时为3,,辅助栈里面是1,2,3
-
-….
-依次执行,最后辅助栈为空。如果不为空说明弹出序列不是该栈的弹出顺序。
-
-
-
-#### **考察内容:**
-栈
-
-#### **示例代码:**
-```java
-import java.util.ArrayList;
-import java.util.Stack;
-//这道题没想出来,参考了Alias同学的答案:https://www.nowcoder.com/questionTerminal/d77d11405cc7470d82554cb392585106
-public class Solution {
- public boolean IsPopOrder(int [] pushA,int [] popA) {
- if(pushA.length == 0 || popA.length == 0)
- return false;
- Stack s = new Stack();
- //用于标识弹出序列的位置
- int popIndex = 0;
- for(int i = 0; i< pushA.length;i++){
- s.push(pushA[i]);
- //如果栈不为空,且栈顶元素等于弹出序列
- while(!s.empty() &&s.peek() == popA[popIndex]){
- //出栈
- s.pop();
- //弹出序列向后一位
- popIndex++;
- }
- }
- return s.empty();
- }
-}
-```
\ No newline at end of file
diff --git "a/\351\235\242\350\257\225\345\277\205\345\244\207/\347\276\216\345\233\242-\345\237\272\347\241\200\347\257\207.md" "b/\351\235\242\350\257\225\345\277\205\345\244\207/\347\276\216\345\233\242-\345\237\272\347\241\200\347\257\207.md"
deleted file mode 100644
index 879f24c69fa..00000000000
--- "a/\351\235\242\350\257\225\345\277\205\345\244\207/\347\276\216\345\233\242-\345\237\272\347\241\200\347\257\207.md"
+++ /dev/null
@@ -1,357 +0,0 @@
-
-
-
-- [1. `System.out.println(3 | 9);`输出什么?](#1-systemoutprintln3-|-9输出什么)
-- [2. 说一下转发\(Forward\)和重定向\(Redirect\)的区别](#2-说一下转发forward和重定向redirect的区别)
-- [3. 在浏览器中输入url地址到显示主页的过程,整个过程会使用哪些协议](#3-在浏览器中输入url地址到显示主页的过程整个过程会使用哪些协议)
-- [4. TCP 三次握手和四次挥手](#4-tcp-三次握手和四次挥手)
- - [为什么要三次握手](#为什么要三次握手)
- - [为什么要传回 SYN](#为什么要传回-syn)
- - [传了 SYN,为啥还要传 ACK](#传了-syn为啥还要传-ack)
- - [为什么要四次挥手](#为什么要四次挥手)
-- [5. IP地址与MAC地址的区别](#5-ip地址与mac地址的区别)
-- [6. HTTP请求、响应报文格式](#6-http请求响应报文格式)
-- [7. 为什么要使用索引?索引这么多优点,为什么不对表中的每一个列创建一个索引呢?索引是如何提高查询速度的?说一下使用索引的注意事项?Mysql索引主要使用的两种数据结构?什么是覆盖索引?](#7-为什么要使用索引索引这么多优点为什么不对表中的每一个列创建一个索引呢索引是如何提高查询速度的说一下使用索引的注意事项mysql索引主要使用的两种数据结构什么是覆盖索引)
-- [8. 进程与线程的区别是什么?进程间的几种通信方式说一下?线程间的几种通信方式知道不?](#8-进程与线程的区别是什么进程间的几种通信方式说一下线程间的几种通信方式知道不)
-- [9. 为什么要用单例模式?手写几种线程安全的单例模式?](#9-为什么要用单例模式手写几种线程安全的单例模式)
-- [10. 简单介绍一下bean。知道Spring的bean的作用域与生命周期吗?](#10-简单介绍一下bean知道spring的bean的作用域与生命周期吗)
-- [11. Spring 中的事务传播行为了解吗?TransactionDefinition 接口中哪五个表示隔离级别的常量?](#11-spring-中的事务传播行为了解吗transactiondefinition-接口中哪五个表示隔离级别的常量)
- - [事务传播行为](#事务传播行为)
- - [隔离级别](#隔离级别)
-- [12. SpringMVC 原理了解吗?](#12-springmvc-原理了解吗)
-- [13. Spring AOP IOC 实现原理](#13-spring-aop-ioc-实现原理)
-
-
-
-
-
-
-**系列文章:**
-
-- [【备战春招/秋招系列1】程序员的简历就该这样写](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484573&idx=1&sn=8c5965d4a3710d405d8e8cc10c7b0ce5&chksm=fd9852fccaefdbea8dfe0bc40188b7579f1cddb1e8905dc981669a3f21d2a04cadceafa9023f&token=1990180468&lang=zh_CN#rd)
-- [【备战春招/秋招系列2】初出茅庐的程序员该如何准备面试?](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484578&idx=1&sn=eea72d80a2325257f00aaed21d5b226f&chksm=fd9852c3caefdbd52dd8a537cc723ed1509314401b3a669a253ef5bc0360b6fddef48b9c2e94&token=1990180468&lang=zh_CN#rd)
-- [【备战春招/秋招系列3】Java程序员必备书单](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484592&idx=1&sn=6d9731ce7401be49e97c1af6ed384ecc&chksm=fd9852d1caefdbc720a361ae65a8ad9d53cfb4800b15a7c68cbdc630b313215c6c52e0934ec2&token=1990180468&lang=zh_CN#rd)
-
-这是我总结的美团面经的基础篇,后面还有进阶和终结篇哦!下面只是我从很多份美团面经中总结的在面试中一些常见的问题。不同于个人面经,这份面经具有普适性。每次面试必备的自我介绍、项目介绍这些东西,大家可以自己私下好好思考。我在前面的文章中也提到了应该怎么做自我介绍与项目介绍,详情可以查看这篇文章:[【备战春招/秋招系列2】初出茅庐的程序员该如何准备面试?](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484578&idx=1&sn=eea72d80a2325257f00aaed21d5b226f&chksm=fd9852c3caefdbd52dd8a537cc723ed1509314401b3a669a253ef5bc0360b6fddef48b9c2e94&token=1990180468&lang=zh_CN#rd)。
-
-
-### 1. `System.out.println(3 | 9);`输出什么?
-
-正确答案:11.
-
-**考察知识点:&和&&;|和||**
-
-**&和&&:**
-
-共同点:两者都可做逻辑运算符。它们都表示运算符的两边都是true时,结果为true;
-
-不同点: &也是位运算符。& 表示在运算时两边都会计算,然后再判断;&&表示先运算符号左边的东西,然后判断是否为true,是true就继续运算右边的然后判断并输出,是false就停下来直接输出不会再运行后面的东西。
-
-**|和||:**
-
-共同点:两者都可做逻辑运算符。它们都表示运算符的两边任意一边为true,结果为true,两边都不是true,结果就为false;
-
-不同点:|也是位运算符。| 表示两边都会运算,然后再判断结果;|| 表示先运算符号左边的东西,然后判断是否为true,是true就停下来直接输出不会再运行后面的东西,是false就继续运算右边的然后判断并输出。
-
-**回到本题:**
-
-3 | 9=0011(二进制) | 1001(二进制)=1011(二进制)=11(十进制)
-
-### 2. 说一下转发(Forward)和重定向(Redirect)的区别
-
-**转发是服务器行为,重定向是客户端行为。**
-
-**转发(Forword)** 通过RequestDispatcher对象的`forward(HttpServletRequest request,HttpServletResponse response)`方法实现的。`RequestDispatcher` 可以通过`HttpServletRequest` 的 `getRequestDispatcher()`方法获得。例如下面的代码就是跳转到 login_success.jsp 页面。
-
-```java
-request.getRequestDispatcher("login_success.jsp").forward(request, response);
-```
-
-**重定向(Redirect)** 是利用服务器返回的状态吗来实现的。客户端浏览器请求服务器的时候,服务器会返回一个状态码。服务器通过HttpServletRequestResponse的setStatus(int status)方法设置状态码。如果服务器返回301或者302,则浏览器会到新的网址重新请求该资源。
-
-1. **从地址栏显示来说:** forward是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器.浏览器根本不知道服务器发送的内容从哪里来的,所以它的地址栏还是原来的地址. redirect是服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址.所以地址栏显示的是新的URL.
-2. **从数据共享来说:** forward:转发页面和转发到的页面可以共享request里面的数据. redirect:不能共享数据.
-3. **从运用地方来说:** forward:一般用于用户登陆的时候,根据角色转发到相应的模块. redirect:一般用于用户注销登陆时返回主页面和跳转到其它的网站等
-4. **从效率来说:** forward:高. redirect:低.
-
-
-### 3. 在浏览器中输入url地址到显示主页的过程,整个过程会使用哪些协议
-
-图解(图片来源:《图解HTTP》):
-
-
-
-总体来说分为以下几个过程:
-
-1. DNS解析
-2. TCP连接
-3. 发送HTTP请求
-4. 服务器处理请求并返回HTTP报文
-5. 浏览器解析渲染页面
-6. 连接结束
-
-具体可以参考下面这篇文章:
-
-- [https://segmentfault.com/a/1190000006879700](https://segmentfault.com/a/1190000006879700)
-
-### 4. TCP 三次握手和四次挥手
-
-为了准确无误地把数据送达目标处,TCP协议采用了三次握手策略。
-
-**漫画图解:**
-
-图片来源:《图解HTTP》
-
-
-**简单示意图:**
-
-
-- 客户端–发送带有 SYN 标志的数据包–一次握手–服务端
-- 服务端–发送带有 SYN/ACK 标志的数据包–二次握手–客户端
-- 客户端–发送带有带有 ACK 标志的数据包–三次握手–服务端
-
-#### 为什么要三次握手
-
-**三次握手的目的是建立可靠的通信信道,说到通讯,简单来说就是数据的发送与接收,而三次握手最主要的目的就是双方确认自己与对方的发送与接收是正常的。**
-
-第一次握手:Client 什么都不能确认;Server 确认了对方发送正常,自己接收正常。
-
-第二次握手:Client 确认了:自己发送、接收正常,对方发送、接收正常;Server 确认了:自己接收正常,对方发送正常
-
-第三次握手:Client 确认了:自己发送、接收正常,对方发送、接收正常;Server 确认了:自己发送、接收正常,对方发送接收正常
-
-所以三次握手就能确认双发收发功能都正常,缺一不可。
-
-#### 为什么要传回 SYN
-接收端传回发送端所发送的 SYN 是为了告诉发送端,我接收到的信息确实就是你所发送的信号了。
-
-> SYN 是 TCP/IP 建立连接时使用的握手信号。在客户机和服务器之间建立正常的 TCP 网络连接时,客户机首先发出一个 SYN 消息,服务器使用 SYN-ACK 应答表示接收到了这个消息,最后客户机再以 ACK(Acknowledgement[汉译:确认字符 ,在数据通信传输中,接收站发给发送站的一种传输控制字符。它表示确认发来的数据已经接受无误。 ])消息响应。这样在客户机和服务器之间才能建立起可靠的TCP连接,数据才可以在客户机和服务器之间传递。
-
-
-#### 传了 SYN,为啥还要传 ACK
-
-双方通信无误必须是两者互相发送信息都无误。传了 SYN,证明发送方(主动关闭方)到接收方(被动关闭方)的通道没有问题,但是接收方到发送方的通道还需要 ACK 信号来进行验证。
-
-
-
-断开一个 TCP 连接则需要“四次挥手”:
-
-- 客户端-发送一个 FIN,用来关闭客户端到服务器的数据传送
-- 服务器-收到这个 FIN,它发回一 个 ACK,确认序号为收到的序号加1 。和 SYN 一样,一个 FIN 将占用一个序号
-- 服务器-关闭与客户端的连接,发送一个FIN给客户端
-- 客户端-发回 ACK 报文确认,并将确认序号设置为收到序号加1
-
-
-#### 为什么要四次挥手
-
-任何一方都可以在数据传送结束后发出连接释放的通知,待对方确认后进入半关闭状态。当另一方也没有数据再发送的时候,则发出连接释放通知,对方确认后就完全关闭了TCP连接。
-
-举个例子:A 和 B 打电话,通话即将结束后,A 说“我没啥要说的了”,B回答“我知道了”,但是 B 可能还会有要说的话,A 不能要求 B 跟着自己的节奏结束通话,于是 B 可能又巴拉巴拉说了一通,最后 B 说“我说完了”,A 回答“知道了”,这样通话才算结束。
-
-上面讲的比较概括,推荐一篇讲的比较细致的文章:[https://blog.csdn.net/qzcsu/article/details/72861891](https://blog.csdn.net/qzcsu/article/details/72861891)
-
-
-
-### 5. IP地址与MAC地址的区别
-
-参考:[https://blog.csdn.net/guoweimelon/article/details/50858597](https://blog.csdn.net/guoweimelon/article/details/50858597)
-
-IP地址是指互联网协议地址(Internet Protocol Address)IP Address的缩写。IP地址是IP协议提供的一种统一的地址格式,它为互联网上的每一个网络和每一台主机分配一个逻辑地址,以此来屏蔽物理地址的差异。
-
-
-
-MAC 地址又称为物理地址、硬件地址,用来定义网络设备的位置。网卡的物理地址通常是由网卡生产厂家写入网卡的,具有全球唯一性。MAC地址用于在网络中唯一标示一个网卡,一台电脑会有一或多个网卡,每个网卡都需要有一个唯一的MAC地址。
-
-### 6. HTTP请求,响应报文格式
-
-
-
-HTTP请求报文主要由请求行、请求头部、请求正文3部分组成
-
-HTTP响应报文主要由状态行、响应头部、响应正文3部分组成
-
-详细内容可以参考:[https://blog.csdn.net/a19881029/article/details/14002273](https://blog.csdn.net/a19881029/article/details/14002273)
-
-### 7. 为什么要使用索引?索引这么多优点,为什么不对表中的每一个列创建一个索引呢?索引是如何提高查询速度的?说一下使用索引的注意事项?Mysql索引主要使用的两种数据结构?什么是覆盖索引?
-
-**为什么要使用索引?**
-
-1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。
-2. 可以大大加快 数据的检索速度(大大减少的检索的数据量), 这也是创建索引的最主要的原因。
-3. 帮助服务器避免排序和临时表
-4. 将随机IO变为顺序IO
-5. 可以加速表和表之间的连接,特别是在实现数据的参考完整性方面特别有意义。
-
-**索引这么多优点,为什么不对表中的每一个列创建一个索引呢?**
-
-1. 当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,这样就降低了数据的维护速度。
-2. 索引需要占物理空间,除了数据表占数据空间之外,每一个索引还要占一定的物理空间,如果要建立聚簇索引,那么需要的空间就会更大。
-3. 创建索引和维护索引要耗费时间,这种时间随着数据量的增加而增加。
-
-**索引是如何提高查询速度的?**
-
-将无序的数据变成相对有序的数据(就像查目录一样)
-
-**说一下使用索引的注意事项**
-
-1. 避免 where 子句中对字段施加函数,这会造成无法命中索引。
-2. 在使用InnoDB时使用与业务无关的自增主键作为主键,即使用逻辑主键,而不要使用业务主键。
-3. 将打算加索引的列设置为 NOT NULL ,否则将导致引擎放弃使用索引而进行全表扫描
-4. 删除长期未使用的索引,不用的索引的存在会造成不必要的性能损耗 MySQL 5.7 可以通过查询 sys 库的 chema_unused_indexes 视图来查询哪些索引从未被使用
-5. 在使用 limit offset 查询缓慢时,可以借助索引来提高性能
-
-**Mysql索引主要使用的哪两种数据结构?**
-
-- 哈希索引:对于哈希索引来说,底层的数据结构就是哈希表,因此在绝大多数需求为单条记录查询的时候,可以选择哈希索引,查询性能最快;其余大部分场景,建议选择BTree索引。
-- BTree索引:Mysql的BTree索引使用的是B树中的B+Tree。但对于主要的两种存储引擎(MyISAM和InnoDB)的实现方式是不同的。
-
-更多关于索引的内容可以查看我的这篇文章:[【思维导图-索引篇】搞定数据库索引就是这么简单](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484486&idx=1&sn=215450f11e042bca8a58eac9f4a97686&chksm=fd985227caefdb3117b8375f150676f5824aa20d1ebfdbcfb93ff06e23e26efbafae6cf6b48e&token=1990180468&lang=zh_CN#rd)
-
-**什么是覆盖索引?**
-
-如果一个索引包含(或者说覆盖)所有需要查询的字段的值,我们就称
-之为“覆盖索引”。我们知道在InnoDB存储引擎中,如果不是主键索引,叶子节点存储的是主键+列值。最终还是要“回表”,也就是要通过主键再查找一次,这样就会比较慢。覆盖索引就是把要查询出的列和索引是对应的,不做回表操作!
-
-
-### 8. 进程与线程的区别是什么?进程间的几种通信方式说一下?线程间的几种通信方式知道不?
- **进程与线程的区别是什么?**
-
-线程与进程相似,但线程是一个比进程更小的执行单位。一个进程在其执行的过程中可以产生多个线程。与进程不同的是同类的多个线程共享同一块内存空间和一组系统资源,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程。另外,也正是因为共享资源,所以线程中执行时一般都要进行同步和互斥。总的来说,进程和线程的主要差别在于它们是不同的操作系统资源管理方式。
-
-**进程间的几种通信方式说一下?**
-
-
-1. **管道(pipe)**:管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有血缘关系的进程间使用。进程的血缘关系通常指父子进程关系。管道分为pipe(无名管道)和fifo(命名管道)两种,有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间通信。
-2. **信号量(semophore)**:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它通常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
-4. **消息队列(message queue)**:消息队列是由消息组成的链表,存放在内核中 并由消息队列标识符标识。消息队列克服了信号传递信息少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。消息队列与管道通信相比,其优势是对每个消息指定特定的消息类型,接收的时候不需要按照队列次序,而是可以根据自定义条件接收特定类型的消息。
-5. **信号(signal)**:信号是一种比较复杂的通信方式,用于通知接收进程某一事件已经发生。
-6. **共享内存(shared memory)**:共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问,共享内存是最快的IPC方式,它是针对其他进程间的通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量配合使用,来实现进程间的同步和通信。
-7. **套接字(socket)**:socket,即套接字是一种通信机制,凭借这种机制,客户/服务器(即要进行通信的进程)系统的开发工作既可以在本地单机上进行,也可以跨网络进行。也就是说它可以让不在同一台计算机但通过网络连接计算机上的进程进行通信。也因为这样,套接字明确地将客户端和服务器区分开来。
-
-**线程间的几种通信方式知道不?**
-
-1、锁机制
-
-- 互斥锁:提供了以排它方式阻止数据结构被并发修改的方法。
-- 读写锁:允许多个线程同时读共享数据,而对写操作互斥。
-- 条件变量:可以以原子的方式阻塞进程,直到某个特定条件为真为止。对条件测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一起使用。
-
-2、信号量机制:包括无名线程信号量与有名线程信号量
-
-3、信号机制:类似于进程间的信号处理。
-
-线程间通信的主要目的是用于线程同步,所以线程没有象进程通信中用于数据交换的通信机制。
-
-### 9. 为什么要用单例模式?手写几种线程安全的单例模式?
-
-**简单来说使用单例模式可以带来下面几个好处:**
-
-- 对于频繁使用的对象,可以省略创建对象所花费的时间,这对于那些重量级对象而言,是非常可观的一笔系统开销;
-- 由于 new 操作的次数减少,因而对系统内存的使用频率也会降低,这将减轻 GC 压力,缩短 GC 停顿时间。
-
-**懒汉式(双重检查加锁版本)**
-
-```java
-public class Singleton {
-
- //volatile保证,当uniqueInstance变量被初始化成Singleton实例时,多个线程可以正确处理uniqueInstance变量
- private volatile static Singleton uniqueInstance;
- private Singleton() {
- }
- public static Singleton getInstance() {
- //检查实例,如果不存在,就进入同步代码块
- if (uniqueInstance == null) {
- //只有第一次才彻底执行这里的代码
- synchronized(Singleton.class) {
- //进入同步代码块后,再检查一次,如果仍是null,才创建实例
- if (uniqueInstance == null) {
- uniqueInstance = new Singleton();
- }
- }
- }
- return uniqueInstance;
- }
-}
-```
-
-**静态内部类方式**
-
-静态内部实现的单例是懒加载的且线程安全。
-
-只有通过显式调用 getInstance 方法时,才会显式装载 SingletonHolder 类,从而实例化 instance(只有第一次使用这个单例的实例的时候才加载,同时不会有线程安全问题)。
-
-```java
-public class Singleton {
- private static class SingletonHolder {
- private static final Singleton INSTANCE = new Singleton();
- }
- private Singleton (){}
- public static final Singleton getInstance() {
- return SingletonHolder.INSTANCE;
- }
-}
-```
-
-### 10. 简单介绍一下bean。知道Spring的bean的作用域与生命周期吗?
-
-在 Spring 中,那些组成应用程序的主体及由 Spring IOC 容器所管理的对象,被称之为 bean。简单地讲,bean 就是由 IOC 容器初始化、装配及管理的对象,除此之外,bean 就与应用程序中的其他对象没有什么区别了。而 bean 的定义以及 bean 相互间的依赖关系将通过配置元数据来描述。
-
-Spring中的bean默认都是单例的,这些单例Bean在多线程程序下如何保证线程安全呢? 例如对于Web应用来说,Web容器对于每个用户请求都创建一个单独的Sevlet线程来处理请求,引入Spring框架之后,每个Action都是单例的,那么对于Spring托管的单例Service Bean,如何保证其安全呢? Spring的单例是基于BeanFactory也就是Spring容器的,单例Bean在此容器内只有一个,Java的单例是基于 JVM,每个 JVM 内只有一个实例。
-
-
-
-Spring的bean的生命周期以及更多内容可以查看:[一文轻松搞懂Spring中bean的作用域与生命周期](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484400&idx=2&sn=7201eb365102fce017f89cb3527fb0bc&chksm=fd985591caefdc872a2fac897288119f94c345e4e12150774f960bf5f816b79e4b9b46be3d7f&token=1990180468&lang=zh_CN#rd)
-
-
-### 11. Spring 中的事务传播行为了解吗?TransactionDefinition 接口中哪五个表示隔离级别的常量?
-
-#### 事务传播行为
-
-事务传播行为(为了解决业务层方法之间互相调用的事务问题):
-当事务方法被另一个事务方法调用时,必须指定事务应该如何传播。例如:方法可能继续在现有事务中运行,也可能开启一个新事务,并在自己的事务中运行。在TransactionDefinition定义中包括了如下几个表示传播行为的常量:
-
-**支持当前事务的情况:**
-
-- TransactionDefinition.PROPAGATION_REQUIRED: 如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。
-- TransactionDefinition.PROPAGATION_SUPPORTS: 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
-- TransactionDefinition.PROPAGATION_MANDATORY: 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)
-
-**不支持当前事务的情况:**
-
-- TransactionDefinition.PROPAGATION_REQUIRES_NEW: 创建一个新的事务,如果当前存在事务,则把当前事务挂起。
-- TransactionDefinition.PROPAGATION_NOT_SUPPORTED: 以非事务方式运行,如果当前存在事务,则把当前事务挂起。
-- TransactionDefinition.PROPAGATION_NEVER: 以非事务方式运行,如果当前存在事务,则抛出异常。
-
-**其他情况:**
-
-- TransactionDefinition.PROPAGATION_NESTED: 如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等价于TransactionDefinition.PROPAGATION_REQUIRED。
-
-
-#### 隔离级别
-
-TransactionDefinition 接口中定义了五个表示隔离级别的常量:
-
-- **TransactionDefinition.ISOLATION_DEFAULT:** 使用后端数据库默认的隔离级别,Mysql 默认采用的 REPEATABLE_READ隔离级别 Oracle 默认采用的 READ_COMMITTED隔离级别.
-- **TransactionDefinition.ISOLATION_READ_UNCOMMITTED:** 最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
-- **TransactionDefinition.ISOLATION_READ_COMMITTED:** 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
-- **TransactionDefinition.ISOLATION_REPEATABLE_READ:** 对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
-- **TransactionDefinition.ISOLATION_SERIALIZABLE:** 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
-
-### 12. SpringMVC 原理了解吗?
-
-
-
-客户端发送请求-> 前端控制器 DispatcherServlet 接受客户端请求 -> 找到处理器映射 HandlerMapping 解析请求对应的 Handler-> HandlerAdapter 会根据 Handler 来调用真正的处理器开处理请求,并处理相应的业务逻辑 -> 处理器返回一个模型视图 ModelAndView -> 视图解析器进行解析 -> 返回一个视图对象->前端控制器 DispatcherServlet 渲染数据(Model)->将得到视图对象返回给用户
-
-关于 SpringMVC 原理更多内容可以查看我的这篇文章:[SpringMVC 工作原理详解](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484496&idx=1&sn=5472ffa687fe4a05f8900d8ee6726de4&chksm=fd985231caefdb27fc75b44ecf76b6f43e4617e0b01b3c040f8b8fab32e51dfa5118eed1d6ad&token=1990180468&lang=zh_CN#rd)
-
-### 13. Spring AOP IOC 实现原理
-
-过了秋招挺长一段时间了,说实话我自己也忘了如何简要概括 Spring AOP IOC 实现原理,就在网上找了一个较为简洁的答案,下面分享给各位。
-
-**IOC:** 控制反转也叫依赖注入。IOC利用java反射机制,AOP利用代理模式。IOC 概念看似很抽象,但是很容易理解。说简单点就是将对象交给容器管理,你只需要在spring配置文件中配置对应的bean以及设置相关的属性,让spring容器来生成类的实例对象以及管理对象。在spring容器启动的时候,spring会把你在配置文件中配置的bean都初始化好,然后在你需要调用的时候,就把它已经初始化好的那些bean分配给你需要调用这些bean的类。
-
-**AOP:** 面向切面编程。(Aspect-Oriented Programming) 。AOP可以说是对OOP的补充和完善。OOP引入封装、继承和多态性等概念来建立一种对象层次结构,用以模拟公共行为的一个集合。实现AOP的技术,主要分为两大类:一是采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;二是采用静态织入的方式,引入特定的语法创建“方面”,从而使得编译器可以在编译期间织入有关“方面”的代码,属于静态代理。
-
-
-
diff --git "a/\351\235\242\350\257\225\345\277\205\345\244\207/\347\276\216\345\233\242-\347\273\210\347\273\223\347\257\207.md" "b/\351\235\242\350\257\225\345\277\205\345\244\207/\347\276\216\345\233\242-\347\273\210\347\273\223\347\257\207.md"
deleted file mode 100644
index c4b30efe491..00000000000
--- "a/\351\235\242\350\257\225\345\277\205\345\244\207/\347\276\216\345\233\242-\347\273\210\347\273\223\347\257\207.md"
+++ /dev/null
@@ -1,353 +0,0 @@
-
-
-
-- [一. Object类有哪些方法?](#一-object类有哪些方法)
- - [1.1 Object类的常见方法总结](#11-object类的常见方法总结)
- - [1.2 hashCode与equals](#12-hashcode与equals)
- - [1.2.1 hashCode()介绍](#121-hashcode()介绍)
- - [1.2.2 为什么要有hashCode](#122-为什么要有hashcode)
- - [1.2.3 hashCode()与equals()的相关规定](#123-hashcode()与equals()的相关规定)
- - [1.2.4 为什么两个对象有相同的hashcode值,它们也不一定是相等的?](#124-为什么两个对象有相同的hashcode值,它们也不一定是相等的?)
- - [1.3 ==与equals](#13-与equals)
-- [二 ConcurrentHashMap 相关问题](#二-concurrenthashmap-相关问题)
- - [2.1 ConcurrentHashMap 和 Hashtable 的区别](#21-concurrenthashmap-和-hashtable-的区别)
- - [2.2 ConcurrentHashMap线程安全的具体实现方式/底层具体实现](#22-concurrenthashmap线程安全的具体实现方式底层具体实现)
- - [JDK1.7(上面有示意图)](#jdk17(上面有示意图))
- - [JDK1.8 (上面有示意图)](#jdk18-(上面有示意图))
-- [三 谈谈 synchronized 和 ReenTrantLock 的区别](#三-谈谈-synchronized-和-reentrantlock-的区别)
-- [四 线程池了解吗?](#四-线程池了解吗?)
- - [4.1 为什么要用线程池?](#41-为什么要用线程池?)
- - [4.2 Java 提供了哪几种线程池?他们各自的使用场景是什么?](#42-java-提供了哪几种线程池?他们各自的使用场景是什么?)
- - [Java 主要提供了下面4种线程池](#java-主要提供了下面4种线程池)
- - [各种线程池的适用场景介绍](#各种线程池的适用场景介绍)
- - [4.3 创建的线程池的方式](#43-创建的线程池的方式)
-- [五 Nginx](#五-nginx)
- - [5.1 简单介绍一下Nginx](#51-简单介绍一下nginx)
- - [反向代理](#反向代理)
- - [负载均衡](#负载均衡)
- - [动静分离](#动静分离)
- - [5.2 为什么要用 Nginx ?](#52-为什么要用-nginx-?)
- - [5.3 Nginx 的四个主要组成部分了解吗?](#53-nginx-的四个主要组成部分了解吗?)
-
-
-
-
-> 下面这个问题,面试中经常出现。我觉得不论是出于应付面试还是说更好地掌握Java这门编程语言,大家都要掌握!
-
-# 一. Object类有哪些方法?
-
-### 1.1 Object类的常见方法总结
-
-Object类是一个特殊的类,是所有类的父类。它主要提供了以下11个方法:
-
-```java
-
-public final native Class> getClass()//native方法,用于返回当前运行时对象的Class对象,使用了final关键字修饰,故不允许子类重写。
-
-public native int hashCode() //native方法,用于返回对象的哈希码,主要使用在哈希表中,比如JDK中的HashMap。
-public boolean equals(Object obj)//用于比较2个对象的内存地址是否相等,String类对该方法进行了重写用户比较字符串的值是否相等。
-
-protected native Object clone() throws CloneNotSupportedException//naitive方法,用于创建并返回当前对象的一份拷贝。一般情况下,对于任何对象 x,表达式 x.clone() != x 为true,x.clone().getClass() == x.getClass() 为true。Object本身没有实现Cloneable接口,所以不重写clone方法并且进行调用的话会发生CloneNotSupportedException异常。
-
-public String toString()//返回类的名字@实例的哈希码的16进制的字符串。建议Object所有的子类都重写这个方法。
-
-public final native void notify()//native方法,并且不能重写。唤醒一个在此对象监视器上等待的线程(监视器相当于就是锁的概念)。如果有多个线程在等待只会任意唤醒一个。
-
-public final native void notifyAll()//native方法,并且不能重写。跟notify一样,唯一的区别就是会唤醒在此对象监视器上等待的所有线程,而不是一个线程。
-
-public final native void wait(long timeout) throws InterruptedException//native方法,并且不能重写。暂停线程的执行。注意:sleep方法没有释放锁,而wait方法释放了锁 。timeout是等待时间。
-
-public final void wait(long timeout, int nanos) throws InterruptedException//多了nanos参数,这个参数表示额外时间(以毫微秒为单位,范围是 0-999999)。 所以超时的时间还需要加上nanos毫秒。
-
-public final void wait() throws InterruptedException//跟之前的2个wait方法一样,只不过该方法一直等待,没有超时时间这个概念
-
-protected void finalize() throws Throwable { }//实例被垃圾回收器回收的时候触发的操作
-
-```
-
-> 问完上面这个问题之后,面试官很可能紧接着就会问你“hashCode与equals”相关的问题。
-
-### 1.2 hashCode与equals
-
-面试官可能会问你:“你重写过 hashcode 和 equals 么,为什么重写equals时必须重写hashCode方法?”
-
-#### 1.2.1 hashCode()介绍
-hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode() 定义在JDK的Object.java中,这就意味着Java中的任何类都包含有hashCode() 函数。另外需要注意的是: Object 的 hashcode 方法是本地方法,也就是用 c 语言或 c++ 实现的,该方法通常用来将对象的 内存地址 转换为整数之后返回。
-
-```java
- public native int hashCode();
-```
-
-散列表存储的是键值对(key-value),它的特点是:能根据“键”快速的检索出对应的“值”。这其中就利用到了散列码!(可以快速找到所需要的对象)
-
-#### 1.2.2 为什么要有hashCode
-
-
-**我们以“HashSet如何检查重复”为例子来说明为什么要有hashCode:**
-
-当你把对象加入HashSet时,HashSet会先计算对象的hashcode值来判断对象加入的位置,同时也会与其他已经加入的对象的hashcode值作比较,如果没有相符的hashcode,HashSet会假设对象没有重复出现。但是如果发现有相同hashcode值的对象,这时会调用equals()方法来检查hashcode相等的对象是否真的相同。如果两者相同,HashSet就不会让其加入操作成功。如果不同的话,就会重新散列到其他位置。(摘自我的Java启蒙书《Head fist java》第二版)。这样我们就大大减少了equals的次数,相应就大大提高了执行速度。
-
-
-#### 1.2.3 hashCode()与equals()的相关规定
-
-1. 如果两个对象相等,则hashcode一定也是相同的
-2. 两个对象相等,对两个对象分别调用equals方法都返回true
-3. 两个对象有相同的hashcode值,它们也不一定是相等的
-4. **因此,equals方法被覆盖过,则hashCode方法也必须被覆盖**
-5. hashCode()的默认行为是对堆上的对象产生独特值。如果没有重写hashCode(),则该class的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)
-
-#### 1.2.4 为什么两个对象有相同的hashcode值,它们也不一定是相等的?
-
-在这里解释一位小伙伴的问题。以下内容摘自《Head Fisrt Java》。
-
-因为hashCode() 所使用的杂凑算法也许刚好会让多个对象传回相同的杂凑值。越糟糕的杂凑算法越容易碰撞,但这也与数据值域分布的特性有关(所谓碰撞也就是指的是不同的对象得到相同的 hashCode)。
-
-我们刚刚也提到了 HashSet,如果 HashSet 在对比的时候,同样的 hashcode 有多个对象,它会使用 equals() 来判断是否真的相同。也就是说 hashcode 只是用来缩小查找成本。
-
-> ==与equals 的对比也是比较常问的基础问题之一!
-
-### 1.3 ==与equals
-
-**==** : 它的作用是判断两个对象的地址是不是相等。即,判断两个对象是不是同一个对象。(基本数据类型==比较的是值,引用数据类型==比较的是内存地址)
-
-**equals()** : 它的作用也是判断两个对象是否相等。但它一般有两种使用情况:
-
-- 情况1:类没有覆盖equals()方法。则通过equals()比较该类的两个对象时,等价于通过“==”比较这两个对象。
-- 情况2:类覆盖了equals()方法。一般,我们都覆盖equals()方法来两个对象的内容相等;若它们的内容相等,则返回true(即,认为这两个对象相等)。
-
-
-**举个例子:**
-
-```java
-public class test1 {
- public static void main(String[] args) {
- String a = new String("ab"); // a 为一个引用
- String b = new String("ab"); // b为另一个引用,对象的内容一样
- String aa = "ab"; // 放在常量池中
- String bb = "ab"; // 从常量池中查找
- if (aa == bb) // true
- System.out.println("aa==bb");
- if (a == b) // false,非同一对象
- System.out.println("a==b");
- if (a.equals(b)) // true
- System.out.println("aEQb");
- if (42 == 42.0) { // true
- System.out.println("true");
- }
- }
-}
-```
-
-**说明:**
-
-- String中的equals方法是被重写过的,因为object的equals方法是比较的对象的内存地址,而String的equals方法比较的是对象的值。
-- 当创建String类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引用。如果没有就在常量池中重新创建一个String对象。
-
-> 在[【备战春招/秋招系列5】美团面经总结进阶篇 (附详解答案)](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484625&idx=1&sn=9c4fa1f7d4291a5fbd7daa44bac2b012&chksm=fd9852b0caefdba6edcf9a827aa4a17ddc97bf6ad2e5ee6f7e1aa1b443b54444d05d2b76732b&token=723699735&lang=zh_CN#rd) 这篇文章中,我们已经提到了一下关于 HashMap 在面试中常见的问题:HashMap 的底层实现、简单讲一下自己对于红黑树的理解、红黑树这么优秀,为何不直接使用红黑树得了、HashMap 和 Hashtable 的区别/HashSet 和 HashMap 区别。HashMap 和 ConcurrentHashMap 这俩兄弟在一般只要面试中问到集合相关的问题就一定会被问到,所以各位务必引起重视!
-
-# 二 ConcurrentHashMap 相关问题
-
-### 2.1 ConcurrentHashMap 和 Hashtable 的区别
-
-ConcurrentHashMap 和 Hashtable 的区别主要体现在实现线程安全的方式上不同。
-
-- **底层数据结构:** JDK1.7的 ConcurrentHashMap 底层采用 **分段的数组+链表** 实现,JDK1.8 采用的数据结构跟HashMap1.8的结构一样,数组+链表/红黑二叉树。Hashtable 和 JDK1.8 之前的 HashMap 的底层数据结构类似都是采用 **数组+链表** 的形式,数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的;
-- **实现线程安全的方式(重要):** ① **在JDK1.7的时候,ConcurrentHashMap(分段锁)** 对整个桶数组进行了分割分段(Segment),每一把锁只锁容器其中一部分数据,多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问率。(默认分配16个Segment,比Hashtable效率提高16倍。) **到了 JDK1.8 的时候已经摒弃了Segment的概念,而是直接用 Node 数组+链表+红黑树的数据结构来实现,并发控制使用 synchronized 和 CAS 来操作。(JDK1.6以后 对 synchronized锁做了很多优化)** 整个看起来就像是优化过且线程安全的 HashMap,虽然在JDK1.8中还能看到 Segment 的数据结构,但是已经简化了属性,只是为了兼容旧版本;② **Hashtable(同一把锁)** :使用 synchronized 来保证线程安全,效率非常低下。当一个线程访问同步方法时,其他线程也访问同步方法,可能会进入阻塞或轮询状态,如使用 put 添加元素,另一个线程不能使用 put 添加元素,也不能使用 get,竞争会越来越激烈效率越低。
-
-**两者的对比图:**
-
-图片来源:http://www.cnblogs.com/chengxiao/p/6842045.html
-
-HashTable:
-
-
-JDK1.7的ConcurrentHashMap:
-
-JDK1.8的ConcurrentHashMap(TreeBin: 红黑二叉树节点
-Node: 链表节点):
-
-
-### 2.2 ConcurrentHashMap线程安全的具体实现方式/底层具体实现
-
-#### JDK1.7(上面有示意图)
-
-首先将数据分为一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据时,其他段的数据也能被其他线程访问。
-
-**ConcurrentHashMap 是由 Segment 数组结构和 HashEntry 数组结构组成**。
-
-Segment 实现了 ReentrantLock,所以 Segment 是一种可重入锁,扮演锁的角色。HashEntry 用于存储键值对数据。
-
-```java
-static class Segment extends ReentrantLock implements Serializable {
-}
-```
-
-一个 ConcurrentHashMap 里包含一个 Segment 数组。Segment 的结构和HashMap类似,是一种数组和链表结构,一个 Segment 包含一个 HashEntry 数组,每个 HashEntry 是一个链表结构的元素,每个 Segment 守护着一个HashEntry数组里的元素,当对 HashEntry 数组的数据进行修改时,必须首先获得对应的 Segment的锁。
-
-#### JDK1.8 (上面有示意图)
-
-ConcurrentHashMap取消了Segment分段锁,采用CAS和synchronized来保证并发安全。数据结构跟HashMap1.8的结构类似,数组+链表/红黑二叉树。
-
-synchronized只锁定当前链表或红黑二叉树的首节点,这样只要hash不冲突,就不会产生并发,效率又提升N倍。
-
-# 三 谈谈 synchronized 和 ReenTrantLock 的区别
-
-**① 两者都是可重入锁**
-
-两者都是可重入锁。“可重入锁”概念是:自己可以再次获取自己的内部锁。比如一个线程获得了某个对象的锁,此时这个对象锁还没有释放,当其再次想要获取这个对象的锁的时候还是可以获取的,如果不可锁重入的话,就会造成死锁。同一个线程每次获取锁,锁的计数器都自增1,所以要等到锁的计数器下降为0时才能释放锁。
-
-**② synchronized 依赖于 JVM 而 ReenTrantLock 依赖于 API**
-
-synchronized 是依赖于 JVM 实现的,前面我们也讲到了 虚拟机团队在 JDK1.6 为 synchronized 关键字进行了很多优化,但是这些优化都是在虚拟机层面实现的,并没有直接暴露给我们。ReenTrantLock 是 JDK 层面实现的(也就是 API 层面,需要 lock() 和 unlock 方法配合 try/finally 语句块来完成),所以我们可以通过查看它的源代码,来看它是如何实现的。
-
-**③ ReenTrantLock 比 synchronized 增加了一些高级功能**
-
-相比synchronized,ReenTrantLock增加了一些高级功能。主要来说主要有三点:**①等待可中断;②可实现公平锁;③可实现选择性通知(锁可以绑定多个条件)**
-
-- **ReenTrantLock提供了一种能够中断等待锁的线程的机制**,通过lock.lockInterruptibly()来实现这个机制。也就是说正在等待的线程可以选择放弃等待,改为处理其他事情。
-- **ReenTrantLock可以指定是公平锁还是非公平锁。而synchronized只能是非公平锁。所谓的公平锁就是先等待的线程先获得锁。** ReenTrantLock默认情况是非公平的,可以通过 ReenTrantLock类的`ReentrantLock(boolean fair)`构造方法来制定是否是公平的。
-- synchronized关键字与wait()和notify/notifyAll()方法相结合可以实现等待/通知机制,ReentrantLock类当然也可以实现,但是需要借助于Condition接口与newCondition() 方法。Condition是JDK1.5之后才有的,它具有很好的灵活性,比如可以实现多路通知功能也就是在一个Lock对象中可以创建多个Condition实例(即对象监视器),**线程对象可以注册在指定的Condition中,从而可以有选择性的进行线程通知,在调度线程上更加灵活。 在使用notify/notifyAll()方法进行通知时,被通知的线程是由 JVM 选择的,用ReentrantLock类结合Condition实例可以实现“选择性通知”** ,这个功能非常重要,而且是Condition接口默认提供的。而synchronized关键字就相当于整个Lock对象中只有一个Condition实例,所有的线程都注册在它一个身上。如果执行notifyAll()方法的话就会通知所有处于等待状态的线程这样会造成很大的效率问题,而Condition实例的signalAll()方法 只会唤醒注册在该Condition实例中的所有等待线程。
-
-如果你想使用上述功能,那么选择ReenTrantLock是一个不错的选择。
-
-**④ 两者的性能已经相差无几**
-
-在JDK1.6之前,synchronized 的性能是比 ReenTrantLock 差很多。具体表示为:synchronized 关键字吞吐量岁线程数的增加,下降得非常严重。而ReenTrantLock 基本保持一个比较稳定的水平。我觉得这也侧面反映了, synchronized 关键字还有非常大的优化余地。后续的技术发展也证明了这一点,我们上面也讲了在 JDK1.6 之后 JVM 团队对 synchronized 关键字做了很多优化。JDK1.6 之后,synchronized 和 ReenTrantLock 的性能基本是持平了。所以网上那些说因为性能才选择 ReenTrantLock 的文章都是错的!JDK1.6之后,性能已经不是选择synchronized和ReenTrantLock的影响因素了!而且虚拟机在未来的性能改进中会更偏向于原生的synchronized,所以还是提倡在synchronized能满足你的需求的情况下,优先考虑使用synchronized关键字来进行同步!优化后的synchronized和ReenTrantLock一样,在很多地方都是用到了CAS操作。
-
-
-# 四 线程池了解吗?
-
-
-### 4.1 为什么要用线程池?
-
-线程池提供了一种限制和管理资源(包括执行一个任务)。 每个线程池还维护一些基本统计信息,例如已完成任务的数量。
-
-这里借用《Java并发编程的艺术》提到的来说一下使用线程池的好处:
-
-- **降低资源消耗。** 通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
-- **提高响应速度。** 当任务到达时,任务可以不需要的等到线程创建就能立即执行。
-- **提高线程的可管理性。** 线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
-
-### 4.2 Java 提供了哪几种线程池?他们各自的使用场景是什么?
-
-#### Java 主要提供了下面4种线程池
-
-- **FixedThreadPool:** 该方法返回一个固定线程数量的线程池。该线程池中的线程数量始终不变。当有一个新的任务提交时,线程池中若有空闲线程,则立即执行。若没有,则新的任务会被暂存在一个任务队列中,待有线程空闲时,便处理在任务队列中的任务。
-- **SingleThreadExecutor:** 方法返回一个只有一个线程的线程池。若多余一个任务被提交到该线程池,任务会被保存在一个任务队列中,待线程空闲,按先入先出的顺序执行队列中的任务。
-- **CachedThreadPool:** 该方法返回一个可根据实际情况调整线程数量的线程池。线程池的线程数量不确定,但若有空闲线程可以复用,则会优先使用可复用的线程。若所有线程均在工作,又有新的任务提交,则会创建新的线程处理任务。所有线程在当前任务执行完毕后,将返回线程池进行复用。
-- **ScheduledThreadPoolExecutor:** 主要用来在给定的延迟后运行任务,或者定期执行任务。ScheduledThreadPoolExecutor又分为:ScheduledThreadPoolExecutor(包含多个线程)和SingleThreadScheduledExecutor (只包含一个线程)两种。
-
-#### 各种线程池的适用场景介绍
-
-- **FixedThreadPool:** 适用于为了满足资源管理需求,而需要限制当前线程数量的应用场景。它适用于负载比较重的服务器;
-- **SingleThreadExecutor:** 适用于需要保证顺序地执行各个任务并且在任意时间点,不会有多个线程是活动的应用场景。
-- **CachedThreadPool:** 适用于执行很多的短期异步任务的小程序,或者是负载较轻的服务器;
-- **ScheduledThreadPoolExecutor:** 适用于需要多个后台执行周期任务,同时为了满足资源管理需求而需要限制后台线程的数量的应用场景,
-- **SingleThreadScheduledExecutor:** 适用于需要单个后台线程执行周期任务,同时保证顺序地执行各个任务的应用场景。
-
-### 4.3 创建的线程池的方式
-
-**(1) 使用 Executors 创建**
-
-我们上面刚刚提到了 Java 提供的几种线程池,通过 Executors 工具类我们可以很轻松的创建我们上面说的几种线程池。但是实际上我们一般都不是直接使用Java提供好的线程池,另外在《阿里巴巴Java开发手册》中强制线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 构造函数 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
-
-```java
-Executors 返回线程池对象的弊端如下:
-
-FixedThreadPool 和 SingleThreadExecutor : 允许请求的队列长度为 Integer.MAX_VALUE,可能堆积大量的请求,从而导致OOM。
-CachedThreadPool 和 ScheduledThreadPool : 允许创建的线程数量为 Integer.MAX_VALUE ,可能会创建大量线程,从而导致OOM。
-
-```
-**(2) ThreadPoolExecutor的构造函数创建**
-
-
-我们可以自己直接调用 ThreadPoolExecutor 的构造函数来自己创建线程池。在创建的同时,给 BlockQueue 指定容量就可以了。示例如下:
-
-```java
-private static ExecutorService executor = new ThreadPoolExecutor(13, 13,
- 60L, TimeUnit.SECONDS,
- new ArrayBlockingQueue(13));
-```
-
-这种情况下,一旦提交的线程数超过当前可用线程数时,就会抛出java.util.concurrent.RejectedExecutionException,这是因为当前线程池使用的队列是有边界队列,队列已经满了便无法继续处理新的请求。但是异常(Exception)总比发生错误(Error)要好。
-
-**(3) 使用开源类库**
-
-Hollis 大佬之前在他的文章中也提到了:“除了自己定义ThreadPoolExecutor外。还有其他方法。这个时候第一时间就应该想到开源类库,如apache和guava等。”他推荐使用guava提供的ThreadFactoryBuilder来创建线程池。下面是参考他的代码示例:
-
-```java
-public class ExecutorsDemo {
-
- private static ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
- .setNameFormat("demo-pool-%d").build();
-
- private static ExecutorService pool = new ThreadPoolExecutor(5, 200,
- 0L, TimeUnit.MILLISECONDS,
- new LinkedBlockingQueue(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());
-
- public static void main(String[] args) {
-
- for (int i = 0; i < Integer.MAX_VALUE; i++) {
- pool.execute(new SubThread());
- }
- }
-}
-```
-
-通过上述方式创建线程时,不仅可以避免OOM的问题,还可以自定义线程名称,更加方便的出错的时候溯源。
-
-# 五 Nginx
-
-### 5.1 简单介绍一下Nginx
-
-Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器。 Nginx 主要提供反向代理、负载均衡、动静分离(静态资源服务)等服务。下面我简单地介绍一下这些名词。
-
-#### 反向代理
-
-谈到反向代理,就不得不提一下正向代理。无论是正向代理,还是反向代理,说到底,就是代理模式的衍生版本罢了
-
-- **正向代理:**某些情况下,代理我们用户去访问服务器,需要用户手动的设置代理服务器的ip和端口号。正向代理比较常见的一个例子就是 VPN了。
-- **反向代理:** 是用来代理服务器的,代理我们要访问的目标服务器。代理服务器接受请求,然后将请求转发给内部网络的服务器,并将从服务器上得到的结果返回给客户端,此时代理服务器对外就表现为一个服务器。
-
-通过下面两幅图,大家应该更好理解(图源:http://blog.720ui.com/2016/nginx_action_05_proxy/):
-
-
-
-
-
-所以,简单的理解,就是正向代理是为客户端做代理,代替客户端去访问服务器,而反向代理是为服务器做代理,代替服务器接受客户端请求。
-
-#### 负载均衡
-
-在高并发情况下需要使用,其原理就是将并发请求分摊到多个服务器执行,减轻每台服务器的压力,多台服务器(集群)共同完成工作任务,从而提高了数据的吞吐量。
-
-Nginx支持的weight轮询(默认)、ip_hash、fair、url_hash这四种负载均衡调度算法,感兴趣的可以自行查阅。
-
-负载均衡相比于反向代理更侧重的时将请求分担到多台服务器上去,所以谈论负载均衡只有在提供某服务的服务器大于两台时才有意义。
-
-#### 动静分离
-
-动静分离是让动态网站里的动态网页根据一定规则把不变的资源和经常变的资源区分开来,动静资源做好了拆分以后,我们就可以根据静态资源的特点将其做缓存操作,这就是网站静态化处理的核心思路。
-
-### 5.2 为什么要用 Nginx ?
-
-> 这部分内容参考极客时间—[Nginx核心知识100讲的内容](https://time.geekbang.org/course/intro/138?code=AycjiiQk6uQRxnVJzBupFkrGkvZlmYELPRsZbWzaAHE=)。
-
-如果面试官问你这个问题,就一定想看你知道 Nginx 服务器的一些优点吗。
-
-Nginx 有以下5个优点:
-
-1. 高并发、高性能(这是其他web服务器不具有的)
-2. 可扩展性好(模块化设计,第三方插件生态圈丰富)
-3. 高可靠性(可以在服务器行持续不间断的运行数年)
-4. 热部署(这个功能对于 Nginx 来说特别重要,热部署指可以在不停止 Nginx服务的情况下升级 Nginx)
-5. BSD许可证(意味着我们可以将源代码下载下来进行修改然后使用自己的版本)
-
-### 5.3 Nginx 的四个主要组成部分了解吗?
-
-> 这部分内容参考极客时间—[Nginx核心知识100讲的内容](https://time.geekbang.org/course/intro/138?code=AycjiiQk6uQRxnVJzBupFkrGkvZlmYELPRsZbWzaAHE=)。
-
-- Nginx 二进制可执行文件:由各模块源码编译出一个文件
-- Nginx.conf 配置文件:控制Nginx 行为
-- acess.log 访问日志: 记录每一条HTTP请求信息
-- error.log 错误日志:定位问题
diff --git "a/\351\235\242\350\257\225\345\277\205\345\244\207/\347\276\216\345\233\242-\350\277\233\351\230\266\347\257\207.md" "b/\351\235\242\350\257\225\345\277\205\345\244\207/\347\276\216\345\233\242-\350\277\233\351\230\266\347\257\207.md"
deleted file mode 100644
index cb2d247ec91..00000000000
--- "a/\351\235\242\350\257\225\345\277\205\345\244\207/\347\276\216\345\233\242-\350\277\233\351\230\266\347\257\207.md"
+++ /dev/null
@@ -1,271 +0,0 @@
-
-
-
-- [一 消息队列MQ的套路](#一-消息队列mq的套路)
- - [1.1 介绍一下消息队列MQ的应用场景/使用消息队列的好处](#11-介绍一下消息队列mq的应用场景使用消息队列的好处)
- - [①.通过异步处理提高系统性能](#①通过异步处理提高系统性能)
- - [②.降低系统耦合性](#②降低系统耦合性)
- - [1.2 那么使用消息队列会带来什么问题?考虑过这个问题吗?](#12-那么使用消息队列会带来什么问题考虑过这个问题吗)
- - [1.3 介绍一下你知道哪几种消息队列,该如何选择呢?](#13-介绍一下你知道哪几种消息队列该如何选择呢)
- - [1.4 关于消息队列其他一些常见的问题展望](#14-关于消息队列其他一些常见的问题展望)
-- [二 谈谈 InnoDB 和 MyIsam 两者的区别](#二-谈谈-innodb-和-myisam-两者的区别)
- - [2.1 两者的对比](#21-两者的对比)
- - [2.2 关于两者的总结](#22-关于两者的总结)
-- [三 聊聊 Java 中的集合吧!](#三-聊聊-java-中的集合吧!)
- - [3.1 Arraylist 与 LinkedList 有什么不同?(注意加上从数据结构分析的内容)](#31-arraylist-与-linkedlist-有什么不同?(注意加上从数据结构分析的内容))
- - [3.2 HashMap的底层实现](#32-hashmap的底层实现)
- - [① JDK1.8之前](#①-jdk18之前)
- - [② JDK1.8之后](#②-jdk18之后)
- - [3.3 既然谈到了红黑树,你给我手绘一个出来吧,然后简单讲一下自己对于红黑树的理解](#33-既然谈到了红黑树,你给我手绘一个出来吧,然后简单讲一下自己对于红黑树的理解)
- - [3.4 红黑树这么优秀,为何不直接使用红黑树得了?](#34-红黑树这么优秀为何不直接使用红黑树得了)
- - [3.5 HashMap 和 Hashtable 的区别/HashSet 和 HashMap 区别](#35-hashmap-和-hashtable-的区别hashset-和-hashmap-区别)
-
-
-
-
-> 该文已加入开源文档:JavaGuide(一份涵盖大部分Java程序员所需要掌握的核心知识)。地址:https://github.com/Snailclimb/JavaGuide.
-
-
-**系列文章:**
-
-- [【备战春招/秋招系列1】程序员的简历就该这样写](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484573&idx=1&sn=8c5965d4a3710d405d8e8cc10c7b0ce5&chksm=fd9852fccaefdbea8dfe0bc40188b7579f1cddb1e8905dc981669a3f21d2a04cadceafa9023f&token=1990180468&lang=zh_CN#rd)
-- [【备战春招/秋招系列2】初出茅庐的程序员该如何准备面试?](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484578&idx=1&sn=eea72d80a2325257f00aaed21d5b226f&chksm=fd9852c3caefdbd52dd8a537cc723ed1509314401b3a669a253ef5bc0360b6fddef48b9c2e94&token=1990180468&lang=zh_CN#rd)
-- [【备战春招/秋招系列3】Java程序员必备书单](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484592&idx=1&sn=6d9731ce7401be49e97c1af6ed384ecc&chksm=fd9852d1caefdbc720a361ae65a8ad9d53cfb4800b15a7c68cbdc630b313215c6c52e0934ec2&token=1990180468&lang=zh_CN#rd)
-- [【备战春招/秋招系列4】美团面经总结基础篇 (附详解答案)](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484601&idx=1&sn=4907b7fef0856791c565d49d788ba8cc&chksm=fd9852d8caefdbce88e51c0a10a4ec77c97f382fd2af4a840ea47cffc828bfd0f993f50d5f0d&token=2045370425&lang=zh_CN#rd)
-
-这是我总结的美团面经的进阶篇,后面还有终结篇哦!下面只是我从很多份美团面经中总结的在美团面试中一些常见的问题。不同于个人面经,这份面经具有普适性。每次面试必备的自我介绍、项目介绍这些东西,大家可以自己私下好好思考。我在前面的文章中也提到了应该怎么做自我介绍与项目介绍,详情可以查看这篇文章:[【备战春招/秋招系列2】初出茅庐的程序员该如何准备面试?](https://mp.weixin.qq.com/s?__biz=MzU4NDQ4MzU5OA==&mid=2247484578&idx=1&sn=eea72d80a2325257f00aaed21d5b226f&chksm=fd9852c3caefdbd52dd8a537cc723ed1509314401b3a669a253ef5bc0360b6fddef48b9c2e94&token=1990180468&lang=zh_CN#rd)。
-
-有人私信我让我对美团面试难度做一个评级,我觉得如果有10级的话,美团面试的难度大概在6级左右吧!部分情况可能因人而异了。
-
-> 消息队列/消息中间件应该是Java程序员必备的一个技能了,如果你之前没接触过消息队列的话,建议先去百度一下某某消息队列入门,然后花2个小时就差不多可以学会任何一种消息队列的使用了。如果说仅仅学会使用是万万不够的,在实际生产环境还要考虑消息丢失等等情况。关于消息队列面试相关的问题,推荐大家也可以看一下视频《Java工程师面试突击第1季-中华石杉老师》,如果大家没有资源的话,可以在我的公众号“Java面试通关手册”后台回复关键字“1”即可!
-
-# 一 消息队列MQ的套路
-
-> 面试官一般会先问你这个问题,预热一下,看你知道消息队列不,一般在第一面的时候面试官可能只会问消息队列MQ的应用场景/使用消息队列的好处、使用消息队列会带来什么问题、消息队列的技术选型这几个问题,不会太深究下去,在后面的第二轮/第三轮技术面试中可能会深入问一下。
-
-### 1.1 介绍一下消息队列MQ的应用场景/使用消息队列的好处
-
-**《大型网站技术架构》第四章和第七章均有提到消息队列对应用性能及扩展性的提升。**
-
-#### ①.通过异步处理提高系统性能
-
-如上图,**在不使用消息队列服务器的时候,用户的请求数据直接写入数据库,在高并发的情况下数据库压力剧增,使得响应速度变慢。但是在使用消息队列之后,用户的请求数据发送给消息队列之后立即 返回,再由消息队列的消费者进程从消息队列中获取数据,异步写入数据库。由于消息队列服务器处理速度快于数据库(消息队列也比数据库有更好的伸缩性),因此响应速度得到大幅改善。**
-
-通过以上分析我们可以得出**消息队列具有很好的削峰作用的功能**——即**通过异步处理,将短时间高并发产生的事务消息存储在消息队列中,从而削平高峰期的并发事务。** 举例:在电子商务一些秒杀、促销活动中,合理使用消息队列可以有效抵御促销活动刚开始大量订单涌入对系统的冲击。如下图所示:
-
-因为**用户请求数据写入消息队列之后就立即返回给用户了,但是请求数据在后续的业务校验、写数据库等操作中可能失败**。因此使用消息队列进行异步处理之后,需要**适当修改业务流程进行配合**,比如**用户在提交订单之后,订单数据写入消息队列,不能立即返回用户订单提交成功,需要在消息队列的订单消费者进程真正处理完该订单之后,甚至出库后,再通过电子邮件或短信通知用户订单成功**,以免交易纠纷。这就类似我们平时手机订火车票和电影票。
-
-### ②.降低系统耦合性
-我们知道模块分布式部署以后聚合方式通常有两种:1.**分布式消息队列**和2.**分布式服务**。
-
-> **先来简单说一下分布式服务:**
-
-目前使用比较多的用来构建**SOA(Service Oriented Architecture面向服务体系结构)**的**分布式服务框架**是阿里巴巴开源的**Dubbo**.如果想深入了解Dubbo的可以看我写的关于Dubbo的这一篇文章:**《高性能优秀的服务框架-dubbo介绍》**:[https://juejin.im/post/5acadeb1f265da2375072f9c](https://juejin.im/post/5acadeb1f265da2375072f9c)
-
-> **再来谈我们的分布式消息队列:**
-
-我们知道如果模块之间不存在直接调用,那么新增模块或者修改模块就对其他模块影响较小,这样系统的可扩展性无疑更好一些。
-
-我们最常见的**事件驱动架构**类似生产者消费者模式,在大型网站中通常用利用消息队列实现事件驱动结构。如下图所示:
-
-**消息队列使利用发布-订阅模式工作,消息发送者(生产者)发布消息,一个或多个消息接受者(消费者)订阅消息。** 从上图可以看到**消息发送者(生产者)和消息接受者(消费者)之间没有直接耦合**,消息发送者将消息发送至分布式消息队列即结束对消息的处理,消息接受者从分布式消息队列获取该消息后进行后续处理,并不需要知道该消息从何而来。**对新增业务,只要对该类消息感兴趣,即可订阅该消息,对原有系统和业务没有任何影响,从而实现网站业务的可扩展性设计**。
-
-消息接受者对消息进行过滤、处理、包装后,构造成一个新的消息类型,将消息继续发送出去,等待其他消息接受者订阅该消息。因此基于事件(消息对象)驱动的业务架构可以是一系列流程。
-
-**另外为了避免消息队列服务器宕机造成消息丢失,会将成功发送到消息队列的消息存储在消息生产者服务器上,等消息真正被消费者服务器处理后才删除消息。在消息队列服务器宕机后,生产者服务器会选择分布式消息队列服务器集群中的其他服务器发布消息。**
-
-**备注:** 不要认为消息队列只能利用发布-订阅模式工作,只不过在解耦这个特定业务环境下是使用发布-订阅模式的,**比如在我们的ActiveMQ消息队列中还有点对点工作模式**,具体的会在后面的文章给大家详细介绍,这一篇文章主要还是让大家对消息队列有一个更透彻的了解。
-
-> 这个问题一般会在上一个问题问完之后,紧接着被问到。“使用消息队列会带来什么问题?”这个问题要引起重视,一般我们都会考虑使用消息队列会带来的好处而忽略它带来的问题!
-
-### 1.2 那么使用消息队列会带来什么问题?考虑过这个问题吗?
-
-- **系统可用性降低:** 系统可用性在某种程度上降低,为什么这样说呢?在加入MQ之前,你不用考虑消息丢失或者说MQ挂掉等等的情况,但是,引入MQ之后你就需要去考虑了!
-- **系统复杂性提高:** 加入MQ之后,你需要保证消息没有被重复消费、处理消息丢失的情况、保证消息传递的顺序性等等问题!
-- **一致性问题:** 我上面讲了消息队列可以实现异步,消息队列带来的异步确实可以提高系统响应速度。但是,万一消息的真正消费者并没有正确消费消息怎么办?这样就会导致数据不一致的情况了!
-
-> 了解下面这个问题是为了我们更好的进行技术选型!该部分摘自:《Java工程师面试突击第1季-中华石杉老师》,如果大家没有资源的话,可以在我的公众号“Java面试通关手册”后台回复关键字“1”即可!
-
-### 1.3 介绍一下你知道哪几种消息队列,该如何选择呢?
-
-
-|特性 | ActiveMQ| RabbitMQ| RocketMQ| Kafaka|
-| :-------- | ----------:|--------:|--------:|--------:|
-| 单机吞吐量 | 万级,吞吐量比RocketMQ和Kafka要低了一个数量级 | 万级,吞吐量比RocketMQ和Kafka要低了一个数量级 |10万级,RocketMQ也是可以支撑高吞吐的一种MQ |10万级别,这是kafka最大的优点,就是吞吐量高。一般配合大数据类的系统来进行实时数据计算、日志采集等场景 |
-| topic数量对吞吐量的影响 | | | topic可以达到几百,几千个的级别,吞吐量会有较小幅度的下降这是RocketMQ的一大优势,在同等机器下,可以支撑大量的topic |topic从几十个到几百个的时候,吞吐量会大幅度下降。所以在同等机器下,kafka尽量保证topic数量不要过多。如果要支撑大规模topic,需要增加更多的机器资源|
-|可用性 |高,基于主从架构实现高可用性|高,基于主从架构实现高可用性 | 非常高,分布式架构|非常高,kafka是分布式的,一个数据多个副本,少数机器宕机,不会丢失数据,不会导致不可用|
-|消息可靠性 | 有较低的概率丢失数据 | | 经过参数优化配置,可以做到0丢失|经过参数优化配置,消息可以做到0丢失 |
-|时效性 |ms级| 微秒级,这是rabbitmq的一大特点,延迟是最低的 | ms级 |延迟在ms级以内 |
-|功能支持| MQ领域的功能极其完备 |基于erlang开发,所以并发能力很强,性能极其好,延时很低 | MQ功能较为完善,还是分布式的,扩展性好 |功能较为简单,主要支持简单的MQ功能,在大数据领域的实时计算以及日志采集被大规模使用,是事实上的标准 |
-|优劣势总结| 非常成熟,功能强大,在业内大量的公司以及项目中都有应用。偶尔会有较低概率丢失消息,而且现在社区以及国内应用都越来越少,官方社区现在对ActiveMQ 5.x维护越来越少,几个月才发布一个版本而且确实主要是基于解耦和异步来用的,较少在大规模吞吐的场景中使用 | erlang语言开发,性能极其好,延时很低;吞吐量到万级,MQ功能比较完备而且开源提供的管理界面非常棒,用起来很好用。社区相对比较活跃,几乎每个月都发布几个版本分在国内一些互联网公司近几年用rabbitmq也比较多一些但是问题也是显而易见的,RabbitMQ确实吞吐量会低一些,这是因为他做的实现机制比较重。而且erlang开发,国内有几个公司有实力做erlang源码级别的研究和定制?如果说你没这个实力的话,确实偶尔会有一些问题,你很难去看懂源码,你公司对这个东西的掌控很弱,基本职能依赖于开源社区的快速维护和修复bug。而且rabbitmq集群动态扩展会很麻烦,不过这个我觉得还好。其实主要是erlang语言本身带来的问题。很难读源码,很难定制和掌控。| 接口简单易用,而且毕竟在阿里大规模应用过,有阿里品牌保障。日处理消息上百亿之多,可以做到大规模吞吐,性能也非常好,分布式扩展也很方便,社区维护还可以,可靠性和可用性都是ok的,还可以支撑大规模的topic数量,支持复杂MQ业务场景。而且一个很大的优势在于,阿里出品都是java系的,我们可以自己阅读源码,定制自己公司的MQ,可以掌控。社区活跃度相对较为一般,不过也还可以,文档相对来说简单一些,然后接口这块不是按照标准JMS规范走的有些系统要迁移需要修改大量代码。还有就是阿里出台的技术,你得做好这个技术万一被抛弃,社区黄掉的风险,那如果你们公司有技术实力我觉得用RocketMQ挺好的|kafka的特点其实很明显,就是仅仅提供较少的核心功能,但是提供超高的吞吐量,ms级的延迟,极高的可用性以及可靠性,而且分布式可以任意扩展。同时kafka最好是支撑较少的topic数量即可,保证其超高吞吐量。而且kafka唯一的一点劣势是有可能消息重复消费,那么对数据准确性会造成极其轻微的影响,在大数据领域中以及日志采集中,这点轻微影响可以忽略这个特性天然适合大数据实时计算以及日志收集。|
-
-> 这部分内容,我这里不给出答案,大家可以自行根据自己学习的消息队列查阅相关内容,我可能会在后面的文章中介绍到这部分内容。另外,下面这些问题在视频《Java工程师面试突击第1季-中华石杉老师》中都有提到,如果大家没有资源的话,可以在我的公众号“Java面试通关手册”后台回复关键字“1”即可!
-
-### 1.4 关于消息队列其他一些常见的问题展望
-
-1. 引入消息队列之后如何保证高可用性
-2. 如何保证消息不被重复消费呢?
-3. 如何保证消息的可靠性传输(如何处理消息丢失的问题)?
-4. 我该怎么保证从消息队列里拿到的数据按顺序执行?
-5. 如何解决消息队列的延时以及过期失效问题?消息队列满了以后该怎么处理?有几百万消息持续积压几小时,说说怎么解决?
-6. 如果让你来开发一个消息队列中间件,你会怎么设计架构?
-
-
-
-# 二 谈谈 InnoDB 和 MyIsam 两者的区别
-
-### 2.1 两者的对比
-
-1) **count运算上的区别:** 因为MyISAM缓存有表meta-data(行数等),因此在做COUNT(*)时对于一个结构很好的查询是不需要消耗多少资源的。而对于InnoDB来说,则没有这种缓存。
-
-2) **是否支持事务和崩溃后的安全恢复:** MyISAM 强调的是性能,每次查询具有原子性,其执行数度比InnoDB类型更快,但是不提供事务支持。但是InnoDB 提供事务支持事务,外部键等高级数据库功能。 具有事务(commit)、回滚(rollback)和崩溃修复能力(crash recovery capabilities)的事务安全(transaction-safe (ACID compliant))型表。
-
-3)**是否支持外键:** MyISAM不支持,而InnoDB支持。
-
-
-
-### 2.2 关于两者的总结
-
-MyISAM更适合读密集的表,而InnoDB更适合写密集的的表。 在数据库做主从分离的情况下,经常选择MyISAM作为主库的存储引擎。
-
-一般来说,如果需要事务支持,并且有较高的并发读取频率(MyISAM的表锁的粒度太大,所以当该表写并发量较高时,要等待的查询就会很多了),InnoDB是不错的选择。如果你的数据量很大(MyISAM支持压缩特性可以减少磁盘的空间占用),而且不需要支持事务时,MyISAM是最好的选择。
-
-
-# 三 聊聊 Java 中的集合吧!
-
-
-### 3.1 Arraylist 与 LinkedList 有什么不同?(注意加上从数据结构分析的内容)
-
-- **1. 是否保证线程安全:** ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全;
-- **2. 底层数据结构:** Arraylist 底层使用的是Object数组;LinkedList 底层使用的是双向链表数据结构(注意双向链表和双向循环链表的区别:);
-- **3. 插入和删除是否受元素位置的影响:** ① **ArrayList 采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响。** 比如:执行`add(E e) `方法的时候, ArrayList 会默认在将指定的元素追加到此列表的末尾,这种情况时间复杂度就是O(1)。但是如果要在指定位置 i 插入和删除元素的话(`add(int index, E element) `)时间复杂度就为 O(n-i)。因为在进行上述操作的时候集合中第 i 和第 i 个元素之后的(n-i)个元素都要执行向后位/向前移一位的操作。 ② **LinkedList 采用链表存储,所以插入,删除元素时间复杂度不受元素位置的影响,都是近似 O(1)而数组为近似 O(n)。**
-- **4. 是否支持快速随机访问:** LinkedList 不支持高效的随机元素访问,而 ArrayList 支持。快速随机访问就是通过元素的序号快速获取元素对象(对应于`get(int index) `方法)。
-- **5. 内存空间占用:** ArrayList的空 间浪费主要体现在在list列表的结尾会预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗比ArrayList更多的空间(因为要存放直接后继和直接前驱以及数据)。
-
-**补充内容:RandomAccess接口**
-
-```java
-public interface RandomAccess {
-}
-```
-
-查看源码我们发现实际上 RandomAccess 接口中什么都没有定义。所以,在我看来 RandomAccess 接口不过是一个标识罢了。标识什么? 标识实现这个接口的类具有随机访问功能。
-
-在binarySearch()方法中,它要判断传入的list 是否RamdomAccess的实例,如果是,调用indexedBinarySearch()方法,如果不是,那么调用iteratorBinarySearch()方法
-
-```java
- public static
- int binarySearch(List extends Comparable super T>> list, T key) {
- if (list instanceof RandomAccess || list.size() Java 中的集合这类问题几乎是面试必问的,问到这类问题的时候,HashMap 又是几乎必问的问题,所以大家一定要引起重视!
-
-### 3.2 HashMap的底层实现
-
-#### ① JDK1.8之前
-
-JDK1.8 之前 HashMap 底层是 **数组和链表** 结合在一起使用也就是 **链表散列**。**HashMap 通过 key 的 hashCode 经过扰动函数处理过后得到 hash 值,然后通过 `(n - 1) & hash` 判断当前元素存放的位置(这里的 n 指的时数组的长度),如果当前位置存在元素的话,就判断该元素与要存入的元素的 hash 值以及 key 是否相同,如果相同的话,直接覆盖,不相同就通过拉链法解决冲突。**
-
-**所谓扰动函数指的就是 HashMap 的 hash 方法。使用 hash 方法也就是扰动函数是为了防止一些实现比较差的 hashCode() 方法 换句话说使用扰动函数之后可以减少碰撞。**
-
-**JDK 1.8 HashMap 的 hash 方法源码:**
-
-JDK 1.8 的 hash方法 相比于 JDK 1.7 hash 方法更加简化,但是原理不变。
-
-```java
- static final int hash(Object key) {
- int h;
- // key.hashCode():返回散列值也就是hashcode
- // ^ :按位异或
- // >>>:无符号右移,忽略符号位,空位都以0补齐
- return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
- }
-```
-对比一下 JDK1.7的 HashMap 的 hash 方法源码.
-
-```java
-static int hash(int h) {
- // This function ensures that hashCodes that differ only by
- // constant multiples at each bit position have a bounded
- // number of collisions (approximately 8 at default load factor).
-
- h ^= (h >>> 20) ^ (h >>> 12);
- return h ^ (h >>> 7) ^ (h >>> 4);
-}
-```
-
-相比于 JDK1.8 的 hash 方法 ,JDK 1.7 的 hash 方法的性能会稍差一点点,因为毕竟扰动了 4 次。
-
-所谓 **“拉链法”** 就是:将链表和数组相结合。也就是说创建一个链表数组,数组中每一格就是一个链表。若遇到哈希冲突,则将冲突的值加到链表中即可。
-
-
-
-
-
-
-#### ② JDK1.8之后
-
-相比于之前的版本, JDK1.8之后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。
-
-
-
-TreeMap、TreeSet以及JDK1.8之后的HashMap底层都用到了红黑树。红黑树就是为了解决二叉查找树的缺陷,因为二叉查找树在某些情况下会退化成一个线性结构。
-
-> 问完 HashMap 的底层原理之后,面试官可能就会紧接着问你 HashMap 底层数据结构相关的问题!
-
-### 3.3 既然谈到了红黑树,你给我手绘一个出来吧,然后简单讲一下自己对于红黑树的理解
-
-
-
-**红黑树特点:**
-
-1. 每个节点非红即黑;
-2. 根节点总是黑色的;
-3. 每个叶子节点都是黑色的空节点(NIL节点);
-4. 如果节点是红色的,则它的子节点必须是黑色的(反之不一定);
-5. 从根节点到叶节点或空子节点的每条路径,必须包含相同数目的黑色节点(即相同的黑色高度)
-
-**红黑树的应用:**
-
-TreeMap、TreeSet以及JDK1.8之后的HashMap底层都用到了红黑树。
-
-**为什么要用红黑树**
-
-简单来说红黑树就是为了解决二叉查找树的缺陷,因为二叉查找树在某些情况下会退化成一个线性结构。
-
-
-### 3.4 红黑树这么优秀,为何不直接使用红黑树得了?
-
-说一下自己对于这个问题的看法:我们知道红黑树属于(自)平衡二叉树,但是为了保持“平衡”是需要付出代价的,红黑树在插入新数据后可能需要通过左旋,右旋、变色这些操作来保持平衡,这费事啊。你说说我们引入红黑树就是为了查找数据快,如果链表长度很短的话,根本不需要引入红黑树的,你引入之后还要付出代价维持它的平衡。但是链表过长就不一样了。至于为什么选 8 这个值呢?通过概率统计所得,这个值是综合查询成本和新增元素成本得出的最好的一个值。
-
-### 3.5 HashMap 和 Hashtable 的区别/HashSet 和 HashMap 区别
-
-**HashMap 和 Hashtable 的区别**
-
-1. **线程是否安全:** HashMap 是非线程安全的,HashTable 是线程安全的;HashTable 内部的方法基本都经过 `synchronized` 修饰。(如果你要保证线程安全的话就使用 ConcurrentHashMap 吧!);
-2. **效率:** 因为线程安全的问题,HashMap 要比 HashTable 效率高一点。另外,HashTable 基本被淘汰,不要在代码中使用它;
-3. **对Null key 和Null value的支持:** HashMap 中,null 可以作为键,这样的键只有一个,可以有一个或多个键所对应的值为 null。。但是在 HashTable 中 put 进的键值只要有一个 null,直接抛出 NullPointerException。
-4. **初始容量大小和每次扩充容量大小的不同 :** ①创建时如果不指定容量初始值,Hashtable 默认的初始大小为11,之后每次扩充,容量变为原来的2n+1。HashMap 默认的初始化大小为16。之后每次扩充,容量变为原来的2倍。②创建时如果给定了容量初始值,那么 Hashtable 会直接使用你给定的大小,而 HashMap 会将其扩充为2的幂次方大小(HashMap 中的`tableSizeFor()`方法保证,下面给出了源代码)。也就是说 HashMap 总是使用2的幂作为哈希表的大小,后面会介绍到为什么是2的幂次方。
-5. **底层数据结构:** JDK1.8 以后的 HashMap 在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。Hashtable 没有这样的机制。
-
-**HashSet 和 HashMap 区别**
-
-如果你看过 HashSet 源码的话就应该知道:HashSet 底层就是基于 HashMap 实现的。(HashSet 的源码非常非常少,因为除了 clone() 方法、writeObject()方法、readObject()方法是 HashSet 自己不得不实现之外,其他方法都是直接调用 HashMap 中的方法。)
-
-
-
-