zoukankan      html  css  js  c++  java
  • Java总结

    Java

    面向过程(性能高)与面向对象(易维护,易复用,易扩展)

    c:直接编译成机器码在电脑执行
    
    Java:半编译语言,最终的执行代码(.class)不是被CPU直接执行的二进制机器码
    

    Java运行过程(可移植性强)

    .java源代码 -> javac编译 -> .class字节码 -> JVM -> CPU可执行的二进制机器码
    

    JDK = JRE(运行时环境 = JVM + Java类库 + Java命令) + javac(编译器) + javadoc(工具)

    Java三大特性

    封装:将对象的属性私有化,对外提供getter,setter方法
    
    继承:
    	子类拥有父类的所有属性和方法(私有属性和方法也拥有,但是无法访问)
    	子类可以拥有自己的属性和方法
    
    多态:
    	程序引用变量的具体类型在编译时无法确定,必须在程序运行期间才能确定
    	实现多态的方法:继承与接口
    

    String StringBuffer StringBuilder

    String不可变:
    	使用final修饰的字节数组保存字符串
    	private final byte[] value;
    
    StringBuilder与StringBuffer可变
    	未用final修饰
    	byte[] value;
    
    线程安全问题:
    	StringBuffer对方法加了同步锁(synchronized),所以线程安全
    	StringBuilder未加同步锁,所以线程不安全
    
    性能:
    	String每次改变都会生成新的对象,适用操作少量数据的情况
    	StringBuffer与StringBuilder操作对象本身,适合操作大量数据
    

    接口

    接口的方法默认是public,不能有实现(Java8开始接口的方法可以有默认实现)
    
    Java8开始接口可以定义静态方法,可以直接用接口名调用(实现类和实现不可以调用)
    
    一个类如果同时实现两个接口,这两个接口中具有相同的默认方法,则此方法必须重写
    
    接口中的变量只能是static,final
    

    hashCode与equals

    hashCode()的作用是获取哈希码,也称散列码,哈希码是确定该对象在哈希表中的索引位置。
    hashCode()在散列表(HashMap)中才有用,在其他情况没有用
    
    如果两个对象相等,hashcode一定也相同
    两个对象有相同的hashcode,他们也不一定相等
    

    final,static

    final
    	修饰变量:初始化后不能更改
    
    	修饰类:不能被继承
    
    	修饰方法:防止继承类修改
    
    static:
    	修饰成员变量和成员方法:类名.静态变量名;类名.静态方法名()
    	静态代码块:静态代码块 -> 非静态代码块 -> 构造方法
    	静态内部类(static修饰类的话只能修饰内部类):
    		它的创建是不需要依赖外围类的创建。
    		它不能使用任何外围类的非static成员变量和方法。
    

    获取键盘输入

    Scanner input = new Scanner(System.in);
    String s = input.nextLine();
    input.close();
    
    BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
    String s = input.readLine();
    

    Collections 工具类和 Arrays 工具类常见方法

    Collections
    
    排序操作
    	void reverse(List list)//反转
    	void shuffle(List list)//随机排序
    	void sort(List list)//按自然排序的升序排序
    	void sort(List list, Comparator c)//定制排序,由Comparator控制排序逻辑
    	void swap(List list, int i , int j)//交换两个索引位置的元素
    	void rotate(List list, int distance)//旋转。当distance为正数时,将list后distance个元素整体移到前面。当distance为负数时,将list的前distance个元素整体移到后面。
    
    查找,替换操作
    	int binarySearch(List list, Object key)//对List进行二分查找,返回索引,注意List必须是有序的
    	int max(Collection coll)//根据元素的自然顺序,返回最大的元素。 类比int min(Collection coll)
    	int max(Collection coll, Comparator c)//根据定制排序,返回最大元素,排序规则由Comparatator类控制。类比int min(Collection coll, Comparator c)
    	void fill(List list, Object obj)//用指定的元素代替指定list中的所有元素。
    	int frequency(Collection c, Object o)//统计元素出现次数
    	int indexOfSubList(List list, List target)//统计target在list中第一次出现的索引,找不到则返回-1,类比int lastIndexOfSubList(List source, list target).
    	boolean replaceAll(List list, Object oldVal, Object newVal)//用新元素替换旧元素
    
    同步控制(效率低):Collections提供了多个synchronizedXxx()方法,该方法可以将指定集合包装成线程同步的集合,从而解决多线程并发访问集合时的线程安全问题
    	synchronizedCollection(Collection<T>  c)//返回指定 collection 支持的同步(线程安全的)collection。
    	synchronizedList(List<T> list)//返回指定列表支持的同步(线程安全的)List。
    	synchronizedMap(Map<K,V> m)//返回由指定映射支持的同步(线程安全的)Map。
    	synchronizedSet(Set<T> s)//返回指定 set 支持的同步(线程安全的)set。
    
    不可变集合,提供了如下三类方法:
    	emptyXxx(): 返回一个空的、不可变的集合对象,此处的集合既可以是List,也可以是Set,还可以是Map。
    	singletonXxx(): 返回一个只包含指定对象(只有一个或一个元素)的不可变的集合对象,此处的集合可以是:List,Set,Map。
    	unmodifiableXxx(): 返回指定集合对象的不可变视图,此处的集合可以是:List,Set,Map。
    	上面三类方法的参数是原有的集合对象,返回值是该集合的”只读“版本。
    
    
    Arrays类的常见操作:
        排序:sort()
        查找:binarySearch()//数组必须有序
        比较:equals()
        填充:fill()
        转列表:asList()//asList得到的数组是的没有add和remove方法的,与Collection.toArray()相结合
        转字符串:toString()
        复制:copyOf()
    

    深拷贝与浅拷贝

    浅拷贝:对基本数据类型进行值传递,对引用数据类型进行引用传递
    深拷贝:对基本数据类型进行值传递,对引用数据类型,创建一个新对象,并复制其内容
    

    ArrayList LinkedList

    ArrayList:
    	底层是数组(transient Object[] elementData)
    	插入,删除受位置影响
    	支持随机访问
    	空间浪费:List列表结尾会预留一定的容量空间
    
    LinkedList:
    	底层是双向链表
    
        private static class Node<E> {
            E item;
            Node<E> next;
            Node<E> prev;
    
            Node(Node<E> prev, E element, Node<E> next) {
                this.item = element;
                this.next = next;
                this.prev = prev;
            }
        }
    
        插入,删除不受位置影响
    	不支持随机访问
    	空间浪费:每个元素需要一个Node节点,占用空间大
    
    遍历方式:
    	实现RandomAccess(支持随机访问):优先选择for(适合顺序储存),其次是foreach(底层是iterator)
    	未实现RandomAccess(不支持随机访问):优先选择iterator(适合链式存储)
    

    ArrayList 扩容机制

    private static final int DEFAULT_CAPACITY = 10;	//初始容量10
    
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};	//无参构造器初始化空数组,只有真正添加元素才分配空间
    
    grow() 方法
    
        /**
         * 要分配的最大数组大小
         */
        private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    
        private int newCapacity(int minCapacity) {
        	//oldCapacity为旧容量,newCapacity为新容量
            int oldCapacity = elementData.length;
            //将oldCapacity右移一位,其效果相当于oldCapacity/2,
            //我们知道位运算的速度远远快于整除运算,整句运算式的结果就是将新容量更新为旧容量的1.5倍
            int newCapacity = oldCapacity + (oldCapacity >> 1);
            if (newCapacity - minCapacity <= 0) {
                if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
                    return Math.max(DEFAULT_CAPACITY, minCapacity);
                if (minCapacity < 0) // overflow
                    throw new OutOfMemoryError();
                return minCapacity;
            }
            return (newCapacity - MAX_ARRAY_SIZE <= 0)
                ? newCapacity
                : hugeCapacity(minCapacity);
        }
    
    最好在 add 大量元素之前用 ensureCapacity 方法,以减少增量重新分配的次数:
    
    	public void ensureCapacity(int minCapacity) {
            if (minCapacity > elementData.length
                && !(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
                     && minCapacity <= DEFAULT_CAPACITY)) {
                modCount++;
                grow(minCapacity);
            }
        }
    
    补充:
        java 中的 length 属性是针对数组说的,比如说你声明了一个数组,想知道这个数组的长度则用到了 length 这个属性
        java 中的 length() 方法是针对字符串说的,如果想看这个字符串的长度则用到 length() 这个方法
        java 中的 size() 方法是针对泛型集合说的,如果想看这个泛型有多少个元素,就调用此方法来查看
    

    HashSet(底层是HashMap)

    private transient HashMap<E,Object> map;
    
    private static final Object PRESENT = new Object();
    
    public boolean add(E e) {
    	return map.put(e, PRESENT)==null;
    }
    

    Comparable 与 Comparator

    Comparable接口有compareTo(Object obj)用来排序
    Comparator接口有compare(Object obj1, Object obj2)用来排序
    
    public interface Comparable<T> {
        public int compareTo(T o);  //obj2.compareTo(obj1):降序(因为默认是升序)
    }
    
    public interface Comparator<T> {
        int compare(T o1, T o2);
    
        boolean equals(Object obj);
    }
    

    JVM

    线程私有:
    	
    	程序计数器:记录当前线程执行的位置,用于流程控制,多线程下用于线程恢复
    	
    	虚拟机栈(为Java方法服务):
    		局部变量表(基本数据类型,对象引用)
    		操作数栈
    		动态链接
    		方法出口信息
    	
    	本地方法栈:为虚拟机用到的Native方法服务
    
    
    线程共享:
    	
    	堆(存放对象实例):
    		新生代:Eden,From Survivor,To Survivor
    		老年代:大对象直接进入,长期存活的对象将进入
    		运行时常量池(Java7以前在方法区中):
    			字面量:
    				文本字符串
    				final常量的值
    				基本数据类型的值
    			符号引用
    	
    	方法区(Java8称为元空间,使用直接内存):
    		被虚拟机加载的类信息
    		常量
    		静态变量
    		即使编译器编译后的代码
    
    	直接内存:不会受到Java堆的限制,用于NIO(基于通道与缓存区,直接使用Native函数库分配堆外内存,然后通过Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作,避免Java堆与Native堆之间来回复制数据)
    
    
    对象的创建过程:
    	
    	类加载检查:检查是否能在常量池中定位到这个类的符号引用,并且检查类是否被加载,解析,初始化
    
    	分配内存:对象所需的内存大小在类加载完成后便可确定,为对象分配空间的任务等同于把一块确定大小的内存从 Java 堆中划分出来
    
    	初始化零值:保证了对象的实例字段在 Java 代码中可以不赋初始值就直接使用
    
    	设置对象头:类信息,元数据信息,哈希码,GC分代年龄
    
    	执行init方法:初始化
    
    
    判断对象死亡:
    	引用计数器:给对象加一个引用计数器,每当有地方引用它,计数器+1,当引用失败,计数器-1。当计数器=0表示不会被使用。
    	可达性分析算法:当一个对象到GC ROOT没有任何引用链相连,证明此对象是不可用的。
    
    判断一个类是无用的类(方法区主要回收无用的类):
    	该类所有实例已被回收
    	该类的 ClassLoader 已被回收
    	该类的class对象没有在任何地方被引用,无法在任何地方通过反射获取该类
    
    引用:
    	强引用(StrongReference)(普遍):垃圾回收器不会回收
    	软引用(SoftReference)(对象可有可无):如果内存不足会被回收,用于内存敏感的高速缓存,可以和引用队列(ReferenceQueue)联合使用
    	弱引用(WeakReference)(对象可有可无):无论内存是否足够都会被回收,可以和引用队列(ReferenceQueue)联合使用
    	虚引用(PhantomReference)(等于没有引用):用来跟踪对象被垃圾回收的活动,必须和引用队列(ReferenceQueue)联合使用
    
    垃圾收集算法:
    	标记-清除:具有效率问题和空间问题(产生大量不连续的碎片)
    	复制算法:将内存分为大小相同的两块,每次使用其中一块,这一块使用完后将存活对象复制到另一块,再把使用的空间一次清理掉。
    	标记-整理算法:标记过程同`标记-清除`,然后将所有存活对象向一端移动,然后直接清理掉边界以外的内存。
    	分代收集算法:将Java堆分为新生代和老年代,然后选择合适的垃圾收集算法。
    
    常见的垃圾回收器:
    	Serial(单线程,没有线程交互开销,高效,适合Client模式):Stop The World,新生代复制算法,老年代标记-整理算法
    	Serial Old(单线程):Serial的老年代版本,CMS后备方案
    	ParNew(多线程,Server模式的首要选择,配合CMS收集器):Serial的多线程版本
    	Parallel Scavenge(多线程,关注于吞吐量=cpu运行用户代码的时间:cpu总消耗时间,高效利用CPU):提供很多参数找到合适的停顿时间或最大吞吐量,新生代复制算法,老年代标记-整理算法
    	Parallel Old(多线程):Parallel Scavenge的老年代版本
    	CMS(并发搜集器(垃圾收集线程与用户线程基本同时工作),关注于停顿时间):优点:低停顿,并发收集;缺点:对CPU资源敏感,无法处理浮动数据,使用标记-清除算法,会产生大量空间碎片
    		初始标记(单线程,快速):暂停其他线程,记录与root相连的对象(可达性分析)
    		并发标记(单线程):同时开启GC和用户线程
    		重新标记(多线程):修正并发标记期间因用户线程运行导致标记发生变动的记录
    		并发清除(单线程):同时启动GC与用户线程
    	G1(面向服务器,针对多处理器与大容量内存,满足停顿时间的同时具有高吞吐量):
    		特点:
    			并行与并发:多处理器并行,用户与垃圾收集器并发
    			分代收集
    			空间整合:整体基于标记-整理,局部基于复制算法
    			可预测的停顿
    		步骤:
    			初始标记
    			并发标记
    			最终标记
    			筛选回收
    		G1后台维护一个优先列表,每次根据允许的收集时间,优先选择回收价值最大的Region(把内存化整为零)。
    		保证了GC收集器在有效的时间有尽可能高的收集效率。
    
    
    类加载过程:
    	加载 -> 连接(验证 -> 准备 -> 解析) -> 初始化
    
    	加载:
    		加载此类的二进制字节流
    		将字节流所代表的静态储存结构转换为方法区的运行时数据结构
    		在内存中生成一个Class对象,作为方法区这些数据的访问入口
    	类加载器:
    		BootstrapClassLoader(启动类加载器):由c++实现,负责加载%JAVA_HOME%/lib目录下的jar包
    		ExtensionClassLoader(扩展类加载器):%JAVA_HOME%/lib/ext目录下的jar包
    		AppClassLoader(应用程序类加载器):面向用户,负责classpath下的所有jar包
    	双亲委派模型:
    		加载类的时候,首先将该请求委托给父类加载器在、一直到BootStrapClassLoader。父类加载器无法处理时,才由子类加载器处理。
    		当父类加载器为null时,会使用启动类加载器BootStrapClassLoader作为父类加载器。
    		好处:避免类被重复加载,保证Java的核心API不被篡改。
    

    多线程

    线程是进程划分成的更小的运行单位
    
    进程之间是独立的,占用某些系统资源(CPU时间,内存空间,输入输出设备的使用权)
    
    多个线程之间可以共享同一块内存空间和系统资源
    
    线程的状态:
    	初始new:被创建,但是没有被调用(start())
    	运行状态runnable:包含就绪和运行
    	阻塞状态blocked:线程阻塞于锁
    	等待状态waiting:进入该状态表示当前线程需要等待其他线程做出一些特定动作(通知或中断)
    	终止状态terminated:线程执行完毕
    
    
    使用多线程的原因:
    	线程可以看作是轻量级进程,线程间的切换和调度成本远远小于进程
    	多核多线程,线程可以并行执行,提高从cpu利用率,减少线程上下文切换的开销
    
    
    上下文切换(任务从保存到再加载的过程称为一次上下文切换):
    	当前任务在执行完cpu时间片,切换到另一个任务之前,会先保存自己的状态,以便下次再切换回这个任务时,可以再加载这个任务的状态
    
    
    调用start()而不是run()的原因:
    	start()会启动线程并使线程进入就绪状态,分配到时间片后就可以运行
    	run()只是一个普通方法调用,还是在主线程中执行
    

    synchronized

    Java6优化:
    	偏向锁:无竞争的情况下会把整个同步都消除掉
    		失败后升级为轻量级锁
    	轻量级锁:无竞争的情况下使用 CAS 操作加锁与解锁去代替使用互斥量
    		失败后升级为自旋锁(默认10次)和自适应自旋锁(让线程执行一个忙循环)
    	自适应自旋锁:自旋时间不固定,由上一次同一个锁上的自旋时间和拥有者的状态来决定
    	
    synchronized 与 ReenTrantLock 的对比:
    	两者都是可重入锁
    	synchronized 依赖JVM,ReenTrantLock 依赖API
    	ReenTrantLock 具有一些高级功能:
    		中断等待锁的线程:lock.lockInterruptibly()
    		可实现公平锁(默认与synchronized一样是非公平锁)
    		RenTrantLock借助Condition实现等待/通知机制(有选择地进行线程通知),synchronzied借助wait()和notify()/notifyAll()实现等待/通知机制
    

    ThreadLocal

    //带有默认值
    private static final ThreadLocal<SimpleDateFormat> formatter = ThreadLocal.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm"));
    
    Thread的run()方法调用:
    	formatter.set(new SimpleDateFormat());
    	formatter.get();
    
    ThreadLocal源码:
    	
    public class ThreadLocal<T> {
    	public void set(T value) {
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null) {
                map.set(this, value);
            } else {
                createMap(t, value);
            }
        }
    
        public T get() {
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null) {
                ThreadLocalMap.Entry e = map.getEntry(this);
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    T result = (T)e.value;
                    return result;
                }
            }
            return setInitialValue();
        }
    
        ThreadLocalMap getMap(Thread t) {
            return t.threadLocals;
        }
    }
    
    class Thread implements Runnable {
    
    	//只有当前线程调用ThreadLocal类的set和get才被创建
    	ThreadLocal.ThreadLocalMap threadLocals = null;
    
    	static class ThreadLocalMap {
    
            /**
             * The entries in this hash map extend WeakReference, using
             * its main ref field as the key (which is always a
             * ThreadLocal object).  Note that null keys (i.e. entry.get()
             * == null) mean that the key is no longer referenced, so the
             * entry can be expunged from table.  Such entries are referred to
             * as "stale entries" in the code that follows.
             */
            static class Entry extends WeakReference<ThreadLocal<?>> {
                /** The value associated with this ThreadLocal. */
                Object value;
    
                Entry(ThreadLocal<?> k, Object v) {
                    super(k);
                    value = v;
                }
            }
    
            /**
             * The initial capacity -- MUST be a power of two.
             */
            private static final int INITIAL_CAPACITY = 16;
    
            /**
             * The table, resized as necessary.
             * table.length MUST always be a power of two.
             */
            private Entry[] table;
    
            /**
             * The number of entries in the table.
             */
            private int size = 0;
    
            /**
             * The next size value at which to resize.
             */
            private int threshold; // Default to 0
        }
    }
    

    线程池

    优点:
    	降低资源消耗(创建和销毁的消耗)
    	提高响应速度
    	提高线程的可管理性
    
    Runnerable接口不会返回结果或抛出检查异常,Callable接口可以
    
    Executors可以实现Runnable与Callable之间的转换:
    	public static Callable<Object> callable(Runnable task)
    	public static <T> Callable<T> callable(Runnable task, T result)
    
    execute()与submit()区别:
    	execute()用于提交不许要返回值的任务
    	submit()用于提交需要返回值的任务(线程池会返回Future类型数据,Future的get()方法会阻塞线程直到任务完成)
    
    创建线程:
    	阿里巴巴开发手册不建议使用Executors创建线程池,规避OOM(FixedThreadPool(线程数量始终不变)和SingleThreadPool(只有一个线程):允许请求的队列长度为Integer.MAX_VALUE/CachedThreadPool(线程数不确定)和ScheduledThreadPool:允许创建的线程数量为Integer.MAX_VALUE)。推荐使用ThreadPoolExecutor的方式。
    
    public class ThreadPoolExecutor extends AbstractExecutorService {
    	
    	public ThreadPoolExecutor(int corePoolSize,
                                  int maximumPoolSize,
                                  long keepAliveTime,
                                  TimeUnit unit,
                                  BlockingQueue<Runnable> workQueue,
                                  ThreadFactory threadFactory,
                                  RejectedExecutionHandler handler) {
            if (corePoolSize < 0 ||
                maximumPoolSize <= 0 ||
                maximumPoolSize < corePoolSize ||
                keepAliveTime < 0)
                throw new IllegalArgumentException();
            if (workQueue == null || threadFactory == null || handler == null)
                throw new NullPointerException();
            //最小可以同时运行的线程数
            this.corePoolSize = corePoolSize;
            //队列满时,最大可以同时运行的线程数
            this.maximumPoolSize = maximumPoolSize;
            //达到核心线程数后储存任务
            this.workQueue = workQueue;
            //核心线程以外的线程在没有任务时的存活时间
            this.keepAliveTime = unit.toNanos(keepAliveTime);
            //线程工厂
            this.threadFactory = threadFactory;
            /**
             * 拒绝策略:
             *   AbortPolicy:抛出异常来拒绝新任务
             *   CallerRunsPolicy:调用自己的线程执行任务(会降低任务提交速度,影响程序整体性能)
             *   DiscardPolicy:不处理,直接丢弃
             *   DiscardOldestPolicy:丢弃最早的未处理任务
        	 */
            this.handler = handler;
        }
    }
    

    AtomicInteger原理

    AtomicInteger类主要利用CAS + volatile + native方法保证原子操作,从而避免synchronized的高开销
    
    public class AtomicInteger extends Number implements java.io.Serializable {
    	public final int getAndIncrement() {
            return U.getAndAddInt(this, VALUE, 1);
        }
    }
    
    public final class Unsafe {
    	public final int getAndAddInt(Object o, long offset, int delta) {
            int v;
            do {
                v = getIntVolatile(o, offset);
            } while (!weakCompareAndSetInt(o, offset, v, v + delta));
            return v;
        }
    
        public final boolean weakCompareAndSetInt(Object o, long offset, int expected, int x) {
            return compareAndSetInt(o, offset, expected, x);
        }
    
        public final native boolean compareAndSetInt(Object o, long offset, int expected, int x);
    }
    

    AQS(构建锁和同步器的框架)

    AQS原理:
    	如果请求的共享资源空闲,则将当前请求资源的线程设为工作线程,并将共享资源设为锁定状态。
    	如果请求的共享资源被占用,就需要一套阻塞/唤醒时的锁分配机制。
    	CAS用CLH队列(虚拟双向队列)实现,将暂时获取不到锁的线程加入到队列中。
    

    计算机网络

    TCP/IP四层协议:
    	应用层
    	运输层(TCP/UDP)
    	网际层IP
    	网络接口层
    五层协议:
    	引用层
    	运输层
    	网络层
    	数据链路层
    	物理层
    
    应用层(DNS,HTTP,SMTP):数据单元称为报文
    	应用进程间的通信和交互规则
    运输层(TCP,UDP):
    	为通信提供通用的数据传输服务,
    	应用进程通过该服务传送应用层报文
    网络层(IP):
    	选择合适的网间路由和交换节点,确保数据及时送达。
    	发送数据时,网络层将运输层产生的报文段或者用户数据封装成分组(也叫IP数据报)和包进行传送。
    数据链路层:
    	数据链路层将网络层的IP数据报封装成帧,在两个相邻节点的链路上传送帧。
    	每一帧包括数据和必要的控制信息(同步信息,地址信息,差错控制)
    物理层:传送的数据单位是比特
    
    
    TCP三次握手四次挥手:
    	
    	三次握手:双方确认自己与对方的发送与接收是正常的
    		第一次握手(客户端发送带有SYN标志的数据包):Server确认对方发送正常,自己接收正常
    		第二场握手(服务端发送SYN/ACK标志的数据包):Client确认自己发送接收正常,对方发送接收正常
    		第三次握手(客户端发送带有ACK标志的数据包):Server确认自己发送接收正常,对方发送接收正常
    	服务端回传SYN:
    		告诉客户端,服务端接收的数据确实是该客户端发送的
    	客户端回传ACK:
    		告诉服务端,客户端接收的数据确实是该服务端发送的
    
    TCP协议保证可靠传输:
    	将应用数据分割为TCP认为最适合发送的数据块
    	TCP给发送的每一个数据包进行编号,接收方对数据包进行排序,把有序数据传送给应用层
    	校验和:TCP将保持它首部和数据的校验和,如果收到的数据校验和有差错,TCP将丢弃此报文,不确认收到此报文
    	TCP接收端会丢弃重复数据
    	流量控制:通过滑动窗口实现
    		接收方可以通过确认报文中的窗口字段,控制发送方窗口的大小,从而影响发送方的发送速率。
    	拥塞控制:当网络拥塞时,减少数据的发送
    		TCP发送方维护一个拥塞窗口(大小取决于网络的拥塞程度,动态变化),发送方让自己的发送窗口取为拥塞窗口和接收方的接收窗口中较小的一个。
    		TCP的拥塞控制采用四种算法:慢开始(先试探),拥塞避免(逐渐缓慢增大拥塞窗口),快重传与快恢复(快速恢复丢失的包)
    	ARQ协议(通过确认和超时这两个机制):每发完一个分组,等待对方确认后再发送
    		停止等待ARQ协议(信道利用率低,等待时间长):每发完一个分组就停止发送,等待对方确认。若接收方收到重复分组,就丢弃该分组,但同时还要发送确认
    		连续ARQ协议(信道利用率高,但是无法确认正确收到的所有分组信息):发送方维护一个发送窗口,窗口内的分组可以连续发送而不需要对方确认。接收方对按序到达的最后一个分组发送确认,表明这个分组以及之间的分组已经正确收到。
    	超时重传:当TCP发送一个段后,等待目的端确认收到这个报文段。如果不能即使收到确认,就重发
    
    
    浏览器输入URL到显示主页的过程:
    	浏览器查找域名的IP:DNS查找(浏览器缓存,路由器缓存,DNS缓存)
    	浏览器向web服务器发送HTTP请求:TCP连接,发送HTTP请求
    	服务器处理请求
    	服务器发回一个HTML页面
    	浏览器显示HTML:浏览器解析渲染页面
    	连接结束
    
    
    HTTP长连接与短连接(实质是TCP协议的长连接和短连接):
    	短连接:客户端每进行一次Http请求,就建立一次连接,任务结束就中断
    	长连接:Connection:keep-alive,打开网页TCP不会关闭,再次访问服务,可以继续使用这条连接
    
    
    HTTP是无状态的,保存用户状态需要Session:
    	Session跟踪:
    		在Cookie中附加一个SessionID
    		利用URL重写把SessionID附加到URL路径后面
    

    数据结构

    Queue:FIFO
    	Deque
    	LinkedList
    	PriorityQueue
    	BlockingQueue
    
    Set:无序,不重复
    	HashSet
    	TreeSet
    
    List:有序,可重复
    	ArrayList
    	LinkedList
    	Stack(栈)
    
    Map:
    	HashMap
    	ConcurrentHashMap
    
    堆
    
  • 相关阅读:
    nodeJs小练习之爬取北京昌平区房价
    2016,加油,奋斗
    1339 字符串王国的种族
    1333 明信片和照片
    1316 你能知道这是几进制数?
    1309 简化版九宫格
    1295 爱翻译
    1288 幸运转盘(一)
    1287 孙武练兵
    1284 喜羊羊的新年礼物
  • 原文地址:https://www.cnblogs.com/loveer/p/12580419.html
Copyright © 2011-2022 走看看