Java常见知识点

#一、HashMap实现原理

HashMap中有一个Entry类,保存key-value键值对, 一个叫table的Entry数组。 Entry在数组中的位置,是根据key的hashcode()方法计算出来的hash值(来决定)。hash值用来计算key在Entry数组的索引。 HashMap解决Hash冲突的办法是拉链法。

要牢记以下关键点:

  • HashMap有一个叫做Entry的内部类,它用来存储key-value对。
  • 上面的Entry对象是存储在一个叫做table的Entry数组中。
  • table的索引在逻辑上叫做“桶”(bucket),它存储了链表的第一个元素。
  • key的hashcode()方法用来找到Entry对象所在的桶。
  • 如果两个key有相同的hash值,他们会被放在table数组的同一个桶里面。
  • key的equals()方法用来确保key的唯一性。
  • value对象的equals()和hashcode()方法根本一点用也没有。

二、HashTable与HashMap的区别

Hashtable的方法是同步的,HashMap未经同步。 Hashtable不允许 null 值(key 和 value 都不可以),HashMap允许 null 值(key和value都可以)。 两者的遍历方式大同小异,Hashtable仅仅比HashMap多一个elements方法。 HashTable使用Enumeration,HashMap使用Iterator 哈希值的使用不同,Hashtable直接使用对象的hashCode;而HashMap重新计算hash值,而且用与代替求模。

// HashTable
int hash = key.hashCode();  
int index = (hash & 0x7FFFFFFF) % tab.length;

// HashMap
int hash = hash(k);
int i = indexFor(hash, table.length);
static int hash(Object x) {
  int h = x.hashCode();
  h += ~(h << 9);
  h ^= (h >>> 14);
  h += (h << 4);
  h ^= (h >>> 10);
  return h;
}

Hashtable中hash数组默认大小是11,增加的方式是 old*2+1。HashMap中hash数组的默认大小是16,而且一定是2的指数。

#三、HashMap线程安全性 HashMap线程不安全的原因: HashMap对于table的操作没有考虑同步的问题,造成在存放value时可能会发生覆盖。

ConcurrentHashMap如何实现并发的? ConcurrentHashMap的table的起始默认值同样是16,它不同于HashTable锁住整张Table表,它对于Table中的每一个元素单独加锁,这样并发效率就大大的高于HashTable了。

get、put方法的分析: get()方法: 在读取操作时,大部分时间是不加锁的。例外情况在第9行: 这里当v为空时,可能是一个线程正在改变节点,而之前的 get操作都未进行锁定,根据bernstein条件,读后写或写后读都会引起数据的不一致,所以这里要对这个e重新上锁再读一遍,以保证得到的是正确值。

 V get(Object key, int hash) {
            if (count != 0) { // read-volatile
                HashEntry<K,V> e = getFirst(hash);
                while (e != null) {
                    if (e.hash == hash && key.equals(e.key)) {
                        V v = e.value;
                        if (v != null)
                            return v;
                        return readValueUnderLock(e); // recheck
                    }
                    e = e.next;
                }
            }
            return null;
        }
        V readValueUnderLock(HashEntry<K,V> e) {
            lock();
            try {
                return e.value;
            } finally {
                unlock();
            }
        }

put()方法:写操作时需要对table进行加锁 在第5行,检查table的容量,如果不够,就进行扩容。 第8行,是从桶中(table)找到链表的入口,之后的操作和HashMap一致,将新元素插入到链表头。

V put(K key, int hash, V value, boolean onlyIfAbsent) {
            lock();
            try {
                int c = count;
                if (c++ > threshold) // ensure capacity
                    rehash();
                HashEntry<K,V>[] tab = table;
                int index = hash & (tab.length - 1);
                HashEntry<K,V> first = tab[index];
                HashEntry<K,V> e = first;
                while (e != null && (e.hash != hash || !key.equals(e.key)))
                    e = e.next;
                V oldValue;
                if (e != null) {
                    oldValue = e.value;
                    if (!onlyIfAbsent)
                        e.value = value;
                }
                else {
                    oldValue = null;
                    ++modCount;
                    tab[index] = new HashEntry<K,V>(key, hash, first, value);
                    count = c; // write-volatile
                }
                return oldValue;
            } finally {
                unlock();
            }
        }

