├── .gitattributes ├── README.md ├── notes ├── CPP │ ├── CPP-Effective-C++.md │ ├── CPP-STL.md │ ├── CPP-基础语法.md │ ├── CPP-算法目录.md │ ├── CPP-设计模式.md │ └── CPP-面向对象.md ├── Go │ ├── Go-基础语法.md │ ├── Go-算法目录.md │ └── Go-错误集锦.md ├── JAVA │ ├── JAVA-J2EE.md │ ├── JAVA-基础语法.md │ ├── JAVA-设计模式.md │ ├── JAVA-面向对象.md │ ├── JAVA-面试题合集.md │ ├── JAVA-高级编程.md │ └── pics │ │ ├── JVM结构.png │ │ ├── UML用例图.png │ │ ├── collection.jpg │ │ ├── java多线程.png │ │ ├── java访问修饰符.png │ │ ├── jsp语法.png │ │ ├── servlet.png │ │ ├── springScope作用域.jpg │ │ └── 内部类.png ├── Java │ ├── Java-SSM框架.md │ └── Java-SpringBoot框架.md ├── Python │ ├── Python-基础语法.md │ ├── Python-常用函数.md │ ├── Python-面试问题.md │ └── Python3-爬虫基本语法.md ├── 操作系统.md ├── 数据库 │ ├── 数据库-MySQL-基本概念.md │ ├── 数据库-MySQL-练习题.md │ ├── 数据库-Redis-基本概念.md │ ├── 数据库-Redis-练习题.md │ └── 数据库-Redis-进阶.md └── 计算机网络 │ ├── 计算机网络-传输层.md │ ├── 计算机网络-基础概念.md │ ├── 计算机网络-应用层.md │ ├── 计算机网络-数据链路层.md │ ├── 计算机网络-物理层.md │ ├── 计算机网络-网络层.md │ ├── 计算机网络-面试题目综合.md │ └── 计算机网络目录.md └── pics ├── Algorithm.png ├── C++.png ├── DataBase.png ├── IQ.jpg ├── Internet.png ├── System.png ├── Web.jpg ├── go.png ├── go继承调用规则.png ├── java-数组.jpg ├── java.png ├── maven目录.png ├── python.png ├── 单工-半双工-全双工.jpg ├── 总线拓扑三种结构.jpg ├── 排序算法时间效率对比图.jpg └── 电路-报文-分组交换.jpg /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | | Java | Python3 | Go | C++ | 数据库 | 计算机网络 | 操作系统 | 2 | |:------:|:------:|:------:|:------:|:------:|:------:|:------:| 3 | | | | | | | | | 4 | 5 | *** 6 | **单击图片跳转到对应主题,Ctrl + Home 回到顶端,Ctrl + End 回到底端** 7 | 8 | 9 | ### Java 10 | * [基础语法](notes/JAVA/JAVA-基础语法.md) 11 | * [高级编程](notes/JAVA/JAVA-高级编程.md) 12 | * [面向对象](notes/JAVA/JAVA-面向对象.md) 13 | * [设计模式](notes/JAVA/JAVA-设计模式.md) 14 | * [J2EE](notes/JAVA/JAVA-J2EE.md) 15 | * [面试题合集](notes/JAVA/JAVA-面试题合集.md) 16 | *** 17 | 18 | 19 | ### Python3 20 | * [基础语法](notes/Python/Python-基础语法.md) 21 | * [常用数据类型 列表、字典、集合](notes/Python/Python-列表-字典-集合.md) 22 | * [常用函数](notes/Python/Python-常用函数.md) 23 | * [面试基础知识](notes/Python/Python-面试问题.md) 24 | *** 25 | 26 | 27 | ### Go 28 | * [基础语法](notes/Go/基础语法.md) 29 | * [错误集锦](notes/Go/错误集锦.md) 30 | **** 31 | 32 | 33 | ### C++ 34 | * [基础语法](notes/CPP/CPP-基础语法.md) 35 | * [面向对象](notes/CPP/CPP-面向对象.md) 36 | * [STL](notes/CPP/CPP-STL.md) 37 | **** 38 | 39 | 40 | ### 数据库 41 | * [MySQL-基本概念](notes/数据库/数据库-MySQL-基本概念.md) 42 | * [MySQL-练习题](notes/数据库/数据库-MySQL-练习题.md) 43 | **** 44 | 45 | 46 | 47 | ### 计算机网络+操作系统 48 | * [计算机网络基础练习题](/notes/计算机网络/计算机网络目录.md) 49 | * [操作系统](notes/操作系统.md) 50 | **** 51 | 52 | 53 | 54 | ### 智力题 55 | * [智力题](notes/牛客网-智力题.md) 56 | **** 57 | 58 | ### 排版 59 | 版面格式很大部分参考了 [CyC2018](https://cyc2018.github.io/CS-Notes),因为自己排版有很大问题嘛,这个项目的排版很棒,内容也很突出,有兴趣的可以了解一下。 60 | -------------------------------------------------------------------------------- /notes/CPP/CPP-Effective-C++.md: -------------------------------------------------------------------------------- 1 | # Effective C++ 3th中文版 2 | 3 | 简介:[美]Scott Meyers [译]侯捷 电子工业出版社 4 | 5 | **** 6 | 7 | ## 0-导读 8 | 9 | **术语** 10 | 11 | ```c++ 12 | extern int x;//对象(object)声明式 13 | class Widget;//类(class)声明式 14 | template//模板(template)声明式 15 | class GraphNode;//typename 见后 16 | std::size_t numDigits(int number);//函数(function)声明式 17 | /* 18 | numDigits 的返回类型是 std::size_t,表示类型 size_t 位于命名空间 std 内,这个命名空间是几乎所有 C++ 19 | 标准程序库元素的栖身处。 20 | */ 21 | 22 | 23 | ``` 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /notes/CPP/CPP-STL.md: -------------------------------------------------------------------------------- 1 | # CPP STL模板库 2 | 3 | * **容器(Containers)**:容器是用来管理某一类对象的集合。C++ 提供了各种不同类型的容器,**比如 deque、list、vector、map 等。** 4 | * **算法(Algorithm)**:相当于容器内置的方法,函数作用于容器。它们提供了执行各种操作的方式,包括对容器内容执行初始化、排序、搜索和转换等操作。 5 | * **迭代器(iterator)**:迭代器用于遍历对象集合的元素。这些集合可能是容器,也可能是容器的子集。 6 | * **函数对象、分配器等** 7 | **** 8 | ### 向量 (Vector) 9 | 10 | * **类似数组,但是大小可变,使用的函数:size()、begin()、end()、添加:push_back()、insert()、删除:erase()、clear(),遍历输出向量的两种方式。** 11 | 12 | ```c++ 13 | #include 14 | #include //需要使用库文件 15 | using namespace std; 16 | int main(){ 17 | vector vec; //创建一个向量存储一维数组 18 | //vector> vec; //创建二维数组 19 | //vector vec; //创建多维数组 20 | 21 | //添加元素的方法,push_back(num)、insert(index,num) 22 | cout << "vector size = " << vec.size() << endl;// 显示 vec 的原始大小 23 | for(int i = 0; i < 5; i++){// 推入 5 个值到向量中 24 | vec.push_back(i); 25 | } 26 | vec.insert(vec.begin(),100); 27 | 28 | //删除元素的方法,erase(index),erase(start,end),clear() 29 | vec.erase(vec.begin()+1); 30 | 31 | //打印 vector 的两种方法 32 | cout << "extended vector size = " << vec.size() << endl;// 显示 vec 扩展后的大小 33 | for(int i = 0; i < 5; i++){// 访问向量中的 5 个值 34 | cout << "value of vec [" << i << "] = " << vec[i] << endl; 35 | } 36 | vector::iterator v = vec.begin();// 使用迭代器 iterator 访问值 37 | while( v != vec.end()) { 38 | cout << "value of v = " << *v << endl; 39 | v++; 40 | } 41 | return 0; 42 | } 43 | ``` 44 | **算法 algorithm 库的使用,reverse()函数** 45 | 46 | ```c++ 47 | #include 48 | #include 49 | #include 50 | using namespace std; 51 | int main(){ 52 | vector vec; 53 | vec.push_back(1); 54 | vec.push_back(2); 55 | vec.push_back(3); 56 | reverse(vec.begin(),vec.end());//倒转向量 57 | for(int i=0; i a; 67 | int n=0,b; 68 | while(cin>>b){ 69 | a.push_back(b); 70 | n++; 71 | if (cin.get() == '\n') 72 | break; 73 | } 74 | ``` 75 | 76 | **** 77 | 78 | ### map 79 | 80 | * 功能类似平常使用的字典,[参考W3C](https://www.w3cschool.cn/cpp/cpp-fu8l2ppt.html),**map 的使用,也有很多函数,insert、erase、count、empty等等。** 81 | ```c++ 82 | #include//需要使用库文件 83 | //定义一个 map 84 | map maplive;//map 的定义方式,键为 int,值为 string。 85 | maplive.insert(pair(102,"aclive"));//方式一 86 | maplive.insert(map::value_type(321,"hai"));//方式二 87 | maplive[112]="April";//map中最简单最常用的插入添加! 88 | 89 | //map 中查找元素 90 | map::iterator l_it; 91 | l_it=maplive.find(112); 92 | if(l_it==maplive.end()) 93 | cout<<"we do not find 112"<::iterator l_it;; 99 | l_it=maplive.find(112); 100 | if(l_it==maplive.end()) 101 | cout<<"we do not find 112"< 107 | #include 108 | using namespace std; 109 | int main( ){ 110 | map m1, m2, m3; 111 | map ::iterator m1_Iter; 112 | m1.insert ( pair ( 1, 10 ) ); 113 | m1.insert ( pair ( 2, 20 ) ); 114 | m2.insert ( pair ( 10, 100 ) ); 115 | cout << "The original map m1 is:"; 116 | for ( m1_Iter = m1.begin( ); m1_Iter != m1.end( ); m1_Iter++ ) 117 | cout << " " << m1_Iter->second; 118 | cout << "." << endl; 119 | 120 | m1.swap( m2 );//将两个 map 的引用交换。 121 | cout << "After swapping with m2, map m1 is:"; 122 | for ( m1_Iter = m1.begin( ); m1_Iter != m1.end( ); m1_Iter++ ) 123 | cout << " " << m1_Iter -> second; 124 | cout << "." << endl; 125 | return 0; 126 | } 127 | ``` 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /notes/CPP/CPP-基础语法.md: -------------------------------------------------------------------------------- 1 | # CPP 基础语法 2 | 3 | [参考W3Cschool](https://www.w3cschool.cn/cpp/cpp-files-streams.html) 4 | **面试考点:指针、引用、运算符重载** 5 | 6 | **** 7 | ### 命名空间(namespace) 8 | 9 | 您可能会写一个名为 xyz() 的函数,在另一个可用的库中也存在一个相同的函数 xyz()。这样,编译器就无法判断您所使用的是哪一个 xyz() 函数。因此,引入了 命名空间 这个概念,专门用于解决上面的问题,它可作为附加信息来区分不同库中相同名称的函数、类、变量等。 10 | 11 | ```c++ 12 | // 这是我们要用到的库,作者也没有很好的理解这个文件和 std 命名空间的具体关系,之后会找找书,了解一下底层构造。 13 | #include 14 | using namespace std;//像例子那样调用会很麻烦,所以直接把前缀写出来,直接用就可以了。 15 | // using std::cout 只为一个函数声明,只有这一个函数可以直接用。 16 | //cout << "申明部分函数的命名空间"<< std::endl; 17 | 18 | namespace first_space{// 自定义的第一个命名空间 19 | void func(){ 20 | cout << "Inside first_space" << endl; 21 | } 22 | } 23 | 24 | namespace second_space{// 自定义的第二个命名空间 25 | void func(){ 26 | cout << "Inside second_space" << endl; 27 | } 28 | } 29 | int main (){ 30 | first_space::func();// 调用第一个命名空间中的函数 31 | second_space::func(); // 调用第二个命名空间中的函数 32 | return 0; 33 | } 34 | ``` 35 | 36 | * **问:在 C 语言中,语句 int a = 016,那么十进制 a 是多少?** 37 | 38 | * **答**:0 开头表示八进制,a 为 14。 39 | **** 40 | ### 运算符的使用 41 | * **问:C 语言中的三目运算符的返回值?** 42 | 43 | * **答**:**count << ( x > y ) ? x : y;** 返回 0 或 1,即真和假,只有 **z = ( x > y ) ? x : y;** 才返回较大值。应该是运算符优先级的问题,**<<** 优先级高于 **?** 44 | [参考博客](https://blog.csdn.net/arbel/article/details/7294247) 45 | 46 | ```c++ 47 | //问:输出是多少? 48 | int main() 49 | { 50 | int a = 1,b = 2,m = 0,n = 0,k; 51 | k = ( n = b < a ) && ( m = a ) ; 52 | printf("%d,%d\n",k,m); 53 | return 0; 54 | } 55 | //答:0,0 运算符的运行方式,&& 运算符,左边是 0 ,直接返回 0 ,左边是 1 则直接返回右边的布尔值。 56 | ``` 57 | **** 58 | ### 宏的使用 59 | 60 | ```c++ 61 | #include 62 | #define SUM(x) 3*x*x+1 //关键字 define 63 | int main() 64 | { 65 | int i=5,j=8; 66 | printf("%d\n",SUM(i+j)); 67 | } 68 | //输出64,宏的使用,代码编译的时候,宏会替换为表达式,即 (3 * i + j * i + j + 1)= 64。 69 | ``` 70 | **** 71 | ### 字符串 72 | 73 | * **第一种**:C++ 风格的字符串起源于 C 语言,并在 C++ 中继续得到支持。字符串实际上是使用 null 字符 '\0' 终止的一维字符数组。因此,一个以 null 结尾的字符串,包含了组成字符串的字符。下面示例长度为 6; 74 | 75 | ```c++ 76 | char greeting[] = "Hello";//字符串长度为6,以'\0'结尾。 77 | ``` 78 | 79 | 赋值可以使用函数 **strcpy(S1,S2)** 将 S2 赋值到 S1。还有其他很多函数 **strcat、strlen、strcmp、strchr、strstr等等** 80 | 81 | * **第二种**:C++ 标准库提供了 string 类类型,推荐此种,比较容易理解。 82 | 83 | ```c++ 84 | #include 85 | #include 86 | using namespace std; 87 | int main (){ 88 | string str1 = "Hello"; 89 | string str2 = "World"; 90 | string str3; 91 | int len ; 92 | 93 | str3 = str1;// 复制 str1 到 str3 94 | cout << "str3 : " << str3 << endl; 95 | 96 | str3 = str1 + str2;// 连接 str1 和 str2 97 | cout << "str1 + str2 : " << str3 << endl; 98 | 99 | len = str3.size();// 连接后,str3 的总长度 100 | cout << "str3.size() : " << len << endl; 101 | 102 | return 0; 103 | } 104 | ``` 105 | **** 106 | ### 指针和引用 107 | 108 | * **指针 [ * ]**:是一个变量,其值为另一个变量的地址,即内存的地址。 109 | 110 | * **(&)[取地址运算符]**:获取某个变量的地址;( * )[地址访问运算符]:访问某个地址的值; 111 | 在变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个 NULL 值是一个良好的编程习惯。赋为 NULL 值的指针被称为空指针。NULL 指针是一个定义在标准库中值为零的常量。 112 | 113 | ```c++ 114 | int var = 100; 115 | int *pt = NULL; //没有 null 也可以,但养成好习惯嘛。 116 | pt = &var; 117 | int *ppt = &pt 118 | **pt;//二级指针,地址里包含的值还是一个地址,这样可以找到变量 var 的值。 119 | ``` 120 | 指向指针的指针是一种多级间接寻址的形式,或者说是一个指针链。通常,一个指针包含一个变量的地址。当我们定义一个指向指针的指针时,第一个指针包含了第二个指针的地址,第二个指针指向包含实际值的位置。 121 | 122 | *** 123 | 124 | * **引用 [ & ]**:引用变量是一个别名,也就是说,它是某个已存在变量的另一个名字。一旦把引用初始化为某个变量,就可以使用该引用名称或变量名称来指向变量。通过使用引用来替代指针,会使 C++ 程序更容易阅读和维护。 125 | 126 | ```c++ 127 | int a; 128 | int& b = a;//b 是 a 的引用,此时 a,b 同值。 129 | //一、最常见的 引用作为参数 用法 130 | void swap(int& x, int& y) 131 | { 132 | int temp; 133 | temp = x; /* 保存地址 x 的值 */ 134 | x = y; /* 把 y 赋值给 x */ 135 | y = temp; /* 把 x 赋值给 y */ 136 | return; 137 | } 138 | //二、引用作为返回值 用法 139 | double vals[] = {10.1, 12.6, 33.1, 24.1, 50.0}; 140 | double& setValues( int i ) 141 | { 142 | return vals[i];// 返回第 i 个元素的引用,注意作用域,不要返回局部变量的引用。 143 | } 144 | setValues(0) = 100;//函数放在左边,重新赋值。 145 | ``` 146 | 147 | * **问:在 C++ 中,引用和指针的区别?** 148 | * **答**:1. 引用总是指向一个对象,指针可能不指向对象。 149 | 2. 引用不能用 const 修饰,而指针可以。 150 | 151 | 3. 引用创建时必须初始化,而指针则可以在任何时候被初始化。 152 | 153 | 4. 引用和指针都可以被重新赋值,错误:指针可以被重新赋值,一旦引用被初始化为一个对象,就不能被指向到另一个对象。 154 | 155 | **** 156 | * **输出函数 cout,cerr,clog** 157 | **cout**:经过缓冲后输出,默认情况下是显示器。缓冲区的目的,就是减少刷屏的次数——比如,你的程序输出圣经中的一篇文章。不带缓冲的话,就会每写一个字母,就输出一个字母,然后刷屏。有了缓冲,你将看到若干句子“同时”就出现在了屏幕上(由内存翻新到显存,然后刷新屏幕)。 158 | **cerr**:不经过缓冲而直接输出,一般用于迅速输出出错信息。因为有时候怕内存满了这种错误,不直接输出就没地方放了。 159 | **clog**:也是输出标准错误流(这点儿和 cerr 是一样的),貌似平时很少用到这个啊。 160 | [参考博客](https://blog.csdn.net/bsmmaoshenbo/article/details/50778068cout) 161 | **** 162 | - **运算符和函数重载** 163 | - **函数重载**:在同一个作用域内,可以声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数、类型或者顺序)必须不同。您不能仅通过返回类型的不同来重载函数。 164 | - **运算符重载[operator]**:与其他函数一样,重载运算符有一个返回类型和一个参数列表。大多数的重载运算符可被定义为普通的非成员函数或这被定义为类成员函数。如果我们定义上面的函数为类的非成员函数,那么我们需要为每次操作传递两个参数 165 | 166 | ### 高级教程 167 | 168 | * **C++ 文件流**:我们已经使用了 **iostream** 标准库,它提供了 **cin** 和 **cout** 方法分别用于从标准输入读取流和向标准输出写入流。下面介绍如何从文件读取流和向文件写入流。这就需要用到 C++ 中另一个标准库 **fstream**。 169 | * **.dat**:硬盘中的 .dat文件,这个是某个程序组的 数据库文件(data),是用来存放数据参数用的,比如说 3D游戏中的 3D 模型,皮肤文件等。这个打开也没用,因为都是编码。 170 | 171 | ```c++ 172 | //具体参看W3C里的介绍,open(文件位置,打开方式)函数用于打开文件。 173 | void open(const char *filename, ios::openmode1 | ios::openmode2); 174 | void close(); //关闭打开的文件 175 | 176 | #include //文件流操作库文件。 177 | #include 178 | using namespace std; 179 | int main (){ 180 | char data[100]; 181 | // 以写模式打开文件 182 | ofstream outfile;//该数据类型表示输出文件流,用于创建文件并向文件写入信息。 183 | outfile.open("afile.dat"); 184 | 185 | cout << "Enter your name: "; 186 | cin.getline(data, 100);//用户的输入保存在 data 中,输入一行数据 187 | outfile << data << endl;//向文件写入一行用户输入的数据 188 | 189 | cout << "Enter your age: "; 190 | cin >> data;//再输入一行数据 191 | cin.ignore(); 192 | outfile << data << endl; 193 | 194 | outfile.close();// 关闭打开的文件 195 | 196 | ifstream infile;// 以读模式打开文件 197 | infile.open("afile.dat"); 198 | 199 | cout << "Reading from the file" << endl; 200 | infile >> data; //读取一行 201 | cout << data << endl;// 在屏幕上显示 202 | 203 | infile >> data; // 再读取一行 204 | cout << data << endl; 205 | 206 | infile.close();//关闭打开的文件 207 | 208 | return 0; 209 | } 210 | ``` 211 | 212 | * **cin、cin.getline()、getline() 用法** 213 | 214 | ```c++ 215 | int a,b; 216 | cin>>a>>b; //遇“空格”、“TAB”、“回车”就结束第一个输入 217 | 218 | char str[100]; 219 | cin.getline(str,10);//接收十个字符到 str,可以接收空格 220 | 221 | #include//需包含头文件 string 222 | string str; 223 | getline(cin, str); 224 | ``` 225 | 226 | **当一个程序同时出现 cin>> 和 getline() 时,需要注意的是,在 cin>> 输入流完成之后,需要通过** 227 | 228 | ```c++ 229 | str="\n"; getline(cin,str); 230 | ``` 231 | 232 | **的方式将回车符作为输入流 cin 以清除缓存,如果不这样做的话,在控制台上就不会出现 getline() 的输入提示,而直接跳过,因为程序默认地将之前的变量作为输入流。** 233 | 234 | ```c++ 235 | #include 236 | #include 237 | using namespace std; 238 | 239 | int main(){ 240 | int age; 241 | //standard input(cin) 242 | cout<<"Please enter an integer value as your age: "; 243 | cin>>age; 244 | cout<<"Your ager is: "< 280 | #include 281 | using namespace std; 282 | 283 | template 284 | inline T const& Max (T const& a, T const& b) 285 | { 286 | return a < b ? b:a; 287 | } //是不是感觉参数有点多,是的,template,typename 当做关键字好了,T 是我们定义的类型,先当做 int 288 | //理解,inline 是内联函数关键字,可以不加,后面是三个 (T const&)一个返回值,两个参数类型。 289 | 290 | int main () 291 | { 292 | 293 | int i = 39; 294 | int j = 20; 295 | cout << "Max(i, j): " << Max(i, j) << endl; 296 | 297 | double f1 = 13.5; 298 | double f2 = 20.7; 299 | cout << "Max(f1, f2): " << Max(f1, f2) << endl; 300 | 301 | string s1 = "Hello"; 302 | string s2 = "World"; 303 | cout << "Max(s1, s2): " << Max(s1, s2) << endl; 304 | 305 | return 0; 306 | } 307 | ``` 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | -------------------------------------------------------------------------------- /notes/CPP/CPP-算法目录.md: -------------------------------------------------------------------------------- 1 | # CPP-算法目录 2 | 3 | * 数组 4 | * 字符串 5 | * 排序算法和练习 6 | * 查找 7 | * 位运算 8 | * 排列组合 9 | * 数学 10 | **** 11 | * 动态规划 12 | * 贪心算法 13 | * 回溯法 14 | * 分治 15 | **** 16 | * 栈和队列 17 | * 链表 18 | * 树 -------------------------------------------------------------------------------- /notes/CPP/CPP-设计模式.md: -------------------------------------------------------------------------------- 1 | # CPP设计模式 2 | 3 | [未整理完成,待续。。。] 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /notes/CPP/CPP-面向对象.md: -------------------------------------------------------------------------------- 1 | # CPP-面向对象 2 | 3 | [参考菜鸟教程](https://www.runoob.com/cplusplus/cpp-tutorial.html ) 4 | 5 | C++ 在 C 语言的基础上增加了面向对象编程,C++ 支持面向对象程序设计。类是 C++ 的核心特性,通常被称为用户定义的类型,类中的数据和方法称为类的成员,函数在一个类中被称为**类的成员函数**。 6 | **** 7 | * **构造函数**:与类名相同,可以带或不带参数。 8 | 9 | * **~析构函数**:与类名相同,前面加个[**~**],它不会返回任何值,也不能带任何参数。怕你忘记,析构函数会在跳出程序(比如关闭文件、释放内存等)前自己释放资源。 10 | 11 | * **友元函数[ Friend ]**:类的友元函数是定义在类外部,但有权访问类的所有私有成员和保护成员即 **任何成员**。 12 | **** 13 | * **内联函数[ Inline ]**:C++ 内联函数是通常与类一起使用。如果一个函数是内联的,那么在编译时,编译器会把该函数的代码副本放置在每个调用该函数的地方,是不是感觉和 宏 很像,确实很像,但是内联函数是一个真正函数,编译器会检查类型参数,而宏是不加任何验证的代码替换,可以见 基础语法 里的宏练习。内联函数 也是为了避免程序在反复调用函数时,保存函数的各种参数,但这种方式会增加内存开销,所以内联函数尽量短,否则就不划算了,这应该也算是 空间换时间 的一种想法吧。 14 | 15 | [参考博客-解释更详细](https://baijiahao.baidu.com/s?id=1625422703705248452&wfr=spider&for=pc) 16 | **** 17 | * **this指针**:在 C++ 中,每一个对象都能通过 this 指针来访问自己的地址。this 指针是所有成员函数的隐含参数。因此,在成员函数内部,它可以用来指向调用对象。友元函数没有 this 指针,因为友元不是类的成员。只有成员函数才有 this 指针。 18 | 19 | **** 20 | 21 | ### 成员访问修饰符 22 | 23 | * **类成员的访问属性[访问修饰符]**:成员和类的 **默认访问修饰符是 private**,一个类可以有多个 public、protected 或 private 标记区域,每个标记区域在下一个标记区域开始之前或者在遇到类主体结束右括号之前都是有效的。 24 | **** 25 | * **public[公共成员]**:在类对象作用域内,公共成员在类的外部是可访问的。 26 | 27 | ```c++ 28 | class Box 29 | { 30 | public: 31 | double length; // Length of a box 32 | double breadth; // Breadth of a box 33 | double height; // Height of a box 34 | 35 | double getVolume(void);//成员函数 36 | }; 37 | 38 | double Box::getVolume(void)// 或者内部声明,定义在外部。 39 | { 40 | return length * breadth * height; 41 | } 42 | ``` 43 | **** 44 | * **private[ 私有的成员]**:不能随便访问的成员,可以设置一个函数如 Get,Set 等操作该成员,默认情况下,类的所有成员都是私有的,private 成员只能被本类成员(类内)和 友元访问,不能被派生类访问。 45 | **** 46 | * **protected[受保护的成员]**:保护成员变量或函数与私有成员十分相似,但有一点不同,保护成员在派生类(即子类)中是可访问的。 47 | **** 48 | * **静态成员**:静态成员在类的所有对象中是共享的。如果不存在其他的初始化语句,在创建第一个对象时,所有的静态数据都会被初始化为零。我们不能把静态成员的初始化放置在类的定义中,但是可以在类的外部通过使用**范围解析运算符 ( :: )**来重新声明静态变量从而对它进行初始化。**静态成员函数即使在类对象不存在的情况下也能被调用,静态函数只要使用类名加范围解析运算符( :: )就可以访问。** 49 | **** 50 | * **( . )[ 直接成员访问符 ]**:类的对象的公共数据成员可以使用**直接成员访问运算符**来访问。 51 | 52 | * **( :: )[ 范围解析运算符 ]**:类的成员函数可以定义在内部,也可以使用此符定义在外部。 53 | 54 | * **( -> )[ 成员访问运算符 ]**:类实例使用成员函数时使用 **( . )**,类的引用使用成员函数时使用**( -> )**。 55 | 56 | ```c++ 57 | address = &class_temp 58 | *address.people; 59 | address->people; 60 | ``` 61 | ### 类的三大特性 62 | 63 | **封装** 就不说了,私有变量外部不能访问,需要用公共函数去操作,保护私有变量。 64 | 65 | #### 继承 66 | 67 | ```c++ 68 | #include 69 | using namespace std; 70 | 71 | class Shape{//俗称 基类-父类 72 | public: 73 | Shape(int w, int h){width = w; height = h;}//构造函数,不能被继承 74 | void setWidth(int w){ width = w;} 75 | void setHeight(int h){ height = h;} 76 | protected: 77 | int width; 78 | int height; 79 | };//不要忘记这个分号 80 | 81 | class Rectangle : public Shape{//公有继承,一定记住加 public,俗称-派生类-子类 82 | public: 83 | Rectangle(int a=0,b=0):Shape(a,b){} //调用父类的构造函数,因为不能继承嘛。 84 | int getArea(){return width * height;} 85 | };//不要忘记这个分号 86 | ``` 87 | 88 | * **继承**:继承默认是 private 继承,但一般用 public 继承,下面介绍原因,支持多继承逗号隔开。一个派生类不能继承基类中的 **构造函数、析构函数、构造函数、重载运算符、友元函数**。 89 | 90 | **公有继承(public):**当一个类派生自**公有**基类时,基类的**公有**成员也是派生类的**公有**成员,基类的**保护**成员也是派生类的**保护**成员,基类的**私有**成员不能直接被派生类访问,但是可以通过调用基类的**公有**和**保护**成员来访问。简单点说就是不变,该啥样还是啥样,这也是用的最多的,下面了解即可。 91 | 92 | **保护继承(protected):** 当一个类派生自**保护**基类时,基类的**公有**和**保护**成员将成为派生类的**保护**成员。 93 | 94 | **私有继承(private):**当一个类派生自**私有**基类时,基类的**公有**和**保护**成员将成为派生类的**私有**成员。 95 | 96 | * **问**:父类哪些成员子类可以访问?**答**:public,protected。 97 | 98 | **** 99 | 100 | * **问:private 继承和 public,protected 继承有区别么?** 101 | 102 | * **答**:访问父类成员,要看父类成员的访问修饰符,继承父类成员要看是什么继承,这些成员在子类中的修饰符如何,public 继承不变;protected 继承 private 不变,其余都是 protected;private 继承都变 private;由此可以得出,继承从宽到紧,private > protected > public,默认 private,即继承的类只能自己访问和拥有,不可传播。 103 | 104 | **** 105 | 106 | #### 多态 107 | 108 | * **虚函数**: 是在基类中使用关键字 virtual 声明的函数。在派生类中重新定义基类中定义的虚函数时,会告诉编译器不要静态链接到该函数。我们想要的是在程序中任意点可以根据所调用的对象类型来选择调用的函数,这种操作被称为动态链接,或后期绑定。**如果不加**,那么子类和父类重名的函数,会优先调用父类的函数。**加了**,如果子类有重写,那么调用子类,如果子类没有,则还是会调用父类。 109 | 110 | * **纯虚函数(接口,抽象类ABC)**:既然不会执行,那么可以设置成 纯虚函数,没有函数主体,也不用。但是这个类也就废了,只能被继承了,它是不能实例化的。**具有纯虚函数的类的任何派生类也都必须要么定义与该类纯虚函数同名且形参完全相同的函数,要么要在派生类中重新将其定义成纯虚函数。** 111 | 112 | **区别**:两者的区别也很明显,只有虚函数的类不仅可以实例化,也可以调用虚函数。而纯虚函数只能被继承,所以纯虚函数肯定不是 private 的,否则没有意义。 113 | 114 | ```c++ 115 | class Shape { 116 | public: 117 | virtual int area(){ 118 | cout << "Parent class area :" < num2){//这里不加冒号,加小括号和python不同。 239 | return num1 240 | } else {//函数体内一定要加大括号,并且这三个东西要写在一行。 241 | return num2 242 | } 243 | } 244 | 245 | //返回两个值的函数 246 | func calculate(num1 int, num2 int) (int, int){ 247 | add,minus := 0,0 248 | add = num1 + num2 249 | minus = num1 - num2 250 | return add,minus 251 | } 252 | 253 | func main(){ 254 | fmt.Println(max(11,12)) 255 | fmt.Println(calculate(11,10)) 256 | } 257 | ``` 258 | 1、Go 语言最少有个 main() 函数。 259 | 2、还能返回多个值,优秀。 260 | 261 | 262 | ### 闭包 263 | ```Go 264 | package main 265 | import "fmt" 266 | 267 | func getSquence() func() int{//后面的参数不是函数名啊 268 | i := 0 269 | return func() int{ 270 | i += 1 271 | return i 272 | } 273 | } 274 | 275 | func main(){ 276 | nextSquence := getSquence() 277 | fmt.Println(nextSquence()) 278 | fmt.Println(nextSquence()) 279 | fmt.Println(nextSquence()) 280 | } 281 | //输出1 2 3 282 | ``` 283 | 1. Go 语言支持匿名函数,可作为闭包。闭包可以简单理解为夹带了外部变量。如果没有,则和普通函数没什么区别,具体可类比 Python 的装饰器。 284 | 285 | ### 结构体(struct) 286 | ```Go 287 | package main 288 | import "fmt" 289 | type Book struct{//这里的变量可以不用加 var 了。 290 | title string 291 | author string 292 | price int 293 | } 294 | 295 | //结构体,下面要给它添加方法 296 | type Circle struct{ 297 | radio float64 298 | } 299 | 300 | //该 method 属于 Circle 类型对象中的方法 301 | func (c Circle) getArea() float64{ 302 | return 3.14 * c.radio * c.radio 303 | } 304 | 305 | func main(){ 306 | var c Circle 307 | c.radio = 1.0 308 | fmt.Println(c.getArea()) 309 | 310 | var book1 Book 311 | var book2 Book 312 | book1.title = "语文" 313 | book2.title = "数学" 314 | fmt.Println(book1) 315 | fmt.Println(book2) 316 | } 317 | }//输出 {语文 0} \n {数学 0} 318 | ``` 319 | 1、声明结构体应该会有默认值,字符串为空,数字为0。 320 | 2、结构体可以当作参数,也可以构造结构体指针。**使用结构体指针访问结构体成员,使用 "." 操作符** 321 | 3、Go 语言中可以为结构体定义方法。 322 | 323 | 324 | ### 接口(interface) 325 | **定义:它把所有的具有共性的方法定义在一起** 326 | 327 | **问:interface { } 是可以指向任意对象的 Any 类型,这一说法是否正确。** 328 | **答**:是的。 329 | 330 | ```Go 331 | package main 332 | import "fmt" 333 | 334 | type Phone interface{//关键字 type,不是 func。 335 | MyName()//要实现一个接口,必须实现该接口里面的所有方法。否则不能用接口调用。 336 | } 337 | type IPhone struct{ 338 | } 339 | type HuaWei struct{ 340 | } 341 | 342 | func (ph IPhone) MyName(){//给结构体添加接口里的方法。 343 | fmt.Println("I am IPhone!") 344 | } 345 | func (ph HuaWei) MyName(){ 346 | fmt.Println("I an HuaWei!") 347 | } 348 | 349 | func main(){ 350 | var ph Phone 351 | ph = new(IPhone) //或者写成 ph = &IPhone 或者 ph = IPhone{ } 都可以。 352 | ph.MyName() 353 | ph = new(HuaWei) 354 | ph.MyName() 355 | } 356 | //没啥感觉,不就是将结构体实现的方法,放在一起,然后用一个东西调用。要实现一个接口,必须实现该接口里面的所有方法。 357 | ``` 358 | interface (接口)是 golang 最重要的特性之一,Interface 类型可以定义一组方法,但是这些不需要实现。并且 interface 不能包含任何变量。简单的说:interface 是方法的集合 interface 是一种类型,并且是指针类型,interface 的更重要的作用在于多态实现。如果一个接口没有任何方法,我们称为空接口,由于空接口没有方法,所以任何类型都实现了空接口,所以可以调用任何类型。 359 | **高级的概念:接口嵌套、类型转换** 360 | 361 | 362 | ### 反射(reflect)有点复杂,晚点再研究 363 | 参考:https://www.cnblogs.com/wdliu/p/9222283.html 364 | * **问:Golang支持反射,反射最常见的使用场景是做对象的序列化,这一说法是否正确?** 365 | * **答:**正确 366 | 367 | ### Go的异常 368 | error 类型是一个接口类型,我们可以在编码中通过实现 error 接口类型来生成错误信息。 369 | 参考:https://blog.csdn.net/fwhezfwhez/article/details/79175376 370 | 371 | 372 | ### 并行编程模式 373 | 374 | **问:Goroutine与线程的区别?** 375 | **答**:我们在使用 Go 语言进行开发时,一般会使用 goroutine 来处理并发任务。和线程一样,golang 的主函数(其实也跑在一个 goroutine 中)并不会等待其它 goroutine 结束。如果主 goroutine 结束了,所有其它 goroutine 都将结束。 376 | 许多人认为 goroutine 比线程运行得更快,这是一个误解。Goroutine 并不会更快,它只是增加了更多的并发性。当一个 goroutine 被阻塞(比如等待 IO),golang 的 scheduler 会调度其它可以执行的 goroutine 运行。与线程相比,它有以下几个优点:内存消耗更少、创建与销毁的开销更小、切换开销更小、 377 | [参考1](https://baijiahao.baidu.com/s?id=1620972759226100794&wfr=spider&for=pc) 378 | [参考2](https://www.cnblogs.com/wdliu/p/9272220.html) 379 | 380 | **问:channle的基本概念?** 381 | **答**:[参考](https://cloud.tencent.com/developer/news/347926) 382 | 383 | **问:同步 goroutine?** 384 | **答**:由于 goroutine 是异步执行的,那很有可能出现主程序退出时还有 goroutine 没有执行完,此时 goroutine 也会跟着退出。go 提供了 sync (synchronization)包和 channel 来解决同步问题,当然如果你能预测每个 goroutine 执行的时间,你还可以通过 time.Sleep 方式等待所有的 groutine 执行完成以后在退出程序。 385 | ```Go 386 | package main 387 | 388 | import ( 389 | "fmt" 390 | "sync" 391 | ) 392 | 393 | func cal(a int , b int ,n *sync.WaitGroup) { 394 | c := a+b 395 | fmt.Printf("%d + %d = %d\n",a,b,c) 396 | defer n.Done() //goroutinue完成后, WaitGroup的计数-1 397 | } 398 | 399 | func main() { 400 | var go_sync sync.WaitGroup //声明一个WaitGroup变量 401 | for i :=0 ; i<10 ;i++{ 402 | go_sync.Add(1) // WaitGroup的计数加1 403 | go cal(i,i+1,&go_sync) 404 | } 405 | go_sync.Wait() //等待所有goroutine执行完毕 406 | } 407 | ``` 408 | 409 | **继承调用规则** 410 | ![Go 继承调用规则](../../pics/go继承调用规则.png) 411 | 412 | 413 | 414 | 415 | -------------------------------------------------------------------------------- /notes/Go/Go-算法目录.md: -------------------------------------------------------------------------------- 1 | # Go-算法目录 2 | 3 | [还没有整理完 go 语法格式,未完成。。。] 4 | * 数组 5 | * 字符串 6 | * 排序算法和练习 7 | * 查找 8 | * 位运算 9 | * 排列组合 10 | * 数学 11 | **** 12 | * 动态规划 13 | * 贪心算法 14 | * 回溯法 15 | * 分治 16 | **** 17 | * 栈和队列 18 | * 链表 19 | * 树 -------------------------------------------------------------------------------- /notes/Go/Go-错误集锦.md: -------------------------------------------------------------------------------- 1 | # Go-错误集锦 2 | 3 | 新手初级错误[参考链接](https://blog.csdn.net/keets1992/article/details/92816775) 4 | 5 | 1、左大括号 { 一般不能单独放一行 6 | 在其他大多数语言中,{  的位置你自行决定。Go 比较特别,遵守分号注入规则(automatic semicolon injection):编译器会在每行代码尾部特定分隔符后加 ; 来分隔多条语句,比如会在 ) 后加分号: 错误提示如下: 7 | ```go 8 | ./main.go: missing function body 9 | ./main.go: syntax error: unexpected semicolon or newline before { 10 | ``` 11 | 12 | 2、未使用的变量和包 13 | 如果在函数体代码中有未使用的变量,则无法通过编译,不过全局变量声明但不使用是可以的。即使变量声明后为变量赋值,依旧无法通过编译,需在某处使用它。import 导入的包也是,未使用也会报错。错误提示如下: 14 | ```go 15 | declared and not used;imported and not used 16 | ``` 17 | 18 | 3、简短声明的变量只能在函数内部使用 19 | [ myvar := 1 ] 这种形式只能在函数体内部使用,即只能是局部变量,全局变量不可以。且此变量之前未被申明。struct 的变量字段不能使用 := 来赋值。 20 | 21 | 4、显式类型的变量无法使用 nil 来初始化 22 | nil 是 interface、function、pointer、map、slice 和 channel 类型变量的默认初始值。但声明时不指定类型,编译器也无法推断出变量的具体类型。 23 | 24 | ```Go 25 | // 错误示例 26 | func main() { 27 | var x = nil // error: use of untyped nil 28 | _ = x 29 | } 30 | // 正确示例 31 | func main() { 32 | var x interface{} = nil 33 | _ = x 34 | } 35 | ``` 36 | 37 | 5、允许对值为 nil 的 slice 添加元素,但对值为 nil 的 map 添加元素则会造成运行时 panic 38 | ```Go 39 | // map 错误示例 40 | func main() { 41 | var m map[string]int //如果是全局变量,那么下面正确申明就不要加冒号了。 42 | m["one"] = 1 // error: panic: assignment to entry in nil map 43 | // m := make(map[string]int)// map 的正确申明,分配了实际的内存 44 | } 45 | 46 | // slice 正确示例 47 | func main() { 48 | var s []int 49 | s = append(s, 1) 50 | } 51 | ``` 52 | 6、string 类型的变量值默认为 "" 不是 nil。0 和 1 也不能表示 True 和 False。 53 | 54 | 7、在 C/C++ 中,数组(名)是指针。将数组作为参数传进函数时,相当于传递了数组内存地址的引用,在函数内部会改变该数组的值。在 Go 中,数组,字符串是值。作为参数传进函数时,传递的是字符串,数组的原始值拷贝,此时在函数内部是无法更新该数组和字符串的,如果要修改数组,应该传递指针。直接使用 slice:即使函数内部得到的是 slice 的值拷贝,但依旧会更新 slice 的原始数据(底层 array) 55 | ```Go 56 | // 传址会修改原数据 57 | func main() { 58 | x := [3]int{1,2,3} //[数字] 是数字,[]没有是切片。 59 | 60 | func(arr *[3]int) { 61 | (*arr)[0] = 7 62 | fmt.Println(arr) // &[7 2 3] 63 | }(&x) 64 | fmt.Println(x) // [7 2 3] 65 | } 66 | 67 | // 会修改 slice 的底层 array,从而修改 slice 68 | func main() { 69 | x := []int{1, 2, 3} 70 | func(arr []int) { 71 | arr[0] = 7 72 | fmt.Println(x) // [7 2 3] 73 | }(x) 74 | fmt.Println(x) // [7 2 3] 75 | } 76 | 77 | ``` 78 | 79 | **问:如何理解 错误是业务过程的一部分,而异常不是?** 80 | **答**:错误指的是可能出现问题的地方出现了问题,比如打开一个文件时失败,这种情况在人们的意料之中 ;而异常指的是不应该出现问题的地方出现了问题,比如引用了空指针,这种情况在人们的意料之外。可见,错误是业务过程的一部分,而异常不是 。Golang 中引入 error 接口类型作为错误处理的标准模式,如果函数要返回错误,则返回值类型列表中肯定包含 error。error 处理过程类似于 C 语言中的错误码,可逐层返回,直到被处理。Golang 中引入两个内置函数 panic 和 recover 来触发和终止异常处理流程,同时引入关键字 defer 来延迟执行 defer 后面的函数。 81 | 一直等到包含 defer 语句的函数执行完毕时,延迟函数(defer 后的函数)才会被执行,而不管包含 defer 语句的函数是通过 return 的正常结束,还是由于 panic 导致的异常结束。你可以在一个函数中执行多条 defer 语句,它们的执行顺序与声明顺序相反。 82 | 当程序运行时,如果遇到引用空指针、下标越界或显式调用 panic 函数等情况,则先触发 panic 函数的执行,然后调用延迟函数。调用者继续传递 panic,因此该过程一直在调用栈中重复发生:函数停止执行,调用延迟执行函数等。如果一路在延迟函数中没有 recover 函数的调用,则会到达该携程的起点,该携程结束,然后终止其他所有携程,包括主携程(类似于 C 语言中的主线程,该携程 ID 为1)。错误和异常从 Golang 机制上讲,就是 error 和 panic 的区别。很多其他语言也一样,比如 C++/Java,没有 error 但有 error,没有 panic 但有 throw。Golang 错误和异常是可以互相转换的:错误转异常,比如程序逻辑上尝试请求某个 URL,最多尝试三次,尝试三次的过程中请求失败是错误,尝试完第三次还不成功的话,失败就被提升为异常了。异常转错误,比如 panic 触发的异常被 recover 恢复后,将返回值中 error 类型的变量进行赋值,以便上层函数继续走错误处理流程。 83 | 84 | **问:对于常量定义zero(const zero = 0.0),zero是浮点型常量,这一说法是否正确?** 85 | **答**:错误,是 float64。Go 语言的常量有个不同寻常之处。虽然一个常量可以有任意有一个确定的基础类型,例如 int 或 float64,或者是类似 time.Duration 这样命名的基础类型,但是许多常量并没有一个明确的基础类型。编译器为这些没有明确的基础类型的数字常量提供比基础类型更高精度的算术运算;你可以认为至少有 256bit 的运算精度。这里有六种未明确类型的常量类型,分别是无类型的布尔型、无类型的整数、无类型的字符、无类型的浮点数、无类型的复数、无类型的字符串。`fmt.Printf("%T",zero)` 可以查看变量类型。 86 | 87 | 面:Go 语言和 python 的区别? 88 | 89 | -------------------------------------------------------------------------------- /notes/JAVA/JAVA-面向对象.md: -------------------------------------------------------------------------------- 1 | # JAVA-面向对象 2 | 3 | 1、基本概念 4 | 5 | 2、封装 6 | 7 | 3、继承 8 | 9 | 4、多态 10 | 11 | 5、内部类 12 | 13 | **下面先介绍一下基本概念,然后再从我们熟知的封装、继承、多态三大部分进行讲解,不要死记概念,去刷相关的笔试题,然后再找找相关概念,下面直接阅读会让人产生眩晕感,之后会慢慢加点问题改进的** 14 | 15 | # 1、基本概念 16 | 17 | **类声明**:一个源文件中只能有一个 public 类且源文件的名称应该和此类名相同,可以有多个非 public 类。此源文件属于哪个包,就在首行标明 package 包名; 18 | 19 | 类包括 外部类 和 内部类,内部类 还包括 成员内部类(单独定义) 和 局部内部类(某个方法里定义,就像是方法中的一个局部变量,不能有修饰符,或者只能是 abstract 或者 final)。 20 | 21 | **外部类**:的上一级是包,所以他只有两个作用域,同一个包内和任意位置。因此只能有两个访问控制修饰符,默认 和 public,非访问控制修饰符有多个可选 abstract, final。 22 | 23 | **内部类**:的上一级是外部类,有四个作用域,当做普通的成员对待,三个修饰符都可以(默认不用修饰符)。但如果是在方法体内(局部内部类),那么是不可以使用访问控制修饰符的。 24 | 25 | **JAVA 包(package)**:包主要用来对 类和接口 进行分类。当开发 Java 程序时,可能编写成百上千的类,因此很有必要对类和接口进行分类。有关联关系的类放在一个源文件中,该文件属于哪个包,那么就在该源文件的首行写 package 语句,而不是你想象的那样将许多源文件打包。 26 | 27 | 1、为了更好地组织类,Java 提供了包机制。包是类的容器,用于分隔类名空间。如果没有指定包名,所有的示例都属于一个默认的无名包。Java 中的包一般均包含相关的类,Java 是跨平台的,所以 Java 中的包和操作系统没有任何关系,Java 的包是用来组织文件的一种虚拟文件系统。 28 | 2、import 语句并没有将对应的 Java 源文件拷贝到此处,仅仅是引入,告诉编译器有使用外部文件,编译的时候要去读取这个外部文件。 29 | 3、Java 提供的包机制与 IDE 也没有关系。 30 | 4、定义在同一个包(package)内的类可以不经过 import 而直接相互使用,因为只有两种关系,要么 public 全局,要么就是默认的包内。 31 | 32 | **import**:在 Java 中,如果给出一个完整的限定名,包括包名、类名,那么 Java 编译器就可以很容易地定位到源代码或者类。Import 语句就是用来提供一个合理的路径,使得编译器可以找到某个类。如果源文件包含 import 语句,那么应该放在 package 语句和类定义之间。 33 | 34 | # 2、封装 35 | ## 2.1、4 种访问控制修饰符 36 | **共有的【public】**:对所有类可见,Java 程序的 main() 方法必须设置成公有的,否则,Java 解释器将不能运行该类。 37 | 38 | **受保护的【protected】**:对同一包内的类和所有子类可见。 39 | 40 | **默认的【default 】**:在同一包内可见,不使用任何修饰符。 41 | 42 | **私有的【private】**:在同一类内可见。主要用来隐藏类的实现细节和保护类的数据。类和接口不能声明为 private。 43 | 44 | 访问范围:【public】> 【protected】> 【default 】> 【private】 45 | 46 | ![访问修饰符](pics/java访问修饰符.png) ![访问修饰符](pics/UML用例图.png) 47 | 48 | ## 2.2、非访问控制修饰符:static、final、interface、abstract 49 | 为了实现一些其他的功能,Java 也提供了许多非访问修饰符(所有成员都肯定有访问控制修饰符,而非访问控制修饰符是可选的,可有可无)。 50 | 51 | **static 修饰符**:用来创建类方法和类变量,这里叫**类方法**,就是静态方法。类的静态成员与类直接相关,与对象无关,在一个类的所有实例之间共享同一个静态成员 52 | 53 | 1、静态变量必须初始化(不赋值,系统会自动初始化),可以修改(例如我们常利用静态成员变量统计某个函数的调用次数) 54 | 2、静态变量只能在类主体中定义,不能在方法中定义,静态方法也不行(不能修饰局部变量:局部变量,由于在方法中,方法中的变量都在栈中,随着方法的进栈出栈产生和销毁。所以不可以被static修饰。一旦被static修饰变量就属于类了),静态的都属于类,不属于实例方法。 55 | 3、静态成员函数中不能调用非静态成员(实例变量 和 实例方法):因为也许这个实例还没有产生。 56 | 4、在类方法中不能使用super、this关键字。 57 | 5、类方法不能被覆盖。 58 | 6、非静态成员函数中可以调用静态成员 59 | 60 | **final 修饰符**:用来修饰类、方法和变量。 61 | 1、final 修饰的类不能够被继承,如 String 类是不可变类 62 | 2、修饰的方法不能被重写 63 | 3、修饰的变量为常量,是不可修改的。赋值 final 修饰的变量有三种方式。 64 | (1、在声明时直接赋值,2、在构造函数中赋值,3、在初始代码块中进行赋值。) 65 | 66 | **abstract 修饰符**:用来创建抽象类和抽象方法。 67 | 68 | **synchronized**: 用于多线程的同步。 69 | 70 | **volatile**:修饰的成员变量在每次被线程访问时,都强制从共享内存中重新读取该成员变量的值。而且,当成员变量发生变化时,会强制线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。 71 | 72 | **transient**:序列化的对象包含被 transient 修饰的实例变量时,java 虚拟机(JVM)跳过该特定的变量。 73 | 74 | ```java 75 | /*问:static class 对么? 76 | static 不能用来修饰类,除非类是内部类,此时该类作为外部类的成员变量,可以用 static 修饰, 77 | 否则一般类(class)只有两种访问控制修饰符(+ abstract,final 这两种是非访问控制修饰符,可有可无)*/ 78 | 79 | private static int numInstances = 0 ; //构造一个私有的静态变量 80 | private static void addInstance(){ //构造一个私有的静态函数 81 | numInstances++; 82 | } 83 | 84 | /*当用final修饰一个类时,表明这个类不能被继承。也就是说,如果一个类你永远不会让他被继承,就可以用final进行修饰。final 类中的成员变量可以根据需要设为 final, 85 | 但是要注意 final 类中的所有成员方法都会被隐式地指定为 final方法。 86 | 如果一个类不允许其子类覆盖某个方法,则可以把这个方法声明为 final 方法。使用final方法的原因有二: 87 | 1、把方法锁定,防止任何继承类修改它的意义和实现。 88 | 2、高效,编译器在遇到调用 final 方法时候会转入内嵌机制,大大提高执行效率。 89 | */ 90 | public final Class String{} // String 是不可变类 91 | public static final int BOXWIDTH = 10; // 声明一个常量 92 | ``` 93 | 94 | **接口(interface)** 95 | 1、除非实现接口的类是抽象类或者接口,否则该类要定义接口中的所有方法 96 | 2、接口可以继承(extends)接口,且是多继承 97 | 3、接口不能用于实例化对象 98 | 4、接口没有构造方法 99 | 5、接口不能包含成员变量,除非是 public static final修饰的常量 100 | 6、一个类可以同时实现多个接口,关键字是(implements) 101 | 102 | 1、JDK1.8之前: 103 | 接口中只能有抽象方法:[(public abstract可以省略)void method(); 104 | 全局静态常量:public static final 常量; 105 | 2、JDK1.8之后: 106 | 抽象,默认,静态方法:1、public abstract void method(); 2、public default void method(){有代码} 3、public static void method(){有代码} 注:接口中的静态方法可以直接使用,interface.method(); 107 | 108 | **新增静态方法和默认方法**: 109 | 1、因为静态方法不可以实例化,在接口中也是一样的 所以在接口中定义静态方法的作用就是静态方法的作用:不需要实例化,直接使用,节省内存空间。 110 | 2、默认方法是可以在接口中写执行体的。主要作用: 111 | 2.1、接口升级,可以避免改变其他实现类;函数拼接。 112 | 2.2、接口默认方法的”类优先”原则 ,若一个接口中定义了一个默认方法,而另外一个父类或接口中 又定义了一个同名的方法时,先调用类中的同名方法。 113 | 2.3、如果一个类实现多个接口。并且这些接口提供了一个具有相同名称和参数列表的方法(不管方法 是否是默认方法),那么必须覆盖该方法来解决冲突。子类必须指定覆盖哪个父类接口中的方法。 114 | 115 | **abstract 修饰符**:修饰抽象类和抽象方法,声明抽象类的唯一目的是为了将来对该类进行扩充。 116 | 1、抽象类不能用来实例化对象,但是可以有构造方法(??) 117 | 2、接口是公开的,里面不能有私有的方法或变量,是用于让别人使用的,而抽象类是可以有私有方法或私有变量的。 118 | 3、抽象类中也可以包含已经实现的方法,这是和接口的一个重要区别(现在可能不是了)。 119 | 4、抽象类中的抽象方法要加 abstract 关键字声明,但是接口中可以省略。 120 | 5、如果一个类包含抽象方法,那么该类一定要声明为抽象类,但是一个抽象类不一定有抽象方法。 121 | 6、抽象方法是一种没有任何实现的方法(不能有大括号),该方法的的具体实现由子类提供。 122 | 7、任何继承抽象类的子类必须实现父类的所有抽象方法,除非该子类也是抽象类。 123 | 8、abstract 不能与private、static、final或native并列修饰同一个方法。 124 | 125 | ```java 126 | /*问:不能用来修饰 接口 里 方法的有哪些关键字? 127 | 答:不能用 private、protected,常用 public static final 修饰属性,public abstract 修饰方法,当然 java8 新 128 | 增 default、static 修饰方法。 129 | (1)增加 default 方法。对已有的接口,如果想对接口增加一个新方法,那么需要对实现该接口的所有类进行修改,如果接口 130 | 的实现类很多,就会带来很大的工作量,而且还很容易破坏以前的代码,带来一些问题。如果把新的方法定义为 default 方 131 | 法,就可以避免对其他实现类的修改。Default 方法是非常有用的,通过在接口定义的方法的访问修饰符前加上关键字 132 | default,那么实现类就无需提供该方法的实现了。 133 | 但是,这样也会有一个问题:如果接口 A 和接口 B 里有一个名字相同并且参数列表也相同的方法都被定义为了default方 134 | 法,那么当类 C 实现接口 A 和接口 B 的时候就会在编译时报错。由于是编译时报错,这个完全可以接受,当类 C 成功实现 135 | 了接口 A 和接口 B 以后(没有冲突),类 C 的实例就可以调用接口 A 和接口 B 里的 default 方法了。 136 | 137 | (2)新增了 static 函数。static 修饰的方法也是非抽象方法,有自己的方法体,在接口中定义一个静态方法,该方法可以 138 | 直接用 接口名.方法名() 的形式来调用。相当于调用类的静态方法一样,给方法的调用带来了方便。 139 | 140 | 问:为什么是 public static final? 141 | 为什么是 public:因为接口必然是要被实现的,如果不是 public,这个属性就没有意义了; 142 | 为什么是 static:因为如果不是 static,那么由于每个类可以继承多个接口,那就会出现重名的情况; 143 | 为什么是 final:这是为了体现 Java 的开闭原则,因为接口是一种模板,既然是模板,那就对修改关闭,对扩展开放。 144 | 145 | 问:接口和抽象类有什么区别? 146 | 答:抽象类是对概念的归纳,接口是对功能的归纳。继承抽象类可以先完成一个总体需求,再增加一个额外的功能,而接口则 147 | 是强调对需求用户提供服务。需求不同,选择也会不一样。抽象方法不能定义方法体。*/ 148 | 149 | /*接口之间应该也是继承,而不是实现 implements 且可以是多继承*/ 150 | public interface interfaceOne extends interfaceTwo,interfaceThree,interfaceFour{} 151 | 152 | /*接口中可以定义的变量和方法*/ 153 | interface Father{ 154 | /*成员变量*/ 155 | public static final int a = 1; // 不写修饰符也可以,默认是 public static final 修饰 156 | int b = 1; // 一般这么写,上面会提示冗余的修饰符 157 | 158 | /*成员函数*/ 159 | public abstract void method(int a); //不写修饰符也可以,默认是 public abstract 160 | void method(); // 一般这么写,上面会提示冗余的修饰符 161 | 162 | /*静态方法,可以直接定义*/ 163 | public static void testOne(){ 164 | System.out.println("static method"); 165 | } 166 | 167 | /*默认方法,可以直接定义,且继承此接口的类无需重新实现此方法,可直接调用,对于后期修改非常友好*/ 168 | public default void testTwo() { 169 | System.out.println("你好,中国!"); 170 | } 171 | } 172 | 173 | /*抽象类:含有抽象方法的就是抽象类,其余和普通类相同*/ 174 | abstract class Caravan{ 175 | private double price; 176 | public abstract void addPrice(); //抽象方法 177 | public abstract void method(){}; //错误,抽象方法不能有方法体,不是可选的。 178 | } 179 | ``` 180 | 181 | # 3、继承(extends 或者 implements) 182 | 183 | 1、类继承是用 extends,而接口是用 implements; 184 | 2、类是单继承,接口可以多继承,接口可以继承多个接口; 185 | 3、子类不可以继承父类的构造方法,只可以调用。(方法没有继承一说,要么重载,要么重写,或者调用父类的方法); 186 | 4、子类中所有的构造函数都会默认访问父类中的无参构造函数,因为默认第一行都会有super(); 当父类中存在有参构造器时,子类调用父类的无参构造函数时,必须提供无参构造器(因为这个时候,系统不会自动生成了) ; 187 | 188 | ```java 189 | /*继承:从项目的角度来看:一般把通用的代码放入父类和接口中,这样可以避免大面积的重复代码*/ 190 | 191 | /*对于本题来说:在只想new Sub(5)的时候,父类先初始化了 int flag = 1,然后执行父类的构造函数Super(),父类构 192 | 造函数中执行的test()方法,因子类是重写了test()方法的,因此父类构造函数中的test()方法实际执行的是子类的 193 | test()方法,所以输出为Sub.test() flag=1,接着执行子类构造函数Sub(5) 将flag赋值为5,因此输出结果Sub.Sub() 194 | flag=5。输出:Sub.test() flag=1,Sub.Sub() flag=5 195 | 这题有没有让你对继承有了新的认识:不要把子类和父类分开,而是将父类放入到继承的子类中,当做一个大类来对待,这样 196 | 很多问题都能容易理解。 197 | */ 198 | 199 | class Super{ 200 | int flag = 1; 201 | Super(){ 202 | test(); 203 | } 204 | void test(){ 205 | System.out.println("Super.test() flag="+flag); 206 | } 207 | } 208 | class Sub extends Super{ 209 | Sub(int i){ 210 | flag = i; 211 | System.out.println("Sub.Sub() flag=" + flag); 212 | } 213 | void test(){ 214 | System.out.println("Sub.test()flag=" + flag); 215 | } 216 | public static void main(String[] args) { 217 | new Sub(5); 218 | } 219 | } 220 | 221 | 222 | /*子类继承父类的执行顺序**:在继承中代码的执行顺序为: 223 | 1.父类静态对象,父类静态变量,父类静态代码块 224 | 2.子类静态对象,子类静态变量,子类静态代码块 225 | 3.父类初始化代码块,父类构造函数 226 | 4.子类初始化代码块,子类构造函数*/ 227 | class A{ 228 | public static int a = testA(); 229 | public static int testA(){ //如果没有变量的调用,是不会初始化的。 230 | System.out.println("父类静态变量"); 231 | return 1; 232 | } 233 | static { 234 | System.out.println("父类静态代码块"); 235 | } 236 | {//这里也叫父类对象成员构造函数,就一大括号 237 | System.out.println("父类初始化块"); 238 | } 239 | public A(){ 240 | System.out.println("父类构造方法"); 241 | } 242 | } 243 | public class B extends A{ 244 | public static int b = testB(); 245 | public static int testB(){ 246 | System.out.println("子类静态变量"); 247 | return 1; 248 | } 249 | static{ 250 | System.out.println("子类静态代码块"); 251 | } 252 | {//这里也叫子类对象成员构造函数,就一大括号 253 | System.out.println("子类初始化块"); 254 | } 255 | public B(){ 256 | System.out.println("子类构造方法"); 257 | } 258 | public static void main(String[] args){ 259 | new B(); 260 | } 261 | } 262 | ``` 263 | 264 | **重载** 265 | 1、是面向对象多态的一种主要实现方式; 266 | 2、构造函数可以被重载但是不能被重写; 267 | 3、Java 的方法重载,它们具有相同的函数名,但是参数一定不同,返回值,访问修饰符可能不同; 268 | 269 | **重写**:实现接口里的方法或者重写时,需要满足: 270 | 1、两同两大一小:两同:方法名、形参都要相同;两大:访问权限>=重写前(并解释),返回值类型>=重写前;一小:抛出异常<=重写前; 271 | 2、声明为 final 的方法不能被重写,final 修饰的类不能被继承; 272 | 3、声明 static 的方法不能被重写(实际是重写不了),父类调用的永远是父类的静态方法; 273 | 4、 @Override :不写也可以,但是写了有如下好处,1-可以当注释用,方便阅读;2-编译器可以给你验证 @Override下面的方法名是否是你 **父类** 中所有的,如果没有则报错。例如,你如果没写 @Override,而你下面的方法名又写错了,这时你的编译器是可以编译通过的,因为编译器以为这个方法是你的子类中自己增加的方法; 274 | 275 | ```java 276 | /***重载与重写的区别? 277 | 参考博客:(https://blog.csdn.net/linzhaojie525/article/details/55213010) 278 | 279 | 问:子类将继承父类所有的数据域和方法? 280 | 正确,在一个子类被创建的时候,首先会在内存中创建一个父类对象,然后在父类对象外部放上子类独有的属性,两者合起来形成一个子类的对象。所以所谓的继承使子类拥有父类所有的属性和方法其实可以这样理解,子类对象确实拥有父类对象中所有的属性和方法,但是父类对象中的私有属性和方法,子类是无法访问到的,只是拥有,但不能使用。就像有些东西你可能拥有,但是你并不能使用。所以子类对象是绝对大于父类对象的,所谓的子类对象只能继承父类非私有的属性及方法的说法是错误的。可以继承,只是无法访问到而已。 281 | 282 | 问:子类可以继承和覆盖父类的类方法? 283 | 答:static 修饰的成员属于类成员,父类字段或方法只能被子类同名字段或方法遮蔽,不能被继承覆盖。 284 | 285 | instanceof 判断是否是继承关系 286 | 287 | Obeject 类是所有类的终极父类,任何类都是它的子类*/ 288 | 289 | System.out.println(dog instanceof Animal); //左边是子类 右边是父类 290 | 291 | Animal cat = new Cat(){ 292 | public String toString(){return "we change the toString methods";} 293 | }; 294 | cat.staticMethod(); // 调用的是父类的静态方法,参考3. 295 | System.out.println(cat); // 会自动调用 toString 方法 296 | ``` 297 | 298 | # 4、多态 299 | ```java 300 | /*从项目的角度看:多态要解决的问题是,把抽象的业务逻辑和具体的实施细节分离,把做什么和怎么做分离。多态有修改点隔离 和 无障碍扩展 两大好处。 301 | 在实际的的开发过程中,这种继承加覆盖的方式可以做到以上两点好处: 302 | 1、修改代码时,只需要修改相应子类的代码即可,无需动其它类,即修改点隔离; 303 | 2、如果增加新的业务,只需要增加子类即可,即无障碍扩展; 304 | 305 | 向上转型:Animal animal = new Dog(); 会自动丢弃子类的方法,调用自己的方法时,如果子类有覆盖方法,则调用子类的,如果调用的方法是父类没有的,则编译会出错(所以如果使用父类进行调用一定是重写的方法!),具体参考下面的代码。 306 | 向下转型:Dog dog = (Dog) animal; 要强转,调用子类重写的方法时会调用父类的方法。*/ 307 | 308 | class Animal {//注意默认是 default,public类 只能有一个,且必须与文件名相同 309 | private int i; //私有成员不能被继承 310 | public void move(){ 311 | System.out.println("动物可以移动"); 312 | } 313 | public void move(int x){//重载了 move 方法 314 | System.out.println("动物移动了 " + x + " 米"); 315 | } 316 | } 317 | 318 | class Dog extends Animal{ 319 | @Override //不写也可以,但是写了有上面介绍的好处 320 | public void move(){ 321 | super.move(); //调用父类方法,使用 super 关键字 322 | System.out.println("小狗可以跑"); 323 | } 324 | public void methodB(){ 325 | System.out.println("extern method"); 326 | } 327 | } 328 | 329 | public class test{ 330 | public static void main(String args[]){ 331 | Animal a = new Animal(); 332 | Animal b = new Dog(); 333 | a.move(); //调用父类函数 334 | b.move(); //调用子类函数 335 | /*b.methodB(); 编译错误? 336 | 多态:父类里没有的函数,在多态实现的时候不能出现。 337 | */ 338 | } 339 | } 340 | 341 | /*问:以下代码可以正常编译并运行么? 342 | 是可以的!即使 Test test = null;也会加载静态方法,所以test包含Test类中的初始化数据,静态的,构造的,成员属 343 | 性。额外,就算不是静态方法,编译也是可以通过的,只有在运行的时候才会报错。 344 | 如果一个成员被声明为static,它就能够在它的类的任何对象创建之前被访问,而不必引用任何对象 345 | (跟类是否有static修饰无关)。*/ 346 | class Test{ 347 | public static void hello(){ 348 | System.out.println("hello"); 349 | } 350 | } 351 | public class MyApp{ 352 | public static void main(String[] args){ 353 | Test test = null; 354 | test.hello(); 355 | } 356 | } 357 | 358 | /*问:普通方法可以和类同名么? 359 | 答:可以的,不只是构造函数,普通函数也可以和类同名。 360 | public void Parent(){} 也是可以的普通方法。*/ 361 | 362 | /*问:构造函数能重载但不能覆盖?下面这个方法会报错么? 363 | 回答:会的,由于子类的构造函数什么都没写,所以系统会默认的添加super();来调用Parent类中无参的构造函数,而父类中没有,则报错。*/ 364 | class Parent{ 365 | private int value; 366 | public Parent(int val){ 367 | this.value = val; 368 | } 369 | } 370 | class SubClass extends Parent{ 371 | public SubClass(int i){ 372 | //可以在这里添加 super(i); 否则系统会默认添加super();找不到父类的无参构造方法报错。 373 | } 374 | } 375 | 376 | /*问:输出的值为多少? 377 | 答:22 34 17*/ 378 | class Test { 379 | public static void main(String[] args) { 380 | System.out.println(new B().getValue()); 381 | } 382 | static class A { 383 | protected int value; 384 | public A (int v) { 385 | setValue(v); 386 | } 387 | public void setValue(int value) { 388 | this.value= value; 389 | } 390 | public int getValue() { 391 | try { 392 | value ++; 393 | return value; 394 | } finally { 395 | this.setValue(value); 396 | System.out.println(value); 397 | } 398 | } 399 | } 400 | static class B extends A { 401 | public B () { 402 | super(5); 403 | setValue(getValue()- 3); 404 | } 405 | public void setValue(int value) { 406 | super.setValue(2 * value); 407 | } 408 | } 409 | } 410 | ``` 411 | 412 | # 5、内部类 413 | ![内部类定义](pics/内部类.png) 414 | 415 | 特点:静态内部类才可以声明静态方法,静态方法不可以使用非静态变量。 416 | ```java 417 | /*在Java中,可以将一个类定义在另一个类里面或者一个方法里边,这样的类称为内部类,广泛意义上的内部类一般包括四种:成员内部类,局部内部类,匿名内部类,静态内部类 。 418 | 419 | 1.成员内部类 420 | (1)该类像是外部类的一个成员,可以无条件的访问外部类的所有成员属性和成员方法(包括private成员和静态成员); 421 | (2)成员内部类拥有与外部类同名的成员变量时,会发生隐藏现象,即默认情况下访问的是成员内部类中的成员。如果要访问 422 | 外部类中的成员,需要以下形式访问:【外部类.this.成员变量 或 外部类.this.成员方法】; 423 | (3)在外部类中如果要访问成员内部类的成员,必须先创建一个成员内部类的对象,再通过指向这个对象的引用来访问; 424 | (4)成员内部类是依附外部类而存在的,也就是说,如果要创建成员内部类的对象,前提是必须存在一个外部类的对象; 425 | (5)内部类可以拥有private访问权限、protected访问权限、public访问权限及包访问权限。如果成员内部类用private修 426 | 饰,则只能在外部类的内部访问;如果用public修饰,则任何地方都能访问;如果用protected修饰,则只能在同一个包下或 427 | 者继承外部类的情况下访问;如果是默认访问权限,则只能在同一个包下访问。外部类只能被public和包访问两种权限修饰。 428 | 429 | 2.局部内部类 430 | (1)局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内; 431 | (2)局部内部类就像是方法里面的一个局部变量一样,是不能有public、protected、private以及static修饰符的。 432 | 433 | 3.匿名内部类 434 | (1)一般使用匿名内部类的方法来编写事件监听代码; 435 | (2)匿名内部类是不能有访问修饰符和static修饰符的; 436 | (3)匿名内部类是唯一一种没有构造器的类; 437 | (4)匿名内部类用于继承其他类或是实现接口,并不需要增加额外的方法,只是对继承方法的实现或是重写。 438 | 439 | 4.静态内部类 440 | (1)静态内部类是不需要依赖于外部类的,这点和类的静态成员属性有点类似; 441 | (2)不能使用外部类的非static成员变量或者方法。*/ 442 | 443 | 444 | /*局部内部类:在某个函数中*/ 445 | public void fun(){ 446 | abstract class innerClassOne{} 447 | final class innerClassTwo{} 448 | class innerClassThree{} 449 | } 450 | 451 | /*匿名内部类:创建了一个匿名内部类,并将所创建的匿名对象赋给 Object (多态:子类对象赋给超类引用)。同时,该匿名内部类重写了 Object 类的 equals 方法。*/ 452 | public class TestObj{ 453 | public static void main(String[] args){ 454 | Object o = new Object(){ 455 | public boolean equals(Object obj){ 456 | return true; 457 | } 458 | }; 459 | System.out.println(o.equals(“Fred”)); 460 | } 461 | } 462 | 463 | /*静态内部类*/ 464 | public class Single{ 465 | private Single(){} 466 | private static class SingleHandler{ 467 | // 这里为什么是 private 类型的 468 | private static final Single INSTANCE = new Single(); 469 | } 470 | public static final Single getInstance(){ 471 | return SingleHandler.INSTANCE; 472 | } 473 | } 474 | ``` 475 | -------------------------------------------------------------------------------- /notes/JAVA/JAVA-面试题合集.md: -------------------------------------------------------------------------------- 1 | # JAVA-面试题合集 2 | 3 | **问:Java 中的值传递和引用传递?** 4 | 错误理解一:值传递和引用传递,区分的条件是传递的内容,如果是个值,就是值传递。如果是个引用,就是引用传递。 5 | 错误理解二:Java是引用传递。 6 | 错误理解三:传递的参数如果是普通类型,那就是值传递,如果是对象,那就是引用传递。 7 | 其实我觉得和 Python 中可变类型与不可变类型的概念类似,具体可参考[Python 五种数据类型](https://blog.csdn.net/qq_29611345/article/details/100736961) 8 | 我的理解就是不可变类型是值传递,如(Integer、String) 9 | 可变类型是引用传递:如(int[],ArrayList) 10 | 11 | **问:.equals 和 == 的区别?** 12 | .equals:这是一个方法,比较两个对象的内容是否相同,基本类型不能用的。 13 | ==:比较两个值的引用是否相同,比前者快,因为只比较引用,如果是基本数据类型,值相同,那么肯定引用相同。[参考博客](https://www.cnblogs.com/Eason-S/p/5524837.html) 14 | 15 | **错误:在循环内进行删除。** 16 | 17 | ```java 18 | ArrayList list = new ArrayList(); 19 | list.add("a"); 20 | list.add("bb"); 21 | list.add("bb"); 22 | list.add("ccc"); 23 | list.add("ccc"); 24 | list.add("ccc"); 25 | //第一种,可能会没有删干净 26 | for (int i = 0; i < list.size(); i++) { 27 | String s = list.get(i); 28 | if (s.equals("bb")) 29 | list.remove(s); 30 | } 31 | //第二种:出现异常 并发修改异常java.util.ConcurrentModificationException 32 | for (String s : list) { 33 | if (s.equals("bb")) 34 | list.remove(s); 35 | } 36 | ``` 37 | 38 | **问:Java 并发相关的包?** 39 | [参考博客](https://www.jianshu.com/p/46728d6bc6b2) 40 | java.util.concurrent(JUC)包,继续发问:用过里面哪些工具类呢? 41 | 报告面试官,JUC中有非常多的类,将部分类按功能进行分类,分别是: 42 | 43 | 1. 原子atomic包 44 | 2. 比synchronized功能更强大的lock包 45 | 3. 线程池 46 | AQS(AbstractQueuedSynchronizer),即队列同步器。它是构建锁或者其他同步组件的基础框架,它是JUC并发包中的核心基础组件。 47 | 48 | **问:线程池?** 49 | 50 | **问:锁的实现?Synchronized 和 Lock 的区别?** 51 | [参考博客](https://blog.csdn.net/youyou1543724847/article/details/52735510) 52 | 关于锁有两个关键字,Synchronized(重量级)和Lock(轻量级),synchronized 是 java中的一个关键字,也就是说是 Java 语言内置的特性。那么为什么会出现Lock呢?如果一个代码块被synchronized 修饰了,当一个线程获取了对应的锁,并执行该代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁,而这里获取锁的线程释放锁只会有两种情况: 53 |   1)获取锁的线程执行完了该代码块,然后线程释放对锁的占有; 54 |   2)线程执行发生异常,此时JVM会让线程自动释放锁。 55 | 那么如果这个获取锁的线程由于要等待 IO 或者其他原因(比如调用 sleep 方法)被阻塞了,但是又没有释放锁,其他线程便只能干巴巴地等待,试想一下,这多么影响程序执行效率。因此就需要有一种机制可以不让等待的线程一直无期限地等待下去(比如只等待一定的时间或者能够响应中断),通过 Lock 就可以办到。再举个例子:当有多个线程读写文件时,读操作和写操作会发生冲突现象,写操作和写操作会发生冲突现象,但是读操作和读操作不会发生冲突现象。但是采用synchronized 关键字来实现同步的话,就会导致一个问题: 56 | 如果多个线程都只是进行读操作,所以当一个线程在进行读操作时,其他线程只能等待无法进行读操作。因此就需要一种机制来使得多个线程都只是进行读操作时,线程之间不会发生冲突,通过Lock 就可以办到。另外,通过 Lock 可以知道线程有没有成功获取到锁。这个是 synchronized 无法办到的。总结一下,也就是说 Lock 提供了比 synchronized 更多的功能。但是要注意以下几点: 57 | 1)Lock 不是 Java 语言内置的,synchronized 是 Java 语言的关键字,因此是内置特性。Lock 是一个类,通过这个类可以实现同步访问; 58 | 2)Lock 和 synchronized 有一点非常大的不同,采用 synchronized 不需要用户去手动释放锁,当synchronized 方法或者 synchronized 代码块执行完之后,系统会自动让线程释放对锁的占用;而Lock则必须要用户去手动释放锁,如果没有主动释放锁,就有可能导致出现死锁现象。 59 | 锁的相关类别: 60 | 可重入锁的机制:方法一内部调用方法二,且都是syn修饰,则第二次可不用申请锁。 61 | 可中断锁:syn不可中断,lock可中断。 62 | 公平锁;按照请求顺序给锁,syn不是公平锁, 63 | 读写锁、偏向锁、乐观锁、悲观锁 64 | 65 | ### 集合框架的问题 66 | 见java-基础语法 67 | 68 | ### 字符串问题 69 | **问:字符串拼接 StringBuffer,StringBuilder,concat 和 + 的区别?** 70 | 答:[参考博客](https://www.cnblogs.com/lojun/articles/9664794.html) 71 | StringBuffer 是线程安全的,StringBuilder 不是线程安全的。其他两者基本相同,因为操作字符串很多时候不需要线程安全,所以在拼接字符串时 StringBuilder 使用较多。 72 | 1、在字符串不经常发生变化的业务场景优先使用String(代码更清晰简洁)。如常量的声明,少量的字符串操作(拼接,删除等)。 73 | 2、在单线程情况下,如有大量的字符串操作情况,应该使用StringBuilder来操作字符串。不能使用String"+"来拼接,避免产生大量无用的中间对象,耗费空间且执行效率低下(新建对象、回收对象花费大量时间)。如JSON的封装等。 74 | 3、在多线程情况下,如有大量的字符串操作情况,应该使用StringBuffer。如HTTP参数解析和封装等。且如果在 for 循环中进行拼接操作,建议使用这两者,整个逻辑都只做字符数组的加长,拷贝,到最后也不会创建新的String对象,所以速度很快。 75 | 76 | **问:equals 和 == 的区别? ** 77 | 答:前者:比较内存地址,如果地址不同再比较值,其实源码可以看出还是用了 == ,但是重写了此方法。 后者:更快,只比较内存地址。 78 | 79 | -------------------------------------------------------------------------------- /notes/JAVA/JAVA-高级编程.md: -------------------------------------------------------------------------------- 1 | # JAVA-高级编程 2 | 以下是学习后的总结,[更具体的,参考这个学习网站,很好用](http://how2j.cn/stage/25.html) 3 | 4 | **JAVA - 高级编程** 5 | 6 | * JAVA - 反射 7 | * JAVA - 泛型 8 | * JAVA - I/O-NIO 9 | * JAVA - 多线程编程 10 | * JAVA - 虚拟机 11 | * JAVA - 垃圾回收,内存优化机制 12 | * JAVA - 网络编程 13 | 14 | ### 序列化 15 | ```java 16 | /**序列化** 17 | 什么是序列化:https://www.cnblogs.com/douzi520/p/9497889.html 18 | 程序羊实例讲解:https://www.zhihu.com/question/26475281?sort=created 19 | 20 | 问:什么情况下需要序列化? 21 | 当你想把内存中的对象保存到一个文件中或者数据库中时候,需要自己编写代码进行序列化和反序列化,不是自动的; 22 | 当你想用序列化在网络上传送对象的时候; 23 | 当你想通过 RMI 传输对象的时候; 24 | 25 | 问:当对一个对象实现序列化时,哪些变量不会被序列化? 26 | Java 在序列化时不会实例化 static 变量和 transient 修饰的变量,因为 static 代表类的成员,transient 代表对象 27 | 的临时数据,被声明这两种类型的数据成员不能被序列化。序列化保存的是对象的状态,静态变量属于类的状态,因此,序列 28 | 化并不保存静态变量。 29 | 30 | 问:java类中 serialVersionUID 的作用? 31 | 如果没有,系统会自动生成,如果以后你不动这个类了,其实没有也没关系,但是一旦你修改了这个类,再把已经序列化的文 32 | 件反序列化回来,不好意思,回不来了,因为两者产生的 serialVersionUID 不同了,生成方式就是下面的第二种方式。 33 | 34 | serialVersionUID有两种显示的生成方式: 35 | 一是默认的1L,比如:private static final long serialVersionUID = 1L; 36 | 37 | 二是根据包名,类名,继承关系,非私有的方法和属性,以及参数,返回值等诸多因子计算得出的,极度复杂生成的一个64位 38 | 的哈希字段。基本上计算出来的这个值是唯一的。比如:private static final long serialVersionUID = xxxxL; 39 | 注意:显示声明serialVersionUID可以避免对象不一致, 40 | 41 | 相关注意事项: 42 | a)当一个父类实现序列化,子类自动实现序列化,不需要显式实现 Serializable 接口; 43 | b)当一个对象的实例变量引用其他对象,序列化该对象时也把引用对象进行序列化; 44 | c)并非所有的对象都可以序列化,,至于为什么不可以,有很多原因了 比如: 45 | 1.安全方面的原因,比如一个对象拥有private,public等field,对于一个要传输的对象,比如写到文件,或者进行rmi传 46 | 输 等等,在序列化进行传输的过程中,这个对象的private等域是不受保护的。 47 | 2. 资源分配方面的原因,比如socket,thread类,如果可以序列化,进行传输或者保存, 48 | 也无法对他们进行重新的资源分配,而且,也是没有必要这样实现把一个对象完全转成字节序列,方便传输。 49 | 就像你寄一箱饼干,因为体积太大,就全压成粉末紧紧地一包寄出去,这就是序列化的作用。 50 | 只不过JAVA的序列化是可以完全还原的。 51 | */ 52 | 53 | ``` 54 | 55 | 56 | ### JAVA - 反射 57 | 58 | **有啥用**:使用反射方式,首先准备一个配置文件,就叫做 spring.txt 吧, 放在 src 目录下。 里面存放的是类的名称,和要调用的方法名。在测试类Test中,首先取出类名称和方法名,然后通过反射去调用这个方法。当需要从调用第一个业务方法,切换到调用第二个业务方法的时候,不需要修改一行代码,也不需要重新编译,只需要修改配置文件 spring.txt,再运行即可。这也是 Spring 框架的最基本的原理,只是它做的更丰富,安全,健壮。 59 | 60 | **反射的常见用法**;有三类,一是 查看(Hero.class),某个类的属性方法等信息;二是 装载 如装载指定的类到内存中;三是 调用,如通过输入参数调用指定的方法。 61 | 62 | **获取类对象的三种方式** 63 | 1. Class.forName(“Hero”) 最常用,通过字符串获取 64 | 2. Hero.class 通过类来获取类 65 | 3. new Hero().getClass() 通过实例获取该类 66 | Hero 是一个类,在一个JVM中,一种类,只会有一个类对象存在。所以以上三种方式取出来的类对象,都是一样的。反射其实就是获取类对象。反射指的是在运行时能够分析类的能力的程序。其中1,3会导致类的静态属性被初始化,而且只会执行一次,后面在调用不会初始化了,2不会。 67 | ```java 68 | /*问:你知不知道反射?或者你有没有用过反射? 69 | 答:有,用的最多的应该就是JDBC装载数据库驱动的时候,通过Class.forName("com.mysql.jdbc.Driver"); 70 | 71 | 问:Class类有什么作用?你用过其中的什么方法? 72 | 答:反射的实现基础是Class类,可以查看某个类的属性和方法。当一个类或接口被装入Java虚拟机时,便会产生一个与它相关联的java.lang.Class对象。 73 | 通过Class类的forName方法,我们能得到一个指定类型的Class对象,通过newInstance方法,可以加载指定的类。 74 | 75 | 问:如果我要看一个 class 文件中的属性和方法,该怎么看? 76 | 上面一行,加上,可以通过 Field 类,得到类中的属性。通过 Method 类,调用类中的方法,通过 Constructor 类,得到类的构造函数。 77 | */ 78 | 79 | 80 | import java.lang.reflect.Field; 81 | import java.util.Stack; 82 | class Student{ 83 | int age; 84 | String name; 85 | public Student() { 86 | System.out.println("构造函数"); 87 | } 88 | } 89 | 90 | public class Main { 91 | public static void main(String[] args){ 92 | try { 93 | // 这里不能只有一个类名,需要包括包名,是绝对路径 94 | Class clazz = Class.forName("Student"); 95 | Field[] fields = clazz.getDeclaredFields();// 获取类的字段 96 | for(Field field : fields) 97 | System.out.println(field.getName()); 98 | }catch(Exception e){ 99 | e.printStackTrace(); 100 | } 101 | } 102 | } 103 | 104 | /*知识点:getClass() ? 105 | SubClass 和 SuperClass 的 getClass() 都没有重写,他们都是调用 Object 的 getClass,而 Object 的 getClass 106 | 作用是返回的是运行时的类的名字。这个运行时的类就是当前类,所以下面的代码并不是获取父类的名字,而是当前类的名 107 | 字。*/ 108 | super.getClass().getName(); //当前类的名称 109 | super.getClass().getSuperclass(); //真正获取父类的名称的方法 110 | 111 | ``` 112 | 113 | 114 | 反射的特点: 115 | 1. 在运行时判断任意一个对象所属的类;在运行时构造任意一个类的对象;在运行时判断任意一个类所具有的成员变量和方法;在运行时调用任意一个对象的方法;生成动态。 116 | 2. 通过反射可以动态的实现一个接口,形成一个新的类,并可以用这个类创建对象,调用对象方法 117 | 3. 通过反射,可以突破Java语言提供的对象成员、类成员的保护机制,访问一般方式不能访问的成员 118 | 4. Java的反射机制会给内存带来额外的开销。例如对永生堆的要求比不通过反射要求的更多 119 | 120 | 问题: 121 | 1. Java反射主要涉及的类如Class, Method, Filed,等,他们都在java.lang.reflet包下? 122 | 答:在运行时分析类的能力--检查类的结构--所用到的就是 java.lang.reflect 包中的 Field、Method、Constructor,分别用于描述类的域、方法和构造器。Class类在java.lang中。 123 | 124 | 2. Java反射机制提供了字节码修改的技术,可以动态的修剪一个类? 125 | 答:不能修改。 126 | 127 | 3. Java反射机制一般会带来效率问题,效率问题主要发生在查找类的方法和字段对象,因此通过缓存需要反射类的字段和方法就能达到与之间调用类的方法和访问类的字段一样的效率? 128 | 答:不能,反射会降低效率,禁止安全检查,可以提高反射的运行速度。 129 | 130 | 手撕代码: 131 | 1、通过反射创建一个对象。 132 | 2、通过反射获取属性,再修改属性值 133 | 问:getField 和 getDeclaredField的区别? 134 | 答:这两个方法都是用于获取字段,getField 只能获取 public 的,包括从父类继承来的字段。 135 | getDeclaredField 可以获取本类所有的字段,包括 private 的,但是不能获取继承来的字段。 (注: 这里只能获取到private的字段,不能访问该private字段的值,除非加上setAccessible(true)) 136 | 3、通过反射调用一个对象方法 137 | 138 | ```java 139 | String className = "charactor.Hero"; //使用反射的方式创建对象 140 | Class pClass=Class.forName(className); //类对象 141 | Constructor c= pClass.getConstructor(); //构造器 142 | Hero h2= (Hero) c.newInstance(); //通过构造器实例化 143 | 144 | //获取类Hero的名字叫做name的字段 145 | Field f1= h.getClass().getDeclaredField("name"); 146 | f1.set(h, "teemo"); //修改这个字段的值 147 | 148 | // 获取这个名字叫做setName,参数类型是String的方法 149 | Method m = h.getClass().getMethod("setName", String.class); 150 | m.invoke(h, "盖伦"); // 对h对象,调用这个方法 151 | 152 | /* 153 | 错误:运用java反射机制获取实体方法报错,java.lang.NoSuchMethodException: int.(java.lang.String)? 154 | 解决:构造Hero类时使用了基本数据类型,public int damage; 要换成包装类 Integer; 155 | 还有其他错误:如 xxx Heroxxx? 156 | 解决:是找不到该类,因为我只写了类名,没有写包名.类名。 157 | */ 158 | 159 | ``` 160 | 161 | ### 泛型 162 | 163 | ```java 164 | ArrayList heros = new ArrayList(); //如果不使用泛型约束,heros里可以存放任意类型,都以Obeject存储,造成记忆和使用困难。 165 | ArrayList heros = new ArrayList<>();//加入泛型约束,那么限制只能某一类存入heros。 166 | 167 | //构造一个支持存入多种类型的类,使用泛型T,Java的源代码可以看见很多。 168 | public class MyStack{ 169 | LinkedList values = new LinkedList(); 170 | public void push(T t) { 171 | values.addLast(t); 172 | } 173 | } 174 | 175 | //extends Hero 表示这是一个Hero泛型的子类泛型,只要是其子类,就认为是同一种类型,如List和ArrayList。 176 | ArrayList heroList = apHeroList; 177 | 178 | //? super Hero 表示 heroList的泛型是Hero或者其父类泛型 179 | ArrayList heroList = new ArrayList(); 180 | 181 | /*如果希望只取出,不插入,就使用? extends Hero 182 | 如果希望只插入,不取出,就使用? super Hero 183 | 如果希望,又能插入,又能取出,就不要用通配符?*/ 184 | ``` 185 | 186 | 187 | ### JAVA - I/O-NIO 188 | 189 | **I/O 文件对象** 190 | 按照流是否直接与特定的地方(如磁盘、内存、设备等)相连,分为 节点流 和 处理流 两类。 191 | **节点流**:可以从或向一个特定的地方(节点)读写数据。 192 | 文 件 FileInputStream FileOutputStrean FileReader FileWriter 文件进行处理的节点流。 193 | 194 | ```java 195 | /**问:序列化有什么作用?java.io.Serializable,将此对象序列化为文件,并在另外一个 JVM 中读取文件,进行反序列 196 | 化,请问此时读出的 Data0bject 对象中的 word 和 i 的值分别为:"123",0 197 | 注意:序列化的是对象,不是类,类变量不会被序列化,Java 在序列化时不会实例化 static 变量和 transient 修饰的变 198 | 量,因为 static 代表类的成员,transient代表对象的临时数据,被声明这两种类型的数据成员不能被序列化。 199 | */ 200 | public class DataObject implements Serializable{ 201 | private static int i=0; 202 | private String word=" "; 203 | public void setWord(String word){ 204 | this.word=word; 205 | } 206 | public void setI(int i){ 207 | Data0bject.i=I; 208 | } 209 | } 210 | DataObject object=new Data0bject ( ); 211 | object.setWord("123"); 212 | object.setI(2); 213 | 214 | /*问:transient 关键字? 215 | 序列化与反序列化:https://blog.csdn.net/SDDDLLL/article/details/92583968 216 | 作用:将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会被序列化。 217 | */ 218 | 219 | /*代码一:一般说来操作系统都会安装在C盘,所以会有一个 C:\WINDOWS目录。遍历这个目录下所有的文件(不用遍历子目 220 | 录),找出这些文件里,最大的和最小(非0)的那个文件,打印出他们的文件名。注: 最小的文件不能是 0 长度。 221 | 知识点:1如何创建文件对象,2返回文件夹下所有子文件的函数,3一个文件的长度 222 | */ 223 | import java.io.File; //引入必要的包 224 | public class test{ 225 | public static void main(String args[]){ 226 | File fs = new File("C:/WINDOWS"); //创建一个文件对象 227 | long max=0, min=Integer.MAX_VALUE; 228 | File maxName=null, minName=null; //文件对象要初始化。 229 | //该函数以文件数组的形式,返回当前文件夹下的所有文件(不包含子文件及子文件夹) 230 | for(File f : fs.listFiles()){ 231 | if(f.length() > max){ 232 | max = f.length(); 233 | maxName = f; 234 | } 235 | if(f.length() < min && f.length() > 0){ 236 | min = f.length(); 237 | minName = f; 238 | } 239 | } 240 | System.out.println("最大的文件为: " + maxName + " 大小为:" + max); 241 | System.out.println("最小的文件为: " + minName + " 大小为:" + min); 242 | //输出文件名 maxName.getAbsoluteFile() 也可以。 243 | } 244 | } 245 | ``` 246 | 247 | ```java 248 | //代码二、输出电子书文件夹下所有的PDF文件,并统计有多少文件 。 249 | import java.io.File; 250 | public class Main{ 251 | public static int num = 0; 252 | static void getCSVInFolder(String filePath) { 253 | File folderName = new File(filePath); 254 | File[] flist = folderName.listFiles(); 255 | if(flist==null || flist.length==0) return; 256 | String fileName = null; 257 | for(File f : flist) { 258 | if(f.isDirectory()) 259 | getCSVInFolder(f.getAbsolutePath()); 260 | else { 261 | fileName = f.getName(); 262 | //这里如果用 == "pdf"比较,则无法匹配成功,因为会产生新的对象,地址不相等的。 263 | if(fileName.substring(fileName.lastIndexOf('.')+1).equals("pdf")) { 264 | System.out.println(f.getName()); 265 | num++; 266 | } 267 | } 268 | } 269 | } 270 | public static void main(String[] args) { 271 | getCSVInFolder("F:\\办公文档\\计算机工作文档\\生活-阅读"); 272 | System.out.println(num); 273 | System.out.println("我一共有"+num+"本电子书"); 274 | } 275 | } 276 | 277 | //代码三、练习:对 d 盘的一个文件进行读取。 278 | 知识点:文件输入输出流的创建, 279 | import java.io.File; 280 | import java.io.FileInputStream; 281 | import java.io.IOException; 282 | 283 | public class test{ 284 | public static void main(String[] args) { 285 | try{ //必须对其进行保护,否则会报错 286 | //File f = new File("F:/aa//bb//cc"); 287 | //f.mkdir(); 防止文件路径不存在,可以创建递归目录。 288 | File f = new File("F:/test.txt"); 289 | FileInputStream fis = new FileInputStream(f); 290 | byte[] buffer = new byte[(int)f.length()]; 291 | fis.read(buffer); 292 | for(byte b : buffer){ 293 | System.out.println(b); //输出asc码 294 | } 295 | } catch(IOException e){ 296 | e.printStackTrace(); 297 | } 298 | finally{ 299 | fis.close(); //每次使用完需要关闭 300 | } 301 | } 302 | } 303 | 304 | //需要记住的常用函数 305 | File folderName = new File(filePath); //创建一个文件对象 306 | File[] flist = folderName.listFiles(); //返回文件夹下所有的文件 307 | flist[0].isDirectory(); //判断是否是文件夹 308 | 309 | ``` 310 | 311 | **非阻塞性IO** 312 | ```java 313 | /* 314 | 前面介绍的IO是(BIO,阻塞性IO),JDK1.4后增加了NIO。在高并发的情况下很有用,与性能优化和架构设计密切相关。 315 | 与传统的IO相比,NIO有 面向缓存 和 非阻塞 两大特点,还可以通过选择器管理多个读写通道。 316 | Channel通道、Buffer缓冲器、Selector选择器,是NIO的三大核心组件。 317 | */ 318 | public class NIOBufferDemo { 319 | public static void main(String[] args) throws IOException { 320 | int bufferSize = 10;//字节 321 | FileChannel src = new FileInputStream("F:\\EFI\\src.txt").getChannel(); 322 | FileChannel dest = new FileOutputStream("F:\\EFI\\dest.txt").getChannel(); 323 | ByteBuffer buffer = ByteBuffer.allocate(bufferSize); 324 | int times = 0;//看读了几次 325 | while(src.read(buffer) != -1) {//一次读满缓存区,10个字节 326 | buffer.flip(); 327 | dest.write(buffer); 328 | buffer.clear(); 329 | System.out.println(++times); 330 | } 331 | System.out.println("ok"); 332 | } 333 | } 334 | 335 | 336 | ``` 337 | 338 | 339 | ### JAVA - 多线程编程 340 | 341 | **一、创建线程 和 线程常用方法** 342 | 创建多线程有 3 种方式,分别是 继承线程类,实现 Runnable 接口,匿名类。 343 | 1、继承线程类(需要重写 run 方法):创建一个对象就是一个线程。 344 | 2、实现 Runnable 接口(需要重写 run 方法):一般会问如果已经继承了一个类,该如何实现多线程。 345 | 3、匿名类(不需要额外增加一个类):和匿名函数相似,在用的时候才写,而且直接写在主函数里面。 346 | 注: 创建线程是start()方法,run()并不能创建一个新的线程// 347 | ![多线程方法](pics/java多线程.png) 348 | 349 | ```java 350 | /* 351 | 问:进程与线程的异同? 352 | A.子进程得到的是除了代码段是与父进程共享以外,其他所有的都是得到父进程的一个副本,子进程的所有资源都继承父 353 | 进程,得到父进程资源的副本,子进程可获得父进程的所有堆和栈的数据,但二者并不共享地址空间。两个是单独的进程,继 354 | 承了以后二者就没有什么关联了,子进程单独运行;进程的线程之间共享由进程获得的资源,但线程拥有属于自己的一小部分 355 | 资源,就是栈空间,保存其运行状态和局部自动变量的。 356 | B.线程之间共享进程获得的数据资源,所以开销小,但不利于资源的管理和保护;而进程执行开销大,但是能够很好的进行 357 | 资源管理和保护。 358 | C.线程的通信速度更快,切换更快,因为他们共享同一进程的地址空间。 359 | D.一个进程可以有多个线程,线程是进程的一个实体,是CPU调度的基本单位。 360 | */ 361 | 362 | /*实现的三种方法*/ 363 | //方法一:继承Thread类 364 | public class SimpleThread extends Thread{ 365 | SimpleThread t = new SimpleThread(); 366 | t.start(); 367 | } 368 | 369 | //方法二:实现Runnable方法 370 | public class SimpleThread implements Runnable{ 371 | Thread t = new Thread(new SimpleThread()); 372 | t.start(); 373 | } 374 | 375 | //方法三:匿名内部类 376 | public class SimpleThread{ 377 | Thread t = new Thread(){ 378 | public void run(){} 379 | }; 380 | t.start(); 381 | } 382 | 383 | /*常用方法: 384 | 通过调用Thread类的start方法来启动一个线程,这是此线程处于就绪状态,并没有运行。当得到cpu时间片后,开始执行 385 | run方法。*/ 386 | 387 | /*启动线程是start()方法,run()并不能启动一个新的线程,但是调用run会执行这个方法,和普通函数调用相同*/ 388 | t1.start(); 389 | t.close(); 390 | Thread.sleep(1000); //线程阻塞 1 秒 391 | t1.join();//在主线程中加入该线程,一般是 main 嘛,当 t1 执行完后,才会继续执行主函数 392 | t1.yield(); //临时暂停,回到就绪状态,让位给同等优先级的其他线程。 393 | t1.setPriority(Thread.MAX_PRIORITY);//为线程 t1 设置优先级,优先级越高,有越大的几率获得 CPU 资源。 394 | t1.setDaemon(true); //如果一个进程只剩下守护线程,则该进程会自动结束。守护线程通常会被用来做日志,性能统计等工作。 395 | 396 | /*问:多线程问题 397 | 多线程问题:sleep、wait、yield、join、volatile、并发、线程池、锁的实现 398 | 399 | 问:调用后释放锁的只有 wait+join。 400 | 答:刚看到一个大佬写的,给大家参考一下,所谓的释放锁资源实际是通知对象内置的monitor对象进行释放,而只有所有对 401 | 象都有内置的monitor对象才能实现任何对象的锁资源都可以释放。又因为所有类都继承自Object,所以wait()就成了 402 | Object方法,也就是通过wait()来通知对象内置的monitor对象释放,而且事实上因为这涉及对硬件底层的操作,所以 403 | wait()方法是native方法,底层是用C写的。其他都是Thread所有,所以其他3个是没有资格释放资源的,而join()有资格释 404 | 放资源其实是通过调用wait()来实现的。 405 | 406 | Thread.sleep(100); 407 | 作用:让当前正在运行的占用 cpu 的线程,阻塞 100 ms,其余线程自己竞争获取 cpu,如果被停止,会抛出 InterruptedException,需要的注意的是就算线程的睡眠时间到了,他也不是立即会被运行,只是从阻塞状态变为了就绪状态,是不会由阻塞状态直接变为运行状态的。 408 | 409 | this.wait() 410 | 自动释放对象锁,挂起,等待this.notify()唤醒,进入就绪状态。注意 wait和notify 两个方法都属于Object类,不是Thread线程上的,生产者-消费者问题。 411 | 412 | 问:ThreadLocal? 413 | 1、ThreadLocal 存放的值是线程封闭,线程间互斥的,主要用于线程内共享一些数据,避免通过参数来传递。 414 | 2、线程的角度看,每个线程都保持一个对其线程局部变量副本的隐式引用,只要线程是活动的并且ThreadLocal实例是可访问的;在线程消失之后,其线程实例的所有副本都会被垃圾回收。 415 | 3、Thread 类中有一个Map,用于存储每一个线程的变量的副本。 416 | 4、对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal 采用了以“空间换时间”的方式。 417 | ThreadLocal类用来提供线程内部的局部变量。这种变量在多线程环境下访问(通过get或set方法访问)时能保证各个线程里的变量相对独立于其他线程内的变量。ThreadLocal实例通常来说都是private static类型的,用于关联线程和线程的上下文。 可以总结为一句话:ThreadLocal的作用是提供线程内的局部变量,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度。 举个例子,我出门需要先坐公交再做地铁,这里的坐公交和坐地铁就好比是同一个线程内的两个函数,我就是一个线程,我要完成这两个函数都需要同一个东西:公交卡(北京公交和地铁都使用公交卡),那么我为了不向这两个函数都传递公交卡这个变量(相当于不是一直带着公交卡上路),我可以这么做:将公交卡事先交给一个机构,当我需要刷卡的时候再向这个机构要公交卡(当然每次拿的都是同一张公交卡)。这样就能达到只要是我(同一个线程)需要公交卡,何时何地都能向这个机构要的目的。 有人要说了:你可以将公交卡设置为全局变量啊,这样不是也能何时何地都能取公交卡吗?但是如果有很多个人(很多个线程)呢?大家可不能都使用同一张公交卡吧(我们假设公交卡是实名认证的),这样不就乱套了嘛。现在明白了吧?这就是ThreadLocal设计的初衷:提供线程内部的局部变量,在本线程内随时随地可取,隔离其他线程。 418 | 419 | 420 | **问:sleep 方法 和 wait 方法区别?** 421 | 对于sleep()方法,我们首先要知道该方法是属于Thread类中的,而wait()方法,则是属于Object类中的。sleep()方法导致了程序暂停执行指定的时间,让出cpu给其他线程,但是他的监控状态依然保持着,当指定的时间到了又会自动恢复运行状态。 422 | 在调用sleep()方法的过程中,线程不会释放对象锁。而当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象锁定池准备获取对象锁进入运行状态。 423 | 424 | Thread.yield(); 425 | 作用:暂停当前正在执行的线程对象,并执行其他线程,其实也不一定执行其他线程。yield()应该做的是让当前运行线程回到就绪状态,以允许具有相同优先级的其他线程获得运行机会。因此,使用yield()的目的是让相同优先级的线程之间能适当的轮转执行。但是,实际中无法保证yield()达到让步目的,因为让步的线程还有可能被线程调度程序再次选中。 426 | 427 | t1.Join() 428 | 把线程t1加入到当前线程,最主要的功能其实是防止主线程执行结束了,子线程还在没执行完成。如果加入这一句,则一定等到t1执行完成后才继续往后(主线程中)执行。如果前面有多个线程在执行了,则也会加入到执行队伍中(顺序不定),并且也是只有到t1结束后,程序才会继续往后执行。 429 | 430 | 问:Synchronized关键字的作用? 431 | 答:synchronized,可以作用在方法上,代码块上,类上(即静态方法)。synchronized关键字不能继承,虽然可以使用synchronized来定义方法,但synchronized并不属于方法定义的一部分,因此,synchronized关键字不能被继承。如果在父类中的某个方法使用了synchronized关键字,而在子类中覆盖了这个方法,在子类中的这个方法默认情况下并不是同步的,而必须显式地在子类的这个方法中加上synchronized关键字才可以。当然,还可以在子类方法中调用父类中相应的方法,这样虽然子类中的方法不是同步的,但子类调用了父类的同步方法,因此,子类的方法也就相当于同步了。 432 | 注意: 433 | 静态方法是属于类的而不属于对象的。同样的,synchronized修饰的静态方法锁定的是这个类的所有对象。 434 | synchronized关键字不能继承,所以在定义接口方法时不能使用synchronized关键字。 435 | 构造方法不能使用synchronized关键字,但可以使用synchronized代码块来进行同步。 436 | 缺点:然后引入与lock的区别。 437 | 438 | 439 | **问:关于 volatile 的一些面试问题** 440 | 有关volatile的几个小面试题:https://blog.csdn.net/u011277123/article/details/72235927 441 | 参考博客2:https://blog.csdn.net/u012723673/article/details/80682208 442 | 面试官提问:https://blog.csdn.net/weixin_33726313/article/details/87976219 443 | 444 | 问:被 volatile 修饰的共享变量,具有以下两点特性? 445 | 1 . 保证了不同线程对该变量操作的内存可见性; 446 | 2 . 禁止指令重排序 447 | JMM 主要就是围绕着如何在并发过程中如何处理原子性、可见性和有序性这3个特征来建立的,通过解决这三个问题,可以解除缓存不一致的问题。而 volatile 跟可见性和有序性都有关。 448 | 449 | 问:volatile 原理? 450 | 当写一个 volatile 变量时,JMM 会把该线程对应的本地内存中的共享变量刷新到主内存; 451 | 当读一个 volatile 变量时,JMM 会把该线程对应的本地内存置为无效,线程接下来将从主内存中读取共享变量; 452 | 但是 volatile 不能保证原子性,也就不能保证线程安全。 453 | 454 | 问:volatile 底层的实现机制? 455 | 如果把加入 volatile 关键字的代码和未加入 volatile 关键字的代码都生成汇编代码,会发现加入 volatile 关键字的 456 | 代码会多出一个 lock 前缀指令。lock 前缀指令实际相当于一个内存屏障,内存屏障提供了以下功能: 457 | 1 . 重排序时不能把后面的指令重排序到内存屏障之前的位置 2 . 使得本 CPU 的 Cache 写入内存 458 | 3 . 写入动作也会引起别的 CPU 或者别的内核无效化其 Cache,相当于让新写入的值对别的线程可见。 459 | 460 | 问:Java 中能创建 volatile 数组吗?修饰一个数组能否保证内存可见性? 461 | 能,Java 中可以创建 volatile 类型数组,不过只是一个指向数组的引用,而不是整个数组。如果改变引用指向的数组, 462 | 将会受到 volatile 的保护,但是如果多个线程同时改变数组的元素,volatile 标示符就不能起到之前的保护作用了。 463 | 464 | 问:synchronized 关键字和 volatile 关键字比较? 465 | 1、volatile 关键字是线程同步的轻量级实现,所以 volatile 性能肯定比 synchronized 关键字要好。但是 volatile 466 | 关键字只能用于变量而 synchronized 关键字可以修饰方法以及代码块。synchronized 关键字在 JavaSE1.6 之后进行了 467 | 主要包括为了减少获得锁和释放锁带来的性能消耗而引入的偏向锁和轻量级锁以及其它各种优化之后执行效率有了显著提升, 468 | 实际开发中使用 synchronized 关键字的场景还是更多一些。 469 | 2、多线程访问 volatile 关键字不会发生阻塞,而 synchronized 关键字可能会发生阻塞; 470 | 3、volatile 关键字能保证数据的可见性,但不能保证数据的原子性。synchronized 关键字两者都能保证。 471 | 4、volatile 关键字主要用于解决变量在多个线程之间的可见性,而 synchronized 关键字解决的是多个线程之间访问资 472 | 源的同步性。 473 | 474 | 问:synchronized 关键字和 lock 关键字比较? 475 | 476 | 问:1、 HashMap和Hashtable的区别 477 | 2、StringBuffer和StringBuilder的区别 478 | 3、ArrayList和Vector的区别 479 | 4、把非线程安全的集合转换为线程安全 480 | 答:https://how2j.cn/k/thread/thread-thread-safe/703.html 481 | 482 | 问:ConcurrentHashMap如何保证线程安全? 483 | 484 | 问:JAVA中常见的锁以及其特性? 485 | 1、自旋锁 486 | 2、自旋锁的其他种类 487 | 3、阻塞锁 488 | 4、可重入锁 489 | 5、读写锁 490 | 6、互斥锁 491 | 7、悲观锁 492 | 8、乐观锁 493 | 9、公平锁 494 | 10、非公平锁 495 | 11、偏向锁 496 | 12、对象锁 497 | 13、线程锁 498 | 14、锁粗化 499 | 15、轻量级锁 500 | 16、锁消除 501 | 17、锁膨胀 502 | 18、信号量 503 | */ 504 | 505 | ``` 506 | 507 | **二、线程同步问题** 508 | 问题:多个线程同时修改一个数据的时候,可能导致问题。 509 | 510 | 1. 线程同步的关键字 511 | 2. 同步主线程和子线程的三种方法 512 | 2.1 构造一个 object 对象,独占此对象才可执行该线程。 513 | 2.2 将此对象设定为 类的实例,则独占此实例才可调用此实例的方法(被关键字 synchronized 修饰过的多个方法,只有独占此实例,才可调用其中的任意一个)。 514 | 2.3 如果在类方法前(静态方法),加上修饰符 synchronized,同步对象是这个类的反射(即这个类独占,才可以调用 被关键字 synchronized 修饰过的多个方法中的一个)。一般是在方法前加 关键字,类前面是不加的。 515 | 3. 线程交互-生产者消费者模型。 516 | ```java 517 | /* 518 | 1. 定义一个全局 object,谁占用谁有话语权 519 | 2. 可以用实例代替 object 520 | 3. 用关键字 synchronized 修饰方法 521 | */ 522 | 523 | Object someObject =new Object(); 524 | synchronized (someObject){}//此处的代码只有占有了someObject后才可以执行 525 | 526 | Hero gareen = new Hero(); 527 | synchronized (gareen){} 528 | 529 | public void hurt() 530 | synchronized(this){ //this 就代表了这个实例 531 | hp = hp - 1; 532 | } 533 | 534 | public synchronized void hurt()//效果相同 535 | hp = hp - 1; 536 | public static synchronized void method(){}//防止多个线程同时访问这个类中的synchronized方法。也就是说此种修饰,可以对此类的所有对象实例起作用。 537 | 538 | /* 539 | 线程交互 wait() 和 notify() 540 | 这里需要强调的是,wait方法和notify方法,并不是Thread线程上的方法,它们是Object上的方法。 因为所有的Object都可以被用来作为同步对象,所以准确的讲,wait和notify是同步对象上的方法。wait()的意思是: 让占用了这个同步对象的线程,临时释放当前的占用,并且等待。 所以调用wait是有前提条件的,一定是在synchronized块里,否则就会出错。 541 | */ 542 | public synchronized void recover() { 543 | hp += 1; 544 | System.out.printf("%s 血量增加到 %.0f \n", name, hp); 545 | this.notify(); 546 | } 547 | 548 | public synchronized void hurt() { 549 | if(hp == 1) { 550 | try { 551 | this.wait(); 552 | }catch(InterruptedException e) { 553 | e.printStackTrace(); 554 | } 555 | } 556 | hp -= 1; 557 | System.out.printf("%s 血量减少为 %.0f \n", name, hp); 558 | } 559 | 560 | /* 561 | 问:可以用线程实现生产者消费者模型么? 562 | 563 | 问:lock 和 synchronized 的区别? 564 | 答:与 synchronized (someObject) 类似的 lock() 方法,表示当前线程占用 lock 对象,一旦占用,其他线程就不能占用了。与 synchronized 不同的是,一旦 synchronized 块结束,就会自动释放对 someObject 的占用。 lock却必须调用 unlock 方法进行手动释放,为了保证释放的执行,往往会把 unlock() 放在 finally 中进行。 565 | 566 | synchronized: 567 | 1、一个对象有多个synchronized方法,只要一个线程访问了其中的一个synchronized方法,其它线程不能同时访问这个对象中任何一个synchronized方法。 568 | 2、在对象方法前,加上修饰符 synchronized ,同步对象是当前实例。 569 | 3、在类方法(静态方法)前,加上修饰符 synchronized,同步对象是这个类。 570 | 571 | 问:synchronized 底层实现,如何实现 Lock? 572 | 堆中实例对象的对象头中的重量级锁指针,因为这个锁对象存放在对象本身,也就是为什么Java中任意对象可以作为锁的原因。 573 | 参考博客:https://baijiahao.baidu.com/s?id=1612142459503895416&wfr=spider&for=pc 574 | 575 | 576 | 问:一个线程运行结束后不会立马销毁么?会发生什么? 577 | 答:结束后等待系统回收资源,线程池内的线程资源应该暂时不会回收。 578 | 579 | 问:两线程对变量i进行加1操作,结果如何?为什么?怎么解决? 580 | 581 | 582 | */ 583 | ``` 584 | 585 | **三、 java 线程池** 586 | 问:如果一个线程出现异常,那么这个线程会被如何处理? 587 | 参考:https://blog.csdn.net/u011635492/article/details/80328815 588 | 未理解?? 589 | 590 | 问:java 实现多个子线程执行完毕后,再执行主线程 591 | 知识点:join 和 countDownLatch 进行阻塞 592 | 593 | ```java 594 | //下面的代码没有实现此功能哦,主线程会先结束,子线程还未执行完毕。 595 | //以下两种做法的区别,参考:https://blog.csdn.net/zhang199416/article/details/70846958 596 | /* 597 | 1 final CountDownLatch latch = new CountDownLatch(3); 需要阻塞的线程数量 598 | 子线程中添加:latch.countDown(); 599 | 600 | 2 子线程.join(); 601 | */ 602 | public class test { 603 | public static void main(String[] args) { 604 | System.out.println("主线程开始执行...."); 605 | for (int i = 0; i < 3; i++) { 606 | new Thread(){ 607 | @Override 608 | public void run() { 609 | try { 610 | System.out.println(Thread.currentThread().getName()+" 开始执行存储过程.."); 611 | Thread.sleep(1000); 612 | System.out.println(Thread.currentThread().getName()+" 存储过程执行完毕..."); 613 | } catch (InterruptedException e) { 614 | e.printStackTrace(); 615 | } 616 | }; 617 | }.start(); 618 | } 619 | System.out.println("主线程执行完毕...."); 620 | } 621 | } 622 | 623 | ``` 624 | 625 | ### JAVA - 虚拟机 626 | 627 | JAVA虚拟机简介 628 | 参考链接:https://blog.csdn.net/qq_41701956/article/details/81664921 629 | 630 | JDK各个版本发布时间和版本名称 631 | 参考链接:https://blog.csdn.net/J080624/article/details/85259041 632 | 633 | JDK1.5,1.6,1.7,1.8,1.9,1.10,1.11的新特性整理 634 | 参考链接:https://blog.csdn.net/J080624/article/details/85092655 635 | 636 | 637 | 638 | ### JAVA - 垃圾回收机制 639 | 640 | 641 | ![多线程方法](pics/JVM结构.png) 642 | 643 | ```java 644 | /**Java 虚拟机内存结构** 645 | 1.程序计数器是一个比较小的内存区域,用于指示当前线程下一条被执行指令的地址,因此也是也不存在OOM的情况,是线程 646 | 隔离的,每个线程都有。 647 | 2.虚拟机栈描述的是Java方法执行的内存模型,用于存储局部变量,操作数栈,动态链接,方法出口等信息,是线程隔离的, 648 | 每个线程都有自己的私有内存,这也是多线程并发时造成数据不一致的原因。 649 | 3.方法区用于存储JVM加载的类信息、常量、静态变量、以及编译器编译后的代码等数据,是线程之间共享的 650 | 4.原则上讲,所有的对象都在堆区上分配内存,是线程之间共享的 651 | 652 | 切入:综合来看,一个对象的创建需要存放多个区域。 653 | 金句:new 出来的对象存在于堆区,而这些new出来对象的引用则存在于栈区。 654 | 金句:类似 String a = "abc"; 之类的常量存在于常量池中,而常量池存在于方法区中。 655 | 656 | **Java 虚拟机内存分配和回收的机制** 657 | 概括的说就是分代分配,分代回收,年轻代(Young Generation)、年老代(Old Generation)、永久代(Permanent Generation,也就是方法区) 658 | 每次调整分区都会调用 轻量级回收 Minor GC;重量级的 Full GC 以下情况会触发: 659 | 1、年老代或持久代被写满;2、程序员显示的调用 System.gc();方法; 660 | 注意:在执行 Full GC 时,会导致 Stop the World,虚拟机会终止所有 Java 程序,专门执行 Full GC,这可能会导致程序一直卡在那; 661 | 662 | **判断对象可回收的依据** 663 | 标准:当某个对象上没有强引用时,该对象就可以被回收了。 664 | 早期版本采用“引用计数”,优点是简单,缺点是无法回收循环引用的对象,如 a=b; b=c; c = a;则无法回收; 665 | 后续版本采用了“根搜索算法”,这个算法将从一个根节点开始,寻找它所对应的引用节点,找到这个节点后,继续寻找该节点的引用节点。主要从方法区的常量和静态变量,两个栈,开始搜索引用; 666 | 667 | **finalize 方法** 668 | 当通过上述介绍的根搜索算法回收某个对象时,先判断该对象是否重写了 Object 的 finalize 方法,没有则直接回收。 669 | 670 | **虚拟机的类加载器** 671 | JDK 中提供了三个 ClassLoader,根据层级从高到低为: 672 | 启动类加载器:Bootstrap ClassLoader,主要加载JVM自身工作需要的类。 673 | 扩展类加载器:Extension ClassLoader,主要加载%JAVA_HOME%\lib\ext目录下的库类。 674 | 应用程序类加载器:Application ClassLoader,主要加载Classpath指定的库类,一般情况下这是程序中的默认类加载器, 675 | 也是ClassLoader.getSystemClassLoader() 的返回值。(这里的Classpath默认指的是环境变量中配置的Classpath,但 676 | 是可以在执行Java命令的时候使用-cp 参数来修改当前程序使用的Classpath)JVM加载类的实现方式,我们称为 双亲委托模 677 | 型:如果一个类加载器收到了类加载的请求,他首先不会自己去尝试加载这个类,而是把这个请求委托给自己的父加载器,每 678 | 一层的类加载器都是如此,因此所有的类加载请求最终都应该传送到顶层的Bootstrap ClassLoader中,只有当父加载器反馈 679 | 自己无法完成加载请求时,子加载器才会尝试自己加载。双亲委托模型的重要用途是为了解决类载入过程中的安全性问题。 680 | 假设有一个开发者自己编写了一个名为Java.lang.Object的类,想借此欺骗JVM。现在他要使用自定义ClassLoader来加载自 681 | 己编写的java.lang.Object类。然而幸运的是,双亲委托模型不会让他成功。因为JVM会优先在Bootstrap ClassLoader的 682 | 路径下找到java.lang.Object类,并载入它 683 | 684 | **强、弱、软、虚四种引用** 685 | 强引用:指向通过 new 得到的内存空间,如 String a = new String("123"); 686 | 软引用(SoftReference):如果一个对象只具有软引用,而当前虚拟机堆内存空间足够,则不会回收,反之回收; 687 | 应用场景:博客管理系统,将博客缓存到内存中,缩短响应时间,当内存不足时,将暂时每人看的博客缓存清除; 688 | 弱引用(WeakRefernce):如果某块内存上只有弱引用,则一定会回收; 689 | 应用场景:优惠券,某个优惠券强引用小时,使用的用户自动更新; 690 | 虚引用(PhantomReference):1.虚引用必须和引用队列(ReferenceQueue)一起使用;2.始终无法通过虚引用得到他所 691 | 指向的值; 692 | 693 | **面试题** 694 | 问:简述 Java 虚拟机的内存结构? 695 | 1、回答五个分区;2、回答不同分区包含的数据;3、金句点题; 696 | 问:简述 Java 垃圾回收的大致流程? 697 | 1.回答三个分区;2.回答Minor GC和Full GC;3.垃圾回收的算法; 698 | 699 | 700 | 701 | ``` 702 | 703 | ### 内存调优 704 | 705 | ```java 706 | /**基本概念** 707 | Stop-the-world意味着从应用中停下来并进入到GC执行过程中去。一旦Stop-the-world发生,除了GC所需的线程外,其他线 708 | 程都将停止工作,中断了的线程直到GC任务结束才继续它们的任务。GC调优通常就是为了改善stop-the-world的时间。 709 | 710 | 711 | 712 | **下面太难了** 713 | 1、线上系统CPU,内存与磁盘IO暴增,你会如何调优? 714 | 715 | 问:你们JVM线上使的什么垃圾回收器?CMS还是G1? 716 | https://blog.csdn.net/gui694278452/article/details/104781237/ 717 | CMS收集器是一种以获取最短回收停顿时间为目标的收集器,CMS是一款优秀的收集器,它的主要优点是:并发收集、低停顿, 718 | 就是与用户线程共同运行,但他有以下3个明显的缺点:1、CMS收集器对CPU资源非常敏感,降低运行效率; 719 | G1(Garbage First)是一款面向服务端应用的垃圾收集器。 720 | 721 | 3、CMS的并发更新失败是怎么回事?如何优化? 722 | 723 | 4、JVM是任何时刻都可以STW吗?为什么? 724 | 725 | 5、线上系统GC问题如何快速定位与分析?阿里巴巴的Arthas用过吗? 726 | 727 | 6、单机几十万并发的系统JVM如何优化? 728 | 729 | 7、高并发系统为何建议选择G1垃圾收集器? 730 | 731 | 8、能说说Mysql索引底层B+树结构与算法吗? 732 | 733 | 9、聚集索引与覆盖索引与索引下推到底是什么? 734 | 735 | 10、能说说Mysql并发支撑底层Buffer Pool机制吗? 736 | 737 | 11、一线互联网公司的数据库架构是如何设计的? 738 | 739 | 12、对线上千万级大表加字段时,性能极慢问题如何处理? 740 | 741 | */ 742 | 743 | ``` 744 | 745 | 746 | ### 网络编程 747 | 748 | [参考W3C 网络编程](https://www.w3cschool.cn/java/java-networking.html) 749 | NIO的Selector实例一般都是Socket网络通信方面,如用户可以在服务端通过一个选择器来管理来自客户端的多个通道,在一些高并发的系统中,会用到选择器来开发各业务模块之间的通信模块,但这是架构师或资深高级程序员要考虑的事情。对于初级程序员,知道一些基本概念即可。 750 | 751 | ```java 752 | Selector selector = Selector.open(); // 创建一个Selector 753 | 754 | ``` 755 | -------------------------------------------------------------------------------- /notes/JAVA/pics/JVM结构.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/light-wl/Learning/23d7c87530993e4948252e9cf7543c67044fcd85/notes/JAVA/pics/JVM结构.png -------------------------------------------------------------------------------- /notes/JAVA/pics/UML用例图.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/light-wl/Learning/23d7c87530993e4948252e9cf7543c67044fcd85/notes/JAVA/pics/UML用例图.png -------------------------------------------------------------------------------- /notes/JAVA/pics/collection.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/light-wl/Learning/23d7c87530993e4948252e9cf7543c67044fcd85/notes/JAVA/pics/collection.jpg -------------------------------------------------------------------------------- /notes/JAVA/pics/java多线程.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/light-wl/Learning/23d7c87530993e4948252e9cf7543c67044fcd85/notes/JAVA/pics/java多线程.png -------------------------------------------------------------------------------- /notes/JAVA/pics/java访问修饰符.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/light-wl/Learning/23d7c87530993e4948252e9cf7543c67044fcd85/notes/JAVA/pics/java访问修饰符.png -------------------------------------------------------------------------------- /notes/JAVA/pics/jsp语法.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/light-wl/Learning/23d7c87530993e4948252e9cf7543c67044fcd85/notes/JAVA/pics/jsp语法.png -------------------------------------------------------------------------------- /notes/JAVA/pics/servlet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/light-wl/Learning/23d7c87530993e4948252e9cf7543c67044fcd85/notes/JAVA/pics/servlet.png -------------------------------------------------------------------------------- /notes/JAVA/pics/springScope作用域.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/light-wl/Learning/23d7c87530993e4948252e9cf7543c67044fcd85/notes/JAVA/pics/springScope作用域.jpg -------------------------------------------------------------------------------- /notes/JAVA/pics/内部类.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/light-wl/Learning/23d7c87530993e4948252e9cf7543c67044fcd85/notes/JAVA/pics/内部类.png -------------------------------------------------------------------------------- /notes/Java/Java-SSM框架.md: -------------------------------------------------------------------------------- 1 | # JAVA-SSM框架 2 | [参考这个学习网站](http://how2j.cn/stage/25.html) 3 | 4 | 5 | ### 工具和中间件 6 | **Maven**:Maven 是专门用于构建和管理Java相关项目的工具。 7 | 一、主要用处一:相同的项目结构 ,1.有一个pom.xml 用于维护当前项目都用了哪些jar包;2. 所有的java代码都放在 src/main/java 下面;3. 所有的测试代码都放在src/test/java 下面。 8 | 二、主要用处二:统一维护jar包,比如说有3个Java 项目,这些项目都不是maven风格。那么这3个项目,就会各自维护一套jar包。 而其中有些jar包是相同的。而maven风格的项目,首先把所有的jar包都放在"仓库“ 里,然后哪个项目需要用到这个jar包,只需要给出jar包的名称和版本号就行了。 这样jar包就实现了共享。 9 | ![maven目录](../../pics/maven目录.png) 10 | 11 | **SSM(Spring + SpringMVC + Mybatis)框架并模拟搭建天猫购物网站** 12 | 13 | * 效果一览:https://how2j.cn/tmall/ 14 | 15 | ### 前台页面 16 | 第一类:登录页、注册页 17 | 第二类:首页、分类页、查询页、产品详情页 18 | 第三类:购物车页、结算页(收货地址)、支付页、订单页(所有订单,待付款,待发货,待评价) 19 | 具体见:需求分析交互->前台需求分析 20 | **亮点添加(没有做)** 21 | 1、登录页面加入动态验证码 22 | 2、支付页面看看能够调用支付宝的API 23 | 24 | 25 | ### 后台功能 26 | 分类管理(图片,属性,产品,名称): 27 | 用户管理: 28 | 订单管理(发货): 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /notes/Java/Java-SpringBoot框架.md: -------------------------------------------------------------------------------- 1 | # SpringBoot框架 2 | 简介 3 | 4 | 遇到的问题; 5 | 1、添加 jar 包:新建文件夹,在F盘里看不见,也赋值不进去文件。只能在IDEA里面新建,然后拖进去。然后右键 add as Library 添加 jar 包。 6 | 7 | 8 | # 注解 9 | ```java 10 | /* 11 | 问:@SuppressWarnings(“deprecation”)的功能是什么? 12 | Java三大注解分别是@Override @Deprecated @Suppresswarnings 13 | *Deprecated 注解* 14 | 可以修饰类、方法、变量,在java源码中被@Deprecated修饰的类、方法、变量等表示不建议使用的,可能会出现错误的,可能以后会被删除的类、方法等,如果现在使用,则在以后使用了这些类、方法的程序在更新新的JDK、jar包等就会出错,不再提供支持。 个人程序中的类、方法、变量用@Deprecated修饰同样是不希望自己和别人在以后的时间再次使用此类、方法。 当编译器编译时遇到了使用@Deprecated修饰的类、方法、变量时会提示相应的警告信息。 15 | 16 | *Suppresswarnings 注解* 17 | 可以达到抑制编译器编译时产生警告的目的,但是很不建议使用@SuppressWarnings注解,使用此注解,编码人员看不到编译时编译器提示的相应的警告,不能选择更好、更新的类、方法或者不能编写更规范的编码。同时后期更新JDK、jar包等源码时,使用@SuppressWarnings注解的代码可能受新的JDK、jar包代码的支持,出现错误,仍然需要修改。 18 | */ 19 | 20 | ``` -------------------------------------------------------------------------------- /notes/Python/Python-基础语法.md: -------------------------------------------------------------------------------- 1 | # Python-基础语法 2 | 3 | **Python 简介** 4 | 5 | - **Python 是一种解释型语言** :这意味着开发过程中没有了编译这个环节。 6 | - **Python 是面向对象语言**: 这意味着Python支持面向对象的风格或代码封装在对象的编程技术。 7 | - **Python 是开源的,最大的优势之一是丰富的库**,所以使用方向广泛,因为机器学习的热浪,开始在程序员间展露头角,可以进行 web 开发、爬虫、数据科学(包括机器学习、数据分析和数据可视化),还是跨平台的,在UNIX,Windows 和 MacOS 兼容很好。 8 | 9 | **Python 语法基础** 10 | 11 | ```python 12 | #查看 Python 的所有关键字,在以后的练习中要做到对每个都很熟悉 13 | import keyword 14 | print(keyword.kwlist) 15 | """ 16 | ['False', 'None', 'True', 'and', 'as', 'assert', 'break', 'class', 'continue', 'def', 'del', 'elif', 'else', 'except', 'finally', 'for', 'from', 'global', 'if', 'import', 'in', 'is', 'lambda', 'nonlocal', 'not', 'or', 'pass', 'raise', 'return', 'try', 'while', 'with', 'yield'] 17 | """ 18 | 19 | #注释 20 | #单行注释 21 | ''' 22 | 多行注释,三个单双引号都可以。 23 | ''' 24 | ``` 25 | 26 | **基本数据类型** 27 | 28 | * 基本数据类型:数字、字符串(str)、列表(list)、元组(tuple)、集合(set)、字典(dict),前三个类型不可更改,具体可参考[我的博客](https://blog.csdn.net/qq_29611345/article/details/99693298)。 29 | * Python中的变量不需要声明,每个变量在使用前都必须赋值,变量赋值以后该变量才会被创建。 30 | 31 | ```python 32 | # / 表示浮点数除法,// 表示整数除法,所以 Python 的单行注释是 # 。 33 | z = x / y 34 | 35 | #使用 type(val) 函数查看变量类型。 36 | 37 | #字符串,使用 '' 或者 "" 都是可以的,没有字符类型,一个字符就是长度为 1 的字符串。 38 | #字符串不可修改,类似 word[0] = 'H',都是错误的。 39 | word = 'hello' 40 | print('str' + 'ing', 'my'*3) 41 | 42 | #数组 43 | arrOne = [0]*n #一维数组可以用乘法,但是二维数组不可以,会造成引用赋值 44 | arrTwo = [[0]*n for _ in range(n)] #n*n 的二维数组 45 | 46 | #元组,不可修改,可以存放多种类型 47 | tup = (11, 'hello', 22) 48 | 49 | #列表,不同于数组,里面存放的值可以是多种类型。 50 | li = [11, 'hello', 25] 51 | 52 | #集合,一个无序不重复的集合,可以利用 set 去重。 53 | student = {'tom', 'jerry', 'mary'} 54 | 55 | #字典, key - value 对 56 | dic = {‘key’:'value', 11:22, 'id':10} 57 | 58 | ``` 59 | 60 | 61 | 62 | **基本控制语句** 63 | 64 | ```python 65 | #Python 变量不用提前声明,随用随写,它会根据赋值的类型自动判断。 66 | a,b,c,d = 10,'hello',[1,2,3],True 67 | 68 | #Python 是使用 缩进 来表示代码块的,而不是使用 { },一般使用 四个空格 表示一个缩进,末尾不要加 分号。 69 | #条件控制 70 | if a > b: #注意条件后面要加冒号,代码块拥有相同的缩进。 71 | print("YES") 72 | print("YES") 73 | elif a < b: 74 | print('NO') 75 | print('NO') 76 | else: 77 | print('equal') 78 | 79 | #循环语句 80 | while True: 81 | print("这是死循环") 82 | 83 | for i in range(10):#range 是一个可迭代的函数,循环输出 0-9。 84 | print(i) 85 | 86 | #函数声明,不用定义返回值,无返回值默认返回空。 87 | def area(width, height): 88 | return width * height 89 | ``` 90 | 91 | * **编程常用语法** 92 | ```python 93 | #输入一串数据,空格分隔,转换为整数如: 1 2 3 4 5 94 | li = list(map(int, input().split())) #split 默认以空格分隔,输入默认是字符串 95 | li = [int(x) for x in input().split()] #这种也可以,效果相同 96 | #li = [1,2,3,4,5] 97 | 98 | import collections 99 | counter = dict(collections.Counter(string)) #将字符串 string 统计成 字典 {字符:数量},需要转换 100 | counter.most_common() #输出:[('a', 3), ('b', 2), ('c', 1)] 按值从大到小 返回 dic 中的对 101 | 102 | ``` 103 | 104 | **文件输入输出流** 105 | ```python 106 | import os 107 | os.chdir('F:') 108 | with open('test.txt') as test: 109 | count = 0 110 | for ch in test.read(): 111 | if ch.isupper():#统计大写字母 112 | count += 1 113 | print(count) 114 | ``` -------------------------------------------------------------------------------- /notes/Python/Python-常用函数.md: -------------------------------------------------------------------------------- 1 | # Python-常用函数 2 | 3 | **python运行速度**:因为 Python 是动态语言,解释执行,因此 Python 代码运行速度慢。如果要提高 Python 代码的运行速度,最简单的方法是把 **某些关键函数用 C 语言重写**,这样就能大大提高执行速度。同样的功能,StringIO 是纯 Python 代码编写的,而 cStringIO 部分函数是 C 写的,因此 cStringIO 运行速度更快。 4 | 5 | 函数列表: 6 | 7 | * map、zip、filter、reduce 8 | 9 | * help、dir 10 | 11 | * itertools 12 | 13 | 14 | 15 | * **帮助文档:help 和 dir 的使用** 16 | ```python 17 | dir(func) #可以查看某个函数的 所有属性 和 方法 18 | help(func) #可以查看该函数的使用方法包括 需要的参数 和 返回值 19 | ``` 20 | 21 | 22 | 23 | * **无穷大与无穷小** 24 | 25 | ```python 26 | float('inf') #无穷大 27 | float('-inf') #无穷小 28 | ``` 29 | 30 | 31 | 32 | * **面:简述 any() 和 all() 方法** 33 | 34 | ```Python 35 | any(li) #只要迭代器中有一个元素为真就为真 36 | all(li) #迭代器中所有的判断项返回都是真,结果才为真 37 | ``` 38 | 39 | 40 | 41 | 42 | 43 | * **global 全局变量** [超详细参考博客](https://blog.csdn.net/qw_sunny/article/details/80972357) 44 | 45 | 46 | 47 | * **Python 中内置的四种函数,map、zip、filter 返回值都是一个 可迭代对象(一个地址),reduce 需要加库文件** 48 | 49 | ```python 50 | # map(func, li) 函数的用法:将函数作用于列表中的每个值 51 | li = map(lambda x:x*x,[1,2,3]); li = list(li) #返回一个可迭代的对象,需用 list 转换 [1,4,9] 52 | li = map(list, map(lambda x:x*x,[1,2,3])) #可两个函数一起使用,和上式效果相同 53 | li = map(int,input().split()) #对输入的值进行类型转换 54 | 55 | # zip(li,li) 函数的用法:将两个参数的值一一对应成元组,返回一个可迭代对象 56 | tup = list(zip([1,2,3],[4,5,6])) #返回:需用 list 转换,得到一系列元组 [(1, 4), (2, 5), (3, 6)] 57 | tup = list(map(list, zip([1,2,3],[4,5,6])))#可以再使用map函数,对里面的元组进行转换 58 | 59 | # filter(func, li)函数的用法:和 map 函数类似,不过只有返回 True 的值会被保留 60 | li = list(filter(lambda x:x%2,range(10))) #返回:需用 list 转换,得到 [1, 3, 5, 7, 9] 61 | 62 | # reduce(func, li)函数的用法:唯一可以使用两个参数的函数 63 | import functools # 需要加库 64 | num = functools.reduce(lambda x,y:x+y, [1,2,3,4,5]) # 返回 15 此函数还可实现阶乘! 65 | 66 | ``` 67 | 68 | 69 | 70 | * **二进制函数** 71 | 72 | ```python 73 | binary = lambda x: '' if x == 0 else binary(x/2) + str(x%2) 74 | bin(10) #转为二进制,python 中二进制表示0b1010 75 | oct(10) #转为八进制, 0o12 76 | hex(10) #转为十六进制,0xa 77 | int('10', 2); int('10', 8); int('10', 16 ); #将字符串以二、八、十六进制进行转换,返回 2,8,16 78 | #将某个字符串转为相应的值,还是需要使用 int 79 | num = int(bin(0b1010)[2:]) #直接转换为数字 1010 80 | ``` 81 | 82 | 83 | 84 | * **sorted 函数** 85 | 86 | ```python 87 | sorted(li, key=None, reverse=False) # 返回排序后的列表,但原列表不变。 88 | li.sort(key=None, reverse=False) # 返回 None,li 递增排序,默认递增排序 89 | 90 | # key 关键字一般需要传入一个函数 91 | sorted("This is a test string from Andrew".split(), key=str.lower) 92 | # 输出 ['a', 'Andrew', 'from', 'is', 'string', 'test', 'This'] 93 | 94 | #如果比较的值是两个值的列表,则下式表示按 第一个值 递增排序,如果相同,按 第二个值 递减排序 95 | li.sort(key = lambda x:(x[0],-x[1])) 96 | 97 | #传入两个参数:2.x版本有cmp参数,现在只有key了,但是加了这个函数模拟以前的功能。 98 | #问:将一个字符串列表,组成字典序最大的串。 99 | from functools import cmp_to_key 100 | li.sort(key=cmp_to_key(lambda s1,s2:s1+s2 if s2+s1 0: 109 | assert a <= 0 #如果出现不在预期的结果,那么可以主动抛出一个异常 110 | ``` 111 | 112 | 113 | 114 | **** 115 | 116 | * **import itertools、import random、import time** 117 | 118 | ```python 119 | import itertools #该函数返回一个可迭代对象,将 li 里的元素,按 num 数量进行组合,每个组合以元组存储 120 | li = [1,2,3]; num = 2 121 | res = list(itertools.combinations(li,num)) #转为列表 [(1, 2), (1, 3), (2, 3)] 122 | 123 | import random 124 | print(random.randint(a,b)) # 返回 [a,b] 125 | print(random.random()) # 返回 [0.0,1.0) 126 | 127 | import time 128 | time.localtime() # 字典形式年月日时分秒,可以单独取出 129 | time.ctime() # 输出标准年月日时分秒 130 | time.sleep(x) # 程序阻塞 x 秒 131 | ``` 132 | [记录程序时间的三种方法:参考博客](https://www.jb51.net/article/118699.htm) 133 | 134 | **** 135 | 136 | 137 | * **enumerate、isinstance、eval、reverse** 138 | ```python 139 | for index, num in enumerate(li): # 迭代输出 li 的 索引 和 值 140 | 141 | isinstance(var, str) # 判断 var 是否是字符串,或者 type(var) is str: 效果相同 142 | 143 | eval('1+1') # 将字符串当做表达式运算,返回 2 144 | 145 | li.reverse() #列表的属性,返回 None 146 | reversed(li) # 返回可迭代对象,原列表不变 147 | 148 | 149 | ``` 150 | 151 | ### 面试题能力测试 152 | * **面:用 Python 实现统计一篇英文文章内每个单词的出现频率,并返回出现频率最高的前 10 个单词及其出现次数,并解答以下问题?(标点符号可忽略)** 153 | ```python 154 | import collections 155 | dic = collections.Counter(article) 156 | dic.most_common()[:11] 157 | ``` 158 | * **面:用两种方法去空格** 159 | ```python 160 | li = s.split() #默认是空格 161 | s = ''.join(li) 162 | 163 | s = s.replace(' ','') 164 | ``` 165 | 166 | * **面:一行代码实现对列表 a 中的偶数位置的元素进行加 3 后求和** 167 | ```python 168 | a = [1,2,3,4,5] 169 | b = [a[i]+3 for i in range(len(a)) if i%2==0] + 170 | [a[i] for i in range(len(a)) if i%2==1] 171 | ``` 172 | 173 | * **面:[[1,2],[3,4],[5,6]]一行代码展开该列表,得出[1,2,3,4,5,6]** 174 | ```python 175 | li = [i for x in li for i in x] 176 | ``` 177 | 178 | * **面:请列出你会的任意一种统计图(条形图、折线图等)绘制的开源库,第三方也行** 179 | ```python 180 | pychart、matplotlib 181 | ``` -------------------------------------------------------------------------------- /notes/Python/Python-面试问题.md: -------------------------------------------------------------------------------- 1 | # Python-面试问题 2 | 3 | **参考链接** 4 | 5 | [**100 道真实面试题**](https://blog.csdn.net/weixin_41666747/article/details/79942847) 6 | 7 | [2016 年阿里校招 Python 面试](https://www.cnblogs.com/acvc/p/4714848.html) 8 | 9 | [阿里 Python 工程师面试题](http://developer.51cto.com/art/201801/564790.htm) 10 | 11 | [Python 36 问](https://baijiahao.baidu.com/s?id=1607651363840614527&wfr=spider&for=pc) 12 | 13 | **** 14 | 15 | * **面:Python 中什么元素为假?** 16 | * **答**:(0,空字符串,空列表、空字典、空元组、None, False) 17 | **** 18 | 19 | * **面:Python 中查看某个关键字的属性?** 20 | * **答**:dir ( key ),help() 函数返回帮助文档和参数说明,dir() 函数返回对象中的所有成员 (任何类型) 21 | **** 22 | 23 | * **面:Python 全局变量 global 的使用?** 24 | * **答**:[参考博客,超详细](https://blog.csdn.net/qw_sunny/article/details/80972357) 25 | **** 26 | 27 | * **面:Python 中的 pass 语句有什么作用?** 28 | * **答**:我们在写代码时,有时可能只写了函数声明而没想好函数怎么写,但为了保证语法检查的正确必须输入一些东西。在这种情况下,我们使用 pass 语句。 29 | **** 30 | 31 | * **面:举例说明异常模块中 try except else finally 的相关意义?** 32 | * **答**:try..except..else 没有捕获到异常,执行 else 语句;try..except..finally 不管是否捕获到异常,都执行 finally 语句。 33 | **** 34 | 35 | * **面:with 方法打开处理文件帮我们做了什么?yield 的使用-生成器?** 36 | * **答**:功能类似 try,except,finally 中 finally 的用法,with:除了有更优雅的语法,真正强大的地方:with 还可以很好的处理上下文环境产生的异常。with 后面的函数需要有 __ exit __ 方法,当程序出现异常的时候会帮助我们打印异常,清理资源,关闭文件。[with 参考博客](https://www.cnblogs.com/DswCnblog/p/6126588.html),[yield 参考博客](https://blog.csdn.net/mieleizhi0522/article/details/82142856) 37 | **** 38 | 39 | * **面:Python 中断言方法举例?** 40 | * **答**:使用 assert 断言是学习 Python 一个非常好的习惯,Python assert 断言句语格式及用法很简单。在没完善一个程序之前,我们不知道程序在哪里会出错,与其让它在运行最崩溃,不如在出现错误条件时就崩溃。 41 | **** 42 | 43 | * **面:列出 Python 中可变数据类型 和 不可变数据类型,并简述原理?** 44 | * **答**:不可变数据类型:数值型、字符串型、和 元组;不允许变量的值发生变化,如果改变了变量的值,相当于是新建了一个对象,而对于相同的值的对象,在内存中则只有一个对象(一个地址)可变数据类型:列表 和 字典,允许变量的值发生变化,即如果对变量进行 append、+= 等这种操作后,只是改变了变量的值,而不会新建一个对象,变量引用的对象的地址也不会变化,不过对于相同的值的不同对象,在内存中则会存在不同的对象,即每个对象都有自己的地址,相当于内存中对于同值的对象保存了多份,这里不存在引用计数,是实实在在的对象。 45 | **** 46 | 47 | * **面:赋值,浅拷贝,深拷贝的区别?** 48 | 49 | **赋值:**简单地拷贝对象的引用,两个对象的 id (内存中的地址)相同,并且共用一个对象,删除 a 也不会删除数组,原因见下一题 引用计数机制。 50 | 51 | **浅拷贝-copy():**创建一个新的组合对象,这个新对象与原对象共享内存中的子对象。当改变原对象时,才会创建一个实例。仅仅是最顶层开辟了新的空间,里层的元素地址还是一样的。 52 | 53 | **深拷贝-deepcopy():**创建一个新的组合对象,**同时递归地拷贝所有子对象**,新的组合对象与原对象没有任何关联。虽然实际上会共享不可变的子对象,但不影响它们的相互独立性。 54 | ```python 55 | #赋值 56 | a = [1,2,3] 57 | b = a #赋值 id(a) == id(b),直接引用计数 58 | 59 | #浅拷贝 60 | import copy 61 | a = 1 #对于不可变数据类型 数字a=1; 字符串a='hello'; 元组a=(1,2,3),不存在啥浅拷贝的,和赋值一样 62 | b = copy.copy(a) #id(a) == id(b) 63 | 64 | a = [1,2,3] #对于可变数据类型 列表,字典,仅仅是最顶层开辟了新的空间,里层的元素地址还是一样的。 65 | b = copy.copy(a) # id(a) != id(b) 66 | id(a[0]) == id(b[0]) #True 里面的不可变类型地址是一样的 67 | 68 | #深拷贝,逻辑同上,但是浅拷贝不会递归拷贝元素是地址的值,浅拷贝对 c 也是简单的拷贝。 69 | import copy 70 | c = [4,5] 71 | a = [1,2,3,c] 72 | b = copy.deepcopy(a) #会递归 a 中的引用,改变 c 的值,b 不会改变。 73 | 74 | ``` 75 | **** 76 | 77 | * **面:简述 Python 引用计数机制?** 78 | * **答**:GC(Garbage Collector(垃圾收集器))机制:以下三种都是; 79 | Python 垃圾回收主要以引用计数为主,标记-清除 和 分代清除 为辅的机制,其中 标记-清除 和 分代回收 主要是为了处理循环引用的难题。 80 | 引用计数算法:当有 1 个变量保存了对象的引用时,此对象的引用计数就会加 1,变为 0 后才会真正清除对象。 81 | 优点:高效、实时性[为 0 直接清除,其他可能需要等到合适的时机]、易于实现 82 | 缺点:消耗资源、无法解决循环引用;解决:标记-清除 和 分代清除 83 | 84 | [循环引用参考博客](https://blog.csdn.net/xiongchengluo1129/article/details/80462651),下面简要介绍。 85 | ```python 86 | #循环引用问题 87 | list1 = [];list2 = [] 88 | list1.append(list2) 89 | list2.append(list1) 90 | ''' 91 | 针对循环引用的情况:我们有一个“孤岛”或是一组未使用的、互相指向的对象,但是谁都没有外部引用。换句话说,我们的程序不再使用这些节点对象了,所以我们希望Python的垃圾回收机制能够足够智能去释放这些对象并回收它们占用的内存空间。但是这不可能,因为所有的引用计数都是1而不是0。 92 | 『标记清除(Mark—Sweep)』算法是一种基于追踪回收(tracing GC)技术实现的垃圾回收算法。它分为两个阶段:第一阶段是标记阶段,GC 会把所有的『活动对象』打上标记,第二阶段是把那些没有标记的对象『非活动对象』进行回收。那么 GC 又是如何判断哪些是活动对象哪些是非活动对象的呢? 93 | 对象之间通过引用(指针)连在一起,构成一个有向图,对象构成这个有向图的节点,而引用关系构成这个有向图的边。从根对象(root object)出发,沿着有向边遍历对象,可达的(reachable)对象标记为活动对象,不可达的对象就是要被清除的非活动对象。根对象就是全局变量、调用栈、寄存器。 94 | ''' 95 | ``` 96 | **** 97 | 98 | * **面:Python 中的 值传递 与 引用传递?** 99 | * **答**:Python 不允许程序员选择采用 传值 还是 传引用。Python 参数传递采用的肯定是 **传对象引用** 的方式。实际上,这种方式相当于传值和传引用的一种综合。如果函数收到的是一个可变对象(比如字典、列表)的引用,就能修改对象的原始值——相当于通过 传引用 来传递对象。如果函数收到的是一个不可变对象(比如数字、字符或者元组)的引用,就不能 直接修改原始对象——相当于通过 传值 来传递对象。 当人们复制 列表 或 字典 时,就复制了对象列表的引用值,如果改变引用的值,则修改了原始的参数。 100 | 101 | ### 基本概念 102 | * **面:避免转义给字符串加哪个字母表示原始字符串?** 103 | ```python 104 | print(r'\abc') #输出原始字符串 \abc,只有在不对 \ 进行转义的时候用到 105 | ``` 106 | 107 | * **面:单引号,双引号,三引号的区别?** 108 | ```python 109 | name = 'tom'; sex = "male" #单引号和双引号单独使用没什么区别 110 | 111 | #下面才是单双引号设计的本质目的,这样里面 'A' 就可以不用添加 \,变为 \'A\' 112 | sentense = "We all know that 'A' and 'B' are two capital letters." 113 | 114 | #三个引号不经常用,用于原格式输出 115 | article = '''从哪来? 116 | 到哪去? 117 | 怎么去? 118 | ''' 119 | ``` 120 | 121 | * **面:Python2 和 Python3 的区别?** 122 | 123 | 1. Python3 使用 print 必须要以小括号包裹打印内容。 124 | 2. 2.x 中 range 函数返回一个列表,3.x 返回一个可迭代对象。 125 | 3. 2.x 中 sort 有 cmp 参数可接受两个参数的函数,3.x 中只有 key 接受一个参数的函数。 126 | 4. 2.x 默认编码:ascii 解决办法:在首行 # -*- encoding:utf-8-*-;3.x 中编码:utf-8。 127 | 5. 2.x 整数除法为 /,3.x 为 //。 128 | **** 129 | 130 | * **面:提高 Python 运行效率的方法?** 131 | * **答**:1.使用生成器,因为可以节约大量内存。2.循环代码优化,避免在循环中调用太多函数,可以用变量代替。 132 | **** 133 | 134 | * **面:列举 3 条以上 PEP8 编码规范?** 135 | 136 | 1. 不要在行尾加分号, 也不要用分号将两条命令放在同一行。 137 | 2. 不要使用反斜杠连接行,与左括号对齐,Python 会将圆括号的行隐式的连接起来。 138 | 3. 顶层函数和类的定义,前后用两个空行隔开。 139 | 4. 一般使用 4 个空格来缩进代码 140 | **** 141 | 142 | * **面:Python 中标识符的命名规则?** 143 | * **答**:Python 中的标识符可以是任意长度,但必须遵循以下命名规则: 144 | 1. 只能以下划线或者 A-Z/a-z 中的字母开头。 145 | 2. 其余部分只能使用 A-Z/a-z/0-9。 146 | 3. Python 标识符区分大小写。 147 | 4. 关键字不能作为标识符。 148 | **** 149 | 150 | * **面:IOError、AttributeError、ImportError、IndentationError IndexError、KeyError、SyntaxError、NameError分别代表什么异常?** 151 | * **答**:IOError:输入输出异常 152 | AttributeError:试图访问一个对象没有的属性 153 | ImportError:无法引入模块或包,基本是路径问题 154 | IndentationError:语法错误,代码没有正确的对齐 155 | IndexError:下标索引超出序列边界 156 | KeyError:试图访问你字典里不存在的键 157 | SyntaxError:Python 代码逻辑语法出错,不能执行 158 | NameError:使用一个还未赋予对象的变量 159 | **** 160 | 161 | * **面:Python中的身份运算符 is 和 == 的区别?** 162 | * **答**:is 判断两个对象 id 是否相同,== 判断两个对象值是否相同,具体参考[我的博客](https://blog.csdn.net/qq_29611345/article/details/99693298) 163 | **** 164 | 165 | * **面:Python 中运算符?** 166 | ```python 167 | [+,-,*,/,%,//,**] #算术运算符 168 | [>,>=,<,<=,==,!=] #比较(关系)运算符 169 | [&,|,~,^,<<,>>] #位运算符 170 | [in] #成员运算符 171 | [and,or,not] #逻辑运算符 172 | ``` 173 | **** 174 | 175 | * **面:在 Python 中使用多进制数字?** 176 | ```python 177 | bin(10) #转为二进制,python 中二进制表示0b1010 178 | oct(10) #转为八进制, 0o12 179 | hex(10) #转为十六进制,0xa 180 | ``` 181 | **** 182 | 183 | * **面:什么是元组的解封装?** 184 | ```python 185 | x,y,z = (1,2,3) #用变量取出 tuple 里面的值 186 | ``` 187 | ### 内部函数 188 | * **面:列出几种魔法方法并简要介绍用途?** 189 | * **答**:两边的下划线省略了, init :对象初始化方法 190 | new :创建对象时候执行的方法,单列模式会用到 191 | str :当使用 print 输出对象的时候,只要自己定义了 __ str __(self) 方法,那么就会打印从在这个方法中 return 的数据 192 | del :删除对象执行的方法 193 | **** 194 | 195 | * **面:Python 中的 sort 是用什么排序实现的,时间复杂度是多少?** 196 | * **答**:TimSort 算法是一种起源于 归并排序 和 插入排序 的混合排序算法,设计初衷是为了在真实世界中的各种数据中可以有较好的性能。基本工作过程是:1.扫描数组,确定其中的单调上升段和严格单调下降段,将严格下降段反转;2.定义最小基本片段长度,短于此的单调片段通过 插入排序集中为长于此的段;3.反复归并一些相邻片段,过程中避免归并长度相差很大的片段,直至整个排序完成,所用分段选择策略可以保证O(n log n)时间复杂性。 可以看到,原则上TimSort是归并排序,但小片段的合并中用了插入排序。 197 | 注意:2.x 中 cmp 可以接受两个参数的函数,3.x 中 key 接受一个参数的函数,需要用key = cmp_to_key(func) 进行模拟。 198 | **** 199 | 200 | * **面:Python 的高阶函数有哪些?** 201 | ```python 202 | #更多可参考 Python 常用函数 203 | map(lambda x: x*x ,[1,2,3]) 204 | zip([1,2,3],[4,5,6]) 205 | filter(lambda x: True if x%2==0 else False, [1,2,3,4]) 206 | import functools 207 | functools.reduce(lambda x,y: x+y, [1,2,3,4]) 208 | 209 | #问:中的 *args,**kwargs 什么意思? 210 | fun(*args,**kwargs) 211 | #主要用于函数定义,将不定数量的参数传递给fun函数,参数存在args中,args是一个元组(tuple)。允许你将不定长度的键值对作为参数传递给一个函数,参数则存储为一个字典(dict)。 212 | ``` 213 | **** 214 | 215 | * **面:Python 中生成随机整数、随机小数、0 - 1 之间小数方法?** 216 | ```python 217 | import random 218 | random.randint(a,b) #生成区间内的整数包括区间 [a,b] 219 | random.random() #生成 [0.0,1.0) 中的随机小数 220 | import numpy 221 | numpy.random.randn(5) #返回一个列表,生成 5 个随机数 222 | ``` 223 | 224 | ### 进阶问题 225 | * **面:简述 Python GIL 的概念, 以及它对 Python 多线程的影响?编写一个多线程抓取网页的程序,并阐明多线程抓取程序是否可比单线程性能有提升,并解释原因。?** 226 | * **答**:**目的**:为了利用多核,Python 开始支持多线程。而解决多线程之间数据完整性和状态同步,最简单方法自然就是加锁,于是有了 GIL 这把超级大锁。 227 | **影响**:开始当然是为了支持多线程,但后来发现对效率有很多影响。看到过一个测试, 228 | 一个循环 1 亿次的计数器函数。一个通过单线程执行两次,一个多线程执行。多线程竟然慢了大约45%。 229 | **原因**:按照 Python 社区的想法,为了让各个线程能够平均利用 CPU 时间,Python 会计算当前已执行。这种模式在只有一个 CPU 核心的情况下毫无问题。任何一个线程被唤起时都能成功获得到 GIL,但当 CPU 有多个核心的时候,问题就来了。从 release GIL 到 acquire GIL 之间几乎是没有间隙的。所以当其他在其他核心上的线程被唤醒时,大部分情况下主线程已经又再一次获取到 GIL了。这个时候被唤醒执行的线程只能白白的浪费 CPU 时间。[参考博客](https://www.cnblogs.com/SuKiWX/p/8804974.html) 230 | **** 231 | 232 | * **面:什么是猴子补丁?** 233 | * **答**:在运行时动态修改类和模块,这种场景也比较多,比如我们引用团队通用库里的一个模块,又想丰富模块的功能,除了继承之外也可以考虑用 Monkey Patch。个人感觉 Monkey Patch 带来了便利的同时也有搞乱源代码优雅的风险。 234 | **** 235 | 236 | * **面:如何在 Python 中管理内存?** 237 | * **答**:Python 用一个私有堆内存空间来放置所有对象和数据结构,我们无法访问它,由解释器来管理它,不过使用一些核心 API,我们可以访问一些 Python 内存管理工具控制内存分配。 238 | **** 239 | 240 | * **面:当退出 Python 时是否释放所有内存分配?** 241 | * **答**:答案是否定的。那些具有对象循环引用或者全局命名空间引用的变量,在 Python 退出是往往不会被释放另外不会释放 C 库保留的部分内容。 242 | **** 243 | 244 | * **面:一句话解释什么样的语言能够用(decorator)装饰器?** 245 | * **答**:函数可以作为参数传递的语言,可以使用装饰器 246 | ```python 247 | def prin_fun_name(func): 248 | def new_method(*args, **kw):#适用于任意参数的装饰器 249 | print('used function' + func.__name__) 250 | return func(*args, **kw) 251 | return new_method 252 | ``` 253 | **** 254 | 255 | * **面:请解释 Python 中的闭包?** 256 | * **答**:1. 假如你需要写一个带参数的装饰器,那么一般都会生成闭包。 257 | 2. 我个人认为,闭包存在的意义就是它夹带了外部变量(私货),如果它不夹带私货,它和普通的函数就没有任何区别。[参考博客](https://betacat.online/posts/2016-10-23/python-closure/) 258 | **** 259 | 260 | * **面:解释 Python 中 metaclass 关键字?** 261 | * **答**:[参考博客](https://www.cnblogs.com/ArsenalfanInECNU/p/9036407.html) 262 | **** 263 | 264 | * **面:?** 265 | * **答**: 266 | 267 | * **面:?** 268 | * **答**: 269 | 270 | * **面:?** 271 | * **答**: 272 | 273 | * **面:?** 274 | * **答**: 275 | 276 | * **面:?** 277 | * **答**: 278 | -------------------------------------------------------------------------------- /notes/Python/Python3-爬虫基本语法.md: -------------------------------------------------------------------------------- 1 | # Python3-爬虫基本语法 2 | 3 | **Python 爬虫基本语法** 4 | ```python 5 | #爬取糗事百科热门段子,爬取地址URL: http://www.qiushibaike.com/hot/page/1 6 | from bs4 import BeautifulSoup 7 | import urllib.request 8 | import csv 9 | import re 10 | import time 11 | #防止错误:HTTP Error 403: Forbidden 12 | headers = {'Accept': '*/*', 13 | 'Accept-Language': 'en-US,en;q=0.8', 14 | 'Cache-Control': 'max-age=0', 15 | 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36', 16 | 'Connection': 'keep-alive', 17 | 'Referer': 'http://www.baidu.com/' 18 | } 19 | 20 | link = 'http://www.qiushibaike.com/hot/page/' 21 | #page = 1 22 | for page in range(2, 14): 23 | time.sleep(1) 24 | print(page) 25 | url = link+str(page) 26 | req = urllib.request.Request(url, headers=headers) 27 | html_doc = urllib.request.urlopen(req) 28 | soup = BeautifulSoup( 29 | html_doc,#HTML文档字符串 30 | 'html.parser'#HTML解析器 31 | #from_encoding = 'utf-8'#bs会自动检测编码格式,然后转换为Unicode,但是如果指定可以加快速度。 32 | ) 33 | #print(soup.prettify()) 34 | #articles = soup.find_all() 35 | articles = soup.find_all(class_="content") 36 | #注意这里class有下划线。 37 | #articles = soup.find_all('div',class_=re.compile("^article block untagged mb15.*$")) 38 | with open(r"comment.csv","a",encoding='utf-8',newline="") as csv_file: 39 | csv_writer = csv.writer(csv_file, delimiter=',') 40 | for article in articles: 41 | temp = article.select('span')[0] 42 | temp = re.sub('\n','',str(temp))#先去掉换行符,再去掉标签。 43 | csv_writer.writerow([re.sub(r'<.*?>','\n',str(temp))]) 44 | 45 | ``` 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | **基本库** 54 | beautifulsoup 中文文档:https://beautifulsoup.readthedocs.io/zh_CN/v4.4.0/ 55 | 参考:https://cuiqingcai.com/1319.html 56 | 57 | ```python 58 | from bs4 import BeautifulSoup 59 | import urllib.request 60 | import re 61 | ``` 62 | 63 | 64 | **向服务器发送请求** 65 | 66 | 向服务器发送获取页面的请求-普通: 67 | ```python 68 | html_doc = urllib.request.urlopen(URL) 69 | ``` 70 | 71 | 疑问:返回的是什么? 72 | 参考:https://blog.csdn.net/bo_mask/article/details/76067790 73 | 74 | 向服务器发送获取页面的请求-加入headers验证: 75 | req = urllib.request.Request(url, headers=headers) 76 | html_doc = urllib.request.urlopen(req) 77 | 78 | **编码** 79 | 参考beautiful Soup官方文档。 80 | 任何HTML或XML文档都有自己的编码方式,比如ASCII 或 UTF-8,但是使用Beautiful Soup解析后,文档都被转换成了Unicode: 81 | 82 | 对于返回的html文档,可以用BeautifulSoup对文档进行解码,方便后序操作。 83 | soup = BeautifulSoup(html_doc, 'html.parser',from_) 84 | soup = BeautifulSoup( 85 | html_doc,#HTML文档字符串 86 | 'html.parser'#HTML解析器 87 | #注意:这里的解析器【html.parser】python内置的解析器,还有多种更好的解析器。 88 | #from_encoding = 'utf-8' 89 | #bs会自动检测编码格式,然后转换为Unicode,但是如果指定可以加快速度,指定错了就唧唧,所以我不加了。 90 | ) 91 | 92 | 93 | **BeautifulSoup的方法** 94 | #获取某个属性的整个标签的内容。 95 | soup.find_all(name, attrs) 96 | print(soup.prettify())#html标准化格式输出。 97 | #找所有img标签,属性为data-original,内容我里面的正则表达式。重点,对于有下划线的属性处理,放在字典里。 98 | 99 | soup.find_all(id='link2')#找id是link2的所有标签【id】查找 100 | soup.find("form", class_="edit_checkout")#寻找form下类名为这个的标签。【标签、类名】查找 101 | data_soup.find_all(attrs={"value": "data-foo"})【其他属性】查找 102 | 103 | 正则查找:比较难 104 | soup.find_all('img', attrs={'data-original':re.compile("^//pic.qiantucdn.com")}) 105 | soup.find_all(re.compile("^b"))#找所有b开头的标签 106 | articles = soup.find_all('div',class_=re.compile("^article block untagged mb15.*$")) 107 | 写了快两个小时喽。 108 | 注意:按照CSS类名搜索tag的功能非常实用,但标识CSS类名的关键字class在Python中是保留字,使用class做参数会导致语法错误.从Beautiful Soup的4.1.1版本开始,可以通过class_参数搜索有指定CSS类名的tag。 109 | 110 | **CSS选择器** 111 | 标签名不加任何修饰,类名前加点,id名前加 #,在这里我们也可以利用类似的方法来筛选元素,用到的方法是 soup.select(),返回类型是 list。 112 | 这里注意:可以选择多次,返回的也是soup对象,但是不要从顶级标签开始选择。 113 | 114 | soup.select('title') #通过标签名查找 115 | soup.select('.sister')#通过类名查找 116 | soup.select('#link1')#通过id名查找 117 | soup.select('p #link1')#组合查找,标签为p的id为link1的标签。 118 | 119 | soup.select("head > title")#子标签查找,head下的title标签。#箭头左右要打空格,不然字符串识别不出来。 120 | house_url = house.select(".listCon > h3 > a")[0]['href']#获取属性 121 | house_title = house.select(".listCon > h3 > a")[0].string#获取内容 122 | 123 | 查找时还可以加入属性元素,属性需要用中括号括起来,注意属性和标签属于同一节点,所以中间不能加空格,否则会无法匹配到。 124 | soup.select('a[class="sister"]')#属性查找。 125 | articles = soup.select('div[class^="article block untagged mb15.*$"]') 126 | articles = soup.select('div[class^="article block untagged mb15"] > a > div > span') 127 | #使用了正则,虽然很隐蔽!就是必须以字符串为开头的类名就可以。 128 | 129 | **保存内容到本地csv文件** 130 | #open(文件地址和名称,有文件就打开-覆盖写入-无就创建,在添加新纪录时默认会添加一个空行) 131 | #注意绝对路径和相对路径的打开参考:https://blog.csdn.net/m0_37693335/article/details/81474995 132 | 133 | import csv#注意添加编码格式,否则可能出现gbk编码错误等。 134 | with open("rent.csv","w",encoding='utf-8', newline="") as csv_file: 135 | csv_writer = csv.writer(csv_file, delimiter=',') 136 | #打开文件的作用域,所有关于文件的读写操作都要在这里面写了,不要出去了。注意缩进。 137 | csv_writer.writerow(lines) 138 | 139 | 140 | 141 | **正则表达式** 142 | ^: 匹配字符串的开头--这里是从头匹配,说明只可能有一个。 143 | $:匹配字符串的末尾,这里是到文档末尾,说明也只能有一个。 144 | .:匹配任意字符 145 | ?:匹配0或1个。 146 | +:匹配1个或多个。 147 | 148 | re.match(pattern, string, flags=0): 149 | re.search(pattern, string, flags=0) 150 | re.sub(pattern, repl, string, count=0, flags=0):替换字符串。 151 | 参考:https://www.cnblogs.com/nkwy2012/p/6548812.html 152 | 153 | re.compile(pattern[,flags]):编译一个正则表达式,给其他函数用。 154 | 区别: re.match只匹配字符串的开始,如果字符串开始不符合正则表达式,则匹配失败,函数返回None;而re.search匹配整个字符串,直到找到一个匹配。实例: 155 | 156 | 157 | 158 | 159 | ### 出错记录 160 | 错误:HTTP Error 403: Forbidden 161 | 解决:添加headers,网站有基本的反爬措施,验证请求的硬件和版本信息。 162 | 参考:https://www.cnblogs.com/lixiaolun/p/4773433.html 163 | 164 | 错误:socket.gaierror: [Errno 11004] getaddrinfo failed 165 | 解决:链接不存在或者链接错误了。 166 | 167 | 爬取糗事百科时的错误。 168 | 错误:Remote end closed connection without response 169 | 解决:利用 urllib 发起的请求,UA 默认是 Python-urllib/3.5 而在 chrome 中访问 UA 则是 User-Agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36,因为服务器根据 UA 来判断拒绝了 python 爬虫,其实就是改变headers里面的内容。 -------------------------------------------------------------------------------- /notes/操作系统.md: -------------------------------------------------------------------------------- 1 | # 操作系统 2 | 3 | ### 进程类 4 | 5 | * **问:简述线程和进程的区别?** 6 | **进程** 7 | 1、操作系统进行资源分配和调度的基本单位,多个进程之间相互独立; 8 | 2、稳定性好,如果一个进程崩溃,不影响其他进程,但是进程消耗资源大,开启的进程数量有限制; 9 | **线程** 10 | 1、CPU进行资源分配和调度的基本单位,线程是进程的一部分,是比进程更小的能独立运行的基本单位,一个进程下的多个线程可以共享该进程的所有资源; 11 | 2、如果IO操作密集,则可以多线程运行效率高,缺点是如果一个线程崩溃,都会造成进程的崩溃; 12 | 3、线程自己不拥有系统资源; 13 | 4、一个线程可以创建和撤销另一个线程; 14 | **** 15 | * **问:一个程序至少有一个进程,一个进程至少有一个线程?** 16 | * **答**:错误,一个程序至少有一个进程,不对。程序放在硬盘里没有执行,不会有对应的进程。程序(program)只能有一个进程,一个进程就是一个程序。有人说,我打开一个程序,比如chrome,有十多个进程呢,这是咋回事。那就是十多个程序,操作系统给他们分配了彼此独立的内存,相互执行不受彼此约束,分配同样时间的CPU。对于用户而言,他们是一个整体,我们通常称之为应用程序(application)。对于计算机而言,一个进程就是一个程序,多个进程(比如一个浏览器的多个进程)对计算机而言就是多个不同的程序,它不会把它们理解为一个完整的“程序”。 17 | **** 18 | **问:进程调度方式?** 19 | 20 | **问:多线程优点?** 21 | **** 22 | * **问:内存泄露 和 内存溢出 的联系与区别?** 23 | **一、内存溢出(memory overflow)** 24 | 指程序在申请内存时,没有足够的内存供申请者使用,或者说,给了你一块存储 int 类型数据的存储空间,但是你却存储 long 类型的数据,就会导致内存不够用,报错 OOM,即出现内存溢出的错误。 25 | **二、内存泄漏(memory leak)** 26 | 1、内存泄漏是指程序中已动态分配的堆内存由于某种原因未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统奔溃等严重后果; 27 | 2、一次内训泄漏似乎不会有大的影响,但内存泄漏后堆积的结果就是内存溢出; 28 | 3、内存泄漏具有隐蔽性,积累性的特征,比其他内存非法访问错误更难检测。这是因为内存泄漏产生的原因是内存块未被释放,属于遗漏型缺陷而不是过错型缺陷。此外,内存泄漏不会直接产生可观察的错误,而是逐渐积累,降低系统的整体性性能; 29 | 4、如何有效的进行内存分配和释放,防止内存泄漏,是软件开发人员的关键问题,比如一个服务器应用软件要长时间服务多个客户端,若存在内存泄漏,则会逐渐堆积,导致一系列严重后果; 30 | **** 31 | * **问:写个LRU算法?** 32 | * **答**:LRU(Least Recently Used),即最近最少使用算法: 33 | 设计并实现了一个最近最少使用(LRU)缓存的数据结构,它应该支持以下操作 get 和 set: 34 | get(key)——如果键存在于缓存中,则获得键值(总是正数),否则返回-1; 35 | set(key, value)——如果键不存在,则设置或插入值。当缓存达到其容量时,应在插入新项之前使最近最少使用的项无效; 36 | 分析:LRU 缓存可使用一个 HashMap 和双向链表实现。HashMap,使得get的时间是O(1);双向链表使节点添加/删除操作O(1)。 37 | ```java 38 | /** 39 | * LRU Cache 40 | * LRU缓存机制 41 | */ 42 | 43 | public class Solution2 { 44 | public class LRUCache { 45 | private class Node { 46 | int key; 47 | int value; 48 | LRUCache.Node prev; 49 | LRUCache.Node next; 50 | 51 | Node(int k, int v) { 52 | this.key = k; 53 | this.value = v; 54 | } 55 | Node() { 56 | this.key = 0; 57 | this.value = 0; 58 | } 59 | } 60 | 61 | private int capacity; 62 | private int count; 63 | private LRUCache.Node head; 64 | private LRUCache.Node tail; 65 | private Map map; 66 | // ATTN: the value should be Node type! This is the whole point of having a class called Node! 67 | 68 | public LRUCache(int capacity) { 69 | this.capacity = capacity; 70 | this.count = 0;// we need a count to keep track of the number of elements in the cache so 71 | // that we know when to evict the LRU one from the cache 72 | this.map = new HashMap(); 73 | head = new LRUCache.Node(); 74 | tail = new LRUCache.Node(); 75 | head.next = tail; 76 | tail.prev = head; 77 | } 78 | 79 | public int get(int key) { 80 | LRUCache.Node node = map.get(key); 81 | // HashMap allows value to be null, this is superior than HashTable! 82 | if (node == null) { 83 | return -1; 84 | } else { 85 | remove(node); 86 | add(node); 87 | return node.value; 88 | } 89 | } 90 | 91 | public void set(int key, int value) { 92 | LRUCache.Node node = map.get(key); 93 | if (node == null) { 94 | node = new LRUCache.Node(key, value); 95 | map.put(key, node); 96 | add(node); 97 | count++; 98 | 99 | if (count > capacity) { 100 | 101 | LRUCache.Node toDelete = tail.prev; 102 | map.remove(toDelete.key); 103 | remove(toDelete); 104 | count--; 105 | } 106 | } else { 107 | remove(node); 108 | node.value = value; 109 | add(node); 110 | } 111 | } 112 | 113 | private void remove(LRUCache.Node node) { 114 | LRUCache.Node next = node.next; 115 | LRUCache.Node prev = node.prev; 116 | prev.next = next; 117 | next.prev = prev; 118 | } 119 | 120 | private void add(LRUCache.Node node) { 121 | // ATTN: we'll always add the node into the first position: head.next!!!! 122 | LRUCache.Node next = head.next; 123 | head.next = node; 124 | node.next = next; 125 | node.prev = head; 126 | next.prev = node; 127 | } 128 | } 129 | } 130 | ``` 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /notes/数据库/数据库-MySQL-基本概念.md: -------------------------------------------------------------------------------- 1 | # 数据库-MySQL-底层概念 2 | 3 | 事务的特性 4 | 数据库引擎 5 | 使用索引 6 | 使用视图 7 | 存储过程 8 | 9 | ## 数据库简介 10 | RDBMS (Relational Database Management System,关系数据库管理系统) 11 | SQL (Structured Query Language,结构化查询语言) 12 | JDBC[ java数据库连接(java DataBase Connectivity)]:由一组使用java语言编写的类与接口组成,可以为多种关系数据库提供统一访问。 13 | 14 | **MySQL**: 是关系型数据库,目前属于 Oracle 旗下产品。MySQL 最流行的关系型数据库管理系统之一,选择如何存储和检索你的数据,这种灵活性是 MySQL 为什么如此受欢迎的主要原因,其中内置了多种存储引擎,是开源的,所以你不需要支付额外的费用;支持多线程,充分利用 CPU 资源;为多种编程语言提供了 API;可以处理拥有上千万条记录的大型数据库; 15 | **InnoDB** :5.5 版本后 Mysql 的默认数据库引擎,事务型数据库的首选引擎,支持 ACID 事务,支持行级锁定,还有其他一些关系型数据库,最简单的是 Excel,如 SQLite 很小,可以随时移动。微软的 SQL Server,Oracle 出品的 MySQL。 16 | 17 | **NoSQL[非关系型数据库]**:MongoDB,Redis[内存存储]等都是非关系型数据库。 18 | 随着互联网 web2.0 网站的兴起,传统的关系数据库在应付 web2.0 网站,特别是超大规模和高并发的 SNS 类型的 web2.0 纯动态网站已经显得力不从心,暴露了很多难以克服的问题,而非关系型的数据库则由于其本身的特点得到了非常迅速的发展。NoSQL 数据库的产生就是为了解决大规模数据集合多重数据种类带来的挑战,尤其是大数据应用难题。 19 | **主要分四大类**:键值(Key-Value)存储数据库、列存储数据库、文档型数据库、图形(Graph)数据库 20 | **** 21 | * **问:MySQL 与 SQL Server 的区别?** 22 | [MySQL 与 SQL Server 的区别](https://www.cnblogs.com/hhx626/p/6010369.html) 23 | 24 | * **问:drop、delete 与 truncate分别在什么场景之下使用?** 25 | 这三个字段都和删除表有关,首先 drop 是删除整个表格,delete 是删除表中的数据,truncate 是还原表。 26 | 有这样一个场景,需要删除表中的说有数据,我们可以 delete from tableName; 效果是一条一条删除表中的数据,且 id 不会重置,不是我们想要的效果。那么我们就会想到使用 truncate tableName; 相当于 drop table,然后create table。但这时又有一种清空,truncate 不能使用,公司线上规定不能使用 drop 表相关的操作,包括 truncate,这个时候我们就只能使用 delete from tableName; 然后再加一条 alter table tableName auto_increment=1; 27 | 28 | * **问:如何优化数据库、如何实现乐观锁、三个范式、主键 超键 候选键 外键、drop、delete 与 truncate分别在什么场景之下使用?** 29 | 30 | 31 | 32 | # 事务的特性 33 | 34 | 35 | * **问:数据库事务的四大特性(ACID)以及事务的隔离级别(高频)?** 36 | * **答**:如果一个数据库声称支持事务的操作,那么该数据库必须要具备以下四个特性: 37 | 38 | **一、原子性(Atomicity)** 39 |   原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚。 40 | 41 | **二、一致性(Consistency)** 42 |   拿转账来说,假设用户 A 和用户 B 两者的钱加起来一共是 5000,那么不管 A 和 B 之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是 5000,这就是事务的一致性。 43 | 44 | **三、隔离性(Isolation)** 45 |   对于任意两个并发的事务 T1 和 T2,在事务 T1 看来,T2 要么在 T1 开始之前就已经结束,要么在 T1 结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行。 46 |   关于事务的隔离性数据库提供了多种隔离级别,稍后会介绍到。这里需要注意的是,上面提到的是第一种隔离级别,隔离性最高,但同样效率最低,后面还会后三种隔离级别。 47 | 48 | **四、持久性(Durability)** 49 |   持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。 50 | 51 | **隔离性是当多个用户(事务)并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。如果不考虑事务的隔离性,会发生哪几种问题:** 52 | 53 | **1. 脏读[一事务在修改,二事务读,一事务回滚]** 54 | 例如:用户 A 向用户 B 转账 100 元,对应 SQL 命令如下 55 | update account set money=money+100 where name=’B’; (此时A通知B) 56 | update account set money=money - 100 where name=’A’; 57 |   当只执行第一条 SQL 时,A 通知 B 查看账户,B 发现确实钱已到账(此时即发生了脏读),而之后无论第二条 SQL 是否执行,只要该事务不提交,则所有操作都将回滚,那么当 B 以后再次查看账户时就会发现钱其实并没有转。脏读是指**在一个事务处理过程里读取了另一个未提交的事务中的数据**。当一个事务正在多次修改某个数据,而在这个事务中这多次的修改都还未提交,这时一个并发的事务来访问该数据,就会造成两个事务得到的数据不一致。所以 B 在 A 未完成事务的情况下不应该读到任何数据。 58 | 59 | **2.不可重复读【一事务在读,二事务修改并提交了数据,一事务在读,注意和脏读的区别】** 60 |   例如事务 T1 在读取某一数据,而事务 T2 立马修改了这个数据并且提交事务给数据库,事务 T1再次读取该数据就得到了不同的结果,发生了不可重复读。 61 |   不可重复读是指在对于数据库中的某个数据,一个事务范围内多次查询却返回了不同的数据值,这是由于在查询间隔,被另一个事务修改并提交了。 62 |   在某些情况下,不可重复读并不是问题,比如我们多次查询某个数据当然以最后查询得到的结果为主。但在另一些情况下就有可能发生问题。 63 |   **不可重复读和脏读的区别是:脏读是某一事务读取了另一个事务未提交的脏数据,而不可重复读则是读取了前一事务提交的数据。** 64 | 65 | **3.虚读(幻读)【对批量数据处理的过程中,另一事务做了修改,注意和不可重复读的区别】** 66 |   幻读是事务非独立执行时发生的一种现象。例如事务 T1 对一个表中所有的行的某个数据项做了从“1”修改为“2”的操作,这时事务 T2 又对这个表中插入了一行数据项,而这个数据项的数值还是为“1”并且提交给数据库。而操作事务 T1 的用户如果再查看刚刚修改的数据,会发现还有一行没有修改,其实这行是从事务 T2 中添加的,就好像产生幻觉一样,这就是发生了幻读。 67 |   **幻读和不可重复读都是读取了另一条已经提交的事务(这点就脏读不同),所不同的是不可重复读查询的都是同一个数据项,而幻读针对的是一批数据整体(比如数据的个数)。** 68 | **** 69 | **现在来看看 MySQL 数据库为我们提供的四种隔离级别**: 70 | [如何实现](https://blog.csdn.net/qq_34462387/article/details/82432593) 71 | **① Serializable (串行化):可避免脏读、不可重复读、幻读的发生。** 72 | 提供严格的事务隔离。它要求事务序列化执行,事务只能一个接着一个地执行,不能并发执行。此隔离级别可有效防止脏读、不可重复读和幻读。但这个级别可能导致大量的超时现象和锁竞争,在实际应用中很少使用。 73 | 74 | **② Repeatable read (可重复读):可避免脏读、不可重复读的发生。** 75 | 一个事务在执行过程中,可以访问其他事务成功提交的新插入的数据,但不可以访问成功修改的数据。读取数据的事务将会禁止写事务(但允许读事务),写事务则禁止任何其他事务。此隔离级别可有效防止不可重复读和脏读。 76 | 77 | **③ Read committed (读已提交):可避免脏读的发生。** 78 | 一个事务在执行过程中,既可以访问其他事务成功提交的新插入的数据,又可以访问成功修改的数据。读取数据的事务允许其他事务继续访问该行数据,但是未提交的写事务将会禁止其他事务访问该行。此隔离级别可有效防止脏读。 79 | 80 | **④ Read uncommitted (读未提交):最低级别,任何情况都无法保证。一个事务在执行过程中,既可以访问其他事务未提交的新插入的数据,又可以访问未提交的修改数据。如果一个事务已经开始写数据,则另外一个事务不允许同时进行写操作,但允许其他事务读此行数据。此隔离级别可防止丢失更新。** 81 |    82 |   以上四种隔离级别最高的是Serializable级别,最低的是Read uncommitted级别,当然级别越高,执行效率就越低。像Serializable这样的级别,就是以锁表的方式(类似于Java多线程中的锁)使得其他的线程只能在锁外等待,所以平时选用何种隔离级别应该根据实际情况。 83 | 84 |   在 MySQL 数据库中,支持上面四种隔离级别,默认的为 Repeatable read (可重复读);而在Oracle 数据库中,只支持 Serializable (串行化)级别和 Read committed (读已提交)这两种级别,其中默认的为 Read committed 级别。 85 | **记住:设置数据库的隔离级别一定要是在开启事务之前!** 86 |   如果是使用 JDBC 对数据库的事务设置隔离级别的话,也应该是在调用 Connection 对象的setAutoCommit(false) 方法之前。调用 Connection 对象的 setTransactionIsolation(level) 即可设置当前链接的隔离级别,至于参数 level,可以使用 Connection 对象的字段: 87 | 在JDBC中设置隔离级别的部分代码: 88 | 89 | **** 90 | * **问:事务和数据库连接池有什么关系?** 91 | [参考博客](https://www.cnblogs.com/panxuejun/p/5920845.html) 92 | 将线程和连接绑定,保证事务能统一执行 93 | 94 | ```java 95 | Class.forName("com.mysql.jdbc.Driver"); 96 | /* 97 | 一次调用只能获得一个 connection,并不能满足高并发情况。 98 | 因为connection不是线程安全的,一个connection对应的是一个事务。 99 | 数据库每次连接都很浪费cpu和内存资源,因此诞生了数据库连接池(线程池)。 100 | 数据库连接池负责分配,管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接, 101 | 而不是重新建立一个。 102 | */ 103 | java.sql.Connection conn = DriverManager.getConnection(jdbcUrl); 104 | 105 | /*数据库连接池(注意这里是线程) 106 | 注意pool.getConnection(),都是先从threadlocal里面拿的,如果threadlocal里面有, 107 | 则用,保证线程里的多个dao操作,用的是同一个connection,以保证事务。 108 | 如果新线程,则将新的connection放在threadlocal里,再get给到线程。 109 | */ 110 | /*问:JTA事务和普通事务的区别? 111 | 答:简单的说 jta是多库的事务 jdbc是单库的事务 112 | */ 113 | ``` 114 | 115 | # 数据库引擎 116 | 117 | 你能用的数据库引擎取决于 mysql 在安装的时候是如何被编译的。要添加一个新的引擎,就必须重新编译 MYSQL。 118 | 在缺省情况下,MYSQL 支持三个引擎:ISAM、MYISAM和HEAP。另外两种类型INNODB和BERKLEY(BDB),也常常可以使用。[参考博客](https://www.cnblogs.com/0201zcr/p/5296843.html) 119 | **ISAM** 120 | ISAM 是一个定义明确且历经时间考验的数据表格管理方法,它在设计之时就考虑到数据库被查询的次数要远大于更新的次数。因此,ISAM 执行读取操作的速度很快,而且不占用大量的内存和存储资源。ISAM 的两个主要不足之处在于,**它不支持事务处理,也不能够容错:如果你的硬盘崩溃了,那么数据文件就无法恢复了**。如果你正在把 ISAM 用在关键任务应用程序里,那就必须经常备份你所有的实时数据,通过其复制特性,MYSQL 能够支持这样的备份应用程序。 121 | 122 | **INNODB 和 BERKLEYDB(BDB)** 123 | 数据库引擎都是造就 MYSQL 灵活性的技术的直接产品,这项技术就是 MYSQL++ API。在使用MYSQL 的时候,你所面对的每一个挑战几乎都源于 ISAM 和 MYISAM 数据库引擎不支持事务处理也不支持外来键。尽管要比 ISAM 和 MYISAM 引擎慢很多,但是 INNODB 和 BDB 包括了对事务处理和外来键的支持,这两点都是前两个引擎所没有的。如前所述,如果你的设计需要这些特性中的一者或者两者,那你就要被迫使用后两个引擎中的一个了。 124 | 125 | **Innodb引擎** 126 |   Innodb 引擎提供了对数据库 ACID 事务的支持,并且实现了 SQL 标准的四种隔离级别,关于数据库事务与其隔离级别的内容请见数据库事务与其隔离级别这篇文章。该引擎还提供了行级锁和外键约束,它的设计目标是处理大容量数据库系统,它本身其实就是基于 MySQL 后台的完整数据库系统,MySQL 运行时 Innodb 会在内存中建立缓冲池,用于缓冲数据和索引。但是该引擎不支持FULLTEXT 类型的索引,而且它没有保存表的行数,当 SELECT COUNT( * ) FROM TABLE 时需要扫描全表。当需要使用数据库事务时,该引擎当然是首选。由于锁的粒度更小,写操作不会锁定全表,所以在并发较高时,使用 Innodb 引擎会提升效率。但是使用行级锁也不是绝对的,如果在执行一个 SQL 语句时 MySQL 不能确定要扫描的范围,InnoDB 表同样会锁全表。 127 | 128 | **MyIASM引擎** 129 |   MyIASM 没有提供对数据库事务的支持,也不支持行级锁和外键,因此当 INSERT(插入)或UPDATE(更新)数据时即写操作需要锁定整个表,效率便会低一些。不过和 Innodb 不同,MyIASM 中存储了表的行数,于是 SELECT COUNT( * ) FROM TABLE 时只需要直接读取已经保存好的值而不需要进行全表扫描。如果表的读操作远远多于写操作且不需要数据库事务的支持,那么 MyIASM 也是很好的选择。 130 | **** 131 | * **问:Innodb的索引实现?** 132 | * **答**:B +树的形式保存。 133 | 134 | * **问:数据库引擎(Innodb)的事务支持粒度?** 135 | * **答**:行级锁。 136 | 137 | * **问:MySQL 中 myisam 与 innodb 的区别,至少 5 点?** 138 | * **答**:不支持事务,行级锁、外键、但返回表的行数更快、 139 | 140 | # 索引 141 | 142 | * **问:索引是什么?有什么作用以及优缺点(高频)?** 143 | 1、在数据之外,数据库系统还维护着满足特定查找算法的数据结构,这些数据结构以某种方式引用(指向)数据,这样就可以在这些数据结构上实现高级查找算法。这种数据结构,就是索引; 144 | 2、索引用来快速地寻找那些具有特定值的记录,所有 MySQL 索引都以 B+ 树的形式保存; 145 | 3、在 MySQL 中,当你建立主键时,索引同时也已建立起来了; 146 | 4、MySQL 提供多种索引类型供选择:唯一性索引、主键索引、聚集索引、非聚集索引; 147 | 148 | **唯一索引**:索引列的值必须唯一,但允许有空值。如果是组合索引,则列值的组合必须唯一。 149 | **聚集索引(Clustered)**:对于一张表来说,聚集索引只能有一个,因为数据真实的物理存储顺序就是按照聚集索引存储的。 150 | **非聚集索引(Non-clustered)**:B+Tree 的数据域存储的内容为实际数据的地址,也就是说它的索引和实际的数据是分开的,只不过是用索引指向了实际的数据,这种索引就是所谓的非聚集索引。 151 | **** 152 | * **问:主键就是聚集索引吗?** 153 | * **问:主键 和 索引的区别?** 154 | 1. 主键一定是唯一性索引,唯一性索引并不一定就是主键; 155 | 2. 一个表中可以有多个唯一性索引,但只能有一个主键; 156 | 3. 主键列不允许空值,而唯一性索引列允许空值;  157 | 4. 索引可以提高查询的速度。 主键和索引都是键,不过主键是逻辑键,索引是物理键,意思就是主键不实际存在,而索引实际存在在数据库中; 158 | 159 | 唯一索引能极大的提高查询速度,而且还有唯一约束的作用,一般索引,只能提高 30% 左右的速度,经常插入,修改,应在查询允许的情况下,尽量减少索引,因为添加索引,插入,修改等操作,需要更多的时间。如果在我们的查询条件使用了函数,那么索引就不可用了。可以用建立函数索引的方式,来解决这个问题。 160 | 161 | **优点** 162 | 1、如果没有索引,执行查询时 MySQL 必须从第一个记录开始扫描整个表的所有记录,直至找到符合要求的记录。表里面的记录数量越多,这个操作的代价就越高。如果作为搜索条件的列上已经创建了索引,MySQL 无需扫描任何记录即可迅速得到目标记录所在的位置。如果表有 1000 个记录,通过索引查找记录至少要比顺序扫描记录快100倍。 163 | 164 | **缺点** 165 | 1、为表设置索引要付出代价的:一是增加了数据库的存储空间,二是在插入和修改数据时要花费较多的时间(因为索引也要随之变动)。 166 | **** 167 | * **问:什么时候【要】创建索引?** 168 | (1)表经常进行 SELECT 操作; 169 | (2)表很大(记录超多),记录内容分布范围很广; 170 | (3)列名经常在 WHERE 子句或连接条件中出现; 171 | 172 | * **问:什么时候【不要】创建索引?** 173 | (1)表经常进行 INSERT/UPDATE/DELETE 操作; 174 | (2)表很小(记录超少); 175 | (3)列名不经常作为连接条件或出现在 WHERE 子句中; 176 | 177 | ```mysql 178 | /*创建表时,不能在同一个字段上建立两个索引(主键默认建立唯一索引), 179 | 在需要经常查询的字段上建立索引。 180 | */ 181 | -- 显示这个表格的所有索引 182 | show index from students; 183 | select * from students where name like 'a%'; 184 | 185 | -- 这是最基本的索引,它没有任何限制。 186 | CREATE INDEX nameIndex on students(NAME(8)); 187 | 188 | -- 这是唯一索引,索引列的值必须唯一,但允许有空值。 189 | CREATE unique INDEX nameIndex on students(NAME(8)); 190 | 191 | -- 删除索引 192 | drop index nameIndex on students; 193 | ``` 194 | 195 | # 视图 196 | [参考博客](https://blog.csdn.net/coolwriter/article/details/79794309) 197 | [参考博客](https://www.cnblogs.com/0201zcr/p/5296843.html) 198 | 199 | 200 | 201 | * **问:什么是视图?以及视图的使用场景有哪些?** 202 | **视图**:是一种基于数据表的一种虚表,与包含数据的表不一样,视图只包含使用时动态检索数据的查询;不包含任何列或数据。使用视图可以简化复杂的 sql 操作,隐藏具体的细节,保护数据;视图创建后,可以使用与表相同的方式利用它们。 203 | **优点**:重用SQL语句,简化复杂的SQL操作,使用表的组成部分而不是整个表,保护数据-可以授予用户表部分的访问权限而不是整个表,更改数据格式-视图可以重新构成不同表不同部分的表示。 204 | 视图不能被索引,也不能有关联的触发器或默认值,如果视图本身内有 order by 则对视图再次order by 将被覆盖。 205 | (1)视图是一种虚表; 206 | (2)视图建立在已有表的基础上, 视图赖以建立的这些表称为基表; 207 | (3)向视图提供数据内容的语句为 SELECT 语句,可以将视图理解为存储起来的 SELECT 语句; 208 | (4)视图向用户提供基表数据的另一种表现形式; 209 | (5)视图没有存储真正的数据,真正的数据还是存储在基表中; 210 | (6)程序员虽然操作的是视图,但最终视图还会转成操作基表; 211 | (7)一个基表可以有0个或多个视图; 212 | 有的时候,我们可能只关系一张数据表中的某些字段,而另外的一些人只关系同一张数据表的某些字段,那么把全部的字段都都显示给他们看,这是不合理的。我们应该做到:他们想看到什么样的数据,我们就给他们什么样的数据,一方面就能够让他们只关注自己的数据,另一方面,我们也保证数据表一些保密的数据不会泄露出来,我们在查询数据的时候,常常需要编写非常长的SQL语句,几乎每次都要写很长很长,.上面已经说了,视图就是基于查询的一种虚表,也就是说,视图可以将查询出来的数据进行封装。那么我们在使用的时候就会变得非常方便。 213 | **值得注意的是**:使用视图可以让我们专注与逻辑,但不提高查询效率 214 | 215 | ### 视图操作 216 | 可以将视图的操作理解为预处理,将很多复杂的逻辑和表进行预处理,构建成一个简单的表,然后在此表的基础上进行操作。 217 | 但记住很多视图是不可更新的,如有分组(group by,having),联结,子查询,并,函数(max,count,sum,distinct)等,好像限制很多,但其实视图主要就是用来检索数据的,术业有专攻 218 | 219 | ```mysql 220 | # 查看某个视图 221 | show create VIEW stuCla; 222 | # 会显示所有的表,包括视图,因为视图也算一种表 223 | show TABLES; 224 | 225 | # 删除视图不存在会报错,下面这个不会 226 | drop view gather; 227 | drop view if EXISTS views; 228 | 229 | # 创建视图,若存在则报错, 后者不存在-创建,存在-覆盖 230 | create view stuCla AS 231 | create or replace view stuCla As 232 | select stu.name stuName,cla.name claName from students stu, class cla 233 | where stu.class = cla.classId; 234 | 235 | # 使用视图 236 | select * from stuCla; 237 | 238 | ``` 239 | 240 | # 存储过程 241 | 242 | 243 | ### 存储过程 244 | 存储过程简单来说就是为以后的使用而保存的一条或多条SQL语句的集合,可视为批文件。但是在使用中,我觉得更像是函数,将一系列必须的操作封装在函数内调用,因此如果过短或者函数本身做的事情很少,那么这个存储过程就有点冗余了,可以按照函数的要求来进行编写。 245 | ```mysql 246 | /* 1.存储过程只在创建时进行编译,以后每次执行存储过程都不需再重新编译,而一般 SQL 语句每执行一次就编译一次,所以使用存储过程可提高数据库执行速度。这一般不作为存储过程的主要优点,况且PreparedStatement有批处理,性能差别不大,针对业务逻辑,要对多个表进行多次进行增删改查操作,那么可以把这些操作汇聚成一个存储过程。 247 | 缺点:移植性很差,出错后不能准确定位错误。 248 | 2.当对数据库进行复杂操作时(如对多个表进行 Update,Insert,Query,Delete 时),可将此复杂操作用存储过程封装起来与数据库提供的事务处理结合一起使用。这些操作,如果用程序来完成,就变成了一条条的 SQL 语句,可能要多次连接数据库。而换成存储,只需要连接一次数据库就可以了。*/ 249 | 250 | create procedure proc1(out s int) // 只有输出 251 | create procedure proc2(in p_in bigint) // 只有输入 252 | create procedure proc15() // 没有输入与输出 253 | create procedure demo_multi_param( 254 | in id bigint, 255 | in name varchar(32), 256 | out c int 257 | ) //多输入与输出 258 | 259 | //显示创建的人,时间,等信息,如果没有like语句,则显示所有,系统的存储过程也会有。 260 | show procedure STATUS like 'proc1'; 261 | 262 | drop procedure proc1; //如果没有会报错,下面的不会 263 | drop procedure if EXISTS proc1; 264 | 265 | 266 | /**编写输出存储过程,下面做的事情其实不用存储过程,这里只是示例,一个存储过程最好有逻辑判断等操作,即复杂的操作封装在存储过程中。*/ 267 | -- 先创建 268 | create procedure proc1( 269 | out max int, 270 | out min int, 271 | out avg int 272 | ) 273 | BEGIN 274 | select max(age) into max from students; 275 | select min(age) into min from students; 276 | select avg(age) into avg from students; 277 | END 278 | -- 再调用 279 | call proc1(@maxAge,@minAge,@avgAge);//执行存储过程 280 | select @maxAge,@minAge,@avgAge;//输出 281 | 282 | -- 编写输入存储过程 283 | CREATE PROCEDURE proc2(in sid int) 284 | BEGIN 285 | SELECT * from students where id=sid; 286 | end 287 | call proc1(1);-- 执行存储过程,返回一条记录,或者 set @pid=1; call proc1(@pid) 288 | 289 | ``` 290 | **** 291 | * **问:存储过程与触发器的区别?** 292 | 触发器与存储过程非常相似,触发器也是SQL语句集,两者唯一的区别是触发器不能用EXECUTE语句调用,而是在用户执行Transact-SQL语句时自动触发(激活)执行。触发器是在一个修改了指定表中的数据时执行的存储过程。通常通过创建触发器来强制实现不同表中的逻辑相关数据的引用完整性和一致性。由于用户不能绕过触发器,所以可以用它来强制实施复杂的业务规则,以确保数据的完整性。触发器不同于存储过程,触发器主要是通过事件执行触发而被执行的,而存储过程可以通过存储过程名称名字而直接调用。当对某一表进行诸如 UPDATE、INSERT、DELETE 这些操作时,SQLSERVER就会自动执行触发器所定义的SQL语句,从而确保对数据的处理必须符合这些SQL语句所定义的规则。 293 | **** 294 | **问:如何优化数据库?** 295 | 1)应尽量避免在 where 子句中使用 != 或 <> 操作符,否则将引擎放弃使用索引而进行全表扫描。 296 | 2)应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索引而进行全表扫描,如:`select id from t where num is null` 297 | 可以在 num 上设置默认值 0,确保表中 num 列没有 null 值,然后这样查询: 298 | `select id from t where num=0` 299 | 3)很多时候用 exists 代替 in 是一个好的选择 300 | 4)用Where子句替换HAVING 子句 因为HAVING 只会在检索出所有记录之后才对结果集进行过滤 301 | **** 302 | **问:什么是乐观锁和悲观锁?** 303 | **乐观锁( Optimistic Locking )**:乐观锁认为并发问题很少发生,所以读某个对象时不会加锁,只有在修改某个数据时,才会进行检查该数据是否被修改。 304 | **乐观锁实现**:使用数据版本(Version)记录机制实现,这是乐观锁最常用的一种实现方式。何谓数据版本?即为数据增加一个版本标识,一般是通过为数据库表增加一个数字类型的 “version” 字段来实现。当读取数据时,将version字段的值一同读出,数据每更新一次,对此version值加1。当我们提交更新的时候,判断数据库表对应记录的当前版本信息与第一次取出来的version值进行比对,如果数据库表当前版本号与第一次取出来的version值相等,则予以更新,否则认为是过期数据。 305 | **悲观锁**:悲观锁总是假设最坏的情况,每次取数据时都会加锁(读锁、写锁、行锁等),当其他线程想要访问数据时,都需要阻塞挂起。可以依靠数据库实现,如行锁、读锁和写锁等,都是在操作之前加锁,在Java中,synchronized的思想也是悲观锁。 306 | **** 307 | **问:三个范式是什么?你在建表时用到的范式?** 308 | 首先要明确的是:满足着第三范式,那么就一定满足第二范式、满足着第二范式就一定满足第一范式; 309 | 第一范式:字段是最小的的单元不可再分,学生信息组成学生信息表,有年龄、性别、学号等信息组成。这些字段都不可再分,所以它是满足第一范式; 310 | 第二范式:满足第一范式,表中的字段必须完全依赖于全部主键而非部分主键; 311 | 其他字段组成的这行记录和主键表示的是同一个东西,而主键是唯一的,它们只需要依赖于主键,也就成了唯一的,学号为1024的同学,姓名为Java3y,年龄是22岁。姓名和年龄字段都依赖着学号主键; 312 | 第三范式:满足第二范式,非主键外的所有字段必须互不依赖; 313 | 就是数据只在一个地方存储,不重复出现在多张表中,可以认为就是消除传递依赖 314 | 比如,我们大学分了很多系(中文系、英语系、计算机系……),这个系别管理表信息有以下字段组成:系编号,系主任,系简介,系架构。那我们能不能在学生信息表添加系编号,系主任,系简介,系架构字段呢?不行的,因为这样就冗余了,非主键外的字段形成了依赖关系(依赖到学生信息表了)!正确的做法是:学生表就只能增加一个系编号字段。 315 | 316 | 在设计一个商业项目的数据表时,离不开“业务需求”,第一,如果已知这个系统的数据量不会太大,那么肯定用第三范式,可以避免数据冗余带来的更新插入上需要同时多表里相同字段的麻烦。第二,如果数据量很大,那么我们可能就需要冗余数据,这样可以免去连接操作(加快执行速度),但在修改时,可能就需要修改多处了。 317 | **** 318 | **问:drop、delete与truncate分别在什么场景之下使用?** 319 | 不再需要一张表的时候,用drop; 320 | 想删除部分数据行时候,用 delete,并且带上 where 子句; 321 | 保留表而删除所有数据的时候用 truncate 322 | **** 323 | **问:主键 超键 候选键 外键?** 324 | **主键**:一个数据列只能有一个主键,且主键的取值不能缺失,即不能为空值(Null); 325 | **外 键**:在一个表中存在的另一个表的主键称此表的外键; 326 | **超 键**:在关系中能唯一标识元组的属性集称为关系模式的超键。一个属性可以为作为一个超键,多个属性组合在一起也可以作为一个超键。超键包含候选键和主键; 327 | **候选键**:是最小超键,即没有冗余元素的超键; 328 | **** 329 | **问:什么是临时表?** 330 | 临时表只在当前连接可见,当关闭连接时,MySQL 会自动删除表并释放所有空间。因此在不同的连接中可以创建同名的临时表,并且操作属于本连接的临时表。 331 | 332 | ```mysql 333 | #手动删除临时表 334 | DROP TEMPORARY TABLE IF EXISTS temp_tb; 335 | #创建临时表 336 | CREATE TEMPORARY TABLE tmp_table ( NAME VARCHAR (10) NOT NULL, time date NOT NULL); 337 | select * from tmp_table; 338 | ``` 339 | **** 340 | **问:共享锁和排斥锁的含义以及用途?** 341 | **共享锁又称读锁**:若事务T对数据对象A加上S锁,则事务T可以读A但不能修改A,其他事务只能再对A加S锁,而不能加X锁,直到T释放A上的S锁。 342 | **排他锁又称写锁**:若事务T对数据对象A加上X锁,事务T可以读A也可以修改A,其他事务不能再对A加任何锁,直到T释放A上的锁。这保证了其他事务在T释放A上的锁之前不能再读取和修改A。排他锁很好理解,是自己独占资源。 343 | 344 | -------------------------------------------------------------------------------- /notes/数据库/数据库-MySQL-练习题.md: -------------------------------------------------------------------------------- 1 | # 数据库-MySQL-编程基本概念 2 | 3 | 基本命令 4 | ```mysql 5 | /* 6 | 问:查询语句不同元素(where、jion、on、limit、group by、having 等等)执行先后顺序? 7 | 查询中用到的关键词主要包含六个,并且他们的顺序依次为 select--from--where--group by--having--order by 8 | 其中 select 和 from 是必须的,其他关键词是可选的,这六个关键词的执行顺序 与 sql 语句的书写顺序并不是一样的,而是按照下面的顺序来执行: 9 | from:需要从哪个数据表检索数据 10 | where:过滤表中数据的条件 11 | group by:如何将上面过滤出的数据分组 12 | having:对上面已经分组的数据进行过滤的条件 13 | select:查看结果集中的哪个列,或列的计算结果 14 | order by :按照什么样的顺序来查看返回的数据 15 | from 后面的表关联,是自右向左解析 而 where 条件的解析顺序是自左而右的。 16 | 也就是说,在写 SQL 文的时候,尽量把数据量小的表放在最右边来进行关联(用小表去匹配大表),而把能筛选出小量数据的条件放在 where 语句的最左边 (用小表去匹配大表) 17 | */ 18 | ``` 19 | 20 | 创建数据库 和 注释,增、删、改表 21 | ```mysql 22 | -- 三种注释之一,两杠后加一空格 23 | #三种注释之二,python语言风格 24 | /*三种注释之三,C语言风格*/ 25 | #语法:不区分大小写,句末记得添加分号 26 | 27 | #查看数据库引擎,语法不区分大小写 28 | show engines; 29 | #查看表结构 30 | DESC tableName; 31 | #查看创建表格的SQL语句 32 | show CREATE table table_name; 33 | 34 | #创建数据库,如果存在则先删除, #数据库名一旦创建好像不好修改,删除重建吧 35 | drop database if exists `school`; 36 | create database `school` default character set utf8; 37 | use `school`; 38 | 39 | #创建表格,字段可选类型:int、char、varchar、datetime、smallint、 40 | auto_increment #自增 41 | primary key(column) #主键 42 | engine=InnoDB default charset=utf8; #设置引擎和编码 43 | #设置外键,将产品 cid,绑定到 Category 的主键 id上 44 | constraint fk_product_category foreign key (cid) references category (id); 45 | varchar(n) #4.0版本的表示字节,但是5.0版本就开始改为字符,即不管中英文,n 表示 n 个字符,不管中英文。 46 | 47 | #注意字段上的点,叫间隔号,建删表时不可省略,查询等操作可省略。 48 | drop table if exists `students`; 49 | create table `students`( 50 | `id` int not null auto_increment, 51 | `name` varchar(20) not null, 52 | `sex` smallint default 1 comment '性别', 53 | `age` int not null, 54 | `in_time` datetime not null, 55 | primary key(`id`) 56 | ) default charset 'UTF8'; 57 | 58 | #修改表格 59 | USE school; 60 | ALTER TABLE `student` RENAME TO `students`; #修改表名 61 | ALTER TABLE `students` add column `age` int not null after `sex`; #为已有表格增加一列 62 | ALTER TABLE students DROP column nickname; #删除表格格的某一列 63 | ALTER TABLE students CHANGE col_name new_name VARCHAR(36); # 修改某一列的名称 64 | #修改表格主键 65 | ALTER TABLE police_org DROP PRIMARY KEY; 66 | 67 | # 字段 68 | /*时间*/ 69 | [9个时间函数获取时间](https://baijiahao.baidu.com/s?id=1608326786755050044&wfr=spider&for=pc) 70 | 日期:DATE(YYYY-MM-DD) //比较常用 使用 CURDATE() 71 | 时间:TIME(HH:MM:SS) //比较常用 使用CURTIME() 72 | 年份:YEAR(YYYY) 73 | 最全时间:DATETIME(YYYY-MM-DD HH:MM:SS) //使用 NOW() 74 | 75 | /*varchar(36) 可以保存多少中英文字符?*/ 76 | 答:具体还是要看版本的,自己在数据库中建个表测试下可以放多少个汉字。 77 | 4.0 版本以下,varchar(100),指的是 100 字节,如果存放UTF8汉字时,只能存33个(每个汉字3字节) 78 | 5.0 版本以上,varchar(100),指的是 100 字符,无论存放的是数字、字母还是UTF8汉字(每个汉字3字节),都可以存放100个。 79 | ``` 80 | 81 | ### 在一个表中查询 82 | 四种基本操作:INSERT、DELETE、UPDATE、SELECT(主要考察 select,所有后面着重介绍) 83 | ```mysql 84 | #VALUES (),();可以一次性插入多条记录,提升性能,但不是每种数据库都支持这种操作。 85 | INSERT INTO students(name,sex,age,in_time) VALUES ('张四',1,25,curdate()); 86 | 87 | #where age in (20,21,22); 可以一次删除多条记录,提升性能。 88 | DELETE FROM students where age = 20; 89 | 90 | UPDATE students set sex = 0 where id = 1; 91 | #一般都用update更新,但有时项目需求为“无则插入,有则更新”,需要用到merge。 92 | #如何使用还需要好好搜索一下 93 | merge into 图书表 a 94 | using 图书进货表 b 95 | on(a.ISBN = b.ISBN) 96 | when matched then 97 | update set a.name = b.name 98 | when not matched then 99 | insert into a(ISBN,name) values(b.ISBN,b.name) 100 | 101 | # 有时存在有则更新,无则插入的情况,可以使用 replace into 102 | 103 | ``` 104 | 105 | ### 基本查询语句 106 | ```mysql 107 | #基本关键字和函数包括 108 | 函数:max、min、sum、avg、count、distinct、 109 | 关键字:group by、having、order by[DESC、ASC]、limit、regexp、like 110 | 赋予权限的关键词:GRANT 111 | select length(filename) from tablename; #查看某个字段的数据长度,字节记 112 | 113 | #日期+时间,日期,时间 114 | now();curdate();curtime(); 115 | 116 | # 查找空值 阿里推荐使用 ISNULL(exp) 117 | SELECT * FROM household_info WHERE ISNULL(org_rank) 118 | # 虽然这种也可以,但是上面效率更好一些,是一个整体 119 | SELECT * FROM household_info WHERE org_rank IS NULL 120 | 121 | # 当某一列的值全是 NULL 时,count(col)的返回结果为 0,但 sum(col)的返回结果为 NULL 122 | ``` 123 | 124 | * **问:查找年龄最大的学生的所有信息?** 125 | ```mysql 126 | SELECT MAX(age) from students; 127 | SELECT * FROM students WHERE age = (SELECT MAX(age) from students); 128 | ``` 129 | * **问:更新最新的一条数据,mysql 不能直接选择查询的数据,中间需要再嵌套一层过度表?** 130 | ```mysql 131 | UPDATE household_borrow SET backer = '1', memo = '1' WHERE id = 132 | (SELECT a.id FROM 133 | (SELECT id FROM household_borrow WHERE household_id = '6141c9d1-8213-4c54-aa00-1d2ed56b30c0' 134 | ORDER BY create_time DESC LIMIT 1) a) 135 | ``` 136 | 137 | * **问:查找年龄在 20-23 岁的学生?** 138 | ```mysql 139 | SELECT * FROM students WHERE age>=20 and age<=23; 140 | ``` 141 | 142 | * **问:查找 id 在 另一张表里的学生?** 143 | ```mysql 144 | SELECT * FROM household_record WHERE household_id NOT IN (SELECT id FROM household_info) 145 | ``` 146 | 147 | * **问:统计表中男女学生的人数?** 148 | * **进阶:统计不同年龄的人数,人数大于等与 2 ** 149 | ```mysql 150 | #关键字:count,group by,having 151 | SELECT sex,count(*) num FROM students GROUP BY sex; 152 | SELECT age,count(id) as num FROM students GROUP BY age HAVING num >= 2; 153 | ``` 154 | 155 | * **问:将学生按照年龄倒序排,按照 id 升序排?** 156 | ```mysql 157 | #关键字 order by, DESC,ASC 158 | SELECT * FROM students ORDER BY age DESC, id ASC; 159 | ``` 160 | 161 | * **问:查找姓名以 a 开头 或者以 b 开头的学生?** 162 | ```mysql 163 | #关键字:regexp 164 | SELECT * FROM students WHERE name REGEXP '^a|^b'; 165 | SELECT * from students where name like 'a%' or name like 'b%'; 166 | /* 167 | LIKE子句 168 | 在 where like 的条件查询中,SQL 提供了四种匹配方式。 169 | %:表示任意 0 个或多个字符。可匹配任意类型和长度的字符,有些情况下若是中文,请使用两个百分号(%%)表示。 170 | _:表示任意单个字符。匹配单个任意字符,它常用来限制表达式的字符长度语句。 171 | []:表示括号内所列字符中的一个(类似正则表达式)。指定一个字符、字符串或范围,要求所匹配对象为它们中的任一个。 172 | [^] :表示不在括号所列之内的单个字符。其取值和 [] 相同,但它要求所匹配对象为指定字符以外的任一个字符。 173 | 查询内容包含通配符时,由于通配符的缘故,导致我们查询特殊字符 “%”、“_”、“[” 的语句无法正常实现,而把特殊字符用 “[ ]” 括起便可正常查询。 174 | 175 | '%a' //以a结尾的数据 176 | 'a%' //以a开头的数据 177 | '%a%' //含有a的数据 178 | '_a_' //三位且中间字母是a的 179 | '_a' //两位且结尾字母是a的 180 | 'a_' //两位且开头字母是a的 181 | */ 182 | 183 | /* 184 | REGEXP :正则表达式 185 | 'a$' //以a结尾的数据 186 | '^a' //以a开头的数据 187 | 'a' //含有a的数据 188 | '^[a-z]|end$' //以字母开头或者以end结尾 189 | */ 190 | 191 | ``` 192 | 193 | * **问:查找所有年龄,不同年龄只显示一次?** 194 | ```mysql 195 | #关键字:distinct 196 | SELECT DISTINCT age FROM students; 197 | #或者使用group by,并且效率会更高 198 | SELECT age from students GROUP BY age; 199 | #下面介绍了计算'2019-03-11'这一天访问过页面的所有用户数 200 | SELECT count(user_id) FROM (SELECT user_id FROM visit WHERE date = '2019-03-11' GROUP BY user_id) f 201 | SELECT count(user_id) FROM (SELECT DISTINCT user_id FROM visit WHERE date = '2019-03-11') f 202 | SELECT count(DISTINCT user_id) FROM visit WHERE date = '2019-03-11' 203 | 204 | ``` 205 | 206 | **问:一道写 SQL 语句的题,计算学生的平均成绩和总成绩?** 207 | 208 | ```mysql 209 | #关键字:AVG,sum 210 | select stuNum, avg(score) average from course GROUP BY stuNum; 211 | select stuNum, sum(score) total from course GROUP BY stuNum ORDER BY total DESC; 212 | ``` 213 | 214 | **问:给定一个表,找出每门成绩(grade)都大于等于80分的学生姓名?** 215 | [参考博客](https://www.cnblogs.com/chenlin/p/7412253.html) 216 | 217 | ```mysql 218 | #exists 219 | SELECT * from (select DISTINCT stuNum from course) course1 220 | where not EXISTS 221 | (select * from (select DISTINCT stuNum from course where score < 80) course2 222 | WHERE course1.stuNum=course2.stuNum); 223 | #解释一:为什么套两层,因为两个语句处理的是一个表,需要重新命名 224 | #解释二:where语句不能少,这里是一个等号!解析从左到右,如果表2中存在,则返回true,否则返回false,具体参考下面exists的用法。 225 | 226 | #in 227 | select DISTINCT stuNum from course 228 | where stuNum not in (select DISTINCT stuNum from course where score < 80); 229 | 230 | /*not exists 参考博客:https://www.cnblogs.com/cjm123/p/8177017.html 231 | 把最重要的给忘记了,where表的解析是从左到右。 232 | exists : 强调的是是否返回结果集,不要求知道返回什么,而 exists 与 in 最大的区别在于 in 引导的子句只能返回一个字段。 233 | */ 234 | 235 | ``` 236 | 237 | **关键字:limit** 238 | 查询某些数据,如前三条,后三条,中间某些等等 239 | ```mysql 240 | #问:查找年龄第三小的员工所有信息? 241 | SELECT * from students ORDER BY age LIMIT 2,1; 242 | #limit start num; 243 | SELECT * from students LIMIT 5;#只要前五条数据,0 省略,表示为0,5。 244 | SELECT * from students LIMIT 5,10;#从第 6 条开始的 10 条数据。 245 | SELECT * from students LIMIT -1,5;#最后 5 条数据不要了,其余全要。 246 | ``` 247 | 248 | ### 涉及到两个或多个表的操作 249 | * **关键字:union ** 250 | MySQL UNION 操作符用于连接两个以上的 SELECT 语句的结果组合到一个结果集合中。多个 SELECT 语句会删除重复的数据。使用 union 的前后两个查询语句的结构必须是一样的,可以采用字段组合或是不足补空行的方式把前后两个结构调整为一样的。 251 | ```mysql 252 | #将两个表的性别汇总,相同性别只记录一次(不包含重复数据),如果要记录所以则使用 union all(包含重复数据)。 253 | select sex from table1 #两条语句的字段不一定相同,但数量一定要一样,不够的用空格代替。 254 | union 255 | select sex from table2 256 | ``` 257 | 258 | * **关键字:join ** [参考博客](https://www.cnblogs.com/fudashi/p/7491039.html) 259 | 即表格的交并补操作,有左连接,右连接,内部连接,外部连接,注意:**解析连接的表是从右往左进行** 260 | 左连接(left join):求两个表的交集外加左表剩下的数据; 261 | 右连接(right join):求两个表的交集外加右表剩下的数据; 262 | 内部连接(inner join):求两个表的交集; 263 | 外部连接:就是求两个集合的并集,但是 MySQL 不支持 OUTER JOIN,但是我们可以对左右连接做 UNION 操作实现。 264 | 交叉连接:笛卡尔积 265 | 266 | ```mysql 267 | table1 (inner)join table1 on foreign1Key = foreign2Key; 268 | #默认是内连接,因为用的比较多,相当于将外键补充出来,说成交集并不是很准确。 269 | left join; #如果理解了内连接,那么左右连接也好理解了,因为有些字段没有外键,因此左连接就将左表中无外键的数据补充出来,外键设置为空,右连接则补充右表。大部分情况下on后面是外键,不然也不会用到两张表嘛,但也可能是其他字段作为条件,这里只是举例。 270 | right join; 271 | 272 | ``` 273 | 274 | * **问:查找学生的姓名,年龄和所对应的班级全称(另一个表的信息)?** 275 | ```mysql 276 | SELECT stu.name, stu.age, cla.name 277 | from students stu inner join class cla 278 | ON stu.class = cla.classId; 279 | #另一个关键字 using(classID):用法相当于 on,但是 using 里面的字段不是可选的且两张表都要有。 280 | #注意,如果两边的表如果某个记录为空,则是不会显示的,因为不符合 on 的条件。 281 | #如果想显示左边空的记录,则选择左连接即可。 282 | ``` 283 | 284 | **left join 如果有多张表,关键字可以使用多次** 285 | 可以使用别名,using 的用法相当于 on,但是 using 里面的字段不是可选的(就是 select 里面你选不了),且两张表都要有。 286 | *select a.last_name,a.first_name,b.dept_no 287 | from employees a 288 | left join dept_emp b 289 | using(emp_no)* 290 | 291 | **差集表示:使用 where is null** 292 | *select e.emp_no 293 | from employees e left join dept_manager d on e.emp_no = d.emp_no 294 | where d.dept_no is null* 295 | 296 | * **问:获取所有部门中当前员工薪水最高的相关信息?有点难理解的,因为东西比较多,我们拆开来看** 297 | *SELECT d.dept_no, s.emp_no, MAX(s.salary) AS salary 298 | FROM salaries AS s INNER JOIN dept_emp As d 299 | ON d.emp_no = s.emp_no 300 | WHERE d.to_date = '9999-01-01' AND s.to_date = '9999-01-01' 301 | GROUP BY d.dept_no* 302 | 303 | * 1-如果没有 group by 和 MAX 函数,那么显示所有 部门-员工-薪水 304 | * 2-如果只加上 MAX 函数,那么只会显示一条记录,就是最高薪水的那条记录 部门-员工-薪水(最大) 305 | * 3-如果不要 MAX 函数,只加上 Group by,那么只会显示分组的种类数,每个部门选第一条数据展示。 306 | * 4-如果都要,可以这样理解,先用 Group by 进行分组,没有结束前是分组状态,不会只抽取第一条记录,当使用 MAX 函数过滤出每组的最大值后,每个分组只剩一条记录。 307 | 308 | * **问:查找两个表的交集?** 309 | 两个要求交集的表(列)的结构要一致,对应的字段数,字段类型都应该相同;将两个数据的数据列用 UNION ALL 关键字合并;将上面的所有需要比较的列 GROUP BY ;最后 HAVING COUNT(任意一列,不能多列)>1,那么就是交集。 310 | ```mysql 311 | #多次关联一个表,如这个表记录了多种单位,重量的 weight_type, 长度的 length_type 312 | 313 | SELECT 314 | b.type_name, 315 | c.type_name, 316 | a.birth, 317 | a.ethinic 318 | FROM 319 | household_info a INNER JOIN type_def b 320 | ON b.type_class = 'weight_type' and a.household_type = b.type_code 321 | INNER JOIN type_def c 322 | ON c.type_class='length_type' and a.household_status = c.type_code 323 | ``` -------------------------------------------------------------------------------- /notes/数据库/数据库-Redis-基本概念.md: -------------------------------------------------------------------------------- 1 | # Redis 基本概念 2 | 3 | # 数据库简介 4 | 1、出现的原因:NoSQL[非关系型数据库],Redis(Remote Dictionary Server远程字典服务)[内存存储]是非关系型数据库,属于开源产品。随着互联网 web2.0 网站的兴起,传统的关系数据库在应付 web2.0 网站,特别是超大规模和高并发的 SNS 类型的 web2.0 纯动态网站已经显得力不从心,暴露了很多难以克服的问题,而非关系型的数据库则由于其本身的特点得到了非常迅速的发展。NoSQL 数据库的产生就是为了解决大规模数据集合多重数据种类带来的挑战,尤其是大数据应用难题。 5 | 2、分四大类:键值(Key-Value)存储数据库、列存储数据库、文档型数据库、图形(Graph)数据库 6 | 3、用途:数据库、缓存和消息中间件。 7 | 4、对象(五个):字符串(SDS)、列表(List)、哈希(Hash)、集合(Set)、有序集合(Zset)、 8 | 5、数据结构(六个):简单动态字符串(SDS)、链表、字典、跳跃表、整数集合、压缩列表等 9 | 6、Redis 数据库里面的每个键值对都是由对象(Object)组成的,一个开源的使用 C 语音编写,支持网络,可基于内存亦可持久化的日志型,key-value 数据库。 10 | 11 | **** 12 | 13 | * 数据库 键 总是一个字符串,键可以包含任何数据(二进制安全),比如 jpg 图片或者序列化的对象,一个键最大能存储512M 14 | * 数据库的 值 可以是五种对象中的一种。 15 | * 命令不区分大小写 16 | **** 17 | * **问:什么是 NoSQL?** 18 | **答**: 19 | 20 | # 优势 21 | 1、性能极高 – Redis 能读的速度是 11 万次/s,写的速度是 8 万次/s 。 22 | 2、丰富的数据类型 – Redis 支持二进制案例的 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。 23 | 3、原子 – Redis 的所有操作都是原子性的,意思就是要么成功执行要么失败完全不执行。单个操作是原子性的。多个操作也支持事务,即原子性,通过 MULTI 和 EXEC 指令包起来。 24 | 4、丰富的特性 – Redis 还支持 publish/subscribe, 通知, key 过期等等特性。 25 | 26 | # 数据结构 27 | * 简单动态字符串 28 | * 链表 29 | * 字典 30 | * 跳跃表 31 | * 整数集合 32 | * 压缩列表 33 | * 对象 34 | 35 | 36 | ## 简单动态字符串 37 | **问:简单动态字符串(Simple Dynamic String,SDS)与 C 字符串的区别?** 38 | **答**:两者的结构不同, SDS 比 C 字符串多了两个属性,可以分别实现常数复杂度获取字符串长度,保证不会造成缓冲区溢出。这是对比 C 字符串所具有的前两个优点,还有三个优点,修改字符串时减少了内存分配次数、二进制安全、兼容部分 C 字符串函数。 39 | 40 | **结构不同?** 41 | C 语言的字符串:以空字符结尾的字符数组。在 Redis 里面,常用在一些无须对字符串值进行修改的地方,比如打印日志。 42 | SDS:结构包括两个属性 free,len 和一个字符数组 buf。其中 buf 和 C 语言的字符串相同,这样便可以重用一部分 C 语音的库函数,如打印语句等。在 Redis 里,用在需要被修改的字符串,如键值对。 43 | 44 | **SDS 比 C 更适用于 Redis(多了两个属性,分别具有如下作用)** 45 | 1、常数复杂度获取字符串长度; 46 | 2、不会造成缓冲区溢出,因为当发现 free 不够时,会先扩展 buf 的空间,再执行拼接操作。 47 | 48 | **减少了内存重分配的次数?** 49 | 内存重新分配涉及复杂的算法,并且可能需要执行系统调用,所以它通常是一个比较耗时的操作。 50 | C 语言的字符串:拼接需要扩展内存,忘记会造成 内存溢出;截断需要释放内存,忘记会造成 内存泄露。(OOM 为内存溢出,是指应用系统中使用的内存过多;内存泄露 memory leak,是指程序在申请内存后,无法释放已申请的内存空间,memory leak 会最终会导致 out of memory!) 51 | SDS 实现了空间预分配和惰性空间释放两种优化策略: 52 | 1、空间预分配(拼接):1M 以下,free = len;如果大于 1 M,free 就分配 1M。 53 | 2、惰性空间释放(截断):不会真正释放,只是改变 free 的数值。 54 | 55 | **二进制安全?** 56 | C 字符串:除了字符串的末尾之外,字符串里面不能包含空字符。这些限制使得它只能保存文本数据,而不能保存像图片、音频、视频、压缩文件这样的二进制数据。 57 | SDS 的 API 都是二进制安全的,因为 SDS 使用 len 属性而不是空字符来判断字符串是否结束。 58 | 59 | **疑问:可以保存二进制数据,为什么还可以使用 C 语言的库函数,如输出函数,库函数不会根据你的 len 来判断的?** 60 | ```C 61 | struct sdshdr{ 62 | int len; 63 | int free; 64 | char buf[]; 65 | } 66 | ``` 67 | 68 | **** 69 | 链表 70 | ## 双端链表 71 | **问:双端链表的结构?** 72 | 双端、无环、带表头指针和表尾指针、带链表长度计数器、多态(难:未理解) 73 | 74 | ```C 75 | typedef struct listNode{ 76 | struct listNode *pre; 77 | struct listNode *next; 78 | void *value; 79 | }listNode; 80 | 81 | typedef struct list{ 82 | listNode *head; // 表头节点 83 | listNode *tail; // 表尾节点 84 | unsigned long len; //链表所包含的节点数量 85 | void *(*dup)(void *ptr); // 节点值复制函数 86 | void *(*free)(void *ptr); // 节点值释放函数 87 | int (*match)(void *ptr, void *key); // 节点值对比函数 88 | }list; 89 | 90 | ``` 91 | 92 | **** 93 | 94 | 字典 95 | ## 字典 96 | 除了表示数据库外,字典还是哈希键的底层实现之一。 97 | **问:字典 与 Java hashmap 的异同?** 98 | 四个点:结构、哈希算法、解决冲突、渐进式 rehash 99 | 哈希算法:redis 使用的是 murmurhash2 算法 100 | 解决冲突:Redis 使用 链地址法 来解决冲突(因为没有尾指针,因此采用了头插法) 101 | (Hashmap 有尾指针么??等会看看) 102 | 渐进式 rehash: 103 | 104 | 相同: 105 | 1、通过链地址法解决冲突; 106 | 107 | 不同: 108 | 1、rehash 的操作 109 | ```C 110 | typedef struct dictEntry{ 111 | void *key; // 键 112 | union{ // 值 113 | void *val; 114 | uint64_tu64; 115 | int64_ts64; 116 | }v; 117 | struct dictEntry *next; // 指向下个哈希表节点,形成链表 118 | }dictEntry; 119 | 120 | typedef struct dictht{ 121 | dictEntry **table; // 哈希表数组 122 | unsigned long size; // 哈希表大小 123 | unsigned long sizemask; // 哈希表掩码,等于 size - 1 124 | unsigned long used; // 节点数量 125 | }dictht; 126 | 127 | typedef struct dict{ 128 | dictType *type; // 类型特定函数 129 | void *privdata; // 私有数据 130 | dictht ht[2]; // 两个哈希表,rehash 的时候使用 131 | int rehashidx; // 不进行时,值为 -1 132 | }dict; 133 | ``` 134 | **** 135 | 136 | 跳跃表 137 | ## 跳跃表 138 | **问:跳跃表的结构?** 139 | 跳跃表包含四个属性:表头节点、表尾节点、节点的最大层数(不包括头节点)、跨度(不包括头结点) 140 | 跳跃表节点的结构:层(包括前进指针和跨度)、后退指针、分值、成员对象 141 | ```C 142 | typedef struct zskiplistNode{ 143 | struct zskiplistLevel{ // 层 144 | struct zskiplistNode *forward; // 前进指针 145 | unsigned int span; // 跨度 146 | }level[]; 147 | struct zskiplistNode *backward; // 后退指针 148 | double score; // 分值 149 | robj *obj; // 成员对象 150 | }zskiplistNode; 151 | 152 | typedef struct zskiplist{ 153 | struct zskiplistNode *header, *tail; // 表头,表尾节点 154 | unsigned long length; // 表中节点数量 155 | int level; // 表中层数最大的节点的层数 156 | }zskiplist; 157 | ``` 158 | 159 | 160 | 161 | 整数集合 162 | ## 整数集合 163 | 结构: 164 | 升级:1、如何升级;2、好处(提升灵活性,节约内存) 165 | 降级:无 166 | ```C 167 | typedef struct intset{ 168 | uint32_t encoding; // 编码方式 169 | uint32_t length; // 集合包含的元素数量 170 | int8_t contents[]; // 保存元素的数组 171 | }intset; 172 | ``` 173 | 174 | 压缩列表 175 | ## 压缩列表 176 | 压缩列表是 Redis 为了节约内存而开发的,是由一系列特殊编码的连续内存块组成的顺序型数据结构。 177 | 结构(复杂): 178 | 连锁更新:连锁更新在最坏情况下需要对压缩列表执行 N 次空间重分配,每次重分配的最坏复杂度为O(N),所以连锁更新的最坏复杂度为 O(N2),但是: 179 | 1、压缩列表多个连续,长度介于 250 - 253 字节之间的节点才有可能引发,而实际情况并不多见。 180 | 2、即使出现连锁更新,但只要更新的节点数量不多,就不会对性能造成任何影响。 181 | ```C 182 | struct sdshdr{ 183 | int len; 184 | int free; 185 | char buf[]; 186 | } 187 | ``` 188 | 189 | 对象 190 | # 对象(Object) 191 | 1、Redis 并没有直接使用下面介绍的数据结构来实现键值对数据库,而是基于上面的数据结构创建了一个对象系统,这个系统包含五种类型的对象。字符串(String)、列表(List)、哈希(Hash)、集合(Set)、有序集合(Zset)。每种对象都至少用到了一种上面的数据结构。好处是,我们可以针对不同的使用场景,为对象设置多种不同的数据结构实现,从而优化对象在不同场景下的效率。 192 | 2、Redis 对象系统还实现了基于引用计数的内存回收机制。 193 | 3、Redis 使用对象来表示数据库中的 键 和 值,每次新建一个键值对时,至少会创建两个对象。 194 | 4、键总是一个字符串对象,而值可以有五种对象,因此当我们称 “列表键” 时,指的是“这个数据库键所对应的值为列表对象”。 195 | 5、Redis 在执行命令前,会根据对象的类型来判断它是否可以执行给定的命令。 196 | 197 | 198 | ```C 199 | /* 200 | type:命令返回的结果为数据库键对应值的 对象类型,不是键的,键只有String。 201 | encoding:决定底层的数据结构,long 类型的整数、简单动态字符串、字典、双端链表、压缩列表、整数集合、跳跃表和字典等等,每种对象都至少使用了两种编码。 202 | */ 203 | typedef struct redisObject{ 204 | unsigned type:4; //类型[五个中的一个 STRING,HASH,LIST,SET,ZSET] 205 | unsigned encoding:4; //编码 206 | void *ptr; //指向底层实现数据结构的指针 207 | int refcount; // 引用计数参数 208 | unsigned lru:22; // 对象最后一次被命令程序访问的时间 209 | }robj; 210 | ``` 211 | 212 | 213 | 214 | # String(字符串) 215 | 216 | ## 简介 217 | 218 | 219 | ## 1、简单动态字符串 220 | ```C 221 | /*没有直接使用 C 语言传统的字符串表示(以空字符结尾的字符数组:C 字符串),而是自己构建一种名为动态字符串(simple dynamic string, SDS)的抽象类型作为默认类型。Redis 中,一些无须对字符串值进行修改的地方如打印日志,会用到 C 字符串。如果需要修改,则会使用 SDS。下面介绍SDS的基本使用规则。 222 | 1、数据结构 223 | 2、空间预分配:在free不够时进行添加操作,会自动分配与len相同的空间(小于1M,大于1M的话,最多分配 1M) 224 | 3、惰性空间释放:其实就是不释放,如果删除字符串的某些字符,则 free 增加即可。 225 | 4、二进制安全:buf 叫做字节数组,因为 SDS 的API 都是二进制安全的,都会以处理二进制的方式来处理 SDS 存放在 buf 中的数据。就算有很多\0也没事,因为有 len。 226 | 227 | 问:SDS 与 C 相比有哪些优点? 228 | 1、常数复杂度获取字符串长度 229 | 2、杜绝缓冲区溢出 230 | 3、减少了修改字符串长度时所需的内存重分配次数 231 | 4、二进制安全 232 | 5、兼容部分 C 字符串函数 233 | 234 | */ 235 | struct sdshdr{ 236 | /*记录 buf 数组中未使用字节的数量,不够会自动分配,防止内存溢出与泄露*/ 237 | int free; 238 | /*记录 buf 数组中已使用字节的数量,而 C 字符串需要遍历统计*/ 239 | int len; 240 | /*字节数组,用于保存字符串(末尾自动添加\0,这样可以方便调用 C 的函数库,所以数组的长度 = len + free + 1)*/ 241 | char buf[]; 242 | } 243 | 244 | ``` 245 | 246 | 247 | # Hash(字典) 248 | 1、又称为符号表(symbol table)、关联数组(associative array)、映射(map)键值对集合 249 | 2、适合存储对象,并且可以像数据库中 update 一个属性一样只修改某一项属性值(Memcached中需要取出整个字符串反序列化成对象修改完再序列化存回去) 存储、读取、修改用户属性 250 | 251 | 应用:Redis 数据库底层实现就是采用字典,对数据库的增、删、改、查都是构建在对字典的操作上的。 252 | ```C 253 | /* 254 | 1、Redis 中的字典使用哈希表作为底层实现,每个字典带有两个哈希表,一个平时使用,一个rehash 时使用。 255 | 2、哈希表使用链地址法来解决冲突(头插法) 256 | 3、对哈希表进行扩展或者收缩操作时(采用渐进式rehash),程序需要将现有哈希表包含的所有键值对rehash到新哈希表里面。 257 | 258 | */ 259 | 260 | //字典 261 | typedef struct dict{ 262 | dictType *type; //类型特定函数 263 | void *privdata; //私有数据 264 | dictht ht[2]; //哈希表,两个,默认使用第一个 265 | in trehashidx; //rehash索引,不再进行时为 -1. 266 | }dict; 267 | 268 | //哈希表 269 | typedef struct dictht{ 270 | dictEntry **table; //哈希表数组 271 | unsigned long size; //哈希表大小 272 | unsigned long sizemask; //哈希表大小掩码,用于计算索引值,总是等于 size - 1 273 | unsigned long used; //哈希表已有节点的数量 274 | }dictht; 275 | 276 | //数组节点的结构 dictEntry 277 | typedef struct dictEntry{ 278 | void *key; 279 | union{ 280 | void *val; 281 | uint64_tu64; 282 | int64_ts64; 283 | }v; 284 | struct dictEntry *next; //指向下一个哈希表节点,形成链表 285 | }dictEntry; 286 | 287 | ``` 288 | 289 | 290 | # List(列表) 291 | ## 链表(双向链表)特点 292 | 1、双端:有前后指针 293 | 2、无环:表头 prev,表尾 tail 的 next 都指向 null 294 | 3、有表头指针和表尾指针 295 | 4、带链表长度 296 | 5、多态 297 | 298 | 功能:发布与订阅、慢查询、监视器、保存多个客户端的状态信息,构建输出缓冲区,最新消息排行等功能(比如朋友圈的时间线)、消息队列(Message Queue,MQ) 299 | 300 | ```C 301 | //节点 302 | typedef struct listNode{ 303 | struct listNode *prev; 304 | struct listNode *next; 305 | void *value; 306 | } 307 | 308 | //虽然使用多个 listNode 结构就可以组成链表,但使用下面的 list 操作起来更方便。 309 | typedef struct list{ 310 | listNode *head; //头结点 311 | listNode *tail; //尾节点 312 | unsigned long len; //长度 313 | void *(*dup)(void *ptr); //复制链表节点所保存的值 314 | void (*free)(void *ptr); //释放链表节点所保存的值 315 | int (*match)(void *ptr, void *key); //比较是否相等 316 | }list; 317 | ``` 318 | ## 压缩列表(ziplist) 319 | 1、压缩列表是列表键 和 哈希键的底层实现之一。 320 | 2、当一个列表键只包含少量列表项,且要么是小整数值,要么就是比较短的字符串,则 Redis 使用压缩列表来做列表键的底层实现。 321 | 3、压缩列表是 Redis 为了节约内存而开发的,是由一系列特殊编码的连续内存块组成的顺序型数据结构。 322 | 4、压缩列表可以包含多个节点,每个节点可以保存一个字节数组或者整数值 323 | 5、添加新节点到压缩列表,或者从压缩列表中删除节点,可能会引发连锁更新操作,但这种操作出现几率不高。 324 | ```C 325 | /* 326 | 压缩列表结构:[zlbytes、zltail、zllen、entry1、entry2、。。。、entryN、zlend] 327 | zlbytes:4字节,记录整个压缩列表占用的内存字节数 328 | zltail:4字节,记录压缩列表表尾节点距离 起始节点有多少字节 329 | zllen:2字节,记录压缩列表包含的节点数量 330 | entryX:压缩列表包含的各个节点,节点的长度由节点保存的内容决定 331 | zlend:特殊值 0xFF,用于标记压缩列表的末端 332 | 333 | 压缩列表节点组成结构:previous_entry_length、encoding、content 三个部分 334 | previous_entry_length: 335 | encoding: 336 | content: 337 | */ 338 | 339 | ``` 340 | 341 | 342 | 343 | # Set(集合) 344 | 1、哈希表实现,元素不重复 345 | 2、添加、删除,查找的复杂度都是O(1) 346 | 3、为集合提供了求交集、并集、差集等操作 347 | 4、应用:共同好友 ,利用唯一性,统计访问网站的所有独立ip ,好友推荐时,根据tag求交集,大于某个阈值就可以推荐 348 | 349 | ```C 350 | /* 351 | 1、整数集合是集合键的底层实现之一 352 | 2、整数集合的底层实现是数组,以有序,无重复的方式保存集合元素,在有需要时,根据新添加的元素,自动升级,改变数组的类型。 353 | 3、升级操作为整数集合带来了操作上的灵活性,并且尽可能的节约了内存。 354 | 4、整数集合只支持升级操作,不支持降级操作。 355 | */ 356 | 357 | typedef struct intset{ 358 | uint32_t encoding; //编码方式,确定数组的类型 359 | uint32_t length; //集合包含的元素数量 360 | int8_t contents[]; //保存元素的数组,类型以最大范围的为准 361 | }intset; 362 | ``` 363 | 364 | 365 | # Sorted Set(有序集合) 366 | 1、将Set中的元素增加一个权重参数score,元素按score有序排列 367 | 2、数据插入集合时,已经进行天然排序 368 | 3、应用:排行榜 ,带权重的消 369 | 370 | 371 | ## 跳跃表 372 | 跳跃表(skiplist)是一种有序数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的。大部分情况下,跳跃表的效率可以和平衡树相媲美,并且因为跳跃表实现更为简单,所以可以使用前者代替后者。 373 | 374 | 应用只有两处:有序集合、集群节点 375 | 376 | ```C 377 | /* 378 | 1、层: level 数组包含多个元素,每个元素都包含一个指向其它节点的指针,可以加快访问其它节点的速度,一般说来,层越多访问其它节点的速度越快。 379 | */ 380 | typedef struct zskiplistNode{ 381 | struct zskiplistLevel{ //层 382 | struct zskiplistNode *forward; //前进指针 383 | unsigned int span; //跨度 384 | }level[] 385 | 386 | struct zskiplistNode *backward; //后退指针 387 | 388 | double score; //分值 389 | robj *obj; //成员对象 390 | }zskiplistNode; 391 | 392 | //仅靠多个跳跃表节点就可以组成一个跳跃表 393 | typedef struct zskiplist{ 394 | struct zskiplistNode *header, *tail; //表头节点和表尾节点 395 | unsigned long length; //表中节点的数量 396 | int level; //表示最大的层数 397 | } 398 | ``` 399 | 400 | # 单机数据库的实现 401 | 402 | ## 数据库 403 | 本章将对 Redis 服务器的数据库实现进行详细介绍, 404 | 1、说明服务器保存数据库的方法 405 | 默认创建 16 个数据库 406 | 2、通过 SELECT 命令来切换数据库,实现原理(修改 redisClient 的 db 指针) 407 | 408 | 2、客户端切换数据库的方法 409 | 3、数据库保存键值对的方法 410 | 4、以及针对数据库的添加、删除、查看,更新操作的实现方法 411 | 5、说明服务器保存键的过期时间的方法 412 | 6、服务器自动删除过期键的方法。 413 | 414 | 415 | ## 持久化 416 | 417 | ## 事件 418 | 419 | ## 客户端 420 | 421 | ## 服务器 422 | 423 | > 424 | > 425 | > -------------------------------------------------------------------------------- /notes/数据库/数据库-Redis-练习题.md: -------------------------------------------------------------------------------- 1 | # Redis 数据库底层原理 2 | 3 | # 基本函数 4 | ```mysql 5 | 6 | ``` 7 | 8 | 9 | # 基本类型 10 | ```mysql 11 | 空:nil 12 | 成功:返回 1,或者 'ok' 13 | ``` 14 | 15 | 16 | # String 的基本操作 17 | ```mysql 18 | '''增''' 19 | set animal Cat #赋值一个变量,没有则创建,有则覆盖 20 | get animal #获取变量的值 21 | mset name Tom age 11 #赋值多个变量 22 | mget name age #获取多个变量 23 | 24 | expire key 5 #定时5秒 25 | set animal 'cat' EX 5 #也是定时5秒 26 | set user:tom:age:45 'abcde' #比较好的编码方式 27 | 28 | '''删''' 29 | del animal #删除 30 | 31 | '''改''' 32 | set animal 'Cat' #赋值一个变量 33 | append animal 'Dog'#追加 34 | 35 | set count 100 36 | incr count #101增加1 37 | decr count #100减少1 38 | 39 | '''查''' 40 | exits count #查询是否存在返回0,1 41 | type count #查询类型 42 | 43 | get animal #获取 44 | mget user1 user2 animal #多个赋值 45 | 46 | ``` 47 | 48 | # Hash 的基本操作 49 | ```mysql 50 | 51 | '''增''' 52 | hmset user username tom birth 1977 sex 1 # 给表格 user 字段赋值 53 | hmget user username birth sex #获取表格字段的值 54 | hset,mget #操作user 表的某个字段 55 | 56 | '''删''' 57 | hdel user sex#删除某个字段 58 | '''改''' 59 | hsetnx user sex #如果存在,则不设置 60 | hincrby user birth 10#给出生年加10 61 | '''查''' 62 | hkeys/hvals user #获取user 表所有的键/值 63 | hlen user #返回user 表字段的数量 64 | hexists #是否存在 65 | ``` 66 | 67 | # List 的基本操作 68 | ```mysql 69 | 70 | '''增''' 71 | rpush mylist A B C #在右边插入值,相当于入栈 72 | lpush mylist E F G #在左边插入值 73 | 74 | '''删''' 75 | rpop mylist #出栈 76 | lpop mylist #出队列 77 | 78 | '''改''' 79 | 80 | '''查''' 81 | lrange mylist 0 -1 #就是显示全部元素,[0,10]显示前十一个元素。 82 | llen mylist #显示长度 83 | 84 | ``` 85 | 86 | 87 | # Set 的基本操作 88 | ```mysql 89 | /* 90 | 问:为什么能存这么多还很快? 91 | 集合中最大的成员数为 232 - 1(4294967295, 每个集合可存储40多亿个成员)。????? 92 | 93 | 94 | 问:zset 如何实现排序? 95 | Redis zset 和 set 一样也是string类型元素的集合,且不允许重复的成员。不同的是每个元素都会关联一个double类型的分数。redis正是通过分数来为集合中的成员进行从小到大的排序。zset的成员是唯一的,但分数(score)却可以重复。 96 | 97 | 98 | 99 | */ 100 | '''增''' 101 | sadd myset 1 2 3 #赋值 102 | '''删''' 103 | srem myset 1 #删除元素 104 | '''改''' 105 | 106 | '''查''' 107 | sismember myset 1#判断元素是否在集合内 108 | smembers myset #返回所有成员,每次返回顺序可能不同 109 | sdiff set1 set2 #返回两个结合的差集set1 - set2 110 | sinter #返回几个集合的交集 111 | sunion #返回几个集合的并集 112 | ``` 113 | 114 | -------------------------------------------------------------------------------- /notes/数据库/数据库-Redis-进阶.md: -------------------------------------------------------------------------------- 1 | # Redis 进阶 2 | 3 | **服务器中的数据库** 4 | 1、Redis 数据库 5 | 2、Redis 持久化 6 | 3、事件 7 | 4、客户端 8 | 5、服务器 9 | 10 | **多机数据库的实现** 11 | 1、复制 12 | 2、Sentinel 13 | 3、集群 14 | 15 | 16 | 17 | 18 | ## redis 数据库 19 | * 切换数据库:SELECT 2,切换到 2 号数据库,数据库下标从 0 开始; 20 | 21 | ```C 22 | // Redis 服务器将所有数据库保存在 redisServer 结构的 db 数组中 23 | typedef struct redisServer{ 24 | redisDb *db; // 一个数组,保存着服务器中的所有数据库 25 | int dbnum; // 服务器的数据库数量,默认 16 个数据库。 26 | } 27 | 28 | // 服务器内存的客户端状态 redisClient 29 | typedef struct redisClient { 30 | redisDb *db; 31 | // 记录客户端当前正在使用的数据库(指向 redisServer.db 数组中的一个元素) 32 | }redisClient; 33 | 34 | typedef struct redisDb{ 35 | dict *dict; // 数据库键空间,保存着数据库中的所有键值对 36 | dict *expires; // 过期字典,保存着键的过期时间 37 | }redisDb; 38 | ``` 39 | 40 | ### 键的生存时间和过期时间 41 | 四个不同的命令设置键的生存时间和过期时间,但实际上前三个命令都是使用 PEXPIREAT 命令来实现的(底层就不写了,自己想一下)。 42 | PERSIST:解除设置的生存时间关系(即会一直存在) 43 | TTL(PTTL):以秒(毫秒)返回键的剩余生存时间; 44 | 45 | ```C 46 | EXPIRE // 设置 key 的生存时间为 ttl 秒 47 | PEXPIRE // 生存时间为 ttl 毫秒 48 | EXPIREAT // 设置 key 的过期时间为指定秒时间戳 49 | PEXPIREAT // 过期时间为指定毫秒时间戳(未来某个日期) 50 | ``` 51 | 52 | ### 过期键删除策略 53 | **三种策略:定时删除、惰性删除、定期删除** 54 | 定时删除:在设置过期时间的同时,同时设置一个定时器,让定时器在键过期时,立刻执行删除操作。对内存友好,但对 CPU 不友好(现阶段不易实现,消耗太大)。 55 | 惰性删除:在取出键时才对键进行过期检查,对内存不友好,但对 CPU 友好,当过期键过多时,有可能会造成内存泄露类似的情况。 56 | 定期删除:前两种方式的一种折中,难点在于确定时长和频率。 57 | 58 | **Redis 使用惰性删除和定期删除两种策略** 59 | 60 | **AOF、RDB 和 复制功能 对过期键的处理** 61 | 1、生成 RDB 文件时不会包含过期键; 62 | 2、载入 RDB 文件时,主服务器不会载入过期键,从服务器则会全部载入; 63 | 3、AOF 文件写入,过期键未删除便不影响,当执行某个删除操作后,会向 AOF 追加删除命令; 64 | 4、AOF 重写,只对未过期的键进行重写; 65 | 5、复制模式下,从服务器的过期键删除动作由主服务器控制;主服务器删除一个键后会向所以的从服务器发送删除命令,从服务器只有接收到此命令才进行删除; 66 | 67 | **数据库通知** 68 | 键空间通知:某个键执行了哪些命令 69 | 键事件通知:某个命令被哪些键执行了 70 | 71 | 72 | ## Redis 持久化 73 | 因为 Redis 是内存数据库,它将自己的数据库状态存储在内存里面,所以如果不想办法将存储在内存中的数据库状态保存到磁盘,那么一旦服务器进程退出,那么数据也会消失。 74 | 75 | ### RDB(Redis DataBase) 持久化 76 | 77 | **RDB 文件的创建与载入** 78 | 两个命令生成 RDB 文件 79 | 1、SAVE:阻塞 Redis 服务器,直到 RDB 文件创建完成; 80 | 2、BGSAVE:派出一个子进程创建 RDB 文件; 81 | Redis 服务器在启动时检测到 RDB 文件存在,就会自动载入 RDB 文件(阻塞状态),直到载入完成。 82 | (因为 AOF 文件的更新频率通常比 RDB 文件的更新频率高,所以如果开启了 AOF 功能,那么会优先使用 AOF 文件来还原) 83 | 84 | **自动间隔性保存** 85 | 通过 save 选项设置多个保存条件,任意一个被满足,就会执行 BGSAVE。 86 | 87 | ```C 88 | typedef struct redisServer{ 89 | struct saveparam *saveparams; // 记录了保存条件的数组 90 | long long dirty; // 修改计数器 91 | time_t lastsave; // 上一次执行保存的时间 92 | } 93 | 94 | typedef struct saveparam{ 95 | time_t seconds; // 秒数 96 | int changes; // 修改数 97 | } 98 | ``` 99 | 100 | **RDB 文件结构** 101 | 五个部分:REDIS、db_version、databases、EOF、check_sum 102 | 103 | **分析 RDB 文件** 104 | 105 | ### AOF(Append Only File) 持久化 106 | **AOF 持久化的实现** 107 | 三个步骤 108 | 命令追加(append):执行完一个命令之后,会以协议格式将命令追加到 aof_buf 末尾; 109 | 文件写入: 110 | 文件同步(sync) 111 | 112 | **AOF 文件的载入与数据还原** 113 | 读,写,即可; 114 | 115 | **AOF 重写** 116 | 因为该文件中会有很多冗余命令(如增了又删等等),因此,需要重写 AOF 文件,这样体积会小很多。重写其实只是重写读 数据库,并不会对 旧AOF 文件进行任何处理。当开始重写 AOF 文件时,此时会开辟一个缓存区,当有新命令到来时,会写入,最后重写完成时,再全部追加到新文件中。 117 | (疑问:新命令执行,如果是重写后的数据还好,如果是还未重写的数据,AOF 重写读一遍,缓存的又追加命令,那不是造成数据不一致了?) 118 | 119 | 120 | ## 事件 121 | Redis 服务器是一个事件驱动程序,有一下两类事件 122 | 文件事件(file event):套接字、I/O 多路复用程序、文件事件分派器、事件处理器 123 | 时间事件(time event): 124 | **文件事件** 125 | 文件事件是对套接字操作的抽象。 126 | 1、文件事件处理器使用 I/O 多路复用程序来同时监听多个套接字,并根据套接字目前执行的任务来为套接字关联不同的事件处理器。 127 | 2、当被监听的套接字准备好执行连接应答、读取、写入、关闭等操作时,与操作相对应的文件事件就会产生,这时文件事件处理器就会调用套接字之前关联好的事件处理器来处理这些事件。 128 | 129 | **时间事件** 130 | 主要分为两类:定时事件、周期性事件 131 | 132 | 133 | ## 客户端 134 | 服务端采用链表保存所有与服务器连接的客户端。 135 | 1、客户端属性(通用属性):12 个 136 | 2、客户端的创建与关闭: 137 | 138 | 139 | ## 服务器 140 | 1、命令请求的执行过程 141 | 2、ServerCron 函数 142 | 3、初始化服务器 143 | 144 | 145 | ## 复制 146 | Redis 中用户可以通过执行 SLAVEOF 命令,让一个服务器去复制另一个服务器。 147 | 旧版复制功能包括:同步、命令传播。 148 | 同步:使用 SYNC 的命令进行同步,在执行 RDB 的持久化过程中,会将新到来的命令缓存,那么,是用什么机制,保证不会在 BGSAVE 命令中将新插入的键写入 RDB 文件中??? 149 | 命令传播:被客户端的命令修改后,向从服务器传播指令,以保证数据一致。 150 | 缺点:在断线重连时,需要复制主服务器的所有数据,而是不是更新之后未接受到的数据,会导致效率低下。 151 | 新版复制:将上面耗时的 SYNC 命令拆分为 PSYNC(里面的两种模式,完整重同步,部分重同步) 152 | 部分重同步如何实现? 153 | 1、偏移量:用于记录主从服务器的数据是否一致,还差多少 154 | 2、复制积压缓冲区(默认为 1 M,可以根据需要调整):记录一部分命令到队列中,以备从服务器断线重连执行部分重同步,如果缓冲区中不存在从服务器偏移量之后的命令(offset + 1),那么就需要执行完整重同步。 155 | 3、服务器运行 ID:主从服务器第一次连接时,主服务器发送的,当从服务器断线重连时,需要发送此 ID 确认是否是之前连接的主服务器,如果相同可以执行部分重同步,如果不同则要执行完全重同步。 156 | 157 | 复制的实现 158 | 1、设置主服务器的地址和端口 159 | 2、建立套接字 160 | 3、发送 PING 命令 161 | 4、身份验证 162 | 5、发送端口信息 163 | 6、同步 164 | 7、命令传播 165 | 166 | 167 | ## Sentinel(哨兵) 168 | 哨兵是干什么的? 169 | Sentinel 系统由多个哨兵实例构成,可以监视任意多个主服务器以及下属所有从服务器。当某个主服务器下线时,哨兵可以将它的某个从服务器升级为新的主服务器继续工作。 170 | (注意:1、其余从服务器与新的主服务器保持同步,2、如果下线的服务器上线了,则会被作为从服务器继续保持同步) 171 | 172 | 什么是哨兵? 173 | 哨兵本质上只是一个运行在特殊模式下的 Redis 服务器,所以启动哨兵的第一步就是初始化一个普通的 Redis 服务器。 174 | 175 | 176 | ## 集群 177 | Redis 集群是 Redis 提供的分布式数据库方案,集群通过分片(sharding)来进行数据共享,并提供复制和故障转移功能。 178 | 节点:一个 Redis 集群通常由多个节点组成。 179 | 槽指派: Redis 集群通过分片的方式来保存数据库中的键值对,整个数据库被分为 16384 个槽, 180 | 181 | 182 | ## 发布与订阅 183 | 由多个命令组成:PUBLISH、SUBSCRIBE、UNSBUSCRIBE、PSUBSCRIBE、UNPSUBSCRIBE 184 | PUBLISH:发布一条消息 185 | SUBSCRIBE:订阅一个频道 186 | PSUBSCRIBE:订阅一个模式(可以匹配多个频道) 187 | 188 | 频道的订阅与退订 189 | * Redis 将所有频道的订阅关系都保存在服务器状态的 pubsub_channels 字典里面,这个字典的键是某个被订阅的频道,值是一个链表,链表记录了所有订阅此频道的客户端。 190 | * 订阅:当一个客户端订阅某个频道时,如果字典存在则直接插入到链表末尾,否则新建一个键值对。 191 | * 退订:与上面相反如果删除后链表为空,则删除该键值对。 192 | 193 | 模式的订阅与退订 194 | * Redis 将所有模式的订阅关系都保存在服务器状态的 pubsub_patterns 链表里面,链表中的每个节点都包含一个 pubsub_Pattern 结构,包括具体的订阅模式和订阅了该模式的客户端。 195 | * 订阅模式:新建一个 pattern 结构,添加到 patterns 链表的末尾。 196 | * 退订模式:删除链表节点 197 | 198 | 发布消息(PUBLISH channel message) 199 | * 将消息 message 发送给所有的订阅者:遍历频道的链表即可 200 | * 将消息发送给符合 pattern 的订阅者:只要有消息发布都需要来遍历一下 201 | 202 | 查看订阅信息(PUBSUB) 203 | * PUBSUB CHANNELS:服务器被订阅的频道 204 | * PUBSUB NUMSUB xxx:返回频道的订阅者数量 205 | * PUBSUB NUMPAT:服务器被订阅模式的数量 206 | 207 | 208 | ## 事务 209 | 事务将多个命令请求打包,然后一次性、按顺序的执行。 210 | 首先通过命令 MULTI 表示事务开始,然后输入多条命令,最后输入 EXEC 开始执行事务。 211 | 212 | * MULTI:将客户端的状态转为事务状态,将发送的命令存入队列 213 | * EXEC:遍历队列,执行命令,最后将所有的结果返回给客户端 214 | * WATCH:是一个乐观锁,在 EXEC 执行前,可以监视指定数据库的键是否被修改(别的客户端),如果是则拒绝执行事务。 215 | * ACID 性质: 216 | 原子性:不支持回滚,会增加复杂度 217 | 一致性:检查三个地方,入队错误:错误的命令不会被入队;执行错误:命令执行错误会返回错误信息;服务器停机: 218 | 隔离性:单线程,并且事务执行时不会中断,因此总是串行的。 219 | 耐久性:由 Redis 的持久化模式决定,当服务器运行在 AOF 持久化模式下且 appendfsync 选项设置为 always 时,事务也具有耐久性。 220 | 221 | 222 | ## LUA 脚本 223 | 初始化:八个步骤创建 Lua 环境 224 | 执行 Redis 脚本的伪客户端: 225 | 保存 Lua 脚本的脚本字典: 226 | 227 | 228 | ## Lua 排序 229 | 230 | 231 | ## 二进制位数组 232 | 233 | 234 | ## 慢查询日志 235 | 236 | 237 | 238 | ## 监视器 239 | -------------------------------------------------------------------------------- /notes/计算机网络/计算机网络-传输层.md: -------------------------------------------------------------------------------- 1 | # 五、传输层 2 | 3 | * **传输单位**:报文段或分组 4 | * **功能**:提供应用进程间的逻辑通信;差错检测;提供有连接和无连接的服务;复用和分用;流量控制(GBN,SR);可靠传输; 5 | * **协议**:TCP、UDP 6 | 7 | **** 8 | 9 | * **问:套接字?** 10 | * **答**:数据链路层按 MAC 地址寻址,网络层按 IP 寻址,而传输层按端口号寻址。套接字是 (IP + 端口),TCP连接的端口,不是IP,不是主机,是套接字。 11 | 12 | **** 13 | 14 | * **问:传输层提供应用进程间的逻辑通信?** 15 | * **答**:是的,传输层以端口寻址,应用进程与外界通信也是通过端口号。 16 | 17 | **** 18 | 19 | * **问:差错检测?** 20 | * **答**:TCP 是面向字节的,TCP 将所要传送的报文看成是字节组成的数据流,并使每一个字节对应于一个序号。对收到的报文进行首部和数据进行差错检测,网络层不检查数据部分。 21 | 22 | **** 23 | 24 | * **问:传输层如何提供有连接和无连接的服务?** 25 | * **答**:一个 TCP,一个 UDP 嘛。 26 | 27 | **** 28 | 29 | * **问:复用和分用?** 30 | * **答**:这里的概念是不同的应用进程可以使用一个传输层协议,分用就是逆过程,将不同的数据取出后分给目标应用。其实看的更多的是数据链路层的复用,为了提高电缆的传输效率,可以同时将多个不同频率信号通过一条电缆传输,有时分-频分-码分-波分多路复用。**时分多路复用**:现在有一个玩具,十个小孩想玩,那么只能将能玩的时间分成十份,然后轮流玩。如果中间轮到有的孩子他睡着了怎么办,跳过它,到下一个,这就是 **异步时分多路复用(ATM)**,如果要干等着,那就是同步了。 31 | 32 | **** 33 | 34 | * **问:在传输层应根据什么原则来确定使用面向连接服务还是无连接服务?** 35 | * **答**:当然是根据上层老板的要求了,一个提供服务的有啥话语权,叫你用啥就用啥。比如要使用 FTP 文件传送协议时,赶紧准备好 TCP,当应用程序要视频点播,实时传送时,用 UDP 伺候着。 36 | 37 | ## TCP[面向连接服务]: 38 | * **特点**:是面向字节流的传输协议,传送数据前需要建立连接,可靠交付,但是时延长,要求实时性肯定不行了,但是要求可靠,那杠杠的。如文件传输协议 FTP,需要使用 TCP连接。 39 | 40 | * 要点:可靠传输、流量控制、拥塞控制、三次握手,四次挥手。 41 | 42 | **** 43 | 44 | * **问:传输层如何实现可靠传输?** 45 | 46 | * **答**:传输层的可靠性是由 TCP 实现的,所以我们可以叫 TCP 可靠传输。TCP 的可靠性表现在:它向应用层提供的数据是 无差错的、有序的、无丢失的,简单的说就是:TCP最终递交给应用层的数据和发送者发送的数据是一模一样的。 采用的技术就是我们经常听说的 流量控制、拥塞控制、连续 ARQ 等技术来保证它的可靠性。 47 | 48 | **** 49 | 50 | * **问:TCP 流量控制(GBN,SR)?** 51 | 52 | * **答**:一般来说,我们总希望数据传输越快越好嘛,但是如果发送的过快,对方接受不过来,也很麻烦。所以这里的流量控制(Flow Control)就是让发送方的发送速率不要太快了,考虑对方的感受嘛。一般措施采用 滑动窗口流量控制 和 停止-等待流量控制,后者效率低,发一帧等回复,再发一帧再等回复。因此就有了滑动窗口,可以连续发好多帧,但是不能超过窗口,窗口不停的向前移动。其中发送方的窗口和接受方的窗口没有关系,可以任意,留给对方足够的空间才能长久相处。简单来看,滑动窗口有三种机制,当接受窗口 = 1 时,肯定按序到达。 53 | 停止-等待(ARQ):发送窗口大小 = 1,接受窗口大小 = 1; 54 | 后退 N 帧(GBN):发送窗口大小 > 1,接受窗口大小 = 1; 55 | 选择重传(SR):发送窗口大小 > 1,接受窗口大小 > 1; 56 | 57 | **** 58 | 59 | * **问:TCP 拥塞控制?** 60 | 61 | * **答**:其实需要从全局来看,若网络产生拥塞,网络的性能就要明显变坏,整个网络的吞吐量将随输入负荷的增大而下降。具体看下面的比较。 62 | 63 | **** 64 | 65 | * **问:TCP 拥塞控制与流量控制的性质对比?** 66 | 67 | * **答**:拥塞控制只有一个目的,使整个网络能够承受现有的网络负荷。是一个全局性的过程,涉及所有主机,所有路由器和降低网络传输性能的有关因素。而流量控制是在指定的发送端和接受端之间点对点通信量的控制,更具体,也很容易实现。而拥塞控制更关注全局,很难设计和实现。所以为了防止发生拥塞,TCP 要求发送端维护以下两个窗口一个和流量控制差不多,接收端窗口:根据接收端反馈的信息,来调整发送端当前能够发送的数据量,另一个是拥塞窗口:根据自己估计的网络拥塞程度,设置的窗口值,所以你能发送的数据肯定是二者中的最小值。以下是最常见的四种算法:慢开始算法,拥塞避免算法,快重传算法,快恢复算法,有兴趣可以自己翻书哦。 68 | 69 | **** 70 | 71 | * **问:TCP 三次握手建立连接?** 72 | 73 | * **答**: 我觉得图标的效果也没想象中好嘛,也不那么容易记忆。我们来讨论一下,为啥三次?如果只要一次就建立连接,假设客户端发送的请求丢失 [ SYN = 1表示我想建立连接, seq = x表示这是数据],那么客户端是不是死等也不会有人理他,所以不行的。那么两次呢,服务器端收到请求后,发送 [ACK=1接受你的请求,ack=x+1请再来点吧,SYN=1也请你和我连接吧,seq=y这是数据],如果这个请求丢失,两边是不是还是死等,而且傻乎乎也不知道对面情况怎么样了。所以最少要三次,最后客户端发送 [ACK=1我代表月亮接受你的请求,ack=y+1请再来点吧,seq=x+1这是你要的东西]。 74 | 75 | * **问:建立连接会经过什么状态变化?** 76 | * 答:首先由客户端发起连接,客户端:CLOSED->SYN_SENT->ESTABLISHED, 77 | 服务端:LISTEN->SYN_RECV->ESTABLISHED 78 | 79 | **** 80 | 81 | * **问:TCP 四次挥手释放连接?** 82 | 83 | * **答**:其实如果理解了上面的三次握手,那么你可以把四次挥手理解为,建立连接时三次握手是一起建立的,你连接我,同时我也连接你了。但是断开不同了,因为可以不同时间,即如果我把数据先发完了,那么我就先断开了,我不能发,但是我能接受哇,你可以继续发,当你发完再结束。因此大致过程是:客户端[FIN=1客户端没数据了,我不发了,seq=x最后的数据],服务器端[ACK=1好的,ack=x+1真的木有数据了么,再来点呗,seq=y这是我的数据] 当服务器结束时:服务器[FIN=1好的吧,我也结束了,seq=w最后的数据,ACK=1知道你不发了,回我一下呗,ack=x+1给我发这个] 客户端[ACK=1好的,ack=w+1真没数据了?,seq=x+1给你] 84 | TCP连接必须经过2MSL后才真正释放。 85 | 86 | * **问:关闭连接会经过什么状态变化?** 87 | * 答:客户端:ESTABLISHED->FIN-WAIT1->FIN-WAIT2->TIME_WAIT->CLOSED 88 | 服务端:ESTABLISHED->CLOSED-WAIT->LAST-ACK->CLOSED 89 | 90 | * **问:只有执行主动关闭端才会出现TIME_WAIT?** 91 | * 答:是的。最后都会执行CLOSED。 92 | 93 | * **问:CLOSE_WAIT 与 TIME_WAIT?** 94 | * 答:TIME_WAIT:即 2MSL,为了保证 A 发送的最后一个确认报文段能够到达 B,如果 A 不等待 2MSL,A 返回的最后确认报文段丢失,则 B 不能进入正常关闭状态,而 A 此时已经关闭了,也不可能再重传。(其次,进过 2MSL 可保证连接持续的时间内所产生的所有报文从网络中消失) 95 | CLOSE_WAIT:同上,只是这是 B 端需要等待关闭连接的时长。 96 | 97 | **** 98 | 99 | * **问:【报文分组交换】方式是把长的报文分成若干个较短的报文组,【报文分组】是交换单位,它与【报文交换】方式有什么不同, 【报文分组交换】多了哪些东西?** 100 | 101 | * **答**:报文交换:整个报文先传送到相邻结点,全部存储下来然后查找转发表,转发到下一个结点。 分组交换:单个分组(这只是整个报文的一部分)传送到相邻结点,存储下来后查找转发表,转发到下一个结点。 显然报文交换在存储过程中报文并没有被分割成多个分组,自然没有所谓的序号。 102 | 103 | **** 104 | 105 | * **问:当应用程序使用面向连接的 TCP 和无连接的 IP 时,这种传输时面向连接的还是无连接的?** 106 | 107 | * **答**:答案应该都是,因为这个问题就有问题,答案要从不同的层次来看,传输层是面向连接的,网络层是无连接的。 108 | 109 | **** 110 | 111 | * **问:TCP 连接和网络的虚电路的区别?** 112 | 113 | * **答**:TCP 是在传输层抽象的端到端的逻辑通信,记录连接状态,通过重发保证可靠传输,没有指定哪条线路,所以 TCP 连接是抽象的概念,就像咱俩是一根绳上的蚂蚱。虚电路是建立了一条线路,记录转发的路由器编号,下次依然是这些路由器转发,这个和真正意义上的连接更近一点。 114 | 115 | ## UDP[面向无连接服务]: 116 | * **特点**:传送数据前无需建立连接,数据达到后也无需确认,不可靠,但是时延短。需要保证实时性的视频等,要用UDP连接。在传输层采用了 UDP 协议,其传输的可靠性则由上层应用决定。 117 | **** 118 | 119 | * **问:既然传输层的 UDP 是不可靠的,为什么又说传输层提供可靠的服务?** 120 | * **答**:某一层是否可靠,确实取决于这一层使用了什么协议。以前说数据链路层是可靠的,是因为它使用了 HDLC 协议,但由于一般链路上不会出现什么传输错误,就换成了无连接的 PPP ,所以现在数据链路层又是不可靠的,网络层也不可靠,因为使用了无连接的 IP 。那么现在这个重担就交给我们传输层了, UDP 不能保证数据报都能正确到达目的地, 发现错误之后选择丢弃或者反馈一个错误信息,但我们还有 TCP 啊,如果用户选择 TCP 就是可靠传输,我们默认传输层是可靠的。 121 | **** 122 | 123 | * **问:下列关于UDP协议的叙述中,正确的是? 124 | Ⅰ 提供无连接服务 125 | Ⅱ 提供复用/分用服务 126 | Ⅲ 通过差错校验,保障可靠数据传输** 127 | 128 | * **答**:I,II 正确 129 | II 见 TCP 部分,III UDP提供检查和校验。其原因是链路层以下的协议在源端和终端之间的某些通道可能不提供错误检测。虽然UDP提供有错误检测,但检测到错误时,UDP不做错误校正,只是简单地把损坏的消息段扔掉,或者给应用程序提供警告信息。UDP的差错校验只是保证接收方接受的UDP数据包是正确的。而可靠传输的含义是:发送方发送的报文都能够正确无误的按序到达接收方。TCP 拥有可靠传输。 130 | 131 | **** 132 | 133 | * **问:TCP/IP 为了高效率传输,传输层采用了 UDP 协议,其传输的可靠性由谁提供?** 134 | * **答**:应用进程。我还以为问传输层的可靠性由谁提供,直接就写了 TCP。底层没做的,肯定交给上层 BOSS 啊。 135 | 136 | **** 137 | 138 | * **问:若因特网上所有链路传输都是可靠的,那么使用 UDP 协议能实现可靠传输?** 139 | * **答**:错误。就 拥塞控制 来说,即使所有链路是可靠的,但如果注入网络的数据太多,网络处理不了,也要丢弃部分数据,因而不能实现可靠传输。 140 | 141 | **** 142 | 143 | * **问:TCP 和 UDP 的端口是相互独立的?** 144 | * **答**:封装时包头信息不一样,任意帧在网络上可以区分出是 UDP 包还是 TCP 包,所以即使IP 地址和端口相同,也不会导致冲突。 145 | 146 | * **问:可以用哪个命令来查看 TCP 和 UDP 连接状态??** 147 | * **答**: 148 | **netstat**: 命令的功能是显示网络连接、路由表和网络接口信息,可以让用户得知有哪些网络连接正在运作。 使用时如果不带参数,netstat 显示活动的 TCP 连接。 149 | **ping**: 命令可以检查网络是否连通,可以很好地帮助我们分析和判定网络故障 150 | **ipconfig**: 当使用 ipconfig 时不带任何参数选项,那么它显示每个已经配置了的接口 IP 地址、子网掩码和缺省网关值。  151 | **nslookup**: 命令用于查询 DNS 的记录,查看域名解析是否正常,在网络故障的时候用来诊断网络问题。 152 | 153 | 154 | * **问:TCP 和 UDP 的比较?** 155 | 1. 对比 156 | UDP TCP 157 | 是否连接 无连接 面向连接 158 | 是否可靠 不可靠传输,不使用流量控制和拥塞控制 可靠传输,使用流量控制和拥塞控制 159 | 连接对象个数 支持一对一,一对多,多对一和多对多交互通信 只能是一对一通信 160 | 传输方式 面向报文 面向字节流 161 | 首部开销 首部开销小,仅8字节 首部最小20字节,最大60字节 162 | 适用场景 适用于实时应用(IP电话、视频会议、直播等) 适用于要求可靠传输的应用,例如文件传输 163 | 2. 总结 164 | TCP向上层提供面向连接的可靠服务 ,UDP向上层提供无连接不可靠服务。 165 | 虽然 UDP 并没有 TCP 传输来的准确,但是也能在很多实时性要求高的地方有所作为 166 | 对数据准确性要求高,速度可以相对较慢的,可以选用TCP 167 | -------------------------------------------------------------------------------- /notes/计算机网络/计算机网络-基础概念.md: -------------------------------------------------------------------------------- 1 | # 一、基础概念 2 | 3 | ### OSI 参考模型的三个主要概念,协议,服务和接口。 4 | 5 | * **答:** 计算机网络要交换数据,必须遵守一些事先约定好的原则,这些原则就是 **协议**。在协议控制下,使得本层可以向上层提供 **服务[这里的服务指,从这一层到物理层各层服务的总和]**,而提供服务,需要 **接口,[上层只能调用相邻下层的服务,因为只有相邻两层才有接口,但是没关系,每一层都包含下面所有层的服务]**。 6 | 7 | **协议**:是水平的,即从我电脑的传输层,到你电脑的传输层需要遵守相同的协议。 8 | **接口**:每一层都向上层提供服务,没有接口就不能提供服务; 9 | **服务**:下层为相邻上层提供的功能调用,协议是水平的,而服务则是垂直的,下层通过接口对上层提供服务。 10 | 补充-端口号:进程通信的地址,和这里的 **端口** 不是一回事。 11 | *** 12 | ### OSI 七层参考模型 13 | 14 | * **OSI 七层参考模型:[ 快速记忆:应表会传网数物 ]** 15 | 16 | 应用层、表示层、会话层、传输层、网络层、数据链路层、物理层。 17 | 18 | * **表示层** 19 | 20 | 数据格式转换、数据加密解密以及数据压缩及解压等。 21 | 22 | * **会话层** 23 | 24 | 主要功能是在两个节点建立、维护和释放面向连接用户的连接,并对会话进行管理和控制,保证会话数据的可靠传输。传输层也是建立连接,但会话层相当于领导告诉传输层去建立连接,传输层就屁颠屁颠的跑去建立连接了。 25 | 26 | ### TCP/IP 五层模型:[ 快速记忆:应传网数物 ] 27 | **TCP/IP 模型仅有四层,但是我们看作五层,即最后一层 网络接口层 拆分成两层,参照 OSI 七层参考模型对应为 数据链路层和物理层,这样可以方便记忆啊,教材也好描述** 四层为:应用层、传输层、网际层、网络接口层。 28 | 此知识点需要达到,快速写出每层的任务、功能、协议。 29 | 30 | * **应用层:(用户对用户)** 31 | 传输单位:应该是 doc、pdf、txt、py、cpp、java 吧哈哈哈 32 | 任务:提供系统与用户的接口 33 | 功能:文件传输、电子邮件服务、远程登录协议 34 | 协议:FTP、SMTP、POP3、Telnet、HTTP 35 | 36 | * **传输层:(应用对应用,进程对进程)** 37 | 传输单位:报文段(TCP)或用户数据报(UDP) 38 | 任务:负责主机中两个进程之间的通信 39 | 功能:为端到端链接提供可靠的传输服务;为端到端连接提供流量控制、差错控制等 40 | 协议:TCP、UDP 41 | 42 | * **网络层:(主机对主机)** 43 | 传输单位:数据报 44 | 硬件:路由器 45 | 任务:将传输层传下来的报文段封装成分组、选择合适的路由,将分组传递到目的主机 46 | 功能:组包[传输分组用到]、拆包[接收分组用到]、路由选择、拥塞控制 47 | 协议:IP、CIDR、DHCP、ARP、NAT、ICMP 48 | 49 | * **数据链路层:** 50 | 传输单位:帧 51 | 硬件:交换机、网桥 52 | 任务:将网络层传下来的IP数据报组装成帧 53 | 功能:链路的连接建立、分离;帧定界和帧同步;差错检测 54 | 协议:PPP、HDLC、ARQ 55 | 56 | * **物理层:** 57 | 传输单位:比特 58 | 硬件:集线器、中继器 59 | 任务:传输比特流 60 | 功能:为硬件设备传送数据 61 | *** 62 | 63 | 64 | * **问:OSI 七层参考模型的工作流程?** 65 | 66 | * **答:** 通过发送一封电子邮件来解释,首先在 **应用层** 编辑这封信件,然后发送给 **表示层**,表示层可以对信件进行加密,压缩,再传给 **会话层**,这时会话层会把 **要开始邮件了** 这个消息反馈给你,并发送消息给 **传输层**,传输层将消息分段,因为数据无法一次传输,需要分段 [**所以叫做报文段嘛**],然后将分段的数据发送给 **网络层**,这时网络层会对分段进行封装并加入报头形成 **数据报**,还要选择一条传输路径传输这些数据,之后再将数据报传给 **数据链路层**,数据链路层加入报头和报尾封装成帧,最后再把帧发送给物理层形成 **比特流**。 67 | *** 68 | * **问:帧、数据报、分段的数据单位大小?** 69 | 70 | * **答:** 从上面的例子可看出来,帧的数据部分是数据报,数据报的数据部分是分段,因此有帧 > 数据报 > 报文段。 71 | *** 72 | * **问:计算机网络最基本的功能是什么?** 73 | 74 | * **答:** **数据通信** 是计算机网络最基本的功能,包括连接控制、传输控制、差错控制、流量控制、路由控制、多路复用等子功能。 75 | *** 76 | * **问:物理层、数据链路层、网络层、传输层的传输单位分别是什么?** 77 | 78 | * **答:** 比特、帧、数据报(或者 IP 数据报,)、报文段(有人看过传输层叫做 UDP 数据报,但是报文段更准确)。 79 | 80 | 81 | -------------------------------------------------------------------------------- /notes/计算机网络/计算机网络-应用层.md: -------------------------------------------------------------------------------- 1 | # 六、应用层 2 | 3 | * **功能**:文件传输、电子邮件服务 4 | * **协议(都是基于 TCP 的)**:FTP、TELNET、DNS、HTTP、HTTPS、电子邮件传输协议:SMTP、POP3、Outlook 5 | 基于 UDP 的有: TFTP、SNMP 与 DNS 6 | 其中 DNS 既可以基于 TCP,也可以基于 UDP 7 | 8 | **** 9 | 10 | **常见的端口号** 11 | 12 | | FTP | SSH | TELNET | SMTP | DNS | TFTP | HTTP | POP3 | HTTPS | 13 | | :----: | :--: | :----: | :--: | :--: | :--: | :--: | :--: | :---: | 14 | | 20,21 | 22 | 23 | 25 | 53 | 69 | 80 | 110 | 443 | 15 | 16 | 登记端口号:1024 - 49151,为没有熟知端口号的应用程序使用。 17 | 客户端端口或短暂端口:49152 - 65535,为客户进程运行时动态选择。 18 | 19 | **** 20 | 21 | * **问:FTP(File Transfer Protocol 文件传输协议)和 TFTP(Trivial File Transfer Protocol 普通文件传输协议)?** 22 | * **答**:FTP 文件传输协议,是因特网上使用最广泛的传送协议。使用 TCP 连接,端口号 21,20。在传输文件时需建立 2 个 TCP 连接,21 号端口 控制连接 用于进行命令控制, 20 号端口 数据连接 进行真正的数据传送。先建立 控制连接,当需要传送数据时建立 数据连接,数据传输完成 关闭数据连接,控制连接只有等到关闭命令时才会关闭,不是一起关闭的,也不是一起建立的。 23 | **TFTP**:使用的是 UDP 协议,对于每一个数据报在获得确认后才发送另一个数据报,灵活,使用资源少,流量控制较简单,是一种平等的传输协议。‘ 24 | 区别:使用协议不同,端口号不同,FTP 在速度方面没有 TFTP 快但是 FTP 安全性好。 25 | 26 | **** 27 | 28 | * **TELNET ?** 29 | * **答**:是 Internet 远程登录服务的标准协议和主要方式。它为用户提供了在本地计算机上完成远程主机工作的能力。在终端使用者的电脑上使用 Telnet 程序,用它连接到服务器。终端使用者可以在 Telnet 程序中输入命令,这些命令会在服务器上运行,就像直接在服务器的控制台上输入一样。可以在本地就能控制服务器。要开始一个 Telnet 会话,必须输入用户名和密码来登录服务器。Telnet 是常用的远程控制 Web 服务器的方法。 30 | **** 31 | 32 | * **问:DNS(Domain Name System 域名解析系统)?** 33 | * **答**:如果不允许你用 www.baiwu.com 来访问百度的首页,你要咋办。当然可以直接用 IP 地址访问,在网上搜了一下,本地可以 ping www.baidu.com 发现有好几个 IP 地址,这也好理解,大公司为了分担服务器压力,可以给一个域名分配好几个 IP 地址,然后使用循环分配使用或者其他方式,降低服务器压力,这样才不会让你感觉卡卡的嘛。题外话,DNS 就是可以根据你给的英文网址,查找对应的 IP 地址,你的浏览器再根据 IP 地址查找目的服务器,如果直接用 IP 地址是不是更快,是的。 34 | 35 | **** 36 | ### 电子邮件使用的协议 37 | * **问:SMTP(Simple Message Transfer Protocol 简单邮件传输协议)?** 38 | * **答**:简单邮件传送协议,使用 25 号端口。 39 | 40 | **** 41 | 42 | * **问:POP3(Post Office Protocol 3 邮局通讯协议第三版)?** 43 | * **答**:邮局协议版本 3,一个邮件读取协议。 44 | 45 | **** 46 | 47 | * **问:Outlook 等常用的电子邮件软件使用 SMTP 协议进行邮件的收发?** 48 | * **答**:错误!使用 SMTP 来发送邮件,使用 POP3 来接受邮件。 49 | 50 | **** 51 | 52 | * **问:电子邮件收发过程?** 53 | * **答**:首先 发信人使用代理来编辑邮件,用户代理使用 SMTP 将邮件发送给邮件服务器缓存队列中等待发送,邮件服务器发现有邮件,便与接收端服务器使用 SMTP 协议进行 TCP 连接,发送完后便断开连接。接收端服务器接受到邮件后放入收信人用户邮箱中。收信人收信时使用 POP3 将自己的邮件从接收端服务器中取出。因此可以简单记忆为发送邮件使用 SMTP,而接受邮件需要使用 POP3。 54 | 55 | **** 56 | 57 | * **问:介绍 HTTP (Hyper Text Transport Protocol 超文本传输协议)协议的传输过程?** 58 | * **答**: 服务流程:从协议执行过程来说,当浏览器要访问 www 服务器时,首先要对服务器进行域名解析(DNS 协议)。一旦获得 IP 地址【网络层的路由选择与转发,找到目的 IP 地址】,浏览器要通过 TCP 三次握手和服务器建立连接。每个服务器都有一个服务进程,它不断监听 TCP 端口号 80,当监听到浏览器请求后便和浏览器建立连接。TCP 连接建立后,浏览器就向服务器发送某个 HTTP 请求。如 localhost/test/index.jsp 。服务器收到请求后,将构建的服务页面返回给浏览器,浏览器对信息进行解释后,显示给客户。 59 | 60 | * **问:介绍 HTTPS (超文本传输安全协议)?** 61 | * **答**: 简单讲是 HTTP 的安全版。即 HTTP 下加入 SSL 层。也就是说它的主要作用可以分为两种:一种是建立一个信息安全通道,来保证数据传输的安全;另一种就是确认网站的真实性,凡是使用了 HTTPS 的网站,都可以通过点击浏览器地址栏的锁头标志来查看网站认证之后的真实信息,也可以通过 CA 机构颁发的安全签章来查询 。 62 | 63 | * **问:HTTPS 和 HTTP 的区别主要?** 64 | * **答**:1、HTTP 是超文本传输协议,信息是明文传输,HTTPS 则是具有安全性的 SSL 加密传输协议。2、HTTP 和 HTTPS 端口不同,前者是 80,后者是 443。 65 | HTTPS 会明显比 HTTP 协议慢很多:HTTPS 比 HTTP 更慢,因为多了协商密钥、验证证书的环节,而且之后传输的所有数据都需要加密解密。同样的服务器,能承受的 HTTPS 并发数要比 HTTP小很多。HTTP 使用 TCP 三次握手建立连接,客户端和服务器需要交换 3 个包,HTTPS 除了 TCP 的三个包,还要加上 SSL 握手需要的 9 个包,所以一共是 12 个包。 66 | HTTPS 已被广泛地用于 Web 浏览器与服务器之间的身份认证和加密数据传输,HTTPS 协议需要到 CA 申请证书,一般免费证书较少,因而需要一定费用。 67 | 68 | * **HTTP 状态码** 69 | 1xx(临时响应)表示临时响应并需要请求者继续执行操作的状态代码。 70 | 2xx (成功)表示成功处理了请求的状态代码。 71 | 3xx (重定向)表示要完成请求,需要进一步操作。 通常,这些状态代码用来重定向。 72 | 4xx(请求错误)这些状态代码表示请求可能出错,妨碍了服务器的处理。 73 | 5xx(服务器错误)这些状态代码表示服务器在尝试处理请求时发生内部错误。 这些错误可能是服务器本身的错误,而不是请求出错。 74 | 75 | 200: 请求已成功,请求所希望的响应头或数据体将随此响应返回。-正常响应。 76 | 304: 如果客户端发送了一个带条件的 GET 请求且该请求已被允许,而文档的内容(自上次访问以来或者根据请求的条件)并没有改变,则服务器应当返回这个状态码。304响应禁止包含消息体,因此始终以消息头后的第一个空行结尾。 77 | 403: 服务器已经理解请求,但是拒绝执行它。-无权限。 78 | 404: 未找到,服务器找不到所请求的网页。 79 | 500:内部服务器错误 80 | 502:作为网关或者代理工作的服务器尝试执行请求时,从上游服务器接收到无效的响应。 81 | 504:作为网关或者代理工作的服务器尝试执行请求时,未能及时从上游服务器(URI标识出的服务器,例如HTTP、FTP、LDAP)或者辅助服务器(例如DNS)收到响应。 82 | 505: 服务器不支持,或者拒绝支持在请求中使用的 HTTP 版本。-服务端错误。 83 | 84 | **练习**:A,B 两台机器都正常工作,B 机器未监听任何端口。如果 A 机器向 B 机器 80 端口发送 SYN 包,会收到何种类型的回包? 85 | **答**:因为 B 机器未监听任何端口,A 又向 B 的 80 端口发送了数据包,所以 B 不会建立连接,会异常结束建立连接,会发送 RST 包,如果是正常结束的话就会发送 FIN 包, RST 是 TCP 连接中 出现差错产生的,RST 置 1可以用来拒绝一个非法的报文段或拒绝打开一个连接。**RST 是 TCP 报文段首部里的字段,还有其他一些很容易忘的字段。** 86 | 87 | **** 88 | 89 | * **问:GET 和 POST的区别?** 90 | * **答**:[参考博客](https://www.cnblogs.com/logsharing/p/8448446.html) 91 | 1、GET 请求只能进行 URL 编码,而 POST 支持多种编码方式。 92 | 2、GET 请求参数会被完整保留在浏览器历史记录里,而 POST 中的参数不会被保留。 93 | 3、GET 参数通过 URL 传递,POST 放在 Request body 中,因此 GET 比 POST 更不安全,因为参数直接暴露在 URL 上,所以不能用来传递敏感信息。 94 | 参考:GET 和 POST 是什么?HTTP 协议中的两种发送请求的方法。HTTP 是什么?HTTP 是基于 TCP/IP 的关于数据如何在万维网中如何通信的协议。HTTP 的底层是 TCP/IP。所以 GET 和 POST 的底层也是 TCP/IP,也就是说,GET/POST 都是 TCP 链接。GET 和 POST 能做的事情是一样一样的。你要给 GET 加上 Request body,给 POST 带上 URL 参数,技术上是完全行的通的。 95 | 96 | * **问:POST 方法比 GET 方法安全?** 97 | * **答**:有人说POST 比 GET 安全,因为数据在地址栏上不可见。然而,从传输的角度来说,他们都是不安全的,因为 HTTP 在网络上是明文传输的,只要在网络节点上捉包,就能完整地获取数据报文。要想安全传输,就只有加密,也就是 HTTPS。 98 | 99 | * **问:GET 方法的长度限制是怎么回事?** 100 | * **答**:网络上都会提到浏览器地址栏输入的参数是有限的。首先说明一点,HTTP 协议没有 Body 和 URL 的长度限制,对 URL 限制的大多是浏览器和服务器的原因。浏览器原因就不说了,服务器是因为处理长 URL 要消耗比较多的资源,为了性能和安全(防止恶意构造长 URL 来攻击)考虑,会给 URL 长度加限制。 101 | 102 | * **问:POST 方法会产生两个 TCP 数据包?** 103 | * **答**:有些文章中提到,POST 会将 header 和 body 分开发送,先发送 header,服务端返回 100 状态码再发送 body。HTTP 协议中没有明确说明 POST 会产生两个 TCP 数据包,而且实际测试(Chrome)发现,header 和 body 不会分开发送。所以,header 和 body 分开发送是部分浏览器或框架的请求方法,不属于 POST 必然行为。 104 | 105 | **** 106 | 107 | * **问:同源策略是浏览器的安全基石,但互联网业务往往需要实现跨域通信,以下哪一种方案可以实现跨域?** 108 | 109 | * **答**:同源策略(Same Origin Policy,SOP)需要同时满足以下三点要求: 110 | 1)协议相同 2)域名相同 3)端口相同 111 | http:www.test.com与https:www.test.com 不同源——协议不同 112 | http:www.test.com与http:www.admin.com 不同源——域名不同 113 | http:www.test.com与http:www.test.com:8081 不同源——端口不同 114 | 只要不满足其中任意一个要求,就不符合同源策略,就会出现“跨域。 115 | 不同源的客户端脚本(js,actionscript)在没有明确的授权的情况下不能读取对方的资源。 116 | 以下三种可实现跨域: 117 | 跨源资源(CrossOrigin Resources Sharing,CORS)是一种允许多种资源在一个 web 页面请求域之外的另一个域的资源的协议,是为了让 AJAX 能够跨域而生的。 118 | 内容安全策略(Content Security Policy,CSP)是一种白名单机制,限制网站中是否可以包含某来源的内容。 119 | Oauth是一个关于授权的开放网络标准,相当于在客户端与服务器之间添加了一个授权层。 120 | 121 | **** 122 | 123 | * **问:简述cookie和session的区别?** 124 | * **答**:1,session 在服务器端,cookie 在客户端(浏览器) 125 | 2、session 的运行依赖 session id,而 session id 是存在 cookie 中的,也就是说,如果浏览器禁用了 cookie ,同时 session 也会失效,存储Session时,键与Cookie中的sessionid相同,值是开发人员设置的键值对信息,进行了base64编码,过期时间由开发人员设置 126 | 3、cookie安全性比session差 127 | 128 | **** 129 | 130 | * **问题?** 131 | **UDP不是面向连接的,所以源IP地址通常可以伪造的? 132 | TCP SYN数据包中的源IP地址通常是可以伪造的? 133 | TCP是面向连接的,三次握手后,源IP不一定是真实的? 134 | 禁止PING就是禁止ICMP协议?** 135 | 136 | 137 | -------------------------------------------------------------------------------- /notes/计算机网络/计算机网络-数据链路层.md: -------------------------------------------------------------------------------- 1 | # 三、数据链路层(MAC层) 2 | 3 | * **传输单位**:帧 4 | * **工作设备**:网桥,交换机(按照MAC地址寻址) 5 | * **功能**:链路的连接建立、分离;帧定界和帧同步;差错检测; 6 | * **协议**:CSMA/CD(载波帧听多路访问/冲突检测)协议;PPP;HDLC 7 | **** 8 | * **问:差错检测?** 9 | * **答**:使用循环冗余码检验差错检测技术,只能做到对帧的无差错接受,即我同意收入怀中的肯定是好货,那些坏的我都丢了,你自己看着办吧,所以要依靠传输层的 TCP可靠传输来检验了。有人问为啥不在这一层就做了呢,其实吧现在电缆质量高,基本传输都能到,如果增加了完整性验证,那么肯定会拖慢速度,得不偿失,99% 到就可以了,你不知道追求剩下的1%可能要花上好几倍的代价么。 10 | 11 | **** 12 | * **问:以太网(Ethernet)?** 13 | * **答**:IEEE802.3 可以等同于以太网标准,以太网 采用总线拓扑结构,所有计算机都共享一条总线,信息以广播方式发送。为了保证数据通信的可靠性,使用了 CSMA/CD 技术对总线进行访问控制。考虑到局域网信道质量好,所以稍微改进了下,增加传输速度,1. 采用无连接的工作方式; 2. 不对发送的数据帧进行编号,也不要求对方发送确认帧。就是我尽量给你发,差错纠正由传输层的 TCP 完成。 14 | **高速以太网(100BASE-T、1000BASE-T)**:传输速率大于等于 100Mbit/s,允许 10Mbit/s 网卡共存,标准定义的 MII 将 MAC 子层与物理层分隔开。为了最大程度的减少冲突,最大程度的提高网络速度和使用效率,使用交换机(Switch)来进行网络连接和组织,这样,以太网的拓扑结构就成了星型,但在逻辑上,以太网仍然使用总线型拓扑和 CSMA/CD(Carrier Sense Multiple Access/Collision Derect 即带冲突检测的载波监听多路访问) 的总线争用技术。 15 | 16 | * **问:X.25 的功能?** 17 | * **答**:虚电路服务、多路复用、流量和差错控制 18 | X.25:目前使用最广泛的分组交换协议, 19 | X.25的优点是经济实惠安装容易、传输可靠性高、适用于 误码率 较高的通路。 20 | X.25的缺点是反复的错误检查过程颇为费时并加长传输时间,协议复杂、时延大,分组长度可变,存储管理复杂。 21 | X.25的速率可高达64K bps(或64kbit/s)。 22 | 23 | * **问:帧中继 FR?** 24 | * **答**:帧中继精简了 X.25 协议,取消第二层的流量控制和差错控制,仅由端到端的高层协议实现。 25 | 26 | * **问:ATM?** 27 | * **答**:异步转移模式(ATM)是一种以固定长度的分组方式,并以异步时分复用方式,传送任意速率的宽带信号和数字等级系列信息的交换设备。它具有高速数据传输率和支持许多种类型如声音、数据、传真、实时视频、CD 质量音频和图像的通信。是未来 21 世纪的主要交换设备。ATM 是一种为了多种业务设计的通用的面向连接的传输模式。它适用于局域网和广域网。 28 | 29 | * **问:X.25、FR、ATM 的异同:?** 30 | * **答**: 31 | 1、 IP 是无连接的,X.25 与 ATM 是面向连接的 32 | 2、帧中继 FR 是 X.25 演变而来的,X.25 去掉校验就是 FR 33 | 3、ATM 与 帧中继 的区别是:ATM 是异步传输,FR 是同步传输 34 | 4、以太网就是我们最常见的,用 MAC 地址工作的,而 帧中继交换机 用的是 DLCI,在接口上以太网是 RJ45,而帧中继是串口 Serial 35 | 36 | * **问:同步传输、异步传输?** 37 | * **答**:同步传输方式中发送方和接收方的时钟是统一的、字符与字符间的传输是同步无间隔的。 38 | 异步传输方式并不要求发送方和接收方的时钟完全一样,字符与字符间的传输是异步的。 39 | **区别点** 40 | 1,异步传输是面向字符的传输,而同步传输是面向比特的传输。 41 | 2,异步传输的单位是字符而同步传输的单位是帧。 42 | 3,异步传输通过字符起始和停止码抓住再同步的机会,而同步传输则是在数据中抽取同步信息。 43 | 4,异步传输对时序的要求较低,同步传输往往通过特定的时钟线路协调时序。 44 | 5,异步传输相对于同步传输效率较低。 45 | **简单形容** 46 | **同步传输** 就是,数据没有被对方确认收到则调用传输的函数就不返回。接收时,如果对方没有发送数据,则你的线程就一直等待,直到有数据了才返回,可以继续执行其他指令。**异步传输** 就是,你调用一个函数发送数据,马上返回,你可以继续处理其他事,接收时,对方的有数据来,你会接收到一个消息,或者你的相关接收函数会被调用。 47 | **形象形容** 48 | 异步传输: 你传输吧,我去做我的事了,传输完了告诉我一声 49 | 同步传输: 你现在传输,我要亲眼看你传输完成,才去做别的事 50 | 51 | **** 52 | 53 | * **问:有人说数据链路层不提供重传机制,为什么以太网会有重传机制?** 54 | * **答**:以太网是不可靠的,根据上面的介绍,它不知道对方有没有收到,但是如果知道自己发错了(CSMA/CD协议,有冲突时要停止发送,发了一半,下次你不给别人重发?)以太网重传是 微秒级 的,传输层是 毫秒级 的,应用层是 秒级 的,所以越底层的重传速度越快,所以可以的话,以太网也要具有重传机制。否则将花费更多代价。 55 | 56 | **** 57 | 58 | * **问:计算机内的传输是()传输,通信线路上的传输是()传输?** 59 | * **答**:并行,串行。并行是指“并排行走”或“同时实行或实施”。在操作系统中是指,一组程序按独立异步的速度执行,无论从微观还是宏观,程序都是一起执行的。对比地,并发是指:在同一个时间段内,两个或多个程序执行,有时间上的重叠(宏观上是同时,微观上仍是顺序执行)。串行通信是指 使用一条数据线,将数据一位一位地依次传输,每一位数据占据一个固定的时间长度。 60 | 61 | **** 62 | 63 | * **问:局域网?** 64 | * **答**:是指一个较小范围内的多台计算机互连,以达到资源和信息共享,优点:具有广播功能。 65 | **** 66 | 67 | * **问:按照因特网的观点,用网桥连接起来的若干局域网仍为一个局域网,因此这些局域网都具有相同的网络号,请问这句话的说法是正确的吗?** 68 | * **答**:对的。 69 | **网桥**:两个或以上端口,可将两个不同物理层,不同 MAC 子层,不同速率的以太网连接起来,适用于用户数不多的情况。可过滤帧,将同一个网段的帧 **丢弃**,不予转发,可隔离冲突域。用网桥互连的网段仍然是一个局域网,只能有一个网络号,这里的分段理解为网桥左边的就是一个段,网桥右边的就是一个段,两个段必须具有相同的网络号,否则我也转发不了。那么问题来了,既然这样不如每台电脑都划分一个段,交换机来了。 70 | **工作方式**:网络 1 和网络 2 通过网桥连接后,网桥接收网络 1 发送的数据包,检查数据包中的地址,如果地址属于网络 1 ,它就将其放弃,相反,如果是网络 2 的地址,它就继续发送给网络 2。这样可利用网桥隔离信息,将同一个网络号划分成多个网段(属于同一个网络号),隔离出安全网段,防止其他网段内的用户非法访问。由于网络的分段,各网段相对独立(属于同一个网络号),一个网段的故障不会影响到另一个网段的运行。 71 | **交换机(switch)**:多端口网桥,每个端口都直接与主机或集线器相连接,并且一般工作在 **全双工** 状态。对于普通的 10 Mbit/s 的共享式网络,若有 N 个用户,则每个用户平均带宽为 1/N。以太网交换机独占传输媒体的带宽,因此对于拥有 N 个端口的全双工交换机的总容量为 2 * N * 10 Mbit/s,每个人都能感觉是 10 Mbit/s,是不是很厉害。生活中可以找一找,公司里面连接电脑都是用的交换机。 72 | 当接收到数据报时,会使用ARP协议在本地地址表寻找,寻找目的IP的MAC地址,如果找不到,会进行广播。有端口回应后,会更新地址表。 73 | 74 | **交换机隔离冲突域**:交换机根据所传递帧的目的地址,将数据报独立从源端口发送到目的端口,避免了与其他端口的碰撞,每个端口属于一个冲突域。如果 MAC 地址不存在,那么交换机才广播到所有端口,接收端口会反馈回新的地址,然后保存。 75 | 76 | **交换机与网桥的异同**:1. 交换机端口更多,网桥一般只有两个端口。2. 网桥端口一般连接局域网网段,交换机一般直接与主机相连或者集线器(Hub)。3. 交换机可允许多对计算机同时通信,网桥只允许两个网段的一对计算机。 77 | 78 | * **问:某一速率为 100M 的交换机有 20 个端口,其一个端口上连着一台笔记本电脑,此电脑从迅雷上下载一部 1G 的电影需要的时间可能是多久?** 79 | * **答**:网速为100Mbps,电影1G为1GByte,先应该变为相同的单位。 80 | 1024MByte/(100/8) = 81.92s。这是最大速度了,所以 **大于这个时间都是合理的**。 81 | 82 | **** 83 | 84 | 85 | * **问:CSMA/CD 工作流程?** 86 | * **答**:先听后发,边听边发,冲突停发,随机重发:即每个站发送数据前先检测总线是否有其他计算机在发送数据,有-不发,没有-发。发送的同时监听着,若有冲突-等待会重发。 87 | 88 | * **问:不同网络结构的访问控制?** 89 | * **答**:1. 星型拓扑结构 和 总线型网络 一般采用 CSMA/CD(载波监听多点接入/碰撞检测) 90 | 2.典型的环形网络有 Token-Ring(IBM的最有影响的令牌环网) 和 FDDI 等,环形网络常用的访问控制方法是基于令牌的访问控制,是一种分布式访问控制技术。令牌环局域网把多个设备安排成一个物理或逻辑环,让一个令牌沿着环依次传递,拿到令牌才能发送数据,所以不会发生冲突。 91 | 3.10BASE-T 采用的逻辑拓扑结构是(总线)方式。 92 | ![图片名称](../../pics/总线拓扑三种结构.jpg) 93 | 94 | **** 95 | 96 | * **问:有固定基础设施的无线局域网的MAC层不能使用 CSMA/CD 协议,而是使用 CSMA/CA 协议。** 97 | * **答**:CSMA/CA协议:工作方式略有不同,比 CSMA/CD 协议可靠,也是先听后发,但在发送完一个帧后会等待一段时间,如果有确认帧返回,则发送下一帧,否则重传上一帧。 98 | 99 | **** 100 | * **问:PPP 接收到一个错误的数据帧时,丢弃该帧并返回一个否认帧?** 101 | * **答**:对的。PPP 数据链路层协议,采用全双工通信方式,是数据链路层的一种可靠协议,但因为它不处理差错,对传输错误的帧只是简单的丢弃并告诉对方,“你发错了”要求对方重发,对错误的处理交给上层。因此不是完全意义上的可靠。 102 | 103 | **** 104 | 105 | * **问:HDLC?** 106 | * **答**:在通信较差的年代,数据链路层使用可靠传输协议是比较好的选择,因此就有了我们的 HDLC 协议,会使用三类反馈帧来确保传输的可靠性,信息帧,监督帧,无编号帧。 107 | 108 | **** 109 | 110 | * **问:HDLC 和 PPP 比较?** 111 | * **答**:PPP 是面向字节的,HDLC 是面向比特的。现在通信质量很高,一般不会出现很多错误,所以不需要那么繁杂的保证可靠,快就完事了,因此 PPP 应用的较多。 112 | **** 113 | 114 | * **问:单工、半双工、全双工?** 115 | * **单工**:单工数据传输只支持数据在一个方向上传输,在同一时间只有一方能接受或发送信息,不能实现双向通信,举例:电视,广播。 116 | 117 | **半双工**:半双工数据传输允许数据在两个方向上传输,但是,在某一时刻,只允许数据在一个方向上传输,它实际上是一种切换方向的单工通信,举例:对讲机。 118 | 119 | **全双工**:全双工数据通信允许数据同时在两个方向上传输,因此,全双工通信是两个单工通信方式的结合,它要求发送设备和接收设备都有独立的接收和发送能力,在同一时间可以同时接受和发送信息,实现双向通信,举例:电话通信。 120 | ![单工-半双工-全双工](../../pics/单工-半双工-全双工.jpg) 121 | **** 122 | * **问:网卡是全双工的、计算机与打印机之间的通信属于?** 123 | * **答**:单工,是不是有点奇怪,不是有反馈么?打印机是输出设备,电脑会依据要打印的内容使打印机工作,如果打印机没有完成指令,卡纸、没墨及一些常见问题,电脑系统 会检查输出设备的工作情况,并作出提示。类似的还有 遥控 等。 124 | 网卡实现的主要功能是物理层与数据链路层的功能: 125 | 1、数据的封装与解封:发送时将上一层交下来的数据加上首部和尾部,成为以太网的帧。接收时将以太网的帧剥去首部和尾部,然后送交上一层。 126 | 2、链路管理:主要是CSMA/CD(Carrier Sense Multiple Access with Collision Detection ,带冲突检测的载波监听多路访问)协议的实现。 127 | 3、编码与译码:即曼彻斯特编码与译码。 -------------------------------------------------------------------------------- /notes/计算机网络/计算机网络-物理层.md: -------------------------------------------------------------------------------- 1 | # 二、物理层 2 | 3 | * **双绞线**:两根铜线相互缠绕,可以传输数字和模拟信号,信号保真传输距离 100 米。 4 | 5 | * **同轴电缆**:内有一根铜芯,抗干扰强,传输距离远。 6 | 7 | * **10Base5**:10Mbit/s,基带信号,每一段电缆最长为 500 米。 8 | 9 | * **10Base-F/T**:F 光纤,T 双绞线。 10 | 11 | * **带宽**:分为模拟信号的带宽和数字信号的带宽,因为以前都是模拟信号,所以以前的带宽定义是:通信线路允许通过的最高频率 - 允许通过的最低频率。比如最高 3400 Hz,最低 300 Hz,那么带宽就是 3100 Hz。现在很多都用上了数字信号,因此现在带宽表示:网络通信线路传送数据的能力,就是我们常常问的网速:x bit/s。 12 | 13 | * **中继器和放大器**: 一般传输数据用双绞线,传输距离过长信号会衰弱,需要中继器放大数字信号,放大器放大模拟信号。 14 | 15 | * **集线器[Hub]**:实质上就是一个多端口的中继器,中继器只有两个口,一进一出,但是集线器可以有多个口。我们电脑端口不足,常用的外接扩展坞就是最常见的Hub。多端口中继器,RJ45 端口协议,功能是不仅对接收到的信号进行放大,扩大网络的传输距离,同时还能把所有节点集中在以它为中心的节点上。集线器采用了 CSMA/CD(载波帧听多路访问/冲突检测)协议,CSMA/CD 为 MAC 层协议,所以集线器也含有数据链路层的内容。\ 16 | **缺点**:1.集线器不能隔离 **碰撞域** 和 **广播域**,因此大部份集线器已被 **交换机** 取代。当集线器收到数据后,并不知道这个数据属于哪个电脑的,只能所有电脑都发一份。2.集线器不能互连使用了不同以太网技术的互联网。\ 17 | **工作方式**:集线器的工作过程是非常简单的:首先是节点发信号到线路,集线器接收该信号,因信号在电缆传输中有衰减,集线器接收信号后将衰减的信号整形放大,最后集线器将放大的信号**以广播的形式**转发给其他所有端口。 18 | 19 | * **碰撞域(冲突域)**:如果一个网络上 **两台计算机在同时通信时会发生冲突,那么这个网络就属于一个冲突域**。当计算机 a 的网卡将信息通过双绞线送到集线器上时,集线器并不会直接将信息送给计算机 b,它会将信息进行 **广播**,所以多台计算机广播肯定会发生冲突,集线器不能隔离冲突域。 20 | 21 | * **广播域**:一块网卡发出一个广播,只要能收到这个广播的网卡就是一个广播域。 22 | 23 | **物理层的设备(集线器(Hub),中继器,放大器)**:不能隔离冲突域,也不能隔离广播域。 24 | 25 | **数据链路层的设备(交换机,网桥)**:能隔离冲突域,也不能隔离广播域。 26 | 27 | **网络层的设备(路由器)**:能隔离冲突域,也能隔离广播域。 28 | 29 | **** 30 | 31 | * **问:ADSL技术主要解决的问题是?** 32 | 33 | * **答**:ADSL 技术是一种非对称数字用户实现 **宽带接入** 互连网的技术,在一对双绞线上提供上行和下行速率不同的带宽,它允许高达 8 Mbps 的下行速度和 1 Mbps 的上行速度。 34 | 35 | **** 36 | 37 | * **问:承载信息量的基本信号单位是?** 38 | * **答**:码元,不是比特哦。一般网络速率有两种描述形式,一种 **比特率**,这个应该都知道 10Mbit/s 嘛,其实它传输的是二进制 **码元**,码元的传输速率又叫 **波特率**,二进制的码元只能携带 1 bit 信息,所以正常的码元会携带更多的信息,一般为 16 进制。 39 | 40 | **** 41 | 42 | * **问:传输二进制数字信号需要的带宽比模拟信号所需要的带宽小?** 43 | * **答**:首先明白带宽是什么?就是表示传输信号的频率宽度,那么信号的波动范围越大肯定需要频率更宽的度来表示,而数字信号只有01,所以只需要两种宽度就可以表示。区间小,所以带宽就小。 44 | 45 | **** 46 | 47 | * **问**:在使用 3 个集线器级连的10BASE-T网络中,计算机之间的最长布线长度为( )米? 48 | * **答**:T 是双绞线,三个集线器可以连接 4 段,所以最远是 400米。 49 | 50 | **** 51 | 52 | * **问:将两台 PC 机通过网卡用网线直接连接,应该采用哪种类型的双绞线?** 53 | * **答**: 双绞线的两种线序: 54 | 568A 线序:绿白—1,绿—2,橙白—3,蓝—4,蓝白—5, 橙—6,棕白—7,棕—8 55 | 568B 线序:橙白—1,橙—2,绿白—3,蓝—4,蓝白—5, 绿—6,棕白—7,棕—8 56 | 两种线序仅仅把 1 和 3 对调,2 和 6 对调。 57 | 直通线:单独使用一种线序,2 头 A 序或 2 头 B 序。交叉线:1 头 A 序 1 头 B 序。同层设备使用交叉线,不同层设备使用直通线。 58 | 主机和路由器同属网络层设备所以使用交叉线。而交换机属于链路层设备,如果与网络层设备相连就需要使用直通线,如果与交换机相连需要使用交叉线。 -------------------------------------------------------------------------------- /notes/计算机网络/计算机网络-网络层.md: -------------------------------------------------------------------------------- 1 | # 四、网络层 2 | * **传输单位**:分组,(数据报)见下面详解 3 | 4 | * **功能**:组包[传输分组用]、拆包[接收分组用]、路由选择、分组转发 5 | 6 | * **协议**:IP、CIDR、NAT;IP相关的三个协议:ARP、DHCP、ICMP;三种常用路由选择协议:RIP、OSPF、BGP 7 | 8 | **** 9 | 10 | * **问:路由器(Router)的功能?** 11 | 12 | * **答**:功能包括:路由选择,分组转发。当中继系统是集线器或交换机时,一般不称为网络互连,这仅仅是把网络扩大了,扔然是一个网络,互联网都是指路由器进行互连的网络,路由器总是具有两个或两个以上的 IP 地址。 13 | 14 | **** 15 | 16 | * **问:转发 和 路由选择 是同一概念,都是指当交换结点收到分组后,根据其目的地址查找转发表,将该分组发送出去?** 17 | 18 | * **答**:错误。转发 发生在 路由器 上,分组到达 路由器 后,由 路由器 检查分组地址并将它转发到一个邻接的局域网( LAN )上。路由选择 是指 通过互连网络从 源节点 到 目的节点 传输信息的路线,包括多个路由器的选择,在确定最佳路径的过程中,路由选择算法需要初始化和维护路由选择表(routingtable)。路由选择表中包含的路由选择信息根据路由选择算法的不同而不同。 19 | 20 | **** 21 | 22 | * **问:路由选择(RIP、OSPF、BGP)?** 23 | 24 | * **答**:网络错综复杂,选择好的路由还是很关键的,有两大类,静态路由选择 和 动态路由选择,我们从字面上看也知道,静态路由选择简单开销小,但不能适应网络状态变化,适合很小的网络,手动配置路由;那我们更多的使用肯定是动态路由,动态调整听起来就很厉害,因此它主要分为两种基本类型 距离-向量路由算法(RIP) 和 链路状态路由算法(OSPF)。**RIP**:简单点说就是经过的路由器越少,那么我就推荐这条路线给你用。**OSPF**:则是每个路由器都有一份世界地图,这样肯定好找,但是要经常更新此地图。OSPF 适合于在大型的互连网上使用,而 RIP 适合于在小型的互连网上使用。记住两个算法都是 **动态路由** 的算法。 25 | 26 | * **BGP**:外部网关协议,是不同自治系统的路由器之间交换路由信息的协议。采用 路径-向量路由选择协议,寻找到一条到达的就很不错了,不是找一条最佳路由,因为规模实在太大,不现实。通过 TCP 连接与其他自治系统进行交流。 27 | 28 | **RIP:传输层 UDP 传送、OSPF:IP 数据报传送、BGP:TCP 连接传送** 29 | 30 | **** 31 | 32 | * **问:电路交换?** 33 | 34 | * **答**:此种方式在通信之前需要在通信的双方间建立成一条被双方独占的物理通道(显然工作在 **物理层**)。这个通道是由双方间的交换设备和链路逐段连接而建成的。优点肯定就是延迟小,实时性强,相当于为你一家拉了一条线,缺点就是利用率低,即使你不发送消息,我也不能用,就很麻烦了。 35 | 36 | **** 37 | 38 | * **问:报文交换?** 39 | 40 | * **答**:以【报文】为数据交换的单位(那么肯定工作在 **传输层**,只有传输层有报文嘛),报文中携带目标地址、源地址等信息,交换结点的过程中采用存储转发的传输方式。就是将整个报文存下来,然后再发往下一个节点。优点:肯定是改进上面的啦,不用独占信道,一个报文,随便走海路空,最终到达即可。 缺点很明显,对报文大小没有限制,网络节点可能需要很大的缓存空间。 41 | 42 | **** 43 | 44 | * **问:分组交换?** 45 | 46 | * **答**:1. 采用存储转发的方式,2. 在整个传送过程中,不需事先建立连接,3. 各分组携带必要的控制信息,比如分组号,网络节点为每个分组进行路由选择。优点:当然是去掉上一种方式的缺点喽,分组就是将原来大的报文拆分,加上序号、IP 地址等必要的信息,再存储转发,这样是不是就快很多了(**这里是不是也解释了 报文 和 分组 的区别了**)。 47 | 分组交换的方式有两种:一种是 **面向连接的虚电路** 和 **无连接的数据报**。是不是又有点绕了,前面介绍的都是以前的概念,电路交换肯定需要一条独占的物理通道,报文交换肯定是随意转发不用固定线路的,那么分组交换就是集各家所长,诞生出来的。 48 | 49 | * **无连接的数据报工作方式**:当主机 A 给主机 B 发送了一个报文,高层协议将 **报文拆分成若干带有序号和完整目的地址的分组**,由路由器查找转发表转发。无需建立连接,最大努力交付,是不是和以前的 **报文交换** 有点像,但是分组显然更小一些。 50 | 51 | * **面向连接的虚电路** :分组获得同上,在发送数据之前,在源主机和目的主机之间建立一条虚连接。这个和物理连接不同,虚电路是建立了一条线路,记录转发的路由器编号,下次依然是这些路由器转发,不是占用一条物理信道,和 电路交换 不同。 52 | 53 | * **问:对 IP 数据报进行分片的主要目的是?** 54 | 55 | * **答**:适应各个物理网络不同的 MTU(允许的最大传输单元) 长度。 56 | 57 | **** 58 | 59 | * **问:电路交换、报文交换、分组交换?** 60 | 61 | * **答**:电路交换是不是最早的,放在了 **物理层**,发现利用率太低。然后我们到 **传输层**,将整个报文当做单位,这样灵活一些,发现报文又太大。最后我们就拆吧,将大报文拆成小的分组,由 **网络层** 进行转发。显然后者更先进,使用也最普遍。 62 | ![图片名称](../../pics/电路-报文-分组交换.jpg) 63 | 64 | **** 65 | 66 | * **问:网络层尽最大努力交付?** 67 | 68 | * **答**:网络层**不能保证**发送出来的 IP 数据报,一定无差错交付、在某一规定时间内交付、按发送顺序交付、不重复交付到目的主机,这些他都**不能保证**!能做的就是不故意丢弃,如果检测出**首部校验和**有错误,或网络信息量过大,或路由器无缓存空闲空间,就会丢弃它。 69 | 70 | **** 71 | 72 | * **问:IP 地址的分类?** 73 | 74 | * **答**:五类,ABC 基本类 DE 保留。 75 | 76 | **A 类**:网络地址8位,0开头;地址(0.0.0.0 - 127.255.255.255) 77 | **B 类**:网络地址16位,10开头;地址(128.0.0.0 - 191.255.255.255) 78 | **C 类**:网络地址24位,110开头;地址(192.0.0.0 - 223.255.255.255) 79 | **D 类**:不分网络地址和主机地址,是一个保留地址,1110开头;地址(224.0.0.0 - 239.255.255.255) 80 | **E 类**:不分网络地址和主机地址,1111开头;地址(240.0.0.0 - 255.255.255.255),为将来使用,看来是没机会了,因为 128 的 ip 地址已经出来了。 81 | **注意**:除网络地址位数后,其余全 0 的是网络号,全 1 的是广播地址,其余才可以当做主机地址分配。 82 | **0.0.0.0**:保留地址,意思是本网络,A 类网络地址少一个。 83 | **127.x.x.x**:127 开头的 IP 地址作为环回地址,用来测试软件。使用这个地址时,分组永远不会离开这个机器,A 类网络少一个。 84 | 85 | 86 | 87 | **D 类**:组播地址,D 类地址不能出现在 IP 报文的源 IP 地址字段。组播换中,数据包的目的地址不止一个,而是一组,一旦有信息流向组地址,数据就开始向接受者传输,组中成员都能接受到数据包,组播中的成员是动态的。 88 | 89 | 区分网络号、主机号和子网号,子网是从主机号中分出来的地址。 90 | 91 | **** 92 | 93 | * **问:单播、组播、广播?** 94 | 95 | * **答**:分别指一对一,一对一组,一对所有的通信模式。 96 | 97 | * **[ 专用 IP 地址 ]**:如果作为目的地址,是不会在网络上传送的,因为这是需要被重复使用的内部IP地址,因特网规定:A类网络:10.0.0.1-10.255.255.254; 1个A类网络作为专用网络 98 | B类网络:172.16.0.1-172.31.255.254;16个B类网络作为专用网络 99 | C类网络:192.168.0.0-192.168.255.0; 256个C类网络作为专用网络 100 | 101 | * **解释 NAT(Network Address Translation,网络地址转换)**:因为某些机构并不需要连接到因特网,只需要内部通信,这样就不需要给每台电脑都分配全球唯一 IP 了,可以参考 IP 地址的分类。这些内部 IP 地址每个公司都可以内部设定,如果需要那么会自动转化为全球 IP 地址,因此一个公司只需要少量的 IP 地址就可以了。(专用网的主机不联系因特网的主机,因特网的主机就一定不会联系专用网的主机,因为找不到) 102 | 103 | 104 | 105 | * **练习:下面哪一个地址不能用作某个局域网内网 IP?** 106 | A. 192.168.201.114 B. 172.16.4.25 **C. 127.0.0.1** D. 10.0.0.1 107 | 108 | **** 109 | 110 | * **问:168.192.33.124/26 (CIDR)所对应的子网掩码是什么?** 111 | 112 | * **答**:255.255.255.192。使用了CIDR(无分类编址),这是为了解决IP地址耗尽而提出的,斜线后的数字 26 表示子网掩码 1 的数量,从左到右 26 个 1,对应的子网掩码如答案。 113 | 114 | **** 115 | 116 | * **IP 相关的三个协议:ARP、RARP、DHCP、ICMP** 117 | 118 | * **问:ARP(IP 地址 -> MAC 地址映射表)RARP(MAC 地址 -> IP 地址 映射表)后者就不介绍了?** 119 | 120 | * **答**:里面存放的是**所有局域网**上的各主机和路由器的 IP 地址到硬件地址的映射表,ARP 的职责就是动态的维护该表。**工作方式:**当源主机向本局域网上的某个主机发送 IP 分组时,应先在 ARP 缓存中查找 IP 地址对应的 MAC 地址,如果没有,广播 ARP 请求分组,获得响应后写入 ARP 缓存。 121 | 122 | **** 123 | 124 | * **问:DHCP(动态主机配置协议)?** 125 | 126 | * **答**:ARP 可以将 IP 地址转换为物理地址,那么有什么可以将物理地址转换为 IP 地址呢,答案就是 DHCP 拥有这种功能,且会动态给主机分配 IP 地址 127 | 128 | **** 129 | 130 | * **问:ICMP(差错报文协议)?** 131 | 132 | * **答**:主机在发送数据报时,经常会由于各种原因反馈回错误,如检测出错误的路由器,那么它会反馈给主机,因此 ICMP 可根据错误类型,重发失败的数据报。一般包括两大类:ICMP 差错报告报文(终点不可达、源站抑制、时间超时、参数问题、改变路由),和 ICMP 询问报文。 133 | 134 | **** 135 | 136 | * **问:网关 和 路由器 的区别?** 137 | * **答**:在计算机网络中,能将异种网络互联起来,实现不同网络协议相互转换的网络互连设备是网关 。网关 和 路由器 最大的区别是是否连接相似的网络。如果连接相似的网络,则称为路由器。而连接不相似的网络,称为网关。逻辑层面:相似的网络:如果都是互联网上的两个网络,我们称为相似的网络。不相似的网络:如果一个是私网,一个是公网。我们称为不相似的网络。物理层面:相似的网络:都是以太网或者同一种介质的网络。不相似的网络:一边是以太,一边是 SDH 或者 ATM 等。 138 | **网关**:由于历史的原因,许多有关 TCP/IP 的文献曾经把网络层使用的路由器称为网关,在今天很多局域网采用都是路由来接入网络,因此通常指的网关就是 路由器的 IP ! 139 | **工作方式**:如果网络 A 中的主机发现数据包的目的主机不在本地网络中,就把数据包转发给它自己的网关,再由网关转发给网络 B 的网关,网络 B 的网关再转发给网络 B 的某个主机 140 | 141 | * **问:路由器和交换机的区别?** 142 | 1、工作层次不同:交换机工作在数据链路层,而路由器工作在网络层。 143 | 2、数据转发所依据的对象不同:交换机的数据转发依据是利用MAC地址来确定转发数据的目的地址;而路由器是依据ip地址进行工作的。 144 | 3、传统的交换机只能分割冲突域,不能分割广播域;而路由器可以分割广播域 145 | -------------------------------------------------------------------------------- /notes/计算机网络/计算机网络-面试题目综合.md: -------------------------------------------------------------------------------- 1 | # 七、计算机网络面试题目综合 2 | 3 | **问:osi七层模型,以及对应都有什么协议?** 4 | **问:tcp 三次握手和四次挥手?** 5 | **问:udp和tcp区别?** 6 | **问:http报文都有什么?** 7 | **问:客户端发起http到服务端返回的详细过程?** 8 | 9 | 10 | * **面:网络相关,桥接和NAT虚拟机三种网络模式?** 11 | * **答**:FTP 文件传输协议,是因特网上使用最广泛的传送协议。使用 TCP 连接,端口号 21,20。在传输文件时需建立 2 个 TCP 连接,21 号端口 控制连接 用于进行命令控制, 20 号端口 数据连接 进行真正的数据传送。先建立 控制连接,当需要传送数据时建立 数据连接,数据传输完成 关闭数据连接,控制连接只有等到关闭命令时才会关闭,不是一起关闭的,也不是一起建立的。 12 | ** TFTP**:使用的是 UDP 协议,对于每一个数据报在获得确认后才发送另一个数据报,灵活,使用资源少,流量控制较简单,是一种平等的传输协议。 13 | 区别:使用协议不同,端口号不同,FTP 在速度方面没有 TFTP 快但是 FTP 安全性好。 14 | 15 | **** 16 | 17 | * **TELNET ?** 18 | * **答**:是 Internet 远程登录服务的标准协议和主要方式。它为用户提供了在本地计算机上完成远程主机工作的能力。在终端使用者的电脑上使用 Telnet 程序,用它连接到服务器。终端使用者可以在 Telnet 程序中输入命令,这些命令会在服务器上运行,就像直接在服务器的控制台上输入一样。可以在本地就能控制服务器。要开始一个 Telnet 会话,必须输入用户名和密码来登录服务器。Telnet 是常用的远程控制 Web 服务器的方法。 19 | 20 | **** 21 | 22 | * **问:DNS(Domain Name System 域名解析系统)?** 23 | * **答**:如果不允许你用 www.baiwu.com 来访问百度的首页,你要咋办。当然可以直接用 IP 地址访问,在网上搜了一下,本地可以 ping www.baidu.com 发现有好几个 IP 地址,这也好理解,大公司为了分担服务器压力,可以给一个域名分配好几个 IP 地址,然后使用循环分配使用或者其他方式,降低服务器压力,这样才不会让你感觉卡卡的嘛。题外话,DNS 就是可以根据你给的英文网址,查找对应的 IP 地址,你的浏览器再根据 IP 地址查找目的服务器,如果直接用 IP 地址是不是更快,是的。 24 | 25 | **** 26 | ### 电子邮件使用的协议 27 | * **问:SMTP(Simple Message Transfer Protocol 简单邮件传输协议)?** 28 | * **答**:简单邮件传送协议,使用 25 号端口。 29 | 30 | **** 31 | 32 | * **问:POP3(Post Office Protocol 3 邮局通讯协议第三版)?** 33 | * **答**:邮局协议版本 3,一个邮件读取协议。 34 | 35 | **** 36 | 37 | * **问:Outlook 等常用的电子邮件软件使用 SMTP 协议进行邮件的收发?** 38 | * **答**:错误!使用 SMTP 来发送邮件,使用 POP3 来接受邮件。 39 | 40 | **** 41 | 42 | * **问:电子邮件收发过程?** 43 | * **答**:首先 发信人使用代理来编辑邮件,用户代理使用 SMTP 将邮件发送给邮件服务器缓存队列中等待发送,邮件服务器发现有邮件,便与接收端服务器使用 SMTP 协议进行 TCP 连接,发送完后便断开连接。接收端服务器接受到邮件后放入收信人用户邮箱中。收信人收信时使用 POP3 将自己的邮件从接收端服务器中取出。因此可以简单记忆为发送邮件使用 SMTP,而接受邮件需要使用 POP3。 44 | 45 | **** 46 | 47 | * **问:介绍 HTTP (Hyper Text Transport Protocol 超文本传输协议)协议的传输过程?** 48 | * **答**: 服务流程:从协议执行过程来说,当浏览器要访问 www 服务器时,首先要对服务器进行域名解析(DNS 协议)。一旦获得 IP 地址【网络层的路由选择与转发,找到目的 IP 地址】,浏览器要通过 TCP 三次握手和服务器建立连接。每个服务器都有一个服务进程,它不断监听 TCP 端口号 80,当监听到浏览器请求后便和浏览器建立连接。TCP 连接建立后,浏览器就向服务器发送某个 HTTP 请求。如 localhost/test/index.jsp 。服务器收到请求后,将构建的服务页面返回给浏览器,浏览器对信息进行解释后,显示给客户。 49 | 50 | * **问:介绍 HTTPS (超文本传输安全协议)?** 51 | * **答**: 简单讲是 HTTP 的安全版。即 HTTP 下加入 SSL 层。也就是说它的主要作用可以分为两种:一种是建立一个信息安全通道,来保证数据传输的安全;另一种就是确认网站的真实性,凡是使用了 HTTPS 的网站,都可以通过点击浏览器地址栏的锁头标志来查看网站认证之后的真实信息,也可以通过 CA 机构颁发的安全签章来查询 。 52 | 53 | * **问:HTTPS 和 HTTP 的区别主要?** 54 | * **答**:1、HTTP 是超文本传输协议,信息是明文传输,HTTPS 则是具有安全性的 SSL 加密传输协议。2、HTTP 和 HTTPS 端口不同,前者是 80,后者是 443。 55 | HTTPS 会明显比 HTTP 协议慢很多:HTTPS 比 HTTP 更慢,因为多了协商密钥、验证证书的环节,而且之后传输的所有数据都需要加密解密。同样的服务器,能承受的 HTTPS 并发数要比 HTTP小很多。HTTP 使用 TCP 三次握手建立连接,客户端和服务器需要交换 3 个包,HTTPS 除了 TCP 的三个包,还要加上 SSL 握手需要的 9 个包,所以一共是 12 个包。 56 | HTTPS 已被广泛地用于 Web 浏览器与服务器之间的身份认证和加密数据传输,HTTPS 协议需要到 CA 申请证书,一般免费证书较少,因而需要一定费用。 57 | 58 | * **HTTP 状态码** 59 | 1xx(临时响应)表示临时响应并需要请求者继续执行操作的状态代码。 60 | 2xx (成功)表示成功处理了请求的状态代码。 61 | 3xx (重定向)表示要完成请求,需要进一步操作。 通常,这些状态代码用来重定向。 62 | 4xx(请求错误)这些状态代码表示请求可能出错,妨碍了服务器的处理。 63 | 5xx(服务器错误)这些状态代码表示服务器在尝试处理请求时发生内部错误。 这些错误可能是服务器本身的错误,而不是请求出错。 64 | 65 | 200: 请求已成功,请求所希望的响应头或数据体将随此响应返回。-正常响应。 66 | 304: 如果客户端发送了一个带条件的 GET 请求且该请求已被允许,而文档的内容(自上次访问以来或者根据请求的条件)并没有改变,则服务器应当返回这个状态码。304响应禁止包含消息体,因此始终以消息头后的第一个空行结尾。 67 | 403: 服务器已经理解请求,但是拒绝执行它。-无权限。 68 | 404: 未找到,服务器找不到所请求的网页。 69 | 500:内部服务器错误 70 | 505: 服务器不支持,或者拒绝支持在请求中使用的 HTTP 版本。-服务端错误。 71 | 72 | **练习**:A,B 两台机器都正常工作,B 机器未监听任何端口。如果 A 机器向 B 机器 80 端口发送 SYN 包,会收到何种类型的回包? 73 | **答**:因为 B 机器未监听任何端口,A 又向 B 的 80 端口发送了数据包,所以 B 不会建立连接,会异常结束建立连接,会发送 RST 包,如果是正常结束的话就会发送 FIN 包, RST 是 TCP 连接中 出现差错产生的,RST 置 1可以用来拒绝一个非法的报文段或拒绝打开一个连接。**RST 是 TCP 报文段首部里的字段,还有其他一些很容易忘的字段。** 74 | **** 75 | 76 | * **问:GET 和 POST的区别?** 77 | * **答**:[参考博客](https://www.cnblogs.com/logsharing/p/8448446.html) 78 | 1、GET 请求只能进行 URL 编码,而 POST 支持多种编码方式。 79 | 2、GET 请求参数会被完整保留在浏览器历史记录里,而 POST 中的参数不会被保留。 80 | 3、GET 参数通过 URL 传递,POST 放在 Request body 中,因此 GET 比 POST 更不安全,因为参数直接暴露在 URL 上,所以不能用来传递敏感信息。 81 | 参考:GET 和 POST 是什么?HTTP 协议中的两种发送请求的方法。HTTP 是什么?HTTP 是基于 TCP/IP 的关于数据如何在万维网中如何通信的协议。HTTP 的底层是 TCP/IP。所以 GET 和 POST 的底层也是 TCP/IP,也就是说,GET/POST 都是 TCP 链接。GET 和 POST 能做的事情是一样一样的。你要给 GET 加上 Request body,给 POST 带上 URL 参数,技术上是完全行的通的。 82 | 83 | 84 | 85 | * **问:POST 方法比 GET 方法安全?** 86 | * **答**:有人说POST 比 GET 安全,因为数据在地址栏上不可见。然而,从传输的角度来说,他们都是不安全的,因为 HTTP 在网络上是明文传输的,只要在网络节点上捉包,就能完整地获取数据报文。要想安全传输,就只有加密,也就是 HTTPS。 87 | 88 | * **问:GET 方法的长度限制是怎么回事?** 89 | * **答**:网络上都会提到浏览器地址栏输入的参数是有限的。首先说明一点,HTTP 协议没有 Body 和 URL 的长度限制,对 URL 限制的大多是浏览器和服务器的原因。浏览器原因就不说了,服务器是因为处理长 URL 要消耗比较多的资源,为了性能和安全(防止恶意构造长 URL 来攻击)考虑,会给 URL 长度加限制。 90 | 91 | * **问:POST 方法会产生两个 TCP 数据包?** 92 | * **答**:有些文章中提到,POST 会将 header 和 body 分开发送,先发送 header,服务端返回 100 状态码再发送 body。HTTP 协议中没有明确说明 POST 会产生两个 TCP 数据包,而且实际测试(Chrome)发现,header 和 body 不会分开发送。所以,header 和 body 分开发送是部分浏览器或框架的请求方法,不属于 POST 必然行为。 93 | 94 | **** 95 | 96 | * **问:同源策略是浏览器的安全基石,但互联网业务往往需要实现跨域通信,以下哪一种方案可以实现跨域?** 97 | 98 | * **答**:同源策略(Same Origin Policy,SOP)需要同时满足以下三点要求: 99 | 1)协议相同 2)域名相同 3)端口相同 100 | http:www.test.com与https:www.test.com 不同源——协议不同 101 | http:www.test.com与http:www.admin.com 不同源——域名不同 102 | http:www.test.com与http:www.test.com:8081 不同源——端口不同 103 | 只要不满足其中任意一个要求,就不符合同源策略,就会出现“跨域。 104 | 不同源的客户端脚本(js,actionscript)在没有明确的授权的情况下不能读取对方的资源。 105 | 以下三种可实现跨域: 106 | 跨源资源(CrossOrigin Resources Sharing,CORS)是一种允许多种资源在一个 web 页面请求域之外的另一个域的资源的协议,是为了让 AJAX 能够跨域而生的。 107 | 内容安全策略(Content Security Policy,CSP)是一种白名单机制,限制网站中是否可以包含某来源的内容。 108 | Oauth是一个关于授权的开放网络标准,相当于在客户端与服务器之间添加了一个授权层。 109 | 110 | **** 111 | 112 | * **问:简述cookie和session的区别?** 113 | * **答**:1,session 在服务器端,cookie 在客户端(浏览器) 114 | 2、session 的运行依赖 session id,而 session id 是存在 cookie 中的,也就是说,如果浏览器禁用了 cookie ,同时 session 也会失效,存储Session时,键与Cookie中的sessionid相同,值是开发人员设置的键值对信息,进行了base64编码,过期时间由开发人员设置 115 | 3、cookie安全性比session差 116 | 117 | **** 118 | 119 | * **问题?** 120 | **UDP不是面向连接的,所以源IP地址通常可以伪造的? 121 | TCP SYN数据包中的源IP地址通常是可以伪造的? 122 | TCP是面向连接的,三次握手后,源IP不一定是真实的? 123 | 禁止PING就是禁止ICMP协议?** 124 | 125 | 126 | -------------------------------------------------------------------------------- /notes/计算机网络/计算机网络目录.md: -------------------------------------------------------------------------------- 1 | **参考资料:《天勤 2019 计算机网络高分笔记》,或者王道的高分笔记也可以。计算机考研 408 必选资料,谁用谁知道。课后习题可以巩固书中通俗易懂的知识,内容较全面。** 2 | 3 | 4 | # 计算机网络 5 | * [一、基础概念](计算机网络-基础概念.md) 6 | * [二、物理层](计算机网络-物理层.md) 7 | * [三、数据链路层](计算机网络-数据链路层.md) 8 | * [四、网络层](计算机网络-网络层.md) 9 | * [五、传输层](计算机网络-传输层.md) 10 | * [六、应用层](计算机网络-应用层.md) 11 | 12 | -------------------------------------------------------------------------------- /pics/Algorithm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/light-wl/Learning/23d7c87530993e4948252e9cf7543c67044fcd85/pics/Algorithm.png -------------------------------------------------------------------------------- /pics/C++.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/light-wl/Learning/23d7c87530993e4948252e9cf7543c67044fcd85/pics/C++.png -------------------------------------------------------------------------------- /pics/DataBase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/light-wl/Learning/23d7c87530993e4948252e9cf7543c67044fcd85/pics/DataBase.png -------------------------------------------------------------------------------- /pics/IQ.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/light-wl/Learning/23d7c87530993e4948252e9cf7543c67044fcd85/pics/IQ.jpg -------------------------------------------------------------------------------- /pics/Internet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/light-wl/Learning/23d7c87530993e4948252e9cf7543c67044fcd85/pics/Internet.png -------------------------------------------------------------------------------- /pics/System.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/light-wl/Learning/23d7c87530993e4948252e9cf7543c67044fcd85/pics/System.png -------------------------------------------------------------------------------- /pics/Web.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/light-wl/Learning/23d7c87530993e4948252e9cf7543c67044fcd85/pics/Web.jpg -------------------------------------------------------------------------------- /pics/go.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/light-wl/Learning/23d7c87530993e4948252e9cf7543c67044fcd85/pics/go.png -------------------------------------------------------------------------------- /pics/go继承调用规则.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/light-wl/Learning/23d7c87530993e4948252e9cf7543c67044fcd85/pics/go继承调用规则.png -------------------------------------------------------------------------------- /pics/java-数组.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/light-wl/Learning/23d7c87530993e4948252e9cf7543c67044fcd85/pics/java-数组.jpg -------------------------------------------------------------------------------- /pics/java.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/light-wl/Learning/23d7c87530993e4948252e9cf7543c67044fcd85/pics/java.png -------------------------------------------------------------------------------- /pics/maven目录.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/light-wl/Learning/23d7c87530993e4948252e9cf7543c67044fcd85/pics/maven目录.png -------------------------------------------------------------------------------- /pics/python.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/light-wl/Learning/23d7c87530993e4948252e9cf7543c67044fcd85/pics/python.png -------------------------------------------------------------------------------- /pics/单工-半双工-全双工.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/light-wl/Learning/23d7c87530993e4948252e9cf7543c67044fcd85/pics/单工-半双工-全双工.jpg -------------------------------------------------------------------------------- /pics/总线拓扑三种结构.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/light-wl/Learning/23d7c87530993e4948252e9cf7543c67044fcd85/pics/总线拓扑三种结构.jpg -------------------------------------------------------------------------------- /pics/排序算法时间效率对比图.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/light-wl/Learning/23d7c87530993e4948252e9cf7543c67044fcd85/pics/排序算法时间效率对比图.jpg -------------------------------------------------------------------------------- /pics/电路-报文-分组交换.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/light-wl/Learning/23d7c87530993e4948252e9cf7543c67044fcd85/pics/电路-报文-分组交换.jpg --------------------------------------------------------------------------------