小知识点
OSI参考模型
- 物理层
集线器 - 数据链路层
网卡,交换机 - 网络层(IP协议)
选择合适的网间路由和交换结点,确保数据及时传送,将从下层接收到的数据进行IP地址的封装与解封装。常把这一层数据叫做数据包,主要设备:路由器。 - 传输层(TCP/UDP协议)
定义了一些传输数据的协议和端口,如TCP、UDP协议,主要将从下层接收的数据进行分段和传输,到达目的地址后再进行重组,以往把这一层数据叫做段。
TCP(传输控制协议)
我给你发了个东西+收到了+好的(这个如果没有的话,那么当过期的数据撞到服务器时,就会建立连接,但如果有第三次,就会再次确认一下)
SYN:SYN=0时,表示同步序号,用来建立连接。SYN标志位和ACK标志位搭配使用,当连接请求的时候,SYN=1,ACK=0;连接被响应的时候,SYN=1,ACK=1;
UDP(用户数据报协议):
只负责发出去,但不保证你收到
可靠性,速度,连接的有无,传输数据大小 - 会话层
- 表示层
主要是进行对接收的数据进行解释(如图片、声音等) - 应用层(HTTP/FTP协议)
FTP(各种文件下载)、浏览器、QQ等
HTTP
- 无状态:对之前的请求是无记忆的。
- 无连接:一次请求和响应,之后就会断开连接
四叉树:
Linux中一个端口能够接受tcp链接数量的理论上限
无上限
linux socket使用16bit无符号整型表示端口号,但端口号可复用,所以一个客户端最多建立65536个socket连接不对。
进程
资源分配的最小单位,每个进程都有独立的代码和数据空间(进程上下文)
线程
并发调度运行的基本单位。线程间相互独立,可以各自独立运行在不同的处理器上。同一类线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器(PC)。
多线程编程
- 继承Thread
class Thread1 extends Thread{
private String name;
public Thread1(String name) {
this.name=name;
}
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(name + "运行 : " + i);
try {
sleep((int) Math.random() * 10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Main {
public static void main(String[] args) {
Thread1 mTh1=new Thread1("A");
Thread1 mTh2=new Thread1("B");
mTh1.start();
mTh2.start();
}
}
- 实现Runnable接口
package com.multithread.runnable;
class Thread2 implements Runnable{
private String name;
public Thread2(String name) {
this.name=name;
}
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println(name + "运行 : " + i);
try {
Thread.sleep((int) Math.random() * 10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class Main {
public static void main(String[] args) {
new Thread(new Thread2("C")).start();
new Thread(new Thread2("D")).start();
}
}
不管是扩展Thread类还是实现Runnable接口来实现多线程,最终还是通过Thread的对象的API来控制线程的,熟悉Thread类的API是进行多线程编程的基础。
main方法其实也是一个线程。在java中所以的线程都是同时启动的,至于什么时候,哪个先执行,完全看谁先得到CPU的资源。
在java中,每次程序运行至少启动2个线程。一个是main线程,一个是垃圾收集线程。因为每当使用java命令执行一个类的时候,实际上都会启动一个JVM,每一个jVM实习在就是在操作系统中启动了一个进程。
public class MyRunnable implements Runnable {
private int ticket = 10;
private String name;
public void run() {
for (int i = 0; i < 500; i++) {
if (this.ticket > 0) {
System.out.println(Thread.currentThread().getName() + "卖票---->" + (this.ticket--));
}
}
}
}
public class RunnableDemo {
public static void main(String[] args) {
MyRunnable myRunnable = new MyRunnable();
Thread t1 = new Thread(myRunnable, "一号窗口");
Thread t2 = new Thread(myRunnable, "二号窗口");
Thread t3 = new Thread(myRunnable, "三号窗口");
t1.start();
t2.start();
t3.start();
}
}
运行结果:
一号窗口卖票—->10
一号窗口卖票—->8
二号窗口卖票—->9
一号窗口卖票—->7
二号窗口卖票—->6
一号窗口卖票—->5
二号窗口卖票—->4
二号窗口卖票—->2
二号窗口卖票—->1
一号窗口卖票—->3
多线程共享一个资源tickt,共同处理一个任务。但结果数字并不是顺序打印,是因为打印有可能有延迟所致。
- 总结
实现Runnable接口比继承Thread类所具有的优势:
1:适合多个相同的程序代码的线程去处理同一个资源
2:可以避免java中的单继承的限制
3:增加程序的健壮性,代码可以被多个线程共享,代码和数据独立
4:线程池只能放入实现Runable或callable类线程,不能直接放入继承Thread的类
多线程
- 同一进程的各线程间共享数据,一次同步会复杂。
- 内存占用少,切换简单
- 编程复杂
- 一个线程挂掉将导致整个进程挂掉
- 适应于多核分布式,进程主要多机分布式。
- 本来是一核一处理器,但现在intel能进行乱序快速模拟两个核。
进程间通信方式
- 管道
无名管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用.进程的亲缘关系一般指的是父子关系。无明管道一般用于两个不同进程之间的通信。当一个进程创建了一个管道,并调用fork创建自己的一个子进程后,父进程关闭读管道端,子进程关闭写管道端,这样提供了两个进程之间数据流动的一种方式。 - 消息队列
- 共享内存+信号量
映射一段能被其他进程访问的共享内存+同步计数器 - 信号
用于通知接收进程某个事件已经发生 - 套接字
适合于客户端和服务器端之间信息实时交互;可以加密,数据安全性强;需对传输的数据进行解析,转化成应用级的数据
线程间通信方式
主要是线程同步,无数据交换通信机制。
- 锁机制
条件变量+互斥锁
读写锁:对写互斥 - 信号量机制
- 信号机制
什么时候用多线程
- 频繁创建销毁的优先用线程
- 切换频繁。需要进行大量计算的优先使用线程
- 强相关的处理用线程,弱相关的处理用进程
Server:消息收发、消息处理。
“消息收发”和“消息处理”就是弱相关的任务,而“消息处理”里面可能又分为“消息解码”、“业务处理”,这两个任务相对来说相关性就要强多了。因此“消息收发”和“消息处理”可以分进程设计,“消息解码”、“业务处理”可以分线程设计。
当然这种划分方式不是一成不变的,也可以根据实际情况进行调整。
LRU
内存管理的页面置换算法
强连通图
强连通图(Strongly Connected Graph)是指在有向图G中,如果对于每一对vi、vj,vi≠vj,从vi到vj和从vj到vi都存在路径,则称G是强连通图
编译的五个过程
- 词法分析
- 语法分析
- 词义分析+记号系统
- 优化(不同编译器不同)
- 目标代码
单继承
一个类不能继承多个父类
分支预测
CPU在判断if语句时提前会对不同分支均run,来提高运行速度
观察者模式
- 当一个抽象模型有两个方面, 其中一个方面依赖于另一方面。将这二者封装在独立的对象中以使它们可以各自独立地改变和复用。
- 当对一个对象的改变需要同时改变其它对象, 而不知道具体有多少对象有待改变。
- 当一个对象必须通知其它对象,而它又不能假定其它对象是谁。换言之, 你不希望这些对象是紧密耦合的。
页面传递对象方法
- request的url
- session
- cookie
- application
java.lang.Throwable
Error
内存溢出,线程死锁等系统问题
- IOError
- VirtualMachineError
Exception
java.lang.RuntimeException之外
强制检查
用户的问题,他再鼓捣鼓捣能克服写代码时必须(try catch)
- IOException
- SQLException
- NoSuchFieldException
java.lang.RuntimeException
非检查
程序员的问题,软件系统就没编对
- ArrayIndexOutOfBoundsException
- ClassCastException
- NullPointerException
- NoSuchElementException
- UnknownElementException
- IllegalStateException
关键字互斥锁
布局管理器
- Border
将版面划分成东、西、南、北、中五个区域,将添加的组件按指定位置放置。
- Flow
组件按从左到右而后从上到下的顺序依次排列,一行不能放完则折到下一行。
- Grid
矩形网格形式对容器的组件进行布置
- GridBag
以表格形式布置容器内的组件,将每个组件放置在每个单元格内,而一个单元格可以跨越多个单元格合并成一个单元格,即多个单元格可以组合成一个单元格,从而实现组件的自由布局。
- Card
以层叠的方式布置组件,如同很多张卡片叠在一起,从而只能看到最上面的那一张卡片
- Box
以嵌套式盒子来管里容器的布局,通过将组件放入水平或垂直形盒子以多层嵌套的方式进行布局。
HashMap/HashTable
- 历史原因
HashTable是陈旧的Dictionary类;而HashMap是java1.2引进的Map接口的实现。 - 安全性
HashTable是线程安全的,同步的;而HashMap不是。 - 值
HashMap允许键为空或值为空;HashTable不允许。
Vector/ArrayList
- 安全性
Vector是线程安全的,同步的;而ArrayList不是。 - 数据增长
当需要数据增长(都有add方法)时,Vector默认增长为原来的一倍;ArrayList是一半。
Collection/Collections
Collections,Arrays都是是辅助的静态方法类。
Set
不能有内容相同的对象(所以地址相同的对象更不能有)
List
可以重复add同一个对象
length
- array.length
- string.length()
补码
如果先判断符号再后续处理,会让电路设计的异常复杂。,人们想到了让符号位也参与运算的设计。
机器数
有符号的二进制数,用最高位存储符号。10000011的真值为+3
00000011的真值为-3原码
[+1]原 = 0000 0001
[-1]原 = 1000 0001反码
正数的反码=本身
负数的反码=原码符号位不变,其余各个位取反.[+1] = [00000001]原 = [00000001]反
[-1] = [10000001]原 = [11111110]反补码
正数的补码=本身
负数的补码=反码+1)[+1] = [00000001]原 = [00000001]反 = [00000001]补
[-1] = [10000001]原 = [11111110]反 = [11111111]补用原码
1 - 1 = 1 + (-1) = [00000001]原 + [10000001]原 = [10000010]原 = -2
用反码
1 - 1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原= [0000 0001]反 + [1111 1110]反 = [1111 1111]反 = [1000 0000]原 = -0
虽然人们理解上+0和-0是一样的, 但是0带符号是没有任何意义的. 而且会有[0000 0000]原和[1000 0000]原两个编码表示0.
- 用补码
1-1 = 1 + (-1) = [0000 0001]原 + [1000 0001]原 = [0000 0001]补 + [1111 1111]补 = [0000 0000]补=[0000 0000]原
(-1) + (-127) = [1000 0001]原 + [1111 1111]原 = [1111 1111]补 +
[1000 0001]补 = [1000 0000]补
这样0用[0000 0000]表示, 而以前出现问题的-0则不存在了.而且可以用
[1000 0000]表示-128
8位二进制, 使用原码或反码表示的范围为[-127, +127], 而使用补码表示的范围为[-128, 127]
==/equals
- ==是比较值是否相等;equals是一个方法,比较内容是否相同
- Object中
viod equals(Object a){
return this==a;
}
try catch语句
String
在java中String类为什么要设计成不可变?
主要是String里所有的方法都小心翼翼地不去改那个数组成员。
而这样子字符串就会近似int等基本类型的不可变性(immutable !=final)。
编译时优化
String s1 = "a";//s1的字面量是“a”
String s2 = s1 + "b";
String s3 = "a" + "b";
System.out.println(s2 == "ab");//false
System.out.println(s3 == "ab");//true,地址都相同
编译可以对字符串常量直接相加的表达式进行优化,不必要等到运行期去进行加法运算处理,而是在编译时去掉其中的加号,直接将其编译成一个这些常量相连的结果。
String s="a"+"b"+"c"+"d";//定义了一个对象,而不是5个。
相当于直接定义了一个”abcd”的字符串。
源码
/*
返回一个新的字符串,它是通过用 newChar 替换此字符串中出现的所有 oldChar 得到的。
如果 oldChar 在此 String 对象表示的字符序列中没有出现,则返回对此 String 对象的引用。否则,创建一个新的 String 对象,它所表示的字符序列除了所有的 oldChar 都被替换为 newChar 之外,与此 String 对象表示的字符序列相同。
示例:
"mesquite in your cellar".replace('e', 'o')
returns "mosquito in your collar"
*/
public String replace(char oldChar, char newChar) {
if (oldChar != newChar) {
int len = value.length;
int i = -1;
char[] val = value; /* avoid getfield opcode */
while (++i < len) {
if (val[i] == oldChar) {
break;
}
}
if (i < len) {
char buf[] = new char[len];
for (int j = 0; j < i; j++) {
buf[j] = val[j];
}
while (i < len) {
char c = val[i];
buf[i] = (c == oldChar) ? newChar : c;
i++;
}
return new String(buf, true);
}
}
return this;
}
Interger/Int
int a=2;
int b=2;
System.out,println(a==b);//true
Interger c=new Interger(2);
Interger d=new Interger(3);
System.out,println(c==d);//false
System.out,println(a==c);//false
final
- 修饰类
仅仅是不能被继承 - 修饰变量
只能赋值一次
immutable
- 基本数据类型的重要性质
- 线程安全,因为不可变
堆与栈
一个类分成员函数以及成员变量。
- 类有以下变量类型:
- 成员变量被static修饰,也叫类变量
- 成员变量不被static修饰,也叫实例变量
- 成员函数中的变量,叫局部变量
- 类变量在堆里
- 实例变量在栈里
- 局部变量也在栈里
Student s=new Student();中局部变量s在生命期结束时会被s及其值(16进制数字)销毁,但new的Student实例还在堆中没被释放,依然被GC监视并决定何时销毁。 - new 出了来的对象在堆上