#四、equals与hashCode 在默认情况下Object的equals所对比的两个应用必须是同一对象,才会是真,这和“==”的作用是相同的。 Object的hashCode()的缺省实施通过将对象的内存地址对映于一个整数值来生成。

HashMap使用Key对象的hashCode()和equals()方法去决定key-value对的索引。当我们从HashMap中获取值的时候,这些方法也会被用到。如果这些方法没有被正确地实现,在这种情况下,两个不同Key也许会产生相同的hashCode()和equals()输出,HashMap将会认为它们是相同的,然后覆盖它们,而非把它们存储到不同的地方。同样的,所有不允许存储重复数据的集合类都使用hashCode()和equals()去查找重复,所以正确实现它们非常重要。equals()和hashCode()的实现应该遵循以下规则: 1.如果o1.equals(o2),那么o1.hashCode() == o2.hashCode()总是为true的。 2.如果o1.hashCode() == o2.hashCode(),并不意味着o1.equals(o2)会为true。

#五、volatile的含义与用法 volatile 变量可以被看作是一种 “程度较轻的 synchronized” 锁提供了两种主要特性:互斥(mutual exclusion) 和可见性(visibility)。 互斥即一次只允许一个线程持有某个特定的锁。 可见性要确保释放锁之前对共享数据做出的更改对于随后获得该锁的另一个线程是可见的。

Volatile 变量具有 synchronized 的可见性特性,但是不具备原子特性。这就是说线程能够自动发现 volatile 变量的最新值。

正确使用 volatile 变量的条件: 对变量的写操作不依赖于当前值。 该变量没有包含在具有其他变量的不变式中。

#六、可重入的锁ReentrantLock

使用方法:

ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
   // synchronized do someting
} finall {
   lock.unlock();
}
 
 // synchronized用法
sychronized(object) {
     // synchronized do someting
}

相比于关键字sychronzied,ReentrantLock使用起来更加的灵活,但是复杂性也更高。 使用lock时,如果没有获得锁,就可以去做其他事情;颗粒度更细

#七、死锁与活锁 死锁: 多个线程之间相互占用部分资源,导致都不能运行。 活锁: 与死锁唯一不同的是,它的状态在不断的变化。 eg: 两个人面对面过桥,他们同时向左右避让,导致无法通过

死锁的四个必要条件

  1. 互斥条件 资源无法共享
  2. 持有条件 持有部分资源,等待其他资源
  3. 资源无法被抢占
  4. 循环等待

破环条件2: 如果无法获得所有的资源,就不获取资源

#八、wait和notify基本使用 wait()和sleep()对比

waitsleep
Object的方法Thread的方法
释放锁不释放锁
notify() notifyAll()之后执行设定时间之后,恢复执行
*notify() 只唤醒一个阻塞的任务,notifyAll()将唤醒所有的任务。

run()与start()比较

runnable的方法Thread的方法
需要执行的实际工作调用run方法
普通的方法执行真正开启一个线程

#九、线程的基本知识

线程的状态

  1. 新建: 线程被创建时的状态, 完成线程所需资源的分配,有获得CPU时间的资格
  2. 就绪: 完成所需资源的分配, 线程可以在运行也可能不在运行,只要分配到时间就运行
  3. 阻塞: 因越少必要的资源处于等待状态,不会被调度器分配时间片
  4. 死亡: 完成任务,不会再被分配时间片

新建、就绪 --> 阻塞 1.调用sleep() 2.调用wait() 3.等待I/O, 无法被程序中断 4.等待共享资源, 无法被程序中断

