-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearch.xml
183 lines (183 loc) · 101 KB
/
search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title><![CDATA[JDBC的了解以及使用]]></title>
<url>%2F2019%2F04%2F21%2FJavaweb%E5%9F%BA%E7%A1%80_JDBC%E6%8E%A5%E5%8F%A3%2F</url>
<content type="text"><![CDATA[一. JDBC概念操作数据库的方式 使用DOS窗口 使用第三方客户端来访问MySQL:SQLyog、Navicat、SQLWave、MyDB Studio、EMS SQL Manager for MySQL 通过java来访问MySQL数据库 什么是JDBC:Java Data Base Connectivity(Java数据库连接) JDBC是Java访问数据库的标准规范JDBC的作用:JDBC是用于执行SQL语句的Java API(Java语言通过JDBC可以操作数据库) 二. JDBC的由来 直接写代码操作数据库存在的问题: 不知道MySQL数据库的操作方式,解析方式 代码繁琐,写起来麻烦 MySQL和Oracle等其他数据库的操作方式和解析方式不同,每个数据库都要写一套代码 MySQL和Oracle等其他数据库相互切换麻烦 JDBC规范定义接口,具体的实现由各大数据库厂商来实现 JDBC的好处: 我们只需要会调用JDBC接口中的方法即可,使用简单 使用同一套Java代码,进行少量的修改就可以访问其他JDBC支持的数据库 JDBC会用到的包: java.sql:JDBC访问数据库的基础包,在JavaSE中的包。如:java.sql.Connection javax.sql: JDBC访问数据库的扩展包 数据库的驱动,各大数据库厂商来实现。如:MySQL的驱动:com.mysql.jdbc.Driver JDBC四个核心类 这几个类都是在java.sql包中 DriverManager: 用于注册驱动以及获得连接对象 Connection: 表示与数据库创建的连接 Statement: 执行SQL语句的对象 ResultSet: 结果集或一张虚拟表 三. JDBC使用 注册驱动 获取连接 获取statement对象 使用statement对象执行SQL语句 释放资源 3.1 案例代码12345678910111213141516171819202122232425262728293031public class Demo03 { public static void main(String[] args) throws Exception { Class.forName("com.mysql.jdbc.Driver"); Connection conn = DriverManager.getConnection("jdbc:mysql:///day24", "root", "root"); System.out.println(conn); // String sql = "SELECT * FROM category;"; // 从连接中拿到一个Statement对象 Statement stmt = conn.createStatement(); // 1.插入记录 String sql = "INSERT INTO category (cname) VALUES ('手机');"; int i = stmt.executeUpdate(sql); System.out.println("影响的行数:" + i); // 2.修改记录 sql = "UPDATE category SET cname='汽车' WHERE cid=4;"; i = stmt.executeUpdate(sql); System.out.println("影响的行数:" + i); // 3.删除记录 sql = "DELETE FROM category WHERE cid=1;"; i = stmt.executeUpdate(sql); System.out.println("影响的行数:" + i); // 释放资源 stmt.close(); conn.close(); }} 案例效果 : 3.2 注册驱动java.sql.DriverManager类用于注册驱动。提供如下方法注册驱动 12static void registerDriver(Driver driver) 向 DriverManager 注册给定驱动程序。 导入驱动包mysql-connector-java-5.1.37-bin.jar 注册驱动DriverManager.registerDriver(new com.mysql.jdbc.Driver()) //通过查询com.mysql.jdbc.Driver源码,我们发现Driver类“主动”将自己进行注册 注意:使用DriverManager.registerDriver(new com.mysql.jdbc.Driver());,存在两方面不足 硬编码,后期不易于程序扩展和维护 驱动被注册两次 使用Class.forName(“com.mysql.jdbc.Driver”); 注册MySQL驱动 3.3 获取连接java.sql.DriverManager类中有如下方法获取数据库连接 12static Connection getConnection(String url, String user, String password) //连接到给定数据库 URL ,并返回连接。 3.4 获取statement对象在java.sql.Connection接口中有如下方法获取到Statement对象 12boolean execute(String sql)此方法可以执行任意sql语句。返回boolean值,表示是否返回ResultSet结果集。仅当执行select语句,且有返回结果时返回true, 其它语句都返回false; 12int executeUpdate(String sql)根据执行的DML(INSERT、UPDATE、DELETE)语句,返回受影响的行数 12ResultSet executeQuery(String sql)根据查询语句返回结果集,只能执行SELECT语句 注意:在MySQL中,只要不是查询就是修改。executeUpdate:用于执行增删改executeQuery:用于执行查询 3.5 结果集ResultSetResultSet用于保存执行查询SQL语句的结果。我们不能一次性取出所有的数据,需要一行一行的取出。 ResultSet的原理: ResultSet内部有一个指针,刚开始记录开始位置 调用next方法, ResultSet内部指针会移动到下一行数据 我们可以通过ResultSet得到一行数据 getXxx得到某列数据 ResultSet获取数据的API其实ResultSet获取数据的API是有规律的get后面加数据类型。我们统称getXXX() 四. SQL注入案例分析4.1 案例需求模拟用户输入账号和密码登录网站 4.2 案例分析 使用数据库保存用户的账号和密码 让用户输入账号和密码 使用SQL根据用户的账号和密码去数据库查询数据 如果查询到数据,说明登录成功 如果查询不到数据,说明登录失败 4.3 实现步骤 创建一个用户表保存用户的账号和密码,并添加一些数据,SQL语句如下: 123456CREATE TABLE USER ( id INT AUTO_INCREMENT PRIMARY KEY, NAME VARCHAR(50), PASSWORD VARCHAR(50));INSERT INTO USER (NAME, PASSWORD) VALUES('admin', '123'), ('test', '123'), ('gm', '123'); 编写代码让用户输入账号和密码 12345678public class Demo07 { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.println("请输入账号: "); String name = sc.nextLine(); System.out.println("请输入密码: "); String password = sc.nextLine();} 使用SQL根据用户的账号和密码去数据库查询数据 123456789101112131415public class Demo07 { public static void main(String[] args) throws Exception { // 让用户输入账号和密码 Scanner sc = new Scanner(System.in); System.out.println("请输入账号: "); String name = sc.nextLine(); System.out.println("请输入密码: "); String password = sc.nextLine(); // 使用SQL根据用户的账号和密码去数据库查询数据 Connection conn = JDBCUtils.getConnection(); Statement stmt = conn.createStatement(); String sql = "SELECT * FROM user WHERE name='" + name + "' AND password='" + password + "';"; } } 如果查询到数据,说明登录成功,如果查询不到数据,说明登录失败 1234567891011121314151617181920212223242526272829public class Demo07 {public static void main(String[] args) throws Exception { // 让用户输入账号和密码 Scanner sc = new Scanner(System.in); System.out.println("请输入账号: "); String name = sc.nextLine(); System.out.println("请输入密码: "); String password = sc.nextLine(); // 使用SQL根据用户的账号和密码去数据库查询数据 Connection conn = JDBCUtils.getConnection(); Statement stmt = conn.createStatement(); String sql = "SELECT * FROM user WHERE name='" + name + "' AND password='" + password + "';"; // 如果查询到数据,说明登录成功,如果查询不到数据,说明登录失败 ResultSet rs = stmt.executeQuery(sql); if (rs.next()) { //能进来查询到了数据. String name2 = rs.getString("name"); System.out.println("欢迎您," + name2); } else { //查询不到数据,说明登录失败 System.out.println("账号或密码错误..."); } JDBCUtils.close(conn, stmt, rs); }} 4.4 SQL注入问题当我们输入以下密码,我们发现我们账号和密码都不对竟然登录成功了1234请输入用户名:hehe请输入密码:a' or '1'='1 问题分析:1234// 代码中的SQL语句"SELECT * FROM user WHERE name='" + name + "' AND password='" + password + "';";// 将用户输入的账号密码拼接后"SELECT * FROM user WHERE name='hehe' AND password='a' or '1'='1';" 我们让用户输入的密码和SQL语句进行字符串拼接。用户输入的内容作为了SQL语句语法的一部分,改变了原有SQL真正的意义,以上问题称为SQL注入。要解决SQL注入就不能让用户输入的密码和我们的SQL语句进行简单的字符串拼接。 五. PreparedSatement预编译对象继承结构: 5.1 PreparedSatement的执行原理我们写的SQL语句让数据库执行,数据库不是直接执行SQL语句字符串。和Java一样,数据库需要执行编译后的SQL语句(类似Java编译后的字节码文件)。 Satement对象每执行一条SQL语句都会先将这条SQL语句发送给数据库编译,数据库再执行。 123Statement stmt = conn.createStatement();stmt.executeUpdate("INSERT INTO users VALUES (1, '张三', '123456')");stmt.executeUpdate("INSERT INTO users VALUES (2, '李四', '666666')"); 上面2条SQL语句我们可以看到大部分内容是相同的,只是数据略有不一样。数据库每次执行都编译一次。如果有1万条类似的SQL语句,数据库需要编译1万次,执行1万次,显然效率就低了。 prepareStatement()会先将SQL语句发送给数据库预编译。PreparedStatement会引用着预编译后的结果。可以多次传入不同的参数给PreparedStatement对象并执行。相当于调用方法多次传入不同的参数。 12345678910111213141516//将原来的SQL语句变成带"?"的SQL语句,这种SQL语句叫做参数化的SQL语句,要传参数的地方使用"?"代替String sql = "INSERT INTO users VALUES (?, ?, ?)";// 会先将SQL语句发送给数据库预编译。PreparedStatement会引用着预编译后的结果。PreparedStatement pstmt = conn.prepareStatement(sql);//进行预编译,会在预编译的时候就确定你这个SQL语句的格式。// 设置参数(设置"?"处的值)pstmt.setInt(1, 1);pstmt.setString(2, "张三");pstmt.setString(3, "123456");pstmt.executeUpdate();// 再次设置参数pstmt.setSInt(1, 2);pstmt.setString(2, "李四");pstmt.setString(3, "66666");pstmt.executeUpdate(); 上面预编译好一条SQL,2次传入了不同的参数并执行。如果有1万条类似的插入数据的语句。数据库只需要预编译一次,传入1万次不同的参数并执行。减少了SQL语句的编译次数,提高了执行效率。 示意图 5.2 PreparedSatement的好处 prepareStatement()会先将SQL语句发送给数据库预编译。PreparedStatement会引用着预编译后的结果。可以多次传入不同的参数给PreparedStatement对象并执行。减少SQL编译次数,提高效率。 安全性更高,没有SQL注入的隐患。 提高了程序的可读性 5.2 PreparedSatement的基本使用5.2.1 API介绍5.2.1.1 获取PreparedSatement的API介绍在java.sql.Connection有获取PreparedSatement对象的方法12PreparedStatement prepareStatement(String sql) 会先将SQL语句发送给数据库预编译。PreparedStatement对象会引用着预编译后的结果。 5.2.1.2 PreparedSatement的API介绍在java.sql.PreparedStatement中有设置SQL语句参数,和执行参数化的SQL语句的方法 12void setDouble(int parameterIndex, double x) 将指定参数设置为给定 Java double 值。 12void setFloat(int parameterIndex, float x) 将指定参数设置为给定 Java REAL 值。 12void setInt(int parameterIndex, int x) 将指定参数设置为给定 Java int 值。 12void setLong(int parameterIndex, long x) 将指定参数设置为给定 Java long 值。 12void setObject(int parameterIndex, Object x) 使用给定对象设置指定参数的值。 12void setString(int parameterIndex, String x) 将指定参数设置为给定 Java String 值。 12ResultSet executeQuery() 在此 PreparedStatement 对象中执行 SQL 查询,并返回该查询生成的ResultSet对象。 12int executeUpdate() 在此 PreparedStatement 对象中执行 SQL 语句,该语句必须是一个 SQL 数据操作语言(Data Manipulation Language,DML)语句,比如 INSERT、UPDATE 或 DELETE 语句;或者是无返回内容的 SQL 语句,比如 DDL 语句。 5.3.2 PreparedSatement使用步骤 编写SQL语句,未知内容使用?占位:"SELECT * FROM user WHERE name=? AND password=?;"; 获得PreparedStatement对象 设置实际参数 执行参数化SQL语句 关闭资源 5.3.3 案例代码12345678910111213141516171819202122232425262728293031public class Demo08 { public static void main(String[] args) throws Exception { // 获取连接 Connection conn = JDBCUtils.getConnection(); // 编写SQL语句,未知内容使用?占位 String sql = "SELECT * FROM user WHERE name=? AND password=?;"; // prepareStatement()会先将SQL语句发送给数据库预编译。 PreparedStatement pstmt = conn.prepareStatement(sql); // 指定?的值 // parameterIndex: 第几个?,从1开始算 // x: 具体的值 pstmt.setString(1, "admin"); pstmt.setString(2, "123"); // 正确的密码 // pstmt.setString(2, "6666"); // 错误的密码 ResultSet rs = pstmt.executeQuery(); if (rs.next()) { String name = rs.getString("name"); System.out.println("name:" + name); } else { System.out.println("没有找到数据..."); } JDBCUtils.close(conn, pstmt, rs); }} 5.3.4 案例效果 输入正确的账号密码: 输入错误的密码: 5.4 PreparedSatement实现增删查改5.4.1 添加数据向Employee表添加3条记录123456789101112131415161718192021222324252627282930// 添加数据: 向Employee表添加3条记录public static void addEmployee() throws Exception { Connection conn = JDBCUtils.getConnection(); String sql = "INSERT INTO employee VALUES (NULL, ?, ?, ?);"; // prepareStatement()会先将SQL语句发送给数据库预编译。 PreparedStatement pstmt = conn.prepareStatement(sql); // 设置参数 pstmt.setString(1, "刘德华"); pstmt.setInt(2, 57); pstmt.setString(3, "香港"); int i = pstmt.executeUpdate(); System.out.println("影响的行数:" + i); // 再次设置参数 pstmt.setString(1, "张学友"); pstmt.setInt(2, 55); pstmt.setString(3, "澳门"); i = pstmt.executeUpdate(); System.out.println("影响的行数:" + i); // 再次设置参数 pstmt.setString(1, "黎明"); pstmt.setInt(2, 52); pstmt.setString(3, "香港"); i = pstmt.executeUpdate(); System.out.println("影响的行数:" + i); JDBCUtils.close(conn, pstmt);} 效果: 5.4.2 修改数据将id为2的学生地址改成台湾12345678910111213// 修改数据: 将id为2的学生地址改成台湾public static void updateEmployee() throws Exception { Connection conn = JDBCUtils.getConnection(); String sql = "UPDATE employee SET address=? WHERE id=?;"; PreparedStatement pstmt = conn.prepareStatement(sql); pstmt.setString(1, "台湾"); pstmt.setInt(2, 2); int i = pstmt.executeUpdate(); System.out.println("影响的行数:" + i); JDBCUtils.close(conn, pstmt);} 效果: 5.4.3 删除数据删除id为2的员工123456789101112// 删除数据: 删除id为2的员工public static void deleteEmployee() throws Exception { Connection conn = JDBCUtils.getConnection(); String sql = "DELETE FROM employee WHERE id=?;"; PreparedStatement pstmt = conn.prepareStatement(sql); pstmt.setInt(1, 2); int i = pstmt.executeUpdate(); System.out.println("影响的行数:" + i); JDBCUtils.close(conn, pstmt);} 效果: 5.4.4 查询数据查询id小于8的员工信息,并保存到员工类中12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152public class Employee { private int id; private String name; private int age; private String address; public Employee() { } public Employee(int id, String name, int age, String address) { this.id = id; this.name = name; this.age = age; this.address = address; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } @Override public String toString() { return "Employee2 [id=" + id + ", name=" + name + ", age=" + age + ", address=" + address + "]"; }} 12345678910111213141516171819202122232425262728293031// 查询数据: 查询id小于8的员工信息,并保存到员工类中public static void queryEmployee() throws Exception { Connection conn = JDBCUtils.getConnection(); String sql = "SELECT * FROM employee WHERE id<?;"; PreparedStatement pstmt = conn.prepareStatement(sql); pstmt.setInt(1, 26); ResultSet rs = pstmt.executeQuery(); // 创建集合存放多个Employee2对象 ArrayList<Employee> list = new ArrayList<>(); while (rs.next()) { // 移动到下一行有数据,取出这行数据 int id = rs.getInt("id"); String name = rs.getString("name"); int age = rs.getInt("age"); String address = rs.getString("address"); // 创建Employee2对象 Employee e = new Employee(id, name, age, address); // 将创建好的员工添加到集合中 list.add(e); } // 输出对象 for (Employee e : list) { System.out.println(e); } JDBCUtils.close(conn, pstmt, rs);} 效果: 六. 案例:PreparedStatement改造登录案例6.1 案例需求模拟用户输入账号和密码登录网站,防止SQL注入 6.2 案例效果 输入正确的账号,密码,显示登录成功 输入错误的账号,密码,显示登录失败 输入"a' or '1'='1"作为密码,解决SQL注入: 6.3 案例分析 使用数据库保存用户的账号和密码 让用户输入账号和密码 编写SQL语句,账号和密码部分使用?占位 使用PreparedSatement给?设置参数 使用PreparedSatement执行预编译的SQL语句 如果查询到数据,说明登录成功 如果查询不到数据,说明登录失败 6.4 实现步骤 编写代码让用户输入账号和密码 12345678public class Demo07 { public static void main(String[] args) { Scanner sc = new Scanner(System.in); System.out.println("请输入账号: "); String name = sc.nextLine(); System.out.println("请输入密码: "); String password = sc.nextLine();} 编写SQL语句,账号和密码部分使用?占位,使用PreparedSatement给?设置参数,使用PreparedSatement执行预编译的SQL语句 123456789101112131415161718192021public class Demo11 { public static void main(String[] args) throws Exception { // 让用户输入账号和密码 Scanner sc = new Scanner(System.in); System.out.println("请输入账号: "); String name = sc.nextLine(); System.out.println("请输入密码: "); String password = sc.nextLine(); // 获取连接 Connection conn = JDBCUtils.getConnection(); // 编写SQL语句,账号和密码使用?占位 String sql = "SELECT * FROM user WHERE name=? AND password=?;"; // 获取到PreparedStatement对象 PreparedStatement pstmt = conn.prepareStatement(sql); // 设置参数 pstmt.setString(1, name); pstmt.setString(2, password); // pstmt.setString(2, "a' or '1'='1"); }} 如果查询到数据,说明登录成功,如果查询不到数据,说明登录失败 1234567891011121314151617181920212223242526272829303132333435public class Demo11 { public static void main(String[] args) throws Exception { // 让用户输入账号和密码 Scanner sc = new Scanner(System.in); System.out.println("请输入账号: "); String name = sc.nextLine(); System.out.println("请输入密码: "); String password = sc.nextLine(); // 获取连接 Connection conn = JDBCUtils.getConnection(); // 编写SQL语句,账号和密码使用?占位 String sql = "SELECT * FROM user WHERE name=? AND password=?;"; // 获取到PreparedStatement对象 PreparedStatement pstmt = conn.prepareStatement(sql); // 设置参数 pstmt.setString(1, name); pstmt.setString(2, password); // pstmt.setString(2, "a' or '1'='1"); // 如果查询到数据,说明登录成功,如果查询不到数据,说明登录失败 ResultSet rs = pstmt.executeQuery(); if (rs.next()) { // 能进来查询到了数据. String name2 = rs.getString("name"); System.out.println("欢迎您," + name2); } else { // 查询不到数据,说明登录失败 System.out.println("账号或密码错误..."); } JDBCUtils.close(conn, pstmt, rs); }}]]></content>
<categories>
<category>Javaweb基础</category>
</categories>
<tags>
<tag>JDBC</tag>
<tag>数据库</tag>
</tags>
</entry>
<entry>
<title><![CDATA[MySQL约束]]></title>
<url>%2F2019%2F04%2F21%2FJavaweb%E5%9F%BA%E7%A1%80_MySQL%E7%BA%A6%E6%9D%9F%2F</url>
<content type="text"><![CDATA[数据库约束作用 : 保证数据的正确性、有效性和完整性。 功能 : 主键 : 规范ID字段 包括自增 非空 唯一 default : 设置字段默认值 外键 : 数据关联的两张表, 可以设置同时删除, 同时添加, 以及不允许不关联数据加入, 也可以设置同时不允许增删 一. 数据库约束约束种类: PRIMARY KEY: 主键 UNIQUE: 唯一 NOT NULL: 非空 DEFAULT: 默认 FOREIGN KEY: 外键 1.1 主键 : primary key作用 : 约束字段值唯一 约束字段不能包含null 使用 : 在创建表的时候给字段添加主键字段名 字段类型 PRIMARY KEY 在已有表中添加主键ALTER TABLE 表名 ADD PRIMARY KEY(字段名); 删除字段ALTER TABLE 表名 DROP PRIMARY KEY; 主键自增 12345CREATE TABLE 表名 ( id INT PRIMARY KEY AUTO_INCREMENT, 字段1 字段类型, 字段2 字段类型); 1.2 唯一 : unique作用 : 约束字段的值不能重复 使用 : 1234CREATE TABLE 表名 ( 字段1 字段类型, 字段 字段类型 UNIQUE); 1.3 非空 : not null作用 : 约束字段的值不能是NULL 使用 : 12345CREATE TABLE 表名 ( id INT, NAME VARCHAR(20) NOT NULL, gender CHAR(2)); 1.4 默认值 : default作用 : 如果字段没有指定值, 就使用默认值 使用 : 12345CREATE TABLE st9 ( id INT, NAME VARCHAR(20), address VARCHAR(50) DEFAULT '广州'); 1.5 外键1.5.2 外键概述背景 : 单表很容易出现很多重复的数据(数据冗余), 如 解决方案 : 将一张表分成2张表(员工表和部门表) 引入问题 : 当我们在employee的dep_id里面输入不存在的部门,数据依然可以添加.但是并没有对应的部门,不能出现这种情况。employee的dep_id中的内容只能是department表中存在的id 外键作用 : 主表约束副表 主表 : 被指向的表 是一对多的 1端(抽象端) 副表/从表 : 有外键的表 使用主表数据 被主表约束 1.5.2 创建外键 : 新建表时增加外键:[CONSTRAINT] [外键约束名称] FOREIGN KEY(外键字段名) REFERENCES 主表名(主键字段名)关键字解释:CONSTRAINT – 约束关键字,用于给外键命名,如果创建的时候不给外键命名,外键会默认命名。FOREIGN KEY(外键字段名) –- 某个字段作为外键REFERENCES – 主表名(主键字段名) 表示参照主表中的某个字段 已有表增加外键:ALTER TABLE 从表 ADD [CONSTRAINT] [外键约束名称] FOREIGN KEY (外键字段名) REFERENCES 主表(主键字段名); 具体操作: 副表/从表: 被别人约束,表结构添加外键约束 删除副表/从表 employee 创建从表 employee 并添加外键约束 123456789CREATE TABLE employee ( id INT PRIMARY KEY AUTO_INCREMENT, NAME VARCHAR(20), age INT, dep_id INT, -- 添加一个外键 -- 外键取名一般fk结尾 CONSTRAINT emp_fk FOREIGN KEY(dep_id) REFERENCES department(id)); 1.5.3 外键删除根据外键名进行删除,记住:不是外键字段名 ALTER TABLE 从表 drop foreign key 外键名称; 具体操作: 删除employee表的emp_depid_ref_dep_id_fk外键 1ALTER TABLE employee DROP FOREIGN KEY emp_depid_ref_dep_id_fk; 在employee表情存在况下添加外键 1ALTER TABLE employee ADD CONSTRAINT emp_depid_ref_dep_id_fk FOREIGN KEY(dep_id) REFERENCES department(id); 1.5.4 外键的级联背景 : 两张表有外键关联, 默认没有级联, 也就是双方数据都不能同时修改 什么是级联操作:在修改和删除主表的主键时,同时更新或删除副表的外键值,称为级联操作ON UPDATE CASCADE – 级联更新,主键发生更新时,外键也会更新ON DELETE CASCADE – 级联删除,主键发生删除时,外键也会删除 二. 表关系2.1 一对多 2.2 多对多理解 : 多张一对多表 2.3 一对一 三. 三范式3.1 什么是范式范式是指:设计数据库表的规则(Normal Form) 好的数据库设计对数据的存储性能和后期的程序开发,都会产生重要的影响。建立科学的,规范的数据库就需要满足一些规则来优化数据的设计和存储 3.2 范式的基本分类 目前关系数据库有六种范式:第一范式(1NF)、第二范式(2NF)、第三范式(3NF)、巴斯-科德范式(BCNF)、第四范式(4NF)和第五范式(5NF,又称完美范式)。满足最低要求的范式是第一范式(1NF)。在第一范式的基础上进一步满足更多规范要求的称为第二范式(2NF),其余范式以次类推。一般说来,数据库只需满足第三范式(3NF)就行了。 3.3 三范式第一范式 : 每一列不能再拆分 总结:如果不遵守第一范式,查询出数据还需要进一步处理(查询不方便)。遵守第一范式,需要什么字段的数据就查询什么数据(方便查询)。 第二范式: 一张表只描述一件事情 表中的每一个字段都依赖于主键 总结:如果不准守第二范式,数据冗余,相同数据无法区分。遵守第二范式减少数据冗余,通过主键区分相同数据。 第三范式:从表的外键必须使用主表的主键 总结:如果不准守第三范式,可能会有相同数据无法区分,修改数据的时候多张表都需要修改(不方便修改)。遵守第三范式通过id可以区分相同数据,修改数据的时候只需要修改一张表(方便修改)。]]></content>
<categories>
<category>Javaweb基础</category>
</categories>
<tags>
<tag>数据库</tag>
<tag>MySQL</tag>
</tags>
</entry>
<entry>
<title><![CDATA[MySQL基础语法]]></title>
<url>%2F2019%2F04%2F19%2FJavaweb%E5%9F%BA%E7%A1%80_MySQL%E5%9F%BA%E7%A1%80%E8%AF%AD%E6%B3%95%2F</url>
<content type="text"><![CDATA[SQL语句分类 DDL(Data Definition Language) 数据定义语言 : 用于创建, 修改, 删除(数据库和表) DML(Data Manipulation Language) 数据操作语言 对表中数据进行增删改 DQL(Data Query Language) 数据查询语言 查询数据, 用的最多 DCL(Data Control Languge)数据控制语言 数据库的访问权限和安全级别 TCL(Transaction Control Language) 事物控制语言 用于控制数据库事物操作 MySQL基础语法1.1 DDL语句 : 库与表的操作 数据库 : 基本上就是增删库, 用的很少 表 : 创建字段, 以及修改字段 1.2 DML语句 : 增删数据 补充 : 增添数据一直往下, 没有where条件 修改和插入数据需要指明条件 删除数据 : 123delete from 表名 -- 一般加上where -- 一行一行删除TRUNCATE TABLE stu2 -- 删除表 再创结构相同的表 1.3 DQL语句 : 表数据查询1.3.1 基础查询 :作用 : 查询表中数据 //主要掌握where 1select 字段1,字段2 from 表1 说明 : *代表所有字段 此处字段代表查询显示的字段 修饰字段 distinc 字段去重 //distinct 字段1,字段2 字段1和字段2同时重复才去 as 别名 //as可以删除 ifnull(字段, 0) //字段值为null 默认不进行算数 需要设null为0 一般后天添加where where补充 : like 和 ‘%’ 和 ‘_’ 进行模糊查询%: 表示0个或多个字符(任意个字符)_ : 表示一个字符 比较运算>大于<小于<=小于等于>=大于等于=等于<>、!=不等于 逻辑运算and(&&) 多个条件同时满足or(||) 多个条件其中一个满足not(!) 不满足 范围 比如:age BETWEEN 80 AND 100相当于: age>=80 && age<=100 1.3.2 排序作用 : 对字段下面的数据进行排序查询, 不修改数据 降序 : DESC 升序 : ASC 用法 : 1select (显示的)字段 from 表1 order by 字段1 desc,字段2 desc 1.3.3 聚合函数作用 : 对字段下面的数据进行统计 用法 : 1select count(字段) from 表1 补充 : IFNULL(expr1, expr2):假如expr1 不为 NULL,则 IFNULL() 的返回值为 expr1; 否则其返回值为expr2 聚合函数得到一个值, 因此显示字段写很多, 也只是显示第一行的 五个聚合函数:count: 统计指定列记录数,记录为NULL的不统计sum: 计算指定列的数值和,如果不是数值类型,那么计算结果为0max: 计算指定列的最大值min: 计算指定列的最小值avg: 计算指定列的平均值,如果不是数值类型,那么计算结果为0 1.3.4 分组作用 : 字段分组统计, 结合聚合函数 用法 : group by 字段 显示该字段下所有单独特性 //比如sex字段 显示两行男女 配合聚合函数 可以分别求出单独特性的值 1select sex,count(sex) from 表1 group by sex 解释 : group by 对sex 进行分组 共男女两组 count(sex) 分别对两组数据进行计数, 并可作为显示字段 sex 显示男女字段 表格更加可视 使用 : 1select 字段1,字段2 from 表名 [where分组前过滤] group by 分组字段 [having分组条件] having与where的区别 having是在分组后对数据进行过滤,写在group by的后面 where是在分组前对数据进行过滤,写在group by的前面 having后面可以使用聚合函数 where后面不可以使用聚合函数 1.3.5 限制查询作用 : 限制查询记录条数 (查太多电脑卡死) 使用 : 1select *from limit offset, length --或者limit length offset是指偏移量,可以认为是跳过的记录数量,默认为0length是指需要显示的总记录数 总结 : SELECT *|字段列表 [as 别名] FROM 表名 [WHERE子句] [GROUP BY子句][HAVING子句][ORDER BY子句][LIMIT子句]; 补充 : limit子句为什么排在最后?因为前面所有的限制条件都处理完了,只剩下显示多少条记录的问题了! 1.3.6 连接作用 : 多张表 在同一张表显示 内连接 : 如果有 null , 那么此行数据不显示 12select 字段 from 左表, 右表 where 关联条件select 字段 from 左表 inner join 右表 on 关联条件 外连接 : 可以指定左右表,哪个必须显示的 12select 字段 from 左表 left outer join 右表 on 关联条件 //左表必须显示的select 字段 from 左表 right outer join 右表 on 关联条件 //右表必须显示的 补充 : 字段处 可以选择显示多少字段 如: 字段1.字段2,字段3 1.3.6 子查询子查询 : where 条件(子句) //嵌套查询 子句结果为一个值 (XXX) 直接使用 子句结果为单列多行 用in(子句) 子句结果为多行多列 放在from 后面 当做引用表使用 1SELECT * FROM dept d, (SELECT * FROM emp WHERE join_date > '2011-1-1') e WHERE e.dept_id = d.id; 总结 : 子查询结果只要是单列,肯定在WHERE后面作为条件SELECT 查询字段 FROM 表 WHERE 字段=(子查询); 子查询结果只要是多列,肯定在FROM后面作为表SELECT 查询字段 FROM (子查询) 表别名 WHERE 条件; 1.4 TCL语句 : 事物安全1.4.1 概述背景 : 当进行转账时, 需要扣款和收款同时进行 事物安全 : 确保两个事件同时进行 实现原理 : 开启事务 -> 进行转账 -> 数据进入中间缓存 -> 提交事务 -> 完成 回滚事务 : 传输错误 -> 回滚(删除中间缓存) 图解 : 1.4.2 DOS执行事务 事务有关的SQL语句:| SQL语句 | 描述 || —————— | ——– || start transaction; | 开启事务 || commit; | 提交事务 || rollback; | 回滚事务 | 补充 : MySQL服务器默认每条数据自动进行提交一次, 因此我们开启事物,也就是关闭自动提交. 执行set autocommit = 0, 会关闭自动执行 1.4.3 回滚点背景 : 为了防止后续操作失败, 需要在当前成功位置设置回滚点(存档) 设置回滚点语法:savepoint 回滚点名字;回到回滚点语法: rollback to 回滚点名字; 1.4.4 事务的四大特性 事务特性 含义 原子性(Atomicity) 事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生。 一致性(Consistency) 事务前后数据的完整性必须保持一致 隔离性(Isolation) 是指多个用户并发访问数据库时,一个用户的事务不能被其它用户的事务所干扰,多个并发事务之间数据要相互隔离,不能相互影响。 持久性(Durability) 指一个事务一旦被提交,它对数据库中数据的改变就是永久性的,接下来即使数据库发生故障也不应该对其有任何影响 记忆 : 原子性 : 转账业务,扣款和收款是一个操作, 不可分割 一致性 : 作用是数据同时增删 隔离性 : 就算是并发, 也不会脏读 持久性 : 提交好, 数据库值改变.网络异常也无关了 1.4.5 事务隔离级别 级别 名字 隔离级别 脏读 不可重复读 幻读 数据库默认隔离级别 1 读未提交 read uncommitted 是 是 是 2 读已提交 read committed 否 是 是 Oracle和SQL Server 3 可重复读 repeatable read 否 否 是 MySQL 4 串行化 serializable 否 否 否 脏读:两事物, 权限设置最低, 一个事务读取到了另外一个事务未提交的数据 不可重复读:两事物, 权限设置过高, 事务1提交了, 因为前后值也必须必保持一致, 事务2值还是不变, 需要事务2也提交. 幻读:一次事务中,两次查询到的数据条数不一致,由于delete或者insert引起的 1.5 DCL语句 : 权限管理 创建用户 CREATE USER '用户名'@'主机名' IDENTIFIED BY '密码'; 给用户授权 GRANT 权限1, 权限2... ON 数据库名.表名 TO '用户名'@'主机名'; 撤销权限 REVOKE 权限1, 权限2... ON 数据库.表名 FROM '用户名'@'主机名'; 查看授权 SHOW GRANTS FOR '用户名'@'主机名'; 删除用户 DROP USER '用户名'@'主机名'; 修改用户密码 mysqladmin -uroot -p password 新密码 -- 新密码不需要加上引号 注意:需要在未登陆MySQL的情况下操作。 set password for '用户名'@'主机名' = password('新密码'); 注意:需要在登陆MySQL的情况下操作。]]></content>
<categories>
<category>Javaweb基础</category>
</categories>
<tags>
<tag>数据库</tag>
<tag>MySQL</tag>
</tags>
</entry>
<entry>
<title></title>
<url>%2F2019%2F04%2F18%2F%E4%B8%80.%20%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3JVM%2F</url>
<content type="text"><![CDATA[一. 深入理解JVM1.1 java技术与java虚拟机 java技术 : java编程语言, java类文件格式, java虚拟机和java API java]]></content>
</entry>
<entry>
<title><![CDATA[Stream流]]></title>
<url>%2F2019%2F04%2F14%2FJava%E5%9F%BA%E7%A1%80_Stream%E6%B5%81%2F</url>
<content type="text"><![CDATA[一. 四个常见的函数式接口1. Supplier(生产者)123456/** * Gets a result. * * @return a result */T get(); 2. Consumer(消费者)123456789public interface Consumer<T> { /** * Performs this operation on the given argument. * * @param t the input argument */ void accept(T t);} 3. Predicate(判断)1234567891011public interface Predicate<T> { /** * Evaluates this predicate on the given argument. * * @param t the input argument * @return {@code true} if the input argument matches the predicate, * otherwise {@code false} */ boolean test(T t);} 4. Function(转换)1234567891011public interface Function<T, R> { /** * Applies this function to the given argument. * * @param t the function argument * @return the function result */ R apply(T t); // 将T类型的数据, 转换R类型} Function<T, R> : T和R可以是相同的数据类型 5. 使用方法函数式接口 : 需要定义一个方法 传入函数式接口实现对象做参数 参数调用方法 主方法调用方法 然后再在定义方法怎么实现的 Stream流 : 方法调用已经写好了, 只需要stream.方法名(方法体).方法名(方法体) 二. 深入Java源码123default Consumer<T> andThen(Consumer<? super T> after) { // after的非空判断 Objects.requireNonNull(after); return (T t) -> { this.accept(t); after.accept(t); }; // 转换成匿名内部类 return new Consumer<T> () { @Override public void accept(T t) { this.accept(t); after.accept(t); } }; } /*andThen方法是Consumer接口中的默认方法, 这个方法要执行起来就需要Consumer接口的实现类对象(1)来调用,andThen方法的参数列表是Consumer, 所以传入的参数是 Consumer接口的实现类对象(2),andThen方法的返回值类型是Consumer, 所以方法执行结束, 返回的是 Consumer接口的实现类对象(3) this: 谁来调用我, 我就代表谁; 谁来调用andThen方法, 我就代表谁 this: Consumer接口的实现类对象(1) after: Consumer接口的实现类对象(2) 先使用调用方法的Consumer消费了一次t, 再使用传入参数的Consumer又消费了一次t 使用返回的 Consumer接口的实现类对象(3), 调用自己的accept(T t), 才会传入一个t 传入的t, 就是上面两个Consumer要消费的t. */12345678```Javadefault <V> Function<T, V> andThen(Function<? super R, ? extends V> after) { Objects.requireNonNull(after); // 先执行参数中的方法 // this.apply(t) : T -> R // 在执行after.apply() : R -> V return (T t) -> after.apply(apply(t)); // T -> R -> V } 三. Stream流1. Stream流的引入1234567891011121314151617181920212223242526272829303132333435/* 1. 首先筛选所有姓张的人; 2. 然后筛选名字有三个字的人; 3. 最后进行对结果进行打印输出。*/// 老式的循环操作List<String> list = new ArrayList<>(); list.add("张无忌"); list.add("周芷若"); list.add("赵敏"); list.add("张强"); list.add("张三丰");// 创建新集合 ArrayList<String> zhangList = new ArrayList<>(); // 遍历老集合 for (String s : list) { // 判断如果以张为开头, 就放入新集合汇总 if (s.startsWith("张")) { zhangList.add(s); } } // 创建新集合 ArrayList<String> zhangThreeList = new ArrayList<>(); // 遍历姓张集合 for (String s : zhangList) { // 如果是3个字, 添加到新集合中 if (s.length() == 3) { zhangThreeList.add(s); } } for (String s : zhangThreeList) { System.out.println(s); } Stream流的操作方式 1234// 使用Stream流的方式list.stream().filter(s -> s.startsWith("张")) .filter(s -> s.length() == 3) .forEach(System.out::println); Stream流相比老式的循环操作优点在哪? 代码简介, 思路清晰 2. 获取流的方式 集合, 数组, 一堆数据 都可获取对应的流(Stream) 12345678910111213141516171819202122232425262728293031// 集合 // List ArrayList<String> list = new ArrayList<>(); Stream<String> stream1 = list.stream(); // Set HashSet<String> set = new HashSet<>(); Stream<String> stream2 = set.stream(); // Map HashMap<String, Integer> map = new HashMap<>(); // 获取Map所有键的流 Stream<String> stream3 = map.keySet().stream(); // 获取Map所有值的流 Stream<Integer> stream4 = map.values().stream(); // 获取Map所有键值对的流 Stream<Map.Entry<String, Integer>> stream5 = map.entrySet().stream(); // 数组 // Stream流的泛型和容器中元素的类型是一致的 // int[]不能获取流, 如果获取, 会得到的是Stream<int[]> String[] arr = new String[3]; Stream<String> stream6 = Stream.of(arr); Integer[] arr2 = new Integer[3]; Stream<Integer> stream7 = Stream.of(arr2); // 一堆数据 Stream<Integer> stream8 = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9); 3. 流中的功能(1) forEach123456void forEach(Consumer<? super T> action) : 将流中的内容, 拿出来, 由我们传入参数的Consumer来定义消费的方式 // forEach stream.forEach(t -> System.out.println(t)); (2) filter123456Stream<T> filter(Predicate<? super T> predicate) : 使用流中的每一个元素, 做判断, 如果返回的是true, 这个元素就留在流中; 如果返回的是false, 这个元素就删除. // 使用方式stream.filter(t -> t.length() == 4).forEach(System.out::println); (3) count123long count() : 获取流中元素的个数 // 使用方式 System.out.println(stream.count()); 注意事项 IllegalStateException: stream has already been operated upon or closed 非法的状态异常: 流已经被操作过或者已经被关闭 终结方法 终结方法, 执行了这个方法之后, 流就被关闭了. forEach(), count(), collect() 终结方法的特点: 方法的返回值类型不是Stream (4) limit和skip12Stream<T> limit(long maxSize) : 获取前n个Stream<T> skip(long n): 跳过前n个 (5) map1234567891011121314151617<R> Stream<R> map(Function<T, R> mapper): 将原来流中T类型的每一个数据, 都转换成R类型 // 将数字格式的字符串, 转换Integer Stream<String> stream = Stream.of("123", "10", "456"); // stream.map(t -> Integer.parseInt(t)) stream.map(Integer::parseInt).forEach(t -> System.out.println(t + 100)); // ============================================================================== Stream<String> stream = Stream.of("张三丰", "张萎", "张王李赵", "迪丽热巴", "阿不都沙拉木", "李四" , "张无忌", "张翠山", "张国荣"); // 将字符串, 转换成Person类型 // stream.map(t -> new Person(t)) stream.map(Person::new).forEach(System.out::println); (6) concat1234567891011public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b) 合并参数中的两个流 Stream<String> stream1 = Stream.of("张智霖"); Stream<String> stream2 = Stream.of("袁咏仪"); // 合并两个流 Stream<String> stream = Stream.concat(stream1, stream2); stream.forEach(System.out::println); (7) parallel1234Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);// 转换成并发流stream.parallel().forEach(System.out::println); (8) collect 将流中的内容, 收集到集合(List, Set)中 123456789Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);List<Integer> list = stream.collect(Collectors.toList());System.out.println(list);/*Set<Integer> set = stream.collect(Collectors.toSet());System.out.println(set);*/ 四. Stream的练习1. 练习一123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263package com.itheima._04test;import com.itheima._03stream.Person;import java.util.ArrayList;import java.util.List;import java.util.stream.Stream;public class Demo01_Stream { /* 1. 第一个队伍只要名字为3个字的成员姓名; 2. 第一个队伍筛选之后只要前3个人; 3. 第二个队伍只要姓张的成员姓名; 4. 第二个队伍筛选之后不要前2个人; 5. 将两个队伍合并为一个队伍; 6. 根据姓名创建Person 对象; 7. 打印整个队伍的Person对象信息。 */ public static void main(String[] args) { List<String> one = new ArrayList<>(); one.add("迪丽热巴"); one.add("宋远桥"); one.add("苏星河"); one.add("老子"); one.add("庄子"); one.add("孙子"); one.add("洪七公"); List<String> two = new ArrayList<>(); two.add("古力娜扎"); two.add("张无忌"); two.add("张三丰"); two.add("赵丽颖"); two.add("张二狗"); two.add("张天爱"); two.add("张三"); /* 1. 第一个队伍只要名字为3个字的成员姓名; 2. 第一个队伍筛选之后只要前3个人; */ Stream<String> stream1 = one.stream().filter(s -> s.length() == 3).limit(3); /* 3. 第二个队伍只要姓张的成员姓名; 4. 第二个队伍筛选之后不要前2个人; */ Stream<String> stream2 = two.stream().filter(s -> s.startsWith("张")).skip(2); /* 5. 将两个队伍合并为一个队伍; 6. 根据姓名创建Person 对象; 7. 打印整个队伍的Person对象信息。 */ Stream.concat(stream1, stream2).map(s -> new Person(s)).forEach(System.out::println); }} 2. 练习二1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950package com.itheima._04test;import java.util.Collections;import java.util.List;import java.util.stream.Collectors;import java.util.stream.Stream;/* 现在有两个List集合存储队伍当中的多个成员姓名,要求使用Stream流的方式依次进行以下若干操作步骤: 第一个队伍:{"tom", "kobe", "jordan", "tracy","westbook","yaoming","ace","stephen"} 第二个队伍:{"beyonce", "jay_z", "adele", "taylor","bieber","ladygaga"} 1:第一个队伍只要名字长度大于4的前三名 2:第二个队伍只要名字中带有a的;筛选后不要第一个 3:将两个队伍合并为一个队伍,并最终转换为List集合 4:将最终的队伍中的名字按照名字的字母降序排列(非自然顺序,与自然排序相反,如zyx...cba) 5:使用foreach(增强for)进行遍历打印最终的名字 */public class Demo02_Stream { public static void main(String[] args) { Stream<String> stream1 = Stream.of("tom", "kobe", "jordan", "tracy", "westbrook", "yaoming", "ace", "stephen"); Stream<String> stream2 = Stream.of("beyonce", "jay_z", "adele", "taylor", "bieber", "ladygaga"); // 1:第一个队伍只要名字长度大于4的前三名 Stream<String> one = stream1.filter(s -> s.length() > 4).limit(3); // 2:第二个队伍只要名字中带有a的;筛选后不要第一个 Stream<String> two = stream2.filter(s -> s.contains("a")).skip(1); // 3:将两个队伍合并为一个队伍,并最终转换为List集合 List<String> list = Stream.concat(one, two).collect(Collectors.toList()); // 4:将最终的队伍中的名字按照名字的字母降序排列(非自然顺序,与自然排序相反,如zyx...cba) Collections.sort(list, ((o1, o2) -> o2.compareTo(o1))); // 5:使用foreach(增强for)进行遍历打印最终的名字 for (String s : list) { System.out.println(s); } }}]]></content>
<categories>
<category>Java基础</category>
</categories>
<tags>
<tag>JDK 8</tag>
<tag>javase</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Lambda表达式&方法引用]]></title>
<url>%2F2019%2F04%2F14%2FJava%E5%9F%BA%E7%A1%80_Lambda%E8%A1%A8%E8%BE%BE%E5%BC%8F%26%E6%96%B9%E6%B3%95%E5%BC%95%E7%94%A8%2F</url>
<content type="text"><![CDATA[一. 函数式接口1. 语法糖 一种语法, 让以前的代码格式变得更加简便. 语法糖前提: 在不改变底层逻辑的基础上, 让代码变得简便. 例如: 增强for, 自动装拆箱, 泛型 lambda是不是语法糖? 匿名内部类的方式会生成.class文件, lambda不会生成.class文件, 所以Lambda表达式只能够称之为应用层面上的语法糖, 但是Lambda已经改变了底层的逻辑. 2. 函数式接口注解 注解: @xxxxx, 对下面的代码, 给一个标签, 用来标注下面的代码的作用 @Override: 用来标注下面的代码一定是重写的方法 @Deprecated: 用来标注下面的代码已经过时了 @FunctionalInterface: 用来标注下面的代码一定是函数式接口 函数式接口的注解 函数式接口: 有且只有一个抽象方法的接口 注解: @FunctionalInterface 3. 自定义无参无返回的函数式接口 定义接口, 模拟方法(函数式接口作为参数), 调用方法 请定义一个函数式接口Eatable ,内含抽象eat 方法,没有参数或返回值。使用该接口作为方法的参数,并进而通过Lambda来使用它。 1234567891011121314151617181920212223242526@FunctionalInterfacepublic interface Eatable { void eat();}// ===================================public class Demo03_自定义无参无返回的函数式接口 { public static void main(String[] args) { // 调用method方法 // 匿名内部类 method(new Eatable() { @Override public void eat() { System.out.println("吃饭才能活着!~"); } }); // lambda method(() -> System.out.println("吃饭才能活着!~")); } // 创建方法, 将函数式接口作为参数 private static void method(Eatable lambda) { lambda.eat(); }} 4. 自定义有参有返回的函数式接口 请定义一个函数式接口Sumable ,内含抽象sum 方法,可以将两个int数字相加返回int结果。使用该接口作为方法 的参数,并进而通过Lambda来使用它。 12345678910111213141516171819202122232425@FunctionalInterfacepublic interface Sumable { int sum(int a, int b);}// =======================================public class Demo04_有参有返回的函数式接口 { public static void main(String[] args) { // 匿名内部类 printSum(new Sumable() { @Override public int sum(int a, int b) { return a + b; } }, 10, 20); // lambda printSum((a, b) -> a + b, 234, 432); } // 两个数字, 直接写 // 两个数字, 通过参数传递 private static void printSum(Sumable lambda, int a, int b) { System.out.println(lambda.sum(a, b)); }} 方法的参数, 又传入了一个方法 二. 函数式编程1. lambda的延迟执行1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253public class Demo01_lambda的延迟执行 { public static void main(String[] args) { String msgA = "Hello"; String msgB = "World"; String msgC = "Java"; print1(2, msgA + msgB + msgC); print3(1, () -> msgA + msgB + msgC); // 延迟执行 /*print3(1, new Logable() { @Override public String getString() { return msgA + msgB + msgC; } });*/ // print2(2, msgA, msgB, msgC); } // 使用lambda表达值 private static void print3(int level, Logable lambda) { if (1 == level) { String s = lambda.getString(); // 打印 System.out.println(s); } } /* * 问题: 无论日志信息是否需要打印, 字符串的拼接操作都已经做完了 */ private static void print1(int level, String message) { // 如果等级为1, 就打印 if (1 == level) { System.out.println(message); } } private static void print2(int level, String ... arr) { if (1 == level) { StringBuilder sb = new StringBuilder(); // 遍历数组 for (String s : arr) { sb.append(s); } System.out.println("执行了拼接操作"); System.out.println(sb); } }} 2. lambda作为方法的参数1234567891011121314151617181920public class Demo02_使用lambda作为方法的参数 { public static void main(String[] args) { // 匿名内部类 startThread(new Runnable() { @Override public void run() { System.out.println("开启了一条新线程!~"); } }); // lambda startThread(() -> System.out.println("开启了一条新线程!~")); } // 模拟 private static void startThread(Runnable lambda) { new Thread(lambda).start(); }} 3. lambda作为方法的返回值1234567891011121314151617181920212223242526272829package com.itheima._03programing.demo03;import java.util.Arrays;import java.util.Comparator;public class Demo03_lambda作为方法的返回值 { public static void main(String[] args) { String[] arr = {"abc", "tracy", "james", "kobe"}; Arrays.sort(arr, getComparator()); System.out.println(Arrays.toString(arr)); } private static Comparator<String> getComparator() { // 方法的返回值类型是函数式接口 // 匿名内部类 /*return new Comparator<String>() { @Override public int compare(String o1, String o2) { return o2.compareTo(o1); } };*/ // lambda return (o1, o2) -> o2.compareTo(o1); }} 4. Lambda即作为参数又作为返回值 请自定义一个函数式接口MySupplier ,含有无参数的抽象方法get 得到Object 类型的返回值。并使用该函数式接口分别作为方法的参数和返回值。 1234567891011121314151617181920212223242526272829303132333435363738394041@FunctionalInterfacepublic interface MySupplier { Object get();}// ================================================================public class Demo04_lambda即作为参数又作为返回值 { public static void main(String[] args) { // 匿名内部类 /*fun(new MySupplier() { @Override public Object get() { return "helloJava"; } });*/ // lambda fun(() -> "helloJava"); // System.out.println(getData()); fun(getData()); } /* lambda作为方法的参数 */ private static void fun(MySupplier lambda) { System.out.println(lambda.get()); } /* lambda作为方法的返回值 */ private static MySupplier getData() { // 返回该接口的实现类对象 /*return new MySupplier() { @Override public Object get() { return "helloWorld"; } };*/ return () -> "helloWorld"; }} 三. 方法引用1. 方法引用的概述方法引用的前提 lambda中大括号的内容, 一定是已经定义好的. :: : 双冒号, 引用运算符. 引用运算符所在的表达值, 就称之为方法引用 格式 A :: B : 使用A中的B功能 使用条件 2. 输出语句的方法引用123lambda : s -> System.out.println(s) 方法引用: System.out::println 3. 使用对象引用成员方法12345678// lambda// printString(s -> System.out.println(s.toUpperCase()));// 方法引用// 对象引用成员方法// 引用 MyMethodRef类中的 printUpper 功能// 这个方法是一个普通的成员方法, 需要对象来调用printString(new MyMethodRef()::printUpper); 4. 使用类名引用静态方法12345printAbs(a -> Math.abs(a));// abs功能已经定义好了// A:: B : 使用Math中的abs功能printAbs(Math::abs); 5. this和super引用成员方法12345678// lambdafun(() -> super.sayHello());fun(() -> this.sayHello());// 方法引用// A :: B : this/super 的 sayHellofun(this::sayHello);fun(super::sayHello); 6. 构造方法的方法引用1234567891011121314 // lambda printPerson(a -> new Person(a)); // 方法引用 , 构造方法这个功能已经定义好了 // A :: B , Person 中的 创建对象功能 printPerson(Person::new);// lambda printArray(len -> new int[len]); // 方法引用 // 使用int[] 的 创建功能 printArray(int[]::new); String中有两个方法: toUpperCase() : 将字符串中的字母转换成大写 toLowerCase() : 将字符串中的字母转换成小写]]></content>
<categories>
<category>Java基础</category>
</categories>
<tags>
<tag>JDK 8</tag>
<tag>javase</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Lambda表达式_初级]]></title>
<url>%2F2019%2F04%2F14%2FJava%E5%9F%BA%E7%A1%80_lambda%E8%A1%A8%E8%BE%BE%E5%BC%8F_%E5%88%9D%E7%BA%A7%2F</url>
<content type="text"><![CDATA[Lambda表达式一. 概述1. 为什么有Lambda表达式 面向对象思想强调的是怎么做 : 导致代码过于冗余 1234567// Thread的参数中需要传入Runnable接口的实现类对象new Thread(new Runnable() { @Override public void run() { System.out.println("开启了一条新线程!~"); }}).start(); 函数式编程 : 强调的是做什么 1new Thread(() -> System.out.println("开启了一条新线程!~2")).start(); 2. 介绍Lambda表达式的格式1. 标准格式 (参数列表) : 一些参数 -> : 一个箭头, 将左边的参数给右边使用 {} : 一段代码 123// lambda表达式中的小括号(), 其实就是匿名内部类中重写方法的参数列表// -> 正常写在() 的后面, 表示将小括号中的参数给右边大括号使用// lambda表达式中的大括号{}, 其实就是匿名内部类中重写方法的方法体 2. 可推导可省略 小括号中的数据类型可以省略 小括号中如果只有一个参数, 小括号可以省略 大括号中如果只有一条语句, 大括号和分号可以省略(一起省略) 大括号中如果只有一条语句,且还是返回语句, 大括号, 分号和return可以省略(一起省略) 3. Lambda表达式使用说明lambda使用的前提条件 必须存在一个接口, 这个接口中有且只有一个抽象方法 接口作为方法的参数 4. Lambda表达式的练习(1) 自定义无参无返回的lambda123456789101112131415// 给定一个厨子Cook 接口,内含唯一的抽象方法makeFood ,且无参数、无返回值。// 模拟创建一个方法, 让接口作为参数// 调用fun方法// 匿名内部类fun(new Cook() { @Override public void makeFood() { System.out.println("吃饭才能活着!~"); }});// lambda的标准格式fun(() -> { System.out.println("不吃饭就死啦!~");}); (2) 自定义有参有返回的lambda123456789101112131415161718192021222324252627// 给定一个计算器Calculator 接口,内含抽象方法calc 可以将两个int数字相加得到和值:public class Demo03_有参有返回的lambda { public static void main(String[] args) { // 调用invokeCalc方法来计算相加的结果 // 匿名内部类 invokeCalc(234, 432, new Calculator() { @Override public int calc(int a, int b) { return a + b; } }); // lambda , 与匿名内部类无关的都直接写下来 invokeCalc(234, 432, (int a, int b) -> { return a + b; }); } private static void invokeCalc(int a, int b, Calculator calculator) { int result = calculator.calc(a, b); System.out.println("结果是:" + result); }}// 省略格式 invokeCalc(234, 432, (a, b) -> a + b); (3) 只有一个参数的lambda12345678910111213141516171819202122232425public class Demo04_只有一个参数的lambda { public static void main(String[] args) { // 调用方法 // 匿名内部类 printAbs(new Calcable() { @Override public int getAbs(int a) { // 返回绝对值 return Math.abs(a); } }); // lambda printAbs((int a) -> { // 返回绝对值 return Math.abs(a); }); } public static void printAbs(Calcable c) { System.out.println(c.getAbs(-10)); }}// 省略格式 printAbs(a -> Math.abs(a)); (4) Runnable接口 12345678910111213141516171819public class Demo05_Runnable { public static void main(String[] args) { // 开启线程 // 匿名内部类 new Thread(new Runnable() { @Override public void run() { System.out.println("新线程!1"); } }).start(); // lambda new Thread(() -> { System.out.println("新线程!2"); }).start(); }}// 省略格式 new Thread(() -> System.out.println("新线程!2")).start(); (5) Comparator接口12345678910111213141516171819202122232425public class Demo06_Comparator { public static void main(String[] args) { ArrayList<Integer> list = new ArrayList<>(); Collections.addAll(list, 3, 2, 4, 1, 5); // 降序排序 // 匿名内部类 /*Collections.sort(list, new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return o2 - o1; } });*/ // lambda Collections.sort(list, (Integer o1, Integer o2) -> { return o2 - o1; }); System.out.println(list); }}// 省略格式 Collections.sort(list, (o1, o2) -> o2 - o1);]]></content>
<categories>
<category>Java基础</category>
</categories>
<tags>
<tag>JDK 8</tag>
<tag>javase</tag>
</tags>
</entry>
<entry>
<title><![CDATA[《图解HTTP》读书笔记_HTTP协议]]></title>
<url>%2F2019%2F04%2F14%2F%E3%80%8A%E5%9B%BE%E8%A7%A3HTTP%E3%80%8B%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0_HTTP%E5%8D%8F%E8%AE%AE%2F</url>
<content type="text"><![CDATA[一. HTTP协议初探1.1 三项WWW构建技术 : HTML : 超文本标记语言 HTTP : 文本传输协议 URL : 统一资源定位符, 指定文档所在地址 1.2 网络基础TCP/IP 通常使用的网络是在TCP/IP协议群的基础上运作的, HTTP只是它的一个子集 TCP/IP是互联网相关的各类协议族的总称 TCP/IP协议群, 按层次分为4层 : 好处 : 比如 : 如果互联网只有一个协议统筹, 某地方需要协议统筹, 某地方需要改变设计时, 就必须把所有部分整体替换掉. 而分层之后只需把变动的层替换的即可 TCP/IP通信传输流 在层与层之间传输数据时, 每经过一层时, 必定会被打上一个该层所属的首部信息. 反之, 接收端在层与层传输数据时, 每经过一层时会把对应的首部消去. 这叫做封装 1.3 与HTTP关系密切的协议 : IP, TCP, DNS1.3.1 负责传输的IP协议IP协议处于网络层, 作用是把各种数据包传送给对方. 需要确定IP地址和MAC地址 IP地址指明了节点被分配到的地址, MAC地址值网卡的固定地址, IP地址可以和MAC地址配对 ARP协议 : 凭借MAC地址进行通信 IP通信依赖MAC地址, 网络上, 通信的双方要经过多台计算机和网络设备中转才能连接到对方, 在中转时, 会利用下一站中转设备的MAC来搜索下一个目标, 这时, 就用到了ARP协议—-根据对方IP地址可以反查MAC地址 中转过程中, 计算机和路由器等网络设备只能获取很粗略的传输路线, 称为路由选择 1.3.2 确保可靠性的TCP协议TCP处于传输层, 提供可靠的字节流服务 字节流服务 : 为了方便传输, 将大块数据分割成以报文段为单位的数据包进行管理 TCP协议 : 为了更容易传输大数据才把数据分割, 而且能够确认数据是否送达(三次握手) 1.3.3 负责域名解析的DNS协议处于应用层, 提供域名到IP地址之间的解析服务 DNS解析过程 二. 简单的HTTP协议2.1 请求和相应HTTP协议用于客户端和服务器之间的通信. HTTP协议能够区分哪些是客户端, 哪些是服务端 HTTP协议通过请求和相应的交换来达成通信 HTTP协议规定 : 请求有客户端发出, 最后由服务器端响应请求并返回 请求报文 : 请求方法, 请求URI, 协议版本, 可选的请求首部字段 和 内容实体构成 响应报文 : 协议版本, 状态码, 用以解释状态码的原因短语, 可选的响应首部字段以及实体主体构成 2.2 HTTP是不保存状态的即无状态协议. HTTP协议自身不对请求和相应之间的通信状态进行保存 为了记录状态, 引入了Cookie技术 2.3 HTTP请求方法 2.4 使用Cookie状态管理 根据服务器发送的相应报文内的一个Set-Cookie的首部字段信息, 通知客户端保存Cookie, 当下次客户端再往服务器发送请求时, 客户端会自动在请求报文中加入Cookie值后发送出去 服务端收到Cookie后, 会去检查是从哪一个客户端发来的连接请求, 然后对比服务器上的记录, 最后的得到之前的状态信息 图解 : 报文显示 三. HTTP报文内的HTTP信息 请求行 : 包含请求的方法, URI, HTTP版本 状态行 : 包含表明相应结果的状态码, 原因短语和HTTP版本 首部字段 : 包含表示请求的相应的各种条件和属性的各类首部]]></content>
<categories>
<category>计算机网络基础</category>
<category>读书笔记</category>
</categories>
<tags>
<tag>《图解HTTP》</tag>
<tag>通信协议</tag>
<tag>HTTP</tag>
</tags>
</entry>
<entry>
<title><![CDATA[《图解HTTP》读书笔记_TCP与UDP]]></title>
<url>%2F2019%2F04%2F12%2F%E3%80%8A%E5%9B%BE%E8%A7%A3HTTP%E3%80%8B%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0_TCP%E4%B8%8EUDP%2F</url>
<content type="text"><![CDATA[TCP与UDP1.1 传输层的作用TCP -> 面向连接的, 可靠的流协议, 一共可靠的通信传输 UDP -> 面向无连接的, 具有不可靠性的数据包协议(让广播和细节控制交给应用的通信传输) 详解: TCP用于在传输层有必要时效内可靠传输的情况, 由于它是面向连接并具备顺序控制, 重发控制等机制的, 所以它可以为应用提供可靠传输. 而UDP则主要用于哪些对高速传输和实时性比较高要求的通信或广播通信 因此,TCP和UDP应该根据应用目的按需使用。另外,在日常使用TCP或UDP时,通常会用到操作系统提供的类库,这种类库一般被称为API,对于TCP或UDP来说会广泛使用到套接字(Socket)的API。应用程序使用套接字时,可以设置对端的IP地址、端口号,并实现数据的发送与接收。 1.2 端口号MAC地址 : 识别链路中不同计算机 IP地址 : 识别TCP/IP网络中互联的主机和路由器 端口号: 传输层, 识别同一台计算机中进行通信的不同应用程序 一般知名端口号在0~1023之间, 而我们经常使用的自定义/动态分配的端口号则一般在49152~65535之间. 1.3 UDP UDP(User Datagram Protocol)不提供复杂的控制机制, 利用IP提供面向无连接的通信服务, 因此它不会负责: 流量控制, 对包重发等. UDP应用于 包量较少的通信(DNS, SNMP等) 视频, 音频等多媒体通信(即时通信) 限定于LAN等特定网络中的应用通信 广播通信(广播, 多播) 1.4 TCP1.4,1 概述 TCP作为一种面向有连接的协议, 只有在确认通信段存在时, 才会发送数据, 从而可以控制流量的浪费 为了通过IP数据包实现可靠性传输, 需要考虑很多事情, 例如: 数据的破坏, 丢包, 重复以及分辨顺序混乱等问题 TCP通过检验和、序列号、确认应答、重发控制、连接管理以及窗口控制等机制实现可靠性传输。 使用TCP的一个连接的建立与断开,正常过程下至少需要来回发送7个包才能完成,也就是我们常常听到的三次握手,两次挥手。 1.4.2 面试题问题 为什么要三次握手? 三次握手的目的是建立可靠的通信信道,说到通讯,简单来说就是数据的发送与接收,而三次握手最主要的目的就是 双方确认自己与对方的发送与接收是正常的。第一次握手: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 回答“知道 了”,这样通话才算结束。]]></content>
<categories>
<category>计算机网络基础</category>
</categories>
<tags>
<tag>《图解HTTP》</tag>
<tag>通信协议</tag>
<tag>TCP/IP</tag>
</tags>
</entry>
<entry>
<title><![CDATA[《图解HTTP》读书笔记_IP协议及其相关技术]]></title>
<url>%2F2019%2F04%2F12%2F%E3%80%8A%E5%9B%BE%E8%A7%A3HTTP%E3%80%8B%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0_IP%E5%8D%8F%E8%AE%AE%E5%8F%8A%E7%9B%B8%E5%85%B3%E6%8A%80%E6%9C%AF%2F</url>
<content type="text"><![CDATA[IP协议及相关技术1.1 IP的主要作用 IP的主要作用就是在复杂的网络环境中将数据包发送给最终的目标地址 1.2 IP的三个作用模块1.2.1 IP寻址 在计算机通信中, 为了识别通信段, 必须要有一个类似于地址的识别码进行标识. 而在数据链路层, 使用MAC地址来表示同一个链路中不同计算机的一种识别码. 在网络层叫IP地址. 一张网卡对应一个MAC地址 1.2.2 路由路由控制(Routing) : 是指将分组数据发送到最终目标地址的功能, 即使网络非常复杂, 也可以通过路由控制确定到达目标地址的通路. Hop中文叫“跳”, 它是指网络中的一个区间, IP包正是在网络中一个跳间被转发. 数据链路实现某一个区间(一跳)内的通信,而IP实现直至最终目标地址的通信(点对点) 为了将数据包发送给目标主机,所有主机都维护者一张路由控制表(Routing Table),该表记录IP数据在下一步应该发给哪一个路由器。IP包将根据这个路由表在各个数据链路上传输。 1.3 IP分包和组包 IP面向无连接,即在发包之前,不需要建立与对端目标地址之间的连接。上层如果遇到需要发送给IP的数据,该数据会被立即压缩成IP包发送出去。 TIP: IP为什么面向无连接: 1.简化:面向连接比起面向无连接处理相对复杂! 2.提速:每次通信之前都需要建立连接,会降低处理速度! 需要有连接时,可以委托上一层(传输层)提供此项服务,因此,IP为了实现简单化与高速化采用面向无连接方式。 1.3 相关技术 DNS 2. ARP 3. ICMP 4. DHCP 5. NAT 6. IP 隧道 1.3.1 DNS 有效管理主机名和IP地址之间的对应关系->DNS系统,那么DNS查询的机制如下 1.3.2 ARP ARP是一种解决地址问题的协议, 以目标地址为线索, 用来定位下一个应该接收数据分包的网络设备对应的MAC地址, 不过ARP只适用于IPv4, 不适用与IPv6. RARP则是将ARP反过来,从MAC地址定位IP地址的一种协议。 1.3.3 ICMP 一个刚刚搭建好的网络, 需要验证该网络的设置是否正确. ICMP(Internet Control Message Protocol)这是提供这类功能的一种协议, 其主要功能包括 : 确认IP包是否成功送达目标地址,通知在发送过程中IP包被废弃的具体原因等等. 例如我们经常使用ping命令, 就是一个典型的ICMP的具体应用. 1.3.4 DHCP 如果逐一地为每一台主机设置IP地址会非常繁琐,于是为了实现自动设置IP地址、统一管理IP地址分配,就产生了DHCP(Dynamic Host Configuration Protocol)协议。有了DHCP,计算机只要连接到网络,就可以进行TCP/IP通信。 1.3.5 NAT NAT(Network Address Translator)用于在本地网络中使用私有地址,在连接互联网时转而使用全局IP地址的技术。 1.3.6 IP隧道 在一个网络环境中, 假如网络A, B使用IPv6, 中间位置的网络C支持使用IPv4的话, 网络A与网络B之间无法直接进行通信. 为了让他们之间正常通信, 这时需要采用IP隧道的功能。 IP隧道中可以将那些从网络A发过来的IPv6的包统和为一个数据, 再为之追加一个IPv4的首部以后转发给网络C, 这种在网络层的首部后面继续追加网络层首部的通信方法就叫做“IP隧道”]]></content>
<categories>
<category>计算机网络基础</category>
<category>读书笔记</category>
</categories>
<tags>
<tag>《图解HTTP》</tag>
<tag>通信协议</tag>
<tag>TCP/IP</tag>
</tags>
</entry>
<entry>
<title><![CDATA[《图解HTTP》读书笔记_TCP_IP协议]]></title>
<url>%2F2019%2F04%2F12%2F%E3%80%8A%E5%9B%BE%E8%A7%A3HTTP%E3%80%8B%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0_TCP_IP%E5%8D%8F%E8%AE%AE%2F</url>
<content type="text"><![CDATA[TCP/IP基础知识1.1 TCP/IP的背景及历史 TCP(Transmission Control Protocol)和IP(Internet Protocol)是互联网的众多通信协议中最为著名的。 1.2 TCP/IP 标准化1.2.1. 具体含义 很多人都会认为TCP/IP是指TCP与IP两种协议,实际生活中有时也确实就是指这两种协议。然后,很多情况下,它只是利用IP进行通信时所必须使用到的协议群的统称。 1.2.2 标准化 由于TCP/IP尽早地制定了可行性较强的协议,提出了应对技术快速革新的协议,并及时进行后期改良的方案,因此打败了OSI模型,成为了事实上的标准 1.3 TCP/IP 协议分层 上图列出了TCP/IP与OSI分层之间的大概关系, 不难看出, TCP/IP与OSI在分层模块上骚又区别. OSI参考模型注重”通信协议必要的功能是什么”, 而TCP/IP则更强调”在计算机上实现协议应该开发那种程序”. 现在再来看看主机A向主机B发送一封电子邮件,在TCP/IP模型下的处理过程: 分组数据包经过以太网的数据链路时, 大致流程如下]]></content>
<categories>
<category>计算机网络基础</category>
</categories>
<tags>
<tag>《图解HTTP》</tag>
<tag>通信协议</tag>
<tag>TCP/IP</tag>
</tags>
</entry>
<entry>
<title><![CDATA[《图解HTTP》_网络基础知识]]></title>
<url>%2F2019%2F04%2F12%2F%E3%80%8A%E5%9B%BE%E8%A7%A3HTTP%E3%80%8B%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0_%E7%BD%91%E7%BB%9C%E5%9F%BA%E7%A1%80%2F</url>
<content type="text"><![CDATA[这是一本图文并茂的网络管理技术书籍,旨在让广大读者理解TCP/IP的基本知识、掌握TCP/IP的基本技能。 书中讲解了网络基础知识、TCP/IP基础知识、数据链路、IP协议、IP协议相关技术、TCP与UDP、路由协议、应用协议、网络安全等内容,引导读者了解和掌握TCP/IP,营造一个安全的、使用放心的网络环境。 网络基础知识1.1 计算机使用模式的演变 1.2 协议 协议 : 就是计算机与计算机之间通过网络实现通信, 实现达成的一种”约定. 这种约定使那些由不同厂商的设备, 不同的CPU以及不同的操作系统组成的计算机之间, 只要遵循相同的协议就能够实现通信, 反之亦然 1.3 协议分层与OSI参考模型协议分层如同计算机软件中的模块化开发, OSI参考模型的建议是比较理想化的 OSI每层的作用 通信流程 : 假设主机A的用户A要给主机B的用户B发送一封电子邮件: 发送方从第7层、第6层到第1层由上至下按照顺序传输数据,而接收端则从第1层、第2层到第7层由下至上向每个上一级分层传输数据。每个分层上,在处理由上一层传过来的数据时可以附上当前分层的协议所必须的“首部”信息。然后接收端对收到的数据进行数据“首部”与“内容”的分离,再转发给上一分层,并最终将发送端的数据恢复为原装。 记忆 : 基础 -> 数据打包 -> 选择地方 -> 保证传输安全 -> 控制开关 -> 转码 -> 协议通道 层级 概述 描述 物理层 信号和介质 数据传输和相关设备 数据链路层 帧和介质访问控制 数据组合成数据块(帧), 以及控制传输速率 网络层 路径选择, 路由及逻辑寻址 寻址和路由选择,连接的建立,保持和终止 传输层 流量, 控制和可靠性 保证数据传输可靠,有流量控.分/重组和差错控制 会话层 对话和交谈 建立和维持会话,使会话同步 表示层 一种通用数据格式 数据修饰,包括转码, 加密,压缩 应用层 协议层 DNS, HTTP协议等]]></content>
<categories>
<category>计算机网络基础</category>
</categories>
<tags>
<tag>《图解HTTP》</tag>
<tag>通信协议</tag>
<tag>TCP/IP</tag>
</tags>
</entry>
<entry>
<title><![CDATA[终于解决hexo对Windows图片不友好的问题]]></title>
<url>%2F2019%2F04%2F11%2F%E5%8D%9A%E5%AE%A2%E6%90%AD%E5%BB%BA_%E8%A7%A3%E5%86%B3%E5%9B%BE%E7%89%87%E9%97%AE%E9%A2%98%2F</url>
<content type="text"><![CDATA[问题 : hexo的next主题, 在Windows系统下, 使用Typora编辑文字, 发现Typora引用的图片无法在网站上显示, 网页默认路径是文章路径. 如果我想让网站显示图片, 只能用Typora引用在文章路径下的图片. 这样就导致了两问题 : 1. 编写文章及其麻烦 2. 图片直接放在路径下及其杂乱 1. 方案一 :利用图床, 图床分有免费网站和七牛云, 免费网站图片容易失去, 舍之. 七牛云需要绑定备案域名(免费送的只能给30天). 还有一个方案是用新浪图床小软件, 但是对于图片难以管理, 而且需要稳定的网络, 目前需要离线编辑文章 2. 方案二 :第一步 : 安装插件 把主页配置文件_config.yml 里的post_asset_folder:这个选项设置为true 在你的hexo目录下执行这样一句话npm install hexo-asset-image --save,这是下载安装一个可以上传本地图片的插件 等待一小段时间后, 看到+ [email protected] 则安装好了 第二步 : 设置Typora 打开file -> 偏好设置 -> 图片插入 选择自定义./${filename} 然后勾选优先使用相对路径 遇到的问题有 : Typora 打开图表显示不出, 解决方案是不能下载X64版本的软件, 即便你的电脑是X64位的, 感觉Typora软件优化有问题 安装软件打不开, 显示主程序JavaScript异常 : 去C:\Users\XXXX\AppData\Roaming\ 把Typora相关名字的东西都删除 软件不能是0.9.4 版本, 因为版本过低, 没有自定义路径功能]]></content>
<categories>
<category>建站相关</category>
</categories>
<tags>
<tag>Hexo</tag>
<tag>博客搭建</tag>
</tags>
</entry>
<entry>
<title><![CDATA[Hexo博客搭建+next主题优化+插件配置+常用操作+错误分析]]></title>
<url>%2F2019%2F04%2F10%2FHexo%E5%8D%9A%E5%AE%A2%E6%90%AD%E5%BB%BA%2Bnext%E4%B8%BB%E9%A2%98%E4%BC%98%E5%8C%96%2B%E6%8F%92%E4%BB%B6%E9%85%8D%E7%BD%AE%2B%E5%B8%B8%E7%94%A8%E6%93%8D%E4%BD%9C%2B%E9%94%99%E8%AF%AF%E5%88%86%E6%9E%90%2F</url>
<content type="text"><![CDATA[前言 hexo 优点是轻量化, 对于喜欢用Markdown编辑的用户很容易入手. 主题好看, 使用简单 缺点: 图片上传Markdown很麻烦, 目前较好的方式是图床解决 一. 博客基本框架搭建1. 准备环境 Node.js 下载, 并安装. 详细步骤 : Git下载, 并安装. 详细步骤 : 安装Hexo, 在任意位置右键->Git Bash运行以下命令即可: 1npm install -g hexo-cli 初始化Hexo, 继续在命令行操作 为初始化文件的路径, 即站点目录 123hexo init <folder>cd <folder>npm install 启动服务器. 继续在命令行(站点目录路径), 运行 1hexo server //hexo s 也可以 浏览器访问网站 : http://localhost:4000/ 注: hexo 相关命令均在站点目录下, 用Git Bash运行 站点配置文件 : 站点目录下的_config.yml. 每次修改,需要重启服务器 路径为<folder>\_config.yml 主题配置文件 : 主题文件夹下的_config.yml. 修改, 实时看到效果, 无需重启 路径为<folder>\themes\<主题文件夹>\_config.yml 2. 实施方案3. 主题优化3.1 添加背景图新建 站点目录\source\images 路径, 存放bg.jpg做背景图 在 themes/*/source/css/_custom/custom.styl 中添加如下代码: 1234567body{ background:url(/images/bg.jpg); background-size:cover; background-repeat:no-repeat; background-attachment:fixed; background-position:center;} 3.2 修改Logo字体在 themes/*/source/css/_custom/custom.styl 中添加如下代码: 12345678@font-face { font-family: Zitiming; src: url('/fonts/Zitiming.ttf');}.site-title { font-size: 40px !important; font-family: 'Zitiming' !important;} 其中字体文件在 themes/next/source/fonts 目录下,里面有个 .gitkeep 的隐藏文件,打开写入你要保留的字体文件,比如我的是就是写入 Zitiming.ttf ,具体字库自己从网上下载即可。 3.3 修改内容区域的宽度编辑主题的 source/css/_variables/custom.styl 文件,新增变量: 12345// 修改成你期望的宽度$content-desktop = 700px// 当视窗超过 1600px 后的宽度$content-desktop-large = 900px 3.4 网站标题栏背景颜色打开 themes/*/source/css/_custom/custom.styl ,在里面写下如下代码: 123.site-meta { background: $blue; //修改为自己喜欢的颜色} 3.5 自定义鼠标样式打开 themes/*/source/css/_custom/custom.styl ,在里面写下如下代码: 1234567// 鼠标样式 * { cursor: url("http://om8u46rmb.bkt.clouddn.com/sword2.ico"),auto!important } :active { cursor: url("http://om8u46rmb.bkt.clouddn.com/sword1.ico"),auto!important } 3.6 文章加密访问打开 themes/*/layout/_partials/head.swig文件,在 之前插入代码: 12345678910<script> (function(){ if('{{ page.password }}'){ if (prompt('请输入密码') !== '{{ page.password }}'){ alert('密码错误'); history.back(); } } })();</script> 写文章时加上password: *: 12345---title: 2018date: 2018-10-25 16:10:03password: 123456--- 3.7 实现点击出现桃心效果 在/themes/*/source/js/src下新建文件click.js,接着把以下粘贴到click.js文件中。代码如下: 1!function(e,t,a){function n(){c(".heart{width: 10px;height: 10px;position: fixed;background: #f00;transform: rotate(45deg);-webkit-transform: rotate(45deg);-moz-transform: rotate(45deg);}.heart:after,.heart:before{content: '';width: inherit;height: inherit;background: inherit;border-radius: 50%;-webkit-border-radius: 50%;-moz-border-radius: 50%;position: fixed;}.heart:after{top: -5px;}.heart:before{left: -5px;}"),o(),r()}function r(){for(var e=0;e<d.length;e++)d[e].alpha<=0?(t.body.removeChild(d[e].el),d.splice(e,1)):(d[e].y--,d[e].scale+=.004,d[e].alpha-=.013,d[e].el.style.cssText="left:"+d[e].x+"px;top:"+d[e].y+"px;opacity:"+d[e].alpha+";transform:scale("+d[e].scale+","+d[e].scale+") rotate(45deg);background:"+d[e].color+";z-index:99999");requestAnimationFrame(r)}function o(){var t="function"==typeof e.onclick&&e.onclick;e.onclick=function(e){t&&t(),i(e)}}function i(e){var a=t.createElement("div");a.className="heart",d.push({el:a,x:e.clientX-5,y:e.clientY-5,scale:1,alpha:1,color:s()}),t.body.appendChild(a)}function c(e){var a=t.createElement("style");a.type="text/css";try{a.appendChild(t.createTextNode(e))}catch(t){a.styleSheet.cssText=e}t.getElementsByTagName("head")[0].appendChild(a)}function s(){return"rgb("+~~(255*Math.random())+","+~~(255*Math.random())+","+~~(255*Math.random())+")"}var d=[];e.requestAnimationFrame=function(){return e.requestAnimationFrame||e.webkitRequestAnimationFrame||e.mozRequestAnimationFrame||e.oRequestAnimationFrame||e.msRequestAnimationFrame||function(e){setTimeout(e,1e3/60)}}(),n()}(window,document); 在\themes\*\layout\_layout.swig文件末尾添加: 12<!-- 页面点击小红心 --><script type="text/javascript" src="/js/src/clicklove.js"></script> 3.8 静态资源压缩在站点目录下: 1$ npm install gulp -g 安装gulp插件: 12345npm install gulp-minify-css --savenpm install gulp-uglify --savenpm install gulp-htmlmin --savenpm install gulp-htmlclean --savenpm install gulp-imagemin --save 在 Hexo 站点下新建 gulpfile.js文件,文件内容如下: 123456789101112131415161718192021222324252627282930313233343536373839404142434445var gulp = require('gulp');var minifycss = require('gulp-minify-css');var uglify = require('gulp-uglify');var htmlmin = require('gulp-htmlmin');var htmlclean = require('gulp-htmlclean');var imagemin = require('gulp-imagemin');// 压缩css文件gulp.task('minify-css', function() { return gulp.src('./public/**/*.css') .pipe(minifycss()) .pipe(gulp.dest('./public'));});// 压缩html文件gulp.task('minify-html', function() { return gulp.src('./public/**/*.html') .pipe(htmlclean()) .pipe(htmlmin({ removeComments: true, minifyJS: true, minifyCSS: true, minifyURLs: true, })) .pipe(gulp.dest('./public'))});// 压缩js文件gulp.task('minify-js', function() { return gulp.src(['./public/**/.js','!./public/js/**/*min.js']) .pipe(uglify()) .pipe(gulp.dest('./public'));});// 压缩 public/demo 目录内图片gulp.task('minify-images', function() { gulp.src('./public/demo/**/*.*') .pipe(imagemin({ optimizationLevel: 5, //类型:Number 默认:3 取值范围:0-7(优化等级) progressive: true, //类型:Boolean 默认:false 无损压缩jpg图片 interlaced: false, //类型:Boolean 默认:false 隔行扫描gif进行渲染 multipass: false, //类型:Boolean 默认:false 多次优化svg直到完全优化 })) .pipe(gulp.dest('./public/uploads'));});// 默认任务gulp.task('default', [ 'minify-html','minify-css','minify-js','minify-images']); 只需要每次在执行 generate 命令后执行 gulp 就可以实现对静态资源的压缩,压缩完成后执行 deploy 命令同步到服务器: 123hexo ggulphexo d 3.9 博文置顶 安装插件12$ npm uninstall hexo-generator-index --save$ npm install hexo-generator-index-pin-top --save 然后在需要置顶的文章的Front-matter中加上top即可: 12345---title: 2018date: 2018-10-25 16:10:03top: 10--- 设置置顶标志 打开 : /themes/*/layout/_macro/post.swig,定位到 插入以下代码即可: 12345{% if post.top %} <i class="fa fa-thumb-tack"></i> <font color=7D26CD>置顶</font> <span class="post-meta-divider">|</span>{% endif %} 3.10 在右上角或者左上角实现fork me on github 选择样式GitHub Ribbons, 修改图片跳转链接,将<a href="https://github.com/you">中的链接换为自己Github链接: 打开 themes/next/layout/_layout.swig 文件,把代码复制到<div class="headband"></div>下面。 3.11 主页文章添加边框阴影效果打开 themes/*/source/css/_custom/custom.styl ,向里面加代码: 12345678// 主页文章添加阴影效果.post { margin-top: 0px; margin-bottom: 60px; padding: 25px; -webkit-box-shadow: 0 0 5px rgba(202, 203, 203, .5); -moz-box-shadow: 0 0 5px rgba(202, 203, 204, .5);} 3.12 显示当前浏览进度修改themes/*/_config.yml,把 false 改为 true: 12345# Back to top in sidebarb2t: true# Scroll percent label in b2t buttonscrollpercent: true 3.13 创建分类页在终端窗口下,定位到 Hexo 站点目录下输入: 12cd <站点目录>hexo new page categories 3.14 加入 广告主要有两种:百度SSP和谷歌Adsense。方法类似: 注册,复制广告代码 部署到网站。 2.1. 新建 theme/*/layout/_custom/google_ad.swig,将 AdSense 上的代码粘贴进去 2.2. 头部。在 theme/*/layout/_custom/head.swig 中也粘贴一份 2.3. 每篇博客。在 theme/*/layout/post.swig 里中在希望看到的地方加上: 1{% include '_custom/google_ad.swig' %} 例如:在 <div id="posts" class="posts-expand"> </div> 中间插入,总代码如下: 123456{% block content %} <div id="posts" class="posts-expand"> {{ post_template.render(page) }} {% include '_custom/google_ad.swig' %} </div>{% endblock %} 等待审核通过。如果失败,可再次申请。 3.15 添加萌萌哒 安装插件 1npm install --save hexo-helper-live2d 复制你喜欢的模型名字: Epsilon2.1 Gantzert_Felixander haru miku ni-j nico nietzche nipsilon nito shizuku tsumiki wanko z16 hibiki koharu haruto Unitychan tororo hijiki 5.16 关闭文章目录数字在主题目录\_config.yml, 搜索toc关键字,设置 : number: false 12345678910toc: enable: true # Automatically add list number to toc. number: false # If true, all words will placed on next lines if header width longer then sidebar width. wrap: false # If true, all level of TOC in a post will be displayed, rather than the activated part of it. expand_all: false # Maximum heading depth of generated toc. You can set it in one post through `toc_max_depth` in Front Matter. max_depth: 6 4. 插件配置以下插件(评论系统、数据统计与分析、内容分享服务、搜索服务)各选一个即可。 4.1 评论系统 推荐指数 优点 缺点 Valine 4 每天30000条评论,10GB的储存 作者评论无标识 来必力/livere 4 多种账号登录 评论无法导出 畅言 3 美观 必须备案域名 gitment 3 简洁 只能登陆github评论 Disqus 1 需要翻*墙 4.1.1 Valine 获取APP ID 和 APP Key 请先登录或注册 LeanCloud, 进入控制台后点击左下角创建应用, 进入刚刚创建的应用,选择左下角的设置>应用Key,然后就能看到你的APP ID和APP Key了。 填写APP ID 和 APP Key到主题配置文件_config.yml 运行hexo g&&hexo d推送到博客。 4.1.2 来必力/livere 登陆 来必力 获取你的 LiveRe UID。 填写LiveRe UID到主题配置文件_config.yml 4.1.3 畅言 获取APP ID 和 APP Key 请先登录或注册 畅言, 点击“立即免费获取畅言”, 新建站点,点击管理,点击评论插件>评论管理, 点击后台总览,然后就能看到你的APP ID和APP Key了。 填写APP ID 和 APP Key到主题配置文件_config.yml 运行hexo g&&hexo d推送到博客。 4.1.4 gitment 安装插件: npm i –save gitment 申请应用 在New OAuth App为你的博客应用一个密钥: 1234Application name:随便写Homepage URL:这个也可以随意写,就写你的博客地址就行Application description:描述,也可以随意写Authorization callback URL:这个必须写你的博客地址 配置 编辑主题配置文件themes/*/_config.yml: 123456789101112131415# Gitment# Introduction: https://imsun.net/posts/gitment-introduction/gitment: enable: true mint: true # RECOMMEND, A mint on Gitment, to support count, language and proxy_gateway count: true # Show comments count in post meta area lazy: false # Comments lazy loading with a button cleanly: false # Hide 'Powered by ...' on footer, and more language: # Force language, or auto switch by theme github_user: {you github user id} github_repo: 公开的git仓库,评论会作为那个项目的issue client_id: {刚才申请的ClientID} client_secret: {刚才申请的Client Secret} proxy_gateway: # Address of api proxy, See: https://github.com/aimingoo/intersect redirect_protocol: # Protocol of redirect_uri with force_redirect_pro 4.1.5 Disqus编辑 主题配置文件themes/*/_config.yml, 将 disqus 下的 enable 设定为 true,同时提供您的 shortname。count 用于指定是否显示评论数量。 1234disqus: enable: false shortname: count: true 4.2 数据统计与分析 推荐指数 优点 缺点 不蒜子 4 可直接将访问次数显示在您在网页上(也可不显示) 只计数 百度统计 3 收录慢 4.2.1 不蒜子编辑 主题配置文件 themes/*/_config.yml中的busuanzi_count的配置项即可。 当enable: true时,代表开启全局开关。 若site_uv(本站访客数)、site_pv(本站访客数)、page_pv(本文总阅读量)的值均为false时,不蒜子仅作记录而不会在页面上显示。 注意: 1不蒜子官方因七牛强制过期原有的『dn-lbstatics.qbox.me』域名(预计2018年10月初),与客服沟通数次无果,即使我提出为此付费也不行,只能更换域名到『busuanzi.ibruce.info』! 解决办法: 找到主题调用不蒜子的swig文件。一般在”\themes*\layout_third-party\analytics\busuanzi-counter.swig” 更改域名 1234把原有的:<script async src="//dn-lbstatics.qbox.me/busuanzi/2.3/busuanzi.pure.mini.js"></script>域名改一下即可:<script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script> 4.2.2 百度统计 登录 百度统计,定位到站点的代码获取页面 复制统计脚本 id,如图: 编辑 主题配置文件themes/*/_config.yml,修改字段 google_analytics,值设置成你的统计脚本 id。 4.3 内容分享服务 推荐指数 优点 缺点 百度分享 4 稳定 不太美观 need-more-share2 4 美观 更新不及时(比如微信分享API) 4.3.1 百度分享编辑 主题配置文件,添加/修改字段 baidushare,值为 true即可。 12# 百度分享服务baidushare: true 4.3.2 need-more-share2编辑 主题配置文件,添加/修改字段 needmoreshare2,值为 true即可。 12needmoreshare2: enable: true 4.4 搜索服务 推荐指数 优点 缺点 Local Search 4 配置方便 Swiftype 2 需注册 Algolia 2 需注册 4.1 Local Search添加百度/谷歌/本地 自定义站点内容搜索 安装 hexo-generator-searchdb,在站点的根目录下执行以下命令: 1$ npm install hexo-generator-searchdb --save 编辑 站点配置文件,新增以下内容到任意位置: 12345search: path: search.xml field: post format: html limit: 10000 编辑 主题配置文件,启用本地搜索功能: 123# Local searchlocal_search: enable: true 5. 错误分析如果你使用Hexo遇到同样的问题,这里有一些常见问题的解决方案。 5.1 YAML Parsing Error12JS-YAML: incomplete explicit mapping pair; a key node is missed at line 18, column 29: last_updated: Last updated: %s 参数中包含冒号,请用加引号,如Last updated: %s 12JS-YAML: bad indentation of a mapping entry at line 18, column 31: last_updated:"Last updated: %s" 字段后面的冒号必须为英文冒号,如:last_updated: 字段冒号后面必须跟一个空格,如:last_updated: “Last updated: %s” 5.2 EMFILE Error1Error: EMFILE, too many open files 生成大量的文件时,可能遇到EMFILE错误。 可以运行以下命令来增加允许同步I / O操作的数量。 1$ ulimit -n 10000 5.3 Process Out of Memory当hexo g时,遇到以下错误: 1FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - process out of memory 如下,更改hexo-cli文件的第一行,来增大nodejs堆内存.该bug已在新版本修复。 1#!/usr/bin/env node --max_old_space_size=8192 5.4 Git Deployment Problems RPC failed 123error: RPC failed; result=22, HTTP code = 403fatal: 'username.github.io' does not appear to be a git repository 确保你有你的电脑上设置git正确或尝试使用HTTPS存储库URL。 Error: ENOENT: no such file or directory 这个需要有一定的git的知识,因为可能是由于写错了标签,类别,或文件名,导致本地和github冲突了,Git不能自动合并这一变化所以它打破了自动分支。 解决办法: 检查文章的标签和类别,确保本地和github上是相同的。 合并分支(Commit)。 清除,重构。在站点目录下,命令行(即Git Bash)运行hexo clean和hexo g 手动将站点目录下的public文件夹复制到您的桌面 从你的master分支切换到部署在本地分支。 从桌面复制public文件夹到本地分支。 合并分支到github(Commit)。 切回master分支。 5.5 Server Problems1Error: listen EADDRINUSE 你可能使用相同的端口,同时开启了两个Hexo服务器。如果需要同时开启,可以尝试修改端口设置: 1$ hexo server -p 5000 5.6 Plugin Installation Problems1npm ERR! node-waf configure build 这个错误可能发生在试图安装一个用Cc++或另一个javascript语言编写的插件。确保您已经安装了正确的编译器在您的计算机上。 5.7 Error with DTrace (Mac OS X)123{ [Error: Cannot find module './build/Release/DTraceProviderBindings'] code: 'MODULE_NOT_FOUND' }{ [Error: Cannot find module './build/default/DTraceProviderBindings'] code: 'MODULE_NOT_FOUND' }{ [Error: Cannot find module './build/Debug/DTraceProviderBindings'] code: 'MODULE_NOT_FOUND' } DTrace安装可能有问题,重装: 1$ npm install hexo --no-optional 详见 #1326 5.8 Iterate Data Model on Jade or SwigHexo使用仓库的数据模型。这不是一个数组,所以你可能需要将对象转换为iterable。 12{% for post in site.posts.toArray() %}{% endfor %} 5.9 Data Not Updated一些数据不能更新或新生成的文件的最后一个版本完全相同。清理缓存,再试一次: 1$ hexo clean 5.10 No command is executed那个不能使用除help、init和version以外的命令行(即Git Bash)时, 有可能时站点目录下的 package.json文件,缺少hexo,如下: 12345{ "hexo": { "version": "3.2.2" }} 5.11 Escape ContentsHexo使用Nunjucks渲染的页面. { { } }或{ % % }将解析和可能会引起麻烦, 如果要在博文中出现,必须使用三引号: 12 Hello 1 ENOSPC Error (Linux)如果运行命令$ hexo server 返回一个错误:12Error: watch ENOSPC … 可以通过运行$ npm dedupe或者以下命令行(即Git Bash):12$ echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo sysctl -p 来增加测试时,你可以看见的文件数量。 EMPERM Error (Windows Subsystem for Linux)如果在Windows Subsystem for Linux,运行命令$ hexo server 返回这个错误:12Error: watch /path/to/hexo/theme/ EMPERM 因为目前在Windows Subsystem for Linux中,有些内容更改时,还不能实时更新到hexo服务器。 所以需要重新编译,再启动服务器:123$ hexo generate$ hexo server -s Template render error有时运行命令$ hexo generate 返回一个错误:123FATAL Something’s wrong. Maybe you can find the solution here: <http://hexo.io/docs/troubleshooting.html>Template render error: (unknown path) 这意味着有些认不出来单词在你的文件,并且很可能在你的新博文,或者配置文件_config.yml中,比如缩进错误: 错误例子:plugins:hexo-generator-feedhexo-generator-sitemap12345678910#### 5.12 Please make sure you have the correct access rights and the repository exists.问题原因 : 无法连接到 github服务器1. 首先我得重新在git设置一下身份的名字和邮箱(进入到需要提交的文件夹底下(因为直接打开git Bash,在没有路径的情况下,无法修改)```shellgit config --global user.name "yourname"git config --global user.email“[email protected]" 注:yourname是你要设置的名字,your@email是你要设置的邮箱。 删除.ssh文件夹(直接搜索该文件夹)下的known_hosts(手动删除即可,不需要git) git输入命令 1ssh-keygen -t rsa -C "[email protected]"(请填你设置的邮箱地址) 接着出现:Generating public/private rsa key pair. Enter file in which to save the key (/Users/your_user_directory/.ssh/id_rsa): 请直接按下回车 回车 yes 回车 回车~ 然后系统会自动在.ssh文件夹下生成两个文件,id_rsa和id_rsa.pub,用记事本打开id_rsa.pub, 将全部的内容复制 打开https://github.com/,登陆你的账户,进入设置 进入ssh设置 在key中将刚刚复制的粘贴进去 点击add ssh key, ok! 在git中输入命令: 1ssh -T [email protected] 然后会跳出一堆话。。 输入命令:yes 注意: 操作这里的前提是根目录下的_config.yml修改成 12345deploy: #博客部署 type: git repo: https://github.com/Github账号名称/Github账号名称.github.io.git #coding: https://git.coding.net/码云账号名称/码云账号名称.git branch: master 而且仓库名必须为<Github账号名称>.github.io GitHub不允许自定义,免得不解析的 6. 常用操作6.1 创建文章命令: 1$ hexo new [layout] <title> 参数说明: [layout]可以为以下三种: 参数名 功能 文章路径 post 新建博文 source/_posts page 新建页面(如404,分类) source draft 草稿 source/_drafts 草稿可通过一下命令发布: 1$ hexo publish [layout] <title> title注意: 不是博文标题, 是博文markdown文件的名字, 也是博文链接的后缀(如https://www.simon96.online/2018/10/12/hexo-tutorial/中的hexo-tutorial) 6.2 文章模版 创建模版 在新建文章时,Hexo 会根据 scaffolds 文件夹内相对应的文件来建立文件,例如: $ hexo new blog “simon” 在执行这行指令时,Hexo 会尝试在 scaffolds 文件夹中寻找 blog.md,并根据其内容建立文章。 修改参数 以下是您可以在模版中使用的变量: 变量 描述 layout 布局 title 标题 date 文件建立日期 6.3 Front-matter就是博文最上方以 --- 分隔的那部分。 默认可以使用的Front-matter: 参数 描述 默认值 layout 布局 title 标题 date 建立日期 文件建立日期 updated 更新日期 文件更新日期 comments 开启文章的评论功能 true tags 标签(不适用于分页) categories 分类(不适用于分页) permalink 覆盖文章网址 <!--more--> 主页显示以上文字 截取文章150字]]></content>
<categories>
<category>建站相关</category>
</categories>
<tags>
<tag>Hexo</tag>
<tag>博客搭建</tag>
</tags>
</entry>
<entry>
<title><![CDATA[IO流详解]]></title>
<url>%2F2019%2F04%2F10%2FJava%E5%9F%BA%E7%A1%80_IO%E6%B5%81%E8%AF%A6%E8%A7%A3%2F</url>
<content type="text"><![CDATA[一. IO流1. IO流概述 IO : In/Out IO流的作用 : 数据的传输 2. IO流的分类 按流向分类 : 输入和输出 按照操作的数据类型 : 字节和字符 字节 输入 输出 字节 字节输入流(InputStream) 字节输出流(OutputSteam) 字符 字符输入流(Reader) 字符输出流(Write) 3. 学习IO流的注意事项 任何文件的底层最小的存储单元是字节 站在内存的角度思考输入和输出 注意事项 : 使用IO流的时候, 关联的一定是文件, 不能是文件夹 4. IO流图 字节输入流]]></content>
<categories>
<category>Java基础</category>
</categories>
<tags>
<tag>IO流</tag>
<tag>javase</tag>
</tags>
</entry>
</search>