中断线程的方法 1.Thread.interrupt() 2.Executor.shutdownNow() 3.Future.cancel()

  • 强制中断I/O的方法:关闭任务的资源

#十、Spring 的核心机制 web.xml基本配置:

  1. 欢迎页
<welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>index1.jsp</welcome-file>
</welcome-file-list>
  1. servlet配置
<!-- servlet 基本设置 --> 
<servlet>
    <servlet-name>servlet1</servlet-name>
    <servlet-class>net.test.TestServlet</servlet-class>
    <init-param>
          <param-name>userName</param-name>
          <param-value>Tommy</param-value>
    </init-param>
</servlet>
<!-- 设置servlet URL -->
<servlet-mapping>
    <servlet-name>servlet1</servlet-name>
    <url-pattern>*.do</url-pattern>
</servlet-mapping>
  1. 错误页设置
<error-page>
    <error-code>404</error-code>
    <location>/error404.jsp</location>
</error-page>
<error-page>
    <exception-type>java.lang.Exception<exception-type>
    <location>/exception.jsp<location>
</error-page>
  1. 过滤器设置
<filter>
    <filter-name>XXXCharaSetFilter</filter-name>
    <filter-class>net.test.CharSetFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>XXXCharaSetFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>
  1. 监听器设置
<listener>
   <listener-class>net.test.XXXLisenet</listener-class>
</listener>
  1. Session设置
<session-config>
    <session-timeout>60</session-timeout>
</session-config>

特点:

+低侵入式设计,代码的污染极低 +独立于各种应用服务器,基于Spring框架的应用,可以真正实现Write Once,Run Anywhere的承诺 +Spring的IoC容器降低了业务对象替换的复杂性,提高了组件之间的解耦 +Spring的AOP支持允许将一些通用任务如安全、事务、日志等进行集中式管理,从而提供了更好的复用 +Spring的ORM和DAO提供了与第三方持久层框架的良好整合,并简化了底层的数据库访问 +Spring的高度开放性,并不强制应用完全依赖于Spring,开发者可自由选用Spring框架的部分或全部

bean管理:

程序主要是通过Spring容器来访问容器中的Bean,ApplicationContext是Spring容器最常用的接口,该接口有如下两个实现类: ClassPathXmlApplicationContext: 从类加载路径下搜索配置文件,并根据配置文件来创建Spring容器 FileSystemXmlApplicationContext: 从文件系统的相对路径或绝对路径下去搜索配置文件,并根据配置文件来创建Spring容器

依赖注入:

调用者不再主动的创建被依赖对象,而是通过通过容器注入

Spring中的注入的方式:

设值注入

设值注入是指IoC容器通过成员变量的setter方法来注入被依赖对象。这种注入方式简单、直观,因而在Spring的依赖注入里大量使用。

构造注入

利用构造器来设置依赖关系的方式,被称为构造注入。通俗来说,就是驱动Spring在底层以反射方式执行带指定参数的构造器,当执行带参数的构造器时,就可利用构造器参数对成员变量执行初始化——这就是构造注入的本质。

两种注入方式的对比

设值注入优势

与传统的JavaBean的写法更相似,程序开发人员更容易理解、接受。通过setter方法设定依赖关系显得更加直观、自然 对于复杂的依赖关系,如果采用构造注入,会导致构造器过于臃肿,难以阅读。Spring在创建Bean实例时,需要同时实例化其依赖的全部实例,因而导致性能下降。而使用设值注入,则能避免这些问题。 尤其在某些成员变量可选的情况下,多参数的构造器更加笨重

构造注入优势

构造注入可以在构造器中决定依赖关系的注入顺序,优先依赖的优先注入 对于依赖关系无需变化的Bean,构造注入更有用处。因为没有setter方法,所有的依赖关系全部在构造器内设定,无须担心后续的代码对依赖关系产生破坏 依赖关系只能在构造器中设定,则只有组件的创建者才能改变组件的依赖关系,对组件的调用者而言,组件内部的依赖关系完全透明,更符合高内聚的原则

利用注解来管理Bean

Spring提供如下几个Annotation来标注Spring Bean @Component: 标注一个普通的Spring Bean类 @Controller: 标注一个控制器组件类 @Service: 标注一个业务逻辑组件类 @Repository: 标注一个DAO组件类 配置扫描路径:

<context:component-scan   base-package = "edu.shu.spring.domain" />  

#十一、Spring AOP

#十二、Spring MVC 请求、反馈流程

#十二、JVM 内存模型 JVM 内存模型

###JVM运行时数据区 1.程序计数器(Program Counter Register)

线程私有 程序计数器是用于存储每个线程下一步将执行的JVM指令,如该方法为native的,则程序计数器中不存储任何信息

2.JVM栈(JVM Stack)

JVM栈是线程私有的,生命周期与线程相同。每个线程创建的同时都会创建JVM栈,JVM栈中存放的为当前线程中局部基本类型的变量(java中定义的八种基本类型:boolean、char、byte、short、int、long、float、double)、部分的返回结果以及Stack Frame,非基本类型的对象在JVM栈上仅存放一个指向堆上的地址 一个栈帧由:局部变量表,操作数栈,动态链接、方法出口组成 局部变量表中存放了编译期可知的基本数据类型和对象引用

3.堆(heap)

它是JVM用来存储对象实例以及数组值的区域,可以认为Java中所有通过new创建的对象的内存都在此分配,Heap中的对象的内存需要等待GC进行回收。

1.堆是JVM中所有线程共享的,因此在其上进行对象内存的分配均需要进行加锁,这也导致了new对象的开销是比较大的

2.Sun Hotspot JVM为了提升对象内存分配的效率,对于所创建的线程都会分配一块独立的空间TLAB(Thread Local Allocation Buffer),其大小由JVM根据运行的情况计算而得,在TLAB上分配对象时不需要加锁,因此JVM在给线程的对象分配内存时会尽量的在TLAB上分配,在这种情况下JVM中分配对象内存的性能和C基本是一样高效的,但如果对象过大的话则仍然是直接使用堆空间分配

3.TLAB仅作用于新生代的Eden Space,因此在编写Java程序时,通常多个小的对象比大的对象分配起来更加高效。

4.方法区(Method Area)

线程共享

1.在Sun JDK中这块区域对应的为PermanetGeneration,又称为持久代。

2.方法区域存放了所加载的类的信息(名称、修饰符等)、类中的静态变量、类中定义为final类型的常量、类中的Field信息、类中的方法信息,当开发人员在程序中通过Class对象中的getName、isInterface等方法来获取信息时,这些数据都来源于方法区域,同时方法区域也是全局共享的,在一定的条件下它也会被GC,当方法区域需要使用的内存超过其允许的大小时,会抛出OutOfMemory的错误信息。

5.本地方法栈(Native Method Stacks)

JVM采用本地方法栈来支持native方法的执行,此区域用于存储每个native方法调用的状态, HotSpot将本地方法栈和JVM栈合二为一。

6.运行时常量池(Runtime Constant Pool)

存放的为类中的固定的常量信息、方法和Field的引用信息等,其空间从方法区域中分配。JVM在加载类时会为每个class分配一个独立的常量池,但是运行时常量池中的字符串常量池是全局共享的。

#十三、JVM 内存垃圾处理

1.垃圾查询算法

引用计数法 给每个被引用对象设置一个引用计数器,新增引用就+1,引用失效就-1; 特点:实现简单、效率高,无法解决循环引用问题 可达性分析 从"GC Roots"作为起点,向下搜索,走过的路程称为“引用链”,不在引用链上的对象就可以被回收。

2.垃圾收集算法

标记-清除算法
    2个阶段,第一个阶段标记出所有可以被回收的对象;第二个阶段对内存进行回收;
    特点:效率低,会产生内存碎片
复制算法
    将可用内存分为两部分,每次做GC时候,就将一部分中存活的对象复制到另一部分中去。
    特点:效率高、无内存碎片、内存使用率低
    为了提高内存使用率:将内存按照8/1/1 分为一个Eden区,两个survivor区,每次使用一个Eden和一个survivor,
    每次清理的时候,复制Eden和survivor到另一个survivor中
标记-整理算法
    2个阶段,第一个阶段与标记-清除相同,第二个阶段中所有存活的对象都向一端移动,然后清除边界外的内存

3.垃圾收集器

垃圾收集器

Serial 单线程,新生代“复制算法”,STW

ParNew 多线程,新生代“复制算法”,STW

Parallel Scavenge 多线程,新生代“复制算法” 特点:达到可控制的吞吐量 = 运行用户代码时间 / (运行用户代码时间 + 垃圾收集时间 )

Serial Old 单线程,老年代“标记-整理”

Parallel Old 多线程,老年代“标记-整理” 配合Parallel Scavenge

CMS 多线程, "标记-清除" 初始标记 STW 标记“GC Roots”直接能关联的对象 并发标记 并发向下搜索 重新标记 STW 修正“并发标记”时可能出现的错误 并发清除 清除标记的对象 缺点: CPU资源敏感,当CPU资源较少时,并发过程中,用于处理GC的线程的百分百就相对较高; 无法处理浮动垃圾,浮动垃圾是在并发过程中用户产生的垃圾 会产生内存碎片,过一段时间会进行一次Full GC

G1 多线程,新老通吃,整体是“标记-整理”,局部看是“复制”; 将Java堆分为多个Region,对Region进行GC,每次选出回收价值最大的Region进行GC 初始标记 并发标记 最终标记 筛选回收

#十四、类加载器 类加载器

Java虚拟机只有两种不同的类加载器:

启动类加载器(Bootstrap ClassLoader) C++ 属于虚拟机

其他加载器 属于Java语言

其他类加载器可细分为:

**扩展类加载器(extensions class loader):**它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。

**系统类加载器(system class loader):**它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。一般来说,Java 应用的类都是由它来完成加载的。可以通过 ClassLoader.getSystemClassLoader()来获取它。 都是继承至java.lang.ClassLoader

**双亲委派:**一个加载器接收到加载请求后,首先拿给父加载器去加载;父加载器无法完成加载,再自己加载。 **好处:**Java类随着加载器有了层次关系,一个类无论拿给那个子类加载器加载,最终都是由自己的加载器完成加载 **如何破坏双亲委派:**①重写loadClass()方法;②使用线程上下文加载器(具体不清楚)

**真正完成类加载的称为:**定义加载器(defining loader) 来确定一个类 启动加载过程的称为: 初始加载器(initiating loader)

#十五、网络 输入图片说明

1.TCP 接连三次握手

+开始client由“关闭”状态变为“开启”, server处于“监听”状态(Listen) +client发送"连接请求"(SYN=1, seq=x), 进入SYN-SEND状态 +server返回“授予连接”(SYN=1, seq=y,ack=x+1), 进入SYN-RCVD状态 +client发送“确认”(SYN=0, seq=x, ack = y+1), client进入“已连接”状态 +server接受到“确认”信息后,也进入“已连接”状态

2.TCP 释放四次挥手

*中断连接的请求既可以由client发出,也可以有server发出。

+client发出FIN报文, 进入FIN-WAIT-1状态。 +server收到FIN报文后,返回ACK报文, 之后还是可以正常的发送数据,进入CLOSE-WAIT状态。 +client收到ACK报文后,进入FIN-WAIT-2状态。 +server发送完必要的数据之后,向client发出FIN报文,进入LAST-ACK状态。 +client收到FIN报文后,返回ACK报文,进入TIME-WAIT状态。 +server收到ACK报文后,关闭连接*。 +client在2MSL之后,关闭连接。

*如果server在重传时间没有收到关于FIN报文的ACK报文的话,会再次发出FIN报文,这也就是TIME-WAIT状态存在的意义。

3.GET与POST的区别

+GET的请求参数会在URL中显示出来 +POST是将数据放到HTTP包的包体中 +GET提交数据的长度收到URL长度的限制 +POST提交数据长度理论上没有限制 +POST的安全性要比GET的安全性高。

*HEAD: 只请求页面的首部。

4.Cookie与Session的区别

Cookie:

+服务器使用response向客户端浏览器颁发一个Cookie。客户端浏览器会把Cookie保存起来。 +当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给服务器。 +服务器检查该Cookie,以此来辨认用户状态。 +服务器还可以根据需要修改Cookie的内容。 +Coockie为纯文本的key-value形式。 +Cookie具有不可跨域名性:一个网站无法查看另一个网站的Cookie

Session:

服务器在内存中开辟一小段位置来保持用户的信息,然后将sessionID发送给用户,用户以后请求都带上该sessionID

5.TCP和UDP的区别?

+TCP提供面向连接的、可靠的数据流传输,而UDP提供的是非面向连接的、不可靠的数据流传输。 +TCP传输单位称为TCP报文段,UDP传输单位称为用户数据报。 +TCP注重数据安全性,UDP数据传输快,因为不需要连接等待,少了许多操作,但是其安全性却一般。

TCP对应的协议:

1.FTP:定义了文件传输协议,使用21端口。 2.Telnet:一种用于远程登陆的端口,使用23端口,用户可以以自己的身份远程连接到计算机上,可提供基于DOS模式下的通信服务。 3.SMTP:邮件传送协议,用于发送邮件。服务器开放的是25号端口。 4.POP3:它是和SMTP对应,POP3用于接收邮件。POP3协议所用的是110端口。 5.HTTP:是从Web服务器传输超文本到本地浏览器的传送协议。

UDP对应的协议:

1.DNS:用于域名解析服务,将域名地址转换为IP地址。DNS用的是53号端口。 2.SNMP:简单网络管理协议,使用161号端口,是用来管理网络设备的。由于网络设备很多,无连接的服务就体现出其优势。 3.TFTP(Trival File Transfer Protocal),简单文件传输协议,该协议在熟知端口69上使用UDP服务。

DNS域名系统,简单描述其工作原理。

当DNS客户机需要在程序中使用名称时,它会查询DNS服务器来解析该名称。客户机发送的每条查询信息包括三条信息:包括:指定的DNS域名,指定的查询类型,DNS域名的指定类别。基于UDP服务,端口53. 该应用一般不直接为用户使用,而是为其他应用服务,如HTTP,SMTP等在其中需要完成主机名到IP地址的转换。

IP地址的分类 +A类地址:以0开头, 第一个字节范围:0~127(1.0.0.0 - 126.255.255.255); +B类地址:以10开头, 第一个字节范围:128~191(128.0.0.0 - 191.255.255.255); +C类地址:以110开头, 第一个字节范围:192~223(192.0.0.0 - 223.255.255.255); 10.0.0.0—10.255.255.255, 172.16.0.0—172.31.255.255, 192.168.0.0—192.168.255.255。(Internet上保留地址用于内部)

ARP是地址解析协议,简单语言解释一下工作原理。

1.首先,每个主机都会在自己的ARP缓冲区中建立一个ARP列表,以表示IP地址和MAC地址之间的对应关系。 2.当源主机要发送数据时,首先检查ARP列表中是否有对应IP地址的目的主机的MAC地址,如果有,则直接发送数据,如果没有,就向本网段的所有主机发送ARP数据包,该数据包包括的内容有:源主机 IP地址,源主机MAC地址,目的主机的IP 地址。 3.当本网络的所有主机收到该ARP数据包时,首先检查数据包中的IP地址是否是自己的IP地址,如果不是,则忽略该数据包,如果是,则首先从数据包中取出源主机的IP和MAC地址写入到ARP列表中,如果已经存在,则覆盖,然后将自己的MAC地址写入ARP响应包中,告诉源主机自己是它想要找的MAC地址。 4.源主机收到ARP响应包后。将目的主机的IP和MAC地址写入ARP列表,并利用此信息发送数据。如果源主机一直没有收到ARP响应数据包,表示ARP查询失败。 广播发送ARP请求,单播发送ARP响应。

RARP协议

RARP是逆地址解析协议,作用是完成硬件地址到IP地址的映射,主要用于无盘工作站,因为给无盘工作站配置的IP地址不能保存。工作流程:在网络中配置一台RARP服务器,里面保存着IP地址和MAC地址的映射关系,当无盘工作站启动后,就封装一个RARP数据包,里面有其MAC地址,然后广播到网络上去,当服务器收到请求包后,就查找对应的MAC地址的IP地址装入响应报文中发回给请求者。因为需要广播请求报文,因此RARP只能用于具有广播能力的网络。

OSI,TCP/IP,五层协议的体系结构,以及各层协议

OSI分层 (7层):物理层、数据链路层、网络层、传输层、会话层、表示层、应用层。 TCP/IP分层(4层):网络接口层、 网际层、运输层、 应用层。

每一层的协议如下: +物理层:RJ45、CLOCK、IEEE802.3 (中继器,集线器,网关) +数据链路:PPP、FR、HDLC、VLAN、MAC (网桥,交换机) +网络层:IP、ICMP、ARP、RARP、OSPF、IPX、RIP、IGRP、 (路由器) +传输层:TCP、UDP、SPX +会话层:NFS、SQL、NETBIOS、RPC +表示层:JPEG、MPEG、ASII +应用层:FTP、DNS、Telnet、SMTP、HTTP、WWW、NFS

每一层的作用如下: +物理层:通过媒介传输比特,确定机械及电气规范(比特Bit) +数据链路层:将比特组装成帧和点到点的传递(帧Frame) +网络层:负责数据包从源到宿的传递和网际互连(包PackeT) +传输层:提供端到端的可靠报文传递和错误恢复(段Segment) +会话层:建立、管理和终止会话(会话协议数据单元SPDU) +表示层:对数据进行翻译、加密和压缩(表示协议数据单元PPDU) +应用层:允许访问OSI环境的手段(应用协议数据单元APDU)

回车按下google.com之后的故事

解析URL 浏览器通过URL能够知道下面的信息: Protocol “http”,使用HTTP协议。Resource "/",请求的资源的位置。知道域名是www.google.com

DNS查询 浏览器提出检查域名的请求,调用操作系统的库函数进行查询,库函数首先看看要查询的域名是否在本地主机里,如果库函数在本地主机中没有找到这个域名的缓存记录,那么就要向默认的DNS服务器去查询,利用UDP的53端口。 具体的流程是:把要查询的域名作为data,然后加上数据报头,目的端口53,封装成一个UDP的数据报文。然后把默认的DNS服务器的地址作为IP层的目的IP地址封装成一个IP报文,IP层根据报文中的目的IP地址,查看本地路由表,判断目标主机跟它本身在不在同一个局域网中,如果在同一个局域网中就直接交付,否则将这个报文发给网关(默认DNS域名服务器的地址是8.8.8.8就是一个例子)。 如果要交付给网关,就通过ARP地址解析网关IP,获得网关的MAC地址,然后直接把报文封装成Frame,发送给网关。 如果要交付给本地局域网,就通过ARP地址解析目的主机的IP地址,获得目的主机的MAC地址,然后直接把报文封装成Frame,发送给目的主机。 不管怎样,不管默认域名服务器(本地域名服务器)在本局域网还是在不同的子网,现在DNS查询报文都到了本地域名服务器。并通过端口把查询数据给力端口对应的那个应用程序。 如果本地域名服务器没有找到结果,主机和本地域名服务器之间采用递归查询的方式,本地域名服务器向根域名服务器以迭代的方式查询。 不管怎样,主机最后得到了Google.com域名对应的IP地址。 使用套接字 当浏览器得到了目标服务器的IP地址之后,以URL中给出的端口号(http协议默认80端口,https默认443),它会调用一个系统库函数socket,请求一个TCP流套接字。 这个 连接请求(这时发送的不是网页请求,而是连接请求,因为TCP要先建立连接) 首先会被交付给传输层,封装成TCP数据报,目标端口放在头部,源端口随机选择一个。 TCP送到网络层,加上IP头部,包含了目标服务器的IP地址和本地IP地址。封装成一个IP Packet。 这个IP Packet交给链路层,链路层会在封包中加入Frame头部,里面包含了本地内置网卡的MAC地址和网关的MAC地址( 具体这个下一跳的MAC地址用谁的,要看路由表怎么指示的,根据路由表提供的下一跳的IP地址,ARP广播这个IP地址对应的主机或路由器端口的MAC地址 ) 到现在为止一个连接请求的帧已经被封装到了MAC帧,然后就是通过什么介质传出去。 就这样这个数据在网络中传输,如果这个报文在传输或者在接收端发生错误,就会给发送端发送一个ICMP差错报文通知发送端。 正确到达对端的TCP后,通过三次握手,建立本地主机和远端服务器的连接。 然后浏览器发送取文件的GET命令,服务器给出响应,把响应的文件发送给本地主机。(客户对服务器文档的请求可以在第三次握手的确认报文中捎带过去) 然后通过四次挥手,双方断开TCP连接。 拿到文件的本地主机,通过浏览器去解析这个文件,在浏览器上呈现出来。

#十六、数据库

数据库三大范式

+第一范式(1NF):数据库表中的字段都是单一属性的,不可再分。

+第二范式(2NF):数据库表中所有非关键字段都完全依赖于联合主键,而不是依赖于联合主键中的某一个或某一些。

+第三范式(3NF):数据库表中所有非关键字段都和主键直接相关,而不能间接相关。

MySQL的存储引擎

MyISAM 它不支持事务,也不支持外键,尤其是访问速度快;SELECT、INSERT为主的应用基本都可以使用这个引擎来创建表 (索引和数据分离,索引被载入内存中)

InnoDB 事务安全、提供自动增长、外键约束;InnoDB写的处理效率差一些并且会占用更多的磁盘空间以保留数据和索引。(索引与数据在一起,无法直接放入内存,而是被放到InnoBD缓冲区)

MEMORY memory使用存在内存中的内容来创建表,MEMORY类型的表访问非常快;但是一旦服务器关闭,表中的数据就会丢失,但表还会继续存在。

MERGE merge存储引擎是一组MyISAM表的组合

事务

事务是应用程序中一系列严密的操作,所有操作必须成功完成,否则在每个操作中所作的所有更改都会被撤消。

事务具有四个特征:

原子性( Atomicity )

事务是数据库的逻辑工作单位,事务中包含的各操作要么都做,要么都不做

一致性( Consistency )

事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。因此当数据库只包含成功事务提交的结果时,就说数据库处于一致性状态。如果数据库系统 运行中发生故障,有些事务尚未完成就被迫中断,这些未完成事务对数据库所做的修改有一部分已写入物理数据库,这时数据库就处于一种不正确的状态,或者说是 不一致的状态。

隔离性( Isolation )

一个事务的执行不能其它事务干扰。即一个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务之间不能互相干扰。

持续性( Durability )

也称永久性,指一个事务一旦提交,它对数据库中的数据的改变就应该是永久性的。接下来的其它操作或故障不应该对其执行结果有任何影响。

转载于:https://my.oschina.net/u/189332/blog/551943

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值