Java基础知识面试题(2022优化版)_第1页
Java基础知识面试题(2022优化版)_第2页
Java基础知识面试题(2022优化版)_第3页
Java基础知识面试题(2022优化版)_第4页
Java基础知识面试题(2022优化版)_第5页
已阅读5页,还剩33页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

Java基础知识面试题(2022优化版)

Java概述

谈谈你对Java平台的理解?“Java是解释执行〞,这句话正确吗?

对Java平台的理解主要包括下列三个方面:面向对象和核心类库方面,跨平台方面和虚拟机和垃圾收集

面向对象和核心类库方面

Java是一门面向对象编程语言,极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程。

Java核心类库提供了包含汇合容器、线程相关类、IO/NIO、J.U.C并发包,异常和平安等类库,极大地方便了程序员的开发;

JDK提供的工具包含:根本的编译工具、虚拟机性能检测相关工具等。

跨平台方面

所谓的“一次编写,到处运行〞〔Writeonce,runanywhere〕,能够非常容易地获得跨平台能力。跟c/c++最大的不同点在于,c/c++编程是面向操作系统的,需要开发者极大地关怀不同操作系统之间的差别性;而Java平台通过虚拟机屏蔽了操作系统的底层细节,使得开发者无需过多地关怀不同操作系统之间的差别性。

通过增加一个间接的中间层来进行〞解耦“是计算机领域非常常用的一种〞艺术手法“,虚拟机是这样,操作系统是这样;

虚拟机和垃圾收集

另外就是Java虚拟机和垃圾收集〔GC,GarbageCollection〕,Java通过垃圾收集器〔GarbageCollector〕回收分配内存,大局部情况下,程序员不需要自己操心内存的分配和回收。

同时,围绕虚拟机的效率问题展开,将波及到一些优化技术,示例:JIT、AOT。因为如果虚拟机加载字节码后,一行一行地解释执行,这势必会影响执行效率。所以,对于这个运行环节,虚拟时机进行一些优化处理,示例JIT技术,会将热点代码编译成机器码。而AOT技术,是在运行前,通过工具直接将字节码转换为机器码。

对于“Java是解释执行〞这句话,这个说法不太准确。我们开发的Java的源代码,首先通过Javac编译成为字节码〔bytecode〕,然后,在运行时,通过Java虚拟机〔JVM〕内嵌的解释器将字节码转换成为最终的机器码。但是常见的JVM,比方我们大多数情况使用的OracleJDK提供的HotspotJVM,都提供了JIT〔Just-In-Time〕编译器,也就是通常所说的动态编译器,JIT能够在运行时将热点代码编译成机器码,这种情况下局部热点代码就属于编译执行,而不是解释执行了。

JVM、JRE和JDK的关系

JVM

JavaVirtualMachine是Java虚拟机,Java程序需要运行在虚拟机上,不同的平台有自己的虚拟机,因此Java语言可以实现跨平台。

JRE

JavaRuntimeEnvironment包括Java虚拟机和Java程序所需的核心类库等。核心类库主要是java.lang包:包含了运行Java程序必不可少的系统类,如包装类型、根本数学函数、字符串处理、线程、异常处理类等,系统缺省加载这个包。如果想要运行一个开发好的Java程序,计算机中只需要安装JRE即可。

JDK

JavaDevelopmentKit是提供应Java开发人员使用的,其中包含了Java的开发工具,也包括了JRE。所以安装了JDK,就无需再独自安装JRE了。其中的开发工具:编译工具(javac.exe),打包工具(jar.exe)等

JVM&JRE&JDK关系图

什么是跨平台性?原理是什么

所谓跨平台性,是指java语言编写的程序,一次编译后,可以在多个系统平台上运行。

实现原理:Java程序是通过虚拟机在系统平台上运行的,只要该系统安装相应的java虚拟机,该系统就可以运行java程序。

什么是字节码?采用字节码的最大好处是什么

字节码:Java源代码经过虚拟机编译器编译后产生的文件〔即扩展为.class的文件〕,它不面向任何特定的处理器或操作系统,只面向虚拟机。

采用字节码的好处:

Java语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保存了解释型语言可移植的特点。所以Java程序运行时比拟高效,而且,由于字节码并不面向任何特定的处理器或操作系统,因此,Java程序无须重新编译便可在多种不同的计算机上运行。

先看下java中的编译器和解释器:

Java源程序经过编译器编译后变成字节码,字节码由虚拟机解释执行,虚拟机将每一条要执行的字节码送给解释器,解释器将其翻译成特定机器上的机器码,然后在特定的机器上运行

Java源代码---->编译器---->jvm可执行的Java字节码(即虚拟指令)---->jvm---->jvm中解释器----->机器可执行的二进制机器码---->程序运行。

根底语法

Java有哪些数据类型

定义:Java语言是强类型语言,对于每一种数据都定义了明确的具体的数据类型,在内存中分配了不同大小的内存空间。

分类

根本数据类型

整数类型(byte,short,int,long)

浮点类型(float,double)

数值型

字符型(char)

布尔型(boolean)

引用数据类型

类(class)

接口(interface)

数组([])

Java根本数据类型图

访问修饰符public,private,protected,以及不写〔默认〕时的区别

定义:Java中可以使用访问修饰符来爱护对类、变量、办法的访问。Java支持4种不同的访问权限。

分类

private:在同一类内可见。使用对象:变量、办法。注意:不能修饰类〔外部类〕

default(即缺省,什么也不写,不使用任何关键字〕:在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、办法。

protected:对同一包内的类和所有子类可见。使用对象:变量、办法。注意:不能修饰类〔外部类〕。

public:对所有类可见。使用对象:类、接口、变量、办法

访问修饰符图

&和&&的区别

&运算符有两种用法:(1)按位与;(2)逻辑与。

&&运算符是短路与运算。逻辑与跟短路与的差异是非常巨大的,虽然二者都要求运算符左右两端的布尔值都是true整个叙述式的值才是true。&&之所以称为短路运算,是因为如果&&左边的叙述式的值是false,右边的叙述式会被直接短路掉,不会进行运算。

注意:逻辑或运算符〔|〕和短路或运算符〔||〕的差异也是如此。

@$finalfinallyfinalize区别

final是一个修饰符关键字,可以修饰类、办法、变量,修饰类表示该类不能被继承、修饰办法表示该办法不能被重写、修饰变量表示该变量是一个常量不能被重新赋值。

finally是一个异常处理的关键字,一般作用在try-catch-finally代码块中,在处理异常的时候,通常我们将一定要执行的代码放在finally代码块中,表示不论是否出现异常,该代码块都会执行,一般用来寄存一些关闭资源的代码。

finalize是属于Object类的一个办法,该办法一般由垃圾回收器来调用,它的设计目的是保证对象在被垃圾收集前完成特定资源的回收。finalize机制现在已经不推荐使用,并且在JDK9被标记为deprecated。

thissuper关键字的用法

this是指向对象本身的一个指针。

this的用法在java中大体可以分为3种:

普通的直接引用,this相当于是指向当前对象本身。

当形参与成员名字重名,用this来辨别

引用本类的构造函数

super可以理解为是指向自己父类对象的一个指针,而这个超类指的是离自己最近的一个父类。

super也有三种用法:

普通的直接引用,super相当于是指向当前对象的父类引用,这样就可以用super.xxx来引用父类的成员。

当子类中的成员变量或办法与父类中的成员变量或办法同名时,用super进行辨别

引用父类构造函数

static存在的主要意义

static的主要意义是在于创立独立于具体对象的变量或者办法。即使没有创立对象,也能使用属性和调用办法!

static关键字还有一个比拟关键的作用就是用来形成静态代码块来优化程序性能。static块可以置于类中的任何地方,类中可以有多个static块。在类初次被加载的时候,会按照static块的顺序来执行每个static块,并且只会执行一次。因此,很多时候会将一些只需要进行一次初始化的操作放在static代码块中。

break,continue,return的区别及作用

break结束当前的循环体

continue跳出本次循环,继续执行下次循环-

return结束当前的办法,直接返回

面向对象

面向对象和面向过程的区别

面向过程:

优点:性能比面向对象高,因为类调用时需要实例化,开销比拟大,比拟耗费资源;比方单片机、嵌入式开发、Linux/Unix等一般采用面向过程开发,性能是最重要的因素。

缺点:没有面向对象易维护、易复用、易扩展

面向对象:

优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护

缺点:性能比面向过程低

面向过程是具体化的,流程化的,解决一个问题,你需要一步一步的分析,一步一步的实现

面向对象是抽象化的,模型化的,你只需抽象出一个类,这是一个封闭的盒子,在这里你拥有数据也拥有解决问题的办法。需要什么功能直接使用就可以了,不必去一步一步的实现,至于这个功能是如何实现的,我们可以不用太关怀,会用就可以了

面向对象的底层其实还是面向过程,把面向过程抽象成类,然后封装,方便我们使用的就是面向对象了。

@$面向对象三大特性

面向对象的特征主要有下列几个方面:

抽象:抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面。抽象只关注对象有哪些属性和行为,并不关注这些行为的细节是什么。

其中Java面向对象编程三大特性:封装继承多态

封装:封装是把一个对象的属性私有化,隐藏内部的实现细节,同时提供一些可以被外界访问属性的办法。通过封装可以使程序便于使用,提高复用性和平安性

继承:继承是使用已存在的类的定义作为根底,建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过继承可以提高代码复用性。继承是多态的前提。

关于继承如下3点请记住

子类拥有父类非private的属性和办法。

子类可以拥有自己的属性和办法,即子类可以对父类进行扩展。

子类可以用自己的方式实现父类的办法。

多态性:父类或接口定义的引用变量可以指向子类或具体实现类的实例对象。多态提高了程序的扩展性。一个引用变量到底会指向哪个类的实例对象,该引用变量发出的办法调用到底是哪个类中实现的办法,必须在程序运行期间才能决定。

Java实现多态有三个必要条件:继承、重写、向上转型。

继承:在多态中必须存在有继承关系的子类和父类。

重写:子类对父类中某些办法进行重新定义,在调用这些办法时就会调用子类的办法。

向上转型:在多态中需要将子类的引用赋给父类对象,只有这样该引用才能够具备调用父类和子类的办法的技能。

只有满足了上述三个条件,我们才能够在同一个继承结构中使用统一的逻辑实现代码处理不同的对象,从而到达执行不同的行为。

@$抽象类和接口的比照

抽象类是用来捕捉子类的通用特性的,实现代码重用。接口是抽象办法的汇合,利用接口可以到达API定义和实现别离的目的,提供程序的扩展性和可维护性。

从设计层面来说,抽象类是对类的抽象,是一种模板设计,接口是行为的抽象,是一种行为的标准。

相同点

接口和抽象类都不能实例化

都位于继承的顶端,用于被其他类实现或继承

都包含抽象办法,其子类都必须重写这些抽象办法

不同点

参数 抽象类 接口

声明 抽象类使用abstract关键字声明 接口使用interface关键字声明

实现 子类使用extends关键字来继承抽象类。如果一个类继承了抽象类,则该子类必须实现抽象类的所有抽象办法。 子类使用implements关键字来实现接口。如果一个类实现了接口,则该子类必须实现父接口的所有办法。

构造器 抽象类可以有构造器 接口不能有构造器

访问修饰符 抽象类中的办法可以是任意访问修饰符 接口办法默认修饰符是public。并且不允许定义为private或者protected

字段声明 抽象类的字段声明可以是任意的 接口的字段默认都是static和final的

多继承 一个类最多只能继承一个抽象类 一个类可以实现多个接口

备注:Java8中接口中引入默认办法和静态办法,以此来减少抽象类和接口之间的差别。现在,我们可以为接口提供默认实现的办法了,并且不用强制子类来实现它。

接口和抽象类各有优缺点,在接口和抽象类的选择上,必须遵守下面的几个原那么:

抽象类用来定义某个领域的固有属性,即抽象类表示它是什么,接口用来定义某个领域的扩展功能,即接口表示它能做什么。

当需要为子类提供公共的实现代码时,应优先考虑抽象类。因为抽象类中的非抽象办法可以被子类继承,使实现功能的代码更简洁。

当注重代码的扩展性和可维护性时,应当优先采用接口。①接口与实现类之间可以不存在任何层次关系,接口可以实现毫不相关类的行为,比抽象类的使用更加方便灵活;②接口只关怀对象之间的交互办法,而不关怀对象所对应的具体类。接口是程序之间的一个协议,比抽象类的使用更平安、清晰。一般使用接口的情况更多。

@$成员变量与部分变量的区别有哪些

变量:在程序执行的过程中,其值可以在某个范围内发生改变的量。从本质上讲,变量其实是内存中的一小块区域

各变量联系与区别

成员变量:作用范围是整个类,相当于C中的全局变量,定义在办法体和语句块之外,一般定义在类的声明之下;成员变量包括实例变量和静态变量(类变量);

实例变量:独立于与办法之外的变量,无static修饰,声明在一个类中,但在办法、构造办法和语句块之外,数值型变量默认值为0,布尔型默认值为false,引用类型默认值为null;

静态变量(类变量):独立于办法之外的变量,用static修饰,默认值与实例变量相似,一个类中只有一份,属于对象共有,存储在静态存储区,经常被声明为常量,调用一般是类名.静态变量名,也可以用对象名.静态变量名调用;

部分变量:类的办法中的变量,访问修饰符不能用于部分变量,声明在办法、构造办法或语句块中,在栈上分配,无默认值,必须初始化后才能使用;

成员变量和部分变量的区别

成员变量 部分变量

作用域 作用范围是整个类 在办法或者语句块内有效

存储位置和生命周期 随着对象的创立而存在,随着对象的消失而消失,存储在堆内存中 在办法被调用的时候存在,办法调用完会自动释放,存储在栈内存中

初始值 有默认初始值 没有默认初始值,使用前必须赋值

使用原那么 就近原那么,首先在部分位置找,有就使用;接着在成员位置找

@$重载〔Overload〕和重写〔Override〕的区别。重载的办法能否根据返回类型进行辨别?

办法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。

重载:发生在同一个类中,办法名相同参数列表不同〔参数类型不同、个数不同、顺序不同〕,与办法返回值和访问修饰符无关,即重载的办法不能根据返回类型进行辨别

重写:发生在父子类中,办法名、参数列表必须相同,返回值小于等于父类,抛出的异常小于等于父类,访问修饰符大于等于父类〔里氏代换原那么〕;如果父类办法访问修饰符为private那么子类中就不是重写。

@$==和equals的区别是什么

==:它的作用是判断两个对象的内存地址是不是相等。即,判断两个对象是不是同一个对象。(根本数据类型==比拟的是值,引用数据类型==比拟的是内存地址)

equals():它的作用也是判断两个对象是否相等。有两种使用情况:

情况1:类没有覆盖equals()办法,等价于通过“==〞比拟这两个对象。

情况2:类覆盖了equals()办法。一般我们都覆盖equals()办法来判断两个对象的内容相等;假设它们的内容相等,那么返回true(即,认为这两个对象相等)。

举个例子

publicclasstest1{

publicstaticvoidmain(String[]args){

Stringa=newString("ab");//a为一个引用

Stringb=newString("ab");//b为另一个引用,对象的内容一样

Stringaa="ab";//放在常量池中

Stringbb="ab";//从常量池中查找

if(aa==bb)//true

System.out.println("aa==bb");

if(a==b)//false,非同一对象

System.out.println("a==b");

if(a.equals(b))//true

System.out.println("aEQb");

if(42==42.0){//true

System.out.println("true");

}

}

}

表明:

String中的equals办法是被重写过的,因为object的equals办法是比拟的对象的内存地址,而String的equals办法比拟的是对象的值。

当创立String类型的对象时,虚拟时机在常量池中查找有没有已经存在的值和要创立的值相同的对象,如果有就把它赋给当前引用。如果没有就在常量池中重新创立一个String对象。

@$hashCode与equals

hashCode()介绍

hashCode()的作用是获取哈希码,也称为散列码;它实际上是返回一个int类型的整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode()定义在JDK的Object类中,这就意味着Java中的任何类都包含有hashCode()函数。

散列表存储的是键值对(key-value),它的特点是:能根据“键〞快速的检索出对应的“值〞。这其中就利用到了散列码!〔可以快速找到所需要的对象〕

为什么要有hashCode

我们以“HashSet如何检查重复〞为例子来表明为什么要有hashCode:

当你把对象参加HashSet时,HashSet会先计算对象的hashcode值来判断对象参加的位置,同时也会与其他已经参加的对象的hashcode值作比拟,如果没有相同的hashcode,HashSet会若对象没有重复出现。但是如果发现有相同hashcode值的对象,这时会调用equals()办法来检查hashcode相等的对象是否真的相同。如果两者相同,HashSet就不会让其参加操作成功。如果不同的话,就会重新散列到其他位置。

先进行hashcode比拟,后进行equals办法比拟的目的:可以大大减少了equals办法比拟的次数,相应就大大提高了执行速度。

hashCode()与equals()的相关规定

如果两个对象相等,那么hashcode一定也是相同的

两个对象相等,对两个对象分别调用equals办法都返回true

两个对象有相同的hashcode值,它们不一定是相等的

因此,当重写equals办法后有必要将hashCode办法也重写,这样做才能保证不违背hashCode办法中“相同对象必须有相同哈希值〞的约定。

值传递和引用传递有什么区别

值传递,按值调用(callbyvalue):指的是在办法调用时,传递的参数是值的拷贝,传递后就互不相关了。

引用传递,引用调用〔callbyreference):指的是在办法调用时,传递的参数是引用变量所对应的内存地址。传递前和传递后都指向同一个引用〔也就是同一个内存空间〕。

一个办法可以修改引用传递所对应的变量值,而不能修改值传递所对应的变量值

为什么Java中只有值传递

Java采用按值调用。也就是说,办法得到的是所有参数值的一个拷贝,也就是说,办法不能修改传递给它的任何参数变量的内容。

下面通过3个例子来给大家表明

example1

publicstaticvoidmain(String[]args){

intnum1=10;

intnum2=20;

swap(num1,num2);

System.out.println("num1="+num1);

System.out.println("num2="+num2);

}

publicstaticvoidswap(inta,intb){

inttemp=a;

a=b;

b=temp;

System.out.println("a="+a);

System.out.println("b="+b);

}

结果:

a=20

b=10

num1=10

num2=20

解析:

在swap办法中,a、b的值进行交换,并不会影响到num1、num2。因为a、b中的值,只是从num1、num2的复制过来的。也就是说,a、b相当于num1、num2的副本,副本的内容无论怎么修改,都不会影响到原件本身。

通过上面例子,我们已经知道了一个办法不能修改一个根本数据类型的参数,而对象引用作为参数就不一样,请看example2.

example2

publicstaticvoidmain(String[]args){

int[]arr={1,2,3,4,5};

System.out.println(arr[0]);

change(arr);

System.out.println(arr[0]);

}

publicstaticvoidchange(int[]array){

//将数组的第一个元素变为0

array[0]=0;

}

结果:

1

0

解析:

array被初始化arr的拷贝也就是一个对象的引用,也就是说array和arr指向的时同一个数组对象。因此,外部对引用对象的改变会反映到所对应的对象上。

通过example2我们已经看到,实现一个改变对象参数状态的办法并不是一件难事。理由很简单,办法得到的是对象引用的拷贝,对象引用及其他的拷贝同时引用同一个对象。

很多程序设计语言〔特别是,C++和Pascal)提供了两种参数传递的方式:值调用和引用调用。有些程序员〔甚至本书的作者〕认为Java程序设计语言对对象采用的是引用调用,实际上,这种理解是不对的。由于这种误解具有一定的普遍性,所下列面给出一个反例来详细地阐述一下这个问题。

example3

publicclassTest{

publicstaticvoidmain(String[]args){

//TODOAuto-generatedmethodstub

Students1=newStudent("小张");

Students2=newStudent("小李");

Test.swap(s1,s2);

System.out.println("s1:"+s1.getName());

System.out.println("s2:"+s2.getName());

}

publicstaticvoidswap(Studentx,Studenty){

Studenttemp=x;

x=y;

y=temp;

System.out.println("x:"+x.getName());

System.out.println("y:"+y.getName());

}

}

结果:

x:小李

y:小张

s1:小张

s2:小李

解析:

交换之前:

交换之后:

通过上面两张图可以很清晰的看出:办法并没有改变存储在变量s1和s2中的对象引用。swap办法的参数x和y被初始化为两个对象引用的拷贝,这个办法交换的是这两个拷贝

总结

Java对对象采用的不是引用调用,实际上,对象引用是按值传递的。

下面再总结一下Java中办法参数的使用情况:

一个办法不能修改一个根本数据类型的参数〔即数值型或布尔型〕

一个办法可以改变一个对象参数的状态。

一个办法不能让对象参数引用一个新的对象。

IO流

java中IO流分为几种《

按照流的流向分,可以分为输入流和输出流;

按照操作单元划分,可以分为字节流和字符流;

按照流的角色划分,可以分为节点流和处理流。

JavaIO流共波及40多个类,这些类看上去很杂乱,但实际上很有规那么,而且彼此之间存在非常紧密的联系,JavaIO流的40多个类大局部都是从如下4个抽象类基类中派生出来的。

InputStream/Reader:所有的输入流的基类,前者是字节输入流,后者是字符输入流。

OutputStream/Writer:所有输出流的基类,前者是字节输出流,后者是字符输出流。

@$BIO,NIO,AIO有什么区别《

在讲BIO,NIO,AIO之前先来回忆一下这样几个概念:同步与异步,阻塞与非阻塞。

同步与异步

同步:同步就是发起一个请求,被调用者未处理完请求之前,调用不返回。

异步:异步就是发起一个请求,立刻得到被调用者的响应表示已接收到请求,但是被调用者并没有返回请求处理结果,此时我们可以处理其他的请求,被调用者通过事件和回调等机制来通知调用者其返回结果。

同步和异步的区别在于调用者需不需要等待被调用者的处理结果。

阻塞和非阻塞

阻塞:阻塞就是发起一个请求,调用者一直等待请求结果返回,也就是当前线程会被挂起,无法从事其他任务,只有当返回结果才能继续。

非阻塞:非阻塞就是发起一个请求,调用者不用一直等着结果返回,可以先去干其他事情。

阻塞和非阻塞的区别在于调用者的线程需不需要挂起。

JavaIO方式有很多种,基于不同的IO抽象模型和交互方式,可以进行简单辨别。

BIO〔jdk1.4之前〕:BlockIO同步阻塞式IO,就是我们平常使用的传统IO,它基于流模型实现,一个连接一个线程,客户端有连接请求时,效劳器端就需要启动一个线程进行处理,线程开销大。伪异步IO:将请求连接放入线程池,一对多,但线程还是很珍贵的资源。它的特点是模式简单使用方便,但并发处理能力低,容易成为应用性能的瓶颈。BIO是面向流的,BIO的Stream是单向的。

很多时候,人们也把包下的局部网络API,比方Socket、ServerSocket、HttpURLConnection也归类到同步阻塞IO,因为网络通信同样是IO行为。

NIO〔jdk1.4之后

linux的多路复用技术select模式〕:NonIO同步非阻塞IO,是传统IO的升级,提供了Channel、Selector、Buffer等新的抽象,客户端和效劳器端通过Channel〔通道〕通讯,实现了多路复用。客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。Mina2.0和Netty5.0网络通信框架都是通过NIO实现的网络通信。NIO是面向缓冲区的,NIO的channel是双向的。

NIO能解决什么问题?

通过一个固定大小的线程池,来负责管理工作线程,防止频繁创立、销毁线程的开销,这是我们构建并发效劳的典型方式。

NIO那么是利用了单线程轮询事件的机制,通过高效地定位就绪的Channel,来决定做什么,仅仅select阶段是阻塞的,可以有效防止大量客户端连接时,频繁线程切换带来的问题,应用的扩展能力有了非常大的提高。

AIO〔jdk

1.7过后又叫NIO2〕:AsynchronousIO异步非梗塞IO,是NIO的升级,异步IO的操作基于事件和回调机制,性能是最好的。底层实现是通过epoll的I/O多路复用机制。

BIO、NIO、AIO实现原理

BIO实现原理

BIO模型是最早的jdk提供的一种处理网络连接请求的模型,是同步阻塞结构,通常由一个独立的Acceptor线程负责监听客户端的连接,它接收到客户端连接请求之后为每个客户端创立一个新的线程进行链路处理,处理完成后,通过输出流返回应答给客户端,线程销毁。即典型的一请求一应答模型。

BIO是同步阻塞式IO,通常在while循环中效劳端会调用accept办法等待接收客户端的连接请求,一旦接收到一个连接请求,就可以建立通信套接字在这个通信套接字上进行读写操作,此时不能再接收其他客户端连接请求,只能等待同当前连接的客户端的操作执行完成。

如果BIO要能够同时处理多个客户端请求,就必须使用多线程,即每次accept阻塞等待来自客户端请求,一旦受到连接请求就建立通信套接字同时开启一个新的线程来处理这个套接字的数据读写请求,然后立刻又继续accept等待其他客户端连接请求,即为每一个客户端连接请求都创立一个线程来独自处理。

NIO实现原理

nio模型事件处理流程:

Acceptor注册Selector,监听accept事件;

当客户端连接后,触发accept事件;

效劳器构建对应的Channel,并在其上注册Selector,监听读写事件;

当发生读写事件后,进行相应的读写处理。

NIO本身是基于事件驱动思想来完成的,其主要想解决的是BIO的大并发问题,即在使用同步I/O的网络应用中,如果要同时处理多个客户端请求,或是在客户端要同时和多个效劳器进行通讯,就必须使用多线程来处理。也就是说,将每一个客户端请求分配给一个线程来独自处理。这样做虽然可以到达我们的要求,但同时又会带来另外一个问题。由于每创立一个线程,就要为这个线程分配一定的内存空间〔也叫工作存储器〕,而且操作系统本身也对线程的总数有一定的限制。如果客户端的请求过多,效劳端程序可能会因为不堪重负而拒绝客户端的请求,甚至效劳器可能会因此而瘫痪。

NIO基于Reactor,当selector有流可读或可写入socket时,操作系统会相应的通知应用程序进行处理,应用再将流读取到缓冲区或写入操作系统。

也就是说,这个时候,已经不是一个连接就要对应一个处理线程了,而是有效的请求,对应一个线程,当连接没有数据时,是没有工作线程来处理的。

Reactor单线程模型

这是最简单的单Reactor单线程模型。Reactor线程负责多路别离套接字、accept新连接,并分派请求到处理器链中。该模型适用于处理器链中业务处理组件能快速完成的场景。不过,这种单线程模型不能充沛利用多核资源,所以实际使用的不多。

这个模型和上面的NIO流程很类似,只是将消息相关处理独立到了Handler中去了。

虽然上面说到NIO一个线程就可以支持所有的IO处理。但是瓶颈也是显而易见的。我们看一个客户端的情况,如果这个客户端屡次进行请求,如果在Handler中的处理速度较慢,则后续的客户端请求都会被积压,导致响应变慢!所以引入了Reactor多线程模型。

Reactor多线程模型

相比上一种模型,该模型在处理器链局部采用了多线程〔线程池〕:

Reactor多线程模型就是将Handler中的IO操作和非IO操作分开,操作IO的线程称为IO线程,非IO操作的线程称为工作线程。这样的话,客户端的请求会直接被丢到线程池中,客户端发送请求就不会梗塞。

但是当用户进一步增加的时候,Reactor会出现瓶颈!因为Reactor既要处理IO操作请求,又要响应连接请求。为了分担Reactor的负担,所以引入了主从Reactor模型。

主从Reactor多线程模型

主从Reactor多线程模型是将Reactor分成两局部,mainReactor负责监听serversocket,accept新连接,并将建立的socket分派给subReactor。subReactor负责多路别离已连接的socket,读写网络数据,对业务处理功能,其扔给worker线程池完成。通常,subReactor个数上可与CPU个数等同:

可见,主Reactor用于响应连接请求,从Reactor用于处理IO操作请求。

AIO

与NIO不同,当进行读写操作时,只须直接调用API的read或write办法即可。这两种办法均为异步的,对于读操作而言,当有流可读取时,操作系统会将可读的流传入read办法的缓冲区,并通知应用程序;对于写操作而言,当操作系统将write办法传递的流写入完毕时,操作系统主动通知应用程序。

AIO是一种接口规范,各家操作系统可以实现也可以不实现。在不同操作系统上在高并发情况下最好都采用操作系统推荐的方式。Linux上还没有真正实现网络方式的AIO。

异步IO那么采用“订阅-通知〞模式:即应用程序向操作系统注册IO监听,然后继续做自己的事情。当操作系统发生IO事件,并且准备好数据后,在主动通知应用程序,触发相应的函数。也可以如图所示理解:

和同步IO一样,异步IO也是由操作系统进行支持的。微软的windows系统提供了一种异步IO技术:IOCP〔I/OCompletionPort,I/O完成端口〕;Linux下由于没有这种异步IO技术,所以使用的是epoll对异步IO进行模拟。

反射

什么是反射机制?

JAVA反射机制是在程序运行过程中,对于任意一个类或对象,都能够知道这个类或对象的所有属性和办法,这种动态获取信息以及动态调用对象办法的功能称为java语言的反射机制。

静态编译和动态编译

静态编译:在编译时确定类型,绑定对象

动态编译:在运行时确定类型,绑定对象

反射机制优缺点

优点:运行期类型的判断,动态加载类,提高代码的灵活性。

缺点:性能瓶颈:反射相当于一系列解释操作,通知JVM要做的事情,性能比直接的java代码要慢很多。

@$反射为什么慢

反射调用过程中会产生大量的临时对象,这些对象会占用内存,可能会导致频繁gc,从而影响性能。

反射调用办法时会从办法数组中遍历查找,并且检查可见性等操作会比拟耗时。

反射在到达一定次数时,会动态编写字节码并加载到内存中,这个字节码没有经过编译器优化,也不能享受JIT优化。

反射一般会波及自动装箱/拆箱和类型转换,都会带来一定的资源开销。

反射机制的应用场景有哪些?

反射是框架设计的灵魂。

在我们平时的工程开发过程中,根本上很少会直接使用到反射机制,但这不能表明反射机制没有用,实际上有很多设计、开发都与反射机制有关,示例模块化的开发,通过反射去调用对应的字节码;动态代理设计模式也采用了反射机制,还有我们日常使用的Spring/Hibernate等框架也大量使用到了反射机制。

举例:①我们在使用JDBC连接数据库时使用Class.forName()通过反射加载数据库的驱动程序;②Spring框架也用到很多反射机制,最经典的就是xml的配置模式。Spring通过XML配置模式装载Bean的过程:1)将程序内所有XML或Properties配置文件加载入内存中;2)Java类里面解析xml或properties里面的内容,得到对应实体类的字节码字符串以及相关的属性信息;3)使用反射机制,根据这个字符串获得某个类的Class实例;4)动态配置实例的属性

网络编程

TCP/IP的五层体系结构分别是什么?

应用层

应用层(application-layer〕的任务是通过应用进程间的交互来完成特定网络应用。应用层协议定义的是应用进程间的通信和交互的规那么。

对于不同的网络应用需要不同的应用层协议。在互联网中应用层协议很多,如域名系统DNS,支持万维网应用的HTTP协议,支持电子邮件的SMTP协议等等。

运输层

运输层(transportlayer)的主要任务就是负责向两台主机进程之间的通信提供通用的数据传输效劳。应用进程利用该效劳传送应用层报文。

运输层主要使用一下两种协议

传输控制协议-TCP:提供面向连接的,可靠的数据传输效劳。

用户数据协议-UDP:提供无连接的,尽最大努力的数据传输效劳〔不保证数据传输的可靠性〕。

UDP TCP

是否连接 无连接 面向连接

是否可靠 不可靠传输,不使用流量控制和拥塞控制 可靠传输,使用流量控制和拥塞控制

连接对象个数 支持一对一,一对多,多对一和多对多交互通信 只能是一对一通信

传输方式 面向报文 面向字节流

首部开销 首部开销小,仅8字节 首部最小20字节,最大60字节

场景 适用于实时应用〔IP、视频会议、直播等〕 适用于要求可靠传输的应用,示例文件传输

每一个应用层〔TCP/IP参考模型的最高层〕协议一般都会使用到两个传输层协议之一:

运行在TCP协议上的协议:

HTTP〔HypertextTransferProtocol,超文本传输协议〕,主要用于普通浏览。

HTTPS〔HTTPoverSSL,平安超文本传输协议〕,HTTP协议的平安版本。

FTP〔FileTransferProtocol,文件传输协议〕,用于文件传输。

POP3〔PostOfficeProtocol,version3,邮局协议〕,收邮件用。

SMTP〔SimpleMailTransferProtocol,简单邮件传输协议〕,用来发送电子邮件。

TELNET〔TeletypeovertheNetwork,网络电传〕,通过一个终端〔terminal〕登陆到网络。

SSH〔SecureShell,用于替代平安性差的TELNET〕,用于加密平安登陆用。

运行在UDP协议上的协议:

BOOTP〔BootProtocol,启动协议〕,应用于无盘设备。

NTP〔NetworkTimeProtocol,网络时间协议〕,用于网络同步。

DHCP〔DynamicHostConfigurationProtocol,动态主机配置协议〕,动态配置IP地址。

运行在TCP和UDP协议上:

DNS〔DomainNameService,域名效劳〕,用于完成地址查找,邮件转发等工作。

网络层

网络层的任务就是选择适宜的网间路由和交换结点,确保计算机通信的数据及时传送。在发送数据时,网络层把运输层产生的报文段或用户数据报封装成分组和包进行传送。在TCP/IP体系结构中,由于网络层使用IP协议,因此分组也叫IP数据报,简称数据报。

互联网是由大量的异构〔heterogeneous〕网络通过路由器〔router〕相互连接起来的。互联网使用的网络层协议是无连接的网际协议〔IntertPrococol〕和许多路由选择协议,因此互联网的网络层也叫做网际层或IP层。

数据链路层

数据链路层(datalinklayer)通常简称为链路层。两台主机之间的数据传输,总是在一段一段的链路上传送的,这就需要使用专门的链路层的协议。

在两个相邻节点之间传送数据时,数据链路层将网络层交下来的IP数据报组装成帧,在两个相邻节点间的链路上传送帧。每一帧包括数据和必要的控制信息〔如同步信息,地址信息,过失控制等〕。

在接收数据时,控制信息使接收端能够知道一个帧从哪个比特开始和到哪个比特结束。

一般的web应用的通信传输流是这样的:

发送端在层与层之间传输数据时,每经过一层时会被打上一个该层所属的首部信息。反之,接收端在层与层之间传输数据时,每经过一层时会把对应的首部信息清除。

物理层

在物理层上所传送的数据单位是比特。物理层(physicallayer)的作用是实现相邻计算机节点之间比特流的透明传送,尽可能屏蔽掉具体传输介质和物理设备的差别。使其上面的数据链路层不必考虑网络的具体传输介质是什么。“透明传送比特流〞表示经实际电路传送后的比特流没有发生变化,对传送的比特流来说,这个电路好似是看不见的。

四层协议,五层协议,七层协议的区别

为了使不同体系结构的计算机网络都能互联,国际规范化组织ISO于1977年提出了一个试图使各种计算机在世界范围内互联成网的规范框架,即驰名的开放系统互联根本参考模型OSI/RM,简称为OSI。

OSI的七层协议体系结构的概念分明,理论也较完整,但它既复杂又不实用,TCP/IP体系结构那么不同,但它现在却得到了非常广泛的应用。TCP/IP是一个四层体系结构,它包含应用层,运输层,网际层和网络接口层〔用网际层这个名字是强调这一层是为了解决不同网络的互连问题〕,不过从实质上讲,TCP/IP只有最上面的三层,因为最下面的网络接口层并没有什么具体内容,因此在学习计算机网络的原理时往往采用折中的方法,即综合OSI和TCP/IP的优点,采用一种只有五层协议的体系结构,这样既简洁又能将概念阐述分明,有时为了方便,也可把最底下两层称为网络接口层。

四层协议,五层协议和七层协议的关系如下:

TCP/IP是一个四层的体系结构,主要包括:应用层、运输层、网际层和网络接口层。

五层协议的体系结构主要包括:应用层、运输层、网络层,数据链路层和物理层。

OSI七层协议模型主要包括是:应用层〔Application〕、表示层〔Presentation〕、会话层〔Session〕、运输层〔Transport〕、网络层〔Network〕、数据链路层〔DataLink〕、物理层〔Physical〕。

注:五层协议的体系结构只是为了介绍网络原理而设计的,实际应用还是TCP/IP四层体系结构。

@$TCP的三次握手四次挥手的过程

TCP是一种面向连接的、可靠的、基于字节流的传输层通信协议,它会处理IP层或下列的层的丢包、重复以及错误问题。在发送数据前,通信双方必须在彼此间建立一条连接。所谓的“连接〞,其实是客户端和效劳端保留的一份关于对方的信息,如ip地址、端口号等,这些信息放在TCP头部

一个TCP连接通常分为三个阶段:连接、数据传输、退出〔关闭〕。通过三次握手建立一个连接,通过四次挥手来关闭一个连接。当一个连接被建立或被终止时,交换的报文段只包含TCP头部,而没有数据。

TCP报文的头部结构

在了解TCP连接之前先来了解一下TCP报文的头部结构。

上图中有几个字段需要重点介绍下:

〔1〕序号:seq序号,占32位,用来标识从TCP源端向目的端发送的字节流,发起方发送数据时对此进行标记。

〔2〕确认序号:ack序号,占32位,只有ACK标志位为1时,确认序号字段才有效,ack=seq+1。

〔3〕标志位:共6个,即URG、ACK、PSH、RST、SYN、FIN等,具体含义如下:

ACK:确认序号有效。

FIN:释放一个连接。

PSH:接收方应该尽快将这个报文交给应用层。

RST:重置连接。

SYN:发起一个新连接。

URG:紧急指针〔urgentpointer〕有效。

需要注意的是:

不要将确认序号ack与标志位中的ACK搞混了。

确认方ack=发起方seq+1,两端配对。

三次握手

三次握手的本质是确认通信双方收发数据的能力

首先,我让信使运输一份信件给对方,对方收到了,则他就知道了我的发件能力和他的收件能力是可以的。

于是他给我回信,我假设收到了,我便知我的发件能力和他的收件能力是可以的,并且他的发件能力和我的收件能力是可以。

然而此时他还不知道他的发件能力和我的收件能力到底可不可以,于是我最后反应一次,他假设收到了,他便分明了他的发件能力和我的收件能力是可以的。

这就是三次握手,这样说,你理解了吗?

第一次握手:客户端要向效劳端发起连接请求,首先客户端随机生成一个起始序列号ISN(比方是100),那客户端向效劳端发送的报文段包含SYN标志位(也就是SYN=1),序列号seq=100。

第二次握手:效劳端收到客户端发过来的报文后,发现SYN=1,知道这是一个连接请求,于是将客户端的起始序列号100存起来,并且随机生成一个效劳端的起始序列号(比方是300)。然后给客户端回复一段报文,回复报文包含SYN和ACK标志(也就是SYN=1,ACK=1)、序列号seq=300、确认号ack=101(客户端发过来的序列号+1)。

第三次握手:客户端收到效劳端的回复后发现ACK=1并且ack=101,于是知道效劳端已经收到了序列号为100的那段报文;同时发现SYN=1,知道了效劳端同意了这次连接,于是就将效劳端的序列号300给存下来。然后客户端再回复一段报文给效劳端,报文包含ACK标志位(ACK=1)、ack=301(效劳端序列号+1)、seq=101(第一次握手时发送报文是占据一个序列号的,所以这次seq就从101开始,需要注意的是不携带数据的ACK报文是不占据序列号的,所以后面第一次正式发送数据时seq还是101)。当效劳端收到报文后发现ACK=1并且ack=301,就知道客户端收到序列号为300的报文了,就这样客户端和效劳端通过TCP建立了连接。

四次挥手

四次挥手的目的是关闭一个连接

比方客户端初始化的序列号ISA=100,效劳端初始化的序列号ISA=300。TCP连接成功后客户端总共发送了1000个字节的数据,效劳端在客户端发FIN报文前总共回复了2000个字节的数据。

第一次挥手:当客户端的数据都传输完成后,客户端向效劳端发出连接释放报文(当然数据没发完时也可以发送连接释放报文并停止发送数据),释放连接报文包含FIN标志位(FIN=1)、序列号seq=1101(100+1+1000,其中的1是建立连接时占的一个序列号)。需要注意的是客户端发出FIN报文段后只是不能发数据了,但是还可以正常收数据;另外FIN报文段即使不携带数据也要占据一个序列号。

第二次挥手:效劳端收到客户端发的FIN报文后给客户端回复确认报文,确认报文包含ACK标志位(ACK=1)、确认号ack=1102(客户端FIN报文序列号1101+1)、序列号seq=2300(300+2000)。此时效劳端处于关闭等待状态,而不是立马给客户端发FIN报文,这个状态还要持续一段时间,因为效劳端可能还有数据没发完。

第三次挥手:效劳端将最后数据(比方50个字节)发送完毕后就向客户端发出连接释放报文,报文包含FIN和ACK标志位(FIN=1,ACK=1)、确认号和第二次挥手一样ack=1102、序列号seq=2350(2300+50)。

第四次挥手:客户端收到效劳端发的FIN报文后,向效劳端发出确认报文,确认报文包含ACK标志位(ACK=1)、确认号ack=2351、序列号seq=1102。注意客户端发出确认报文后不是立马释放TCP连接,而是要经过2MSL(最长报文段寿命的2倍时长)后才释放TCP连接。而效劳端一旦收到客户端发出确实认报文就会立马释放TCP连接,所以效劳端结束TCP连接的时间要比客户端早一些。

为什么TCP连接的时候是3次?2次不可以吗?

因为需要考虑连接时丢包的问题,如果只握手2次,第二次握手时如果效劳端发给客户端确实认报文段丧失,此时效劳端已经准备好了收发数(可以理解效劳端已经连接成功)据,而客户端一直没收到效劳端确实认报文,所以客户端就不知道效劳端是否已经准备好了(可以理解为客户端未连接成功),这种情况下客户端不会给效劳端发数据,也会忽略效劳端发过来的数据。

如果是三次握手,即便发生丢包也不会有问题,比方如果第三次握手客户端发确实认ack报文丧失,效劳端在一段时间内没有收到确认ack报文的话就会重新进行第二次握手,也就是效劳端会重发SYN报文段,客户端收到重发的报文段后会再次给效劳端发送确认ack报文。

为什么TCP连接的时候是3次,关闭的时候却是4次?

因为只有在客户端和效劳端都没有数据要发送的时候才能断开TCP。而客户端发出FIN报文时只能保证客户端没有数据发了,效劳端还有没有数据发客户端是不知道的。而效劳端收到客户端的FIN报文后只能先回复客户端一个确认报文来告诉客户端我效劳端已经收到你的FIN报文了,但我效劳端还有一些数据没发完,等这些数据发完了,效劳端才能给客户端发FIN报文(所以不能一次性将确认报文和FIN报文发给客户端,就是这里多出来了一次)。

为什么客户端发出第四次挥手确实认报文后要等2MSL的时间才能释放TCP连接?

要考虑丢包的问题,如果第四次挥手的报文丧失,效劳端没收到确认ack报文就会重发第三次挥手的报文,这样报文一去一回最长时间就是2MSL,所以需要等这么长时间来确认效劳端的确已经收到了。

@$滑动窗口协议

为了最大效率的利用网络资源,增加网络的吞吐量,便产生了“滑动窗口〞这种协议。滑动窗口机制是TCP协议中用来控制发送数据包速率的,发送方每次只能发送滑动窗口内部的数据包,同时解决了丢包,出错,乱序等一些情况。

问题一:如何保证次序?

提出问题:在我们滑动窗口协议之前,我们如何来保证发送方与接收方之间,每个包都能被收到。并且是按次序的呢?

发送方发送一个包1,这时候接收方确认包1。发送包2,确认包2。就这样一直下去,直到把数据完全发送完毕,这样就结束了。则就解决了丢包,出错,乱序等一些情况!同时也存在一些问题。问题:吞吐量非常的低。我们发完包1,一定要等确认包1。我们才能发送第二个包。

问题二:如何提高吞吐量?

提出问题:则我们就不能先连发几个包等他一起确认吗?这样的话,我们的速度会不会更快,吞吐量更高些呢?

如图,这个就是我们把两个包一起发送,然后一起确认。可以看出我们改良的计划比之前的好很多,所花的时间只是一个来回的时间。接下来,我们还有一个问题:改善了吞吐量的问题

问题三:如何实现最优解?

问题:我们每次需要发多少个包过去呢?发送多少包是最优解呢?

我们能不能把第一个和第二个包发过去后,收到第一个确认包就把第三个包发过去呢?而不是去等到第二个包确实认包才去发第三个包。这样就很自然的产生了我们"滑动窗口"的实现。

每收到一个新确实认(ack),滑动窗口的位置就向右移动一格。

在图中,我们可看出灰色1号2号3号包已经发送完毕,并且已经收到Ack。这些包就已经是过去式。4、5、6、7号包是黄色的,表示已经发送了。但是并没有收到对方的Ack,所以也不知道接收方有没有收到。8、9、10号包是绿色的。是我们还没有发送的。这些绿色也就是我们接下来马上要发送的包。后面的11-16还没有被读进内存。

正常情况

可以看到4号包对方已经被接收到,所以被涂成了灰色。“窗口〞就往右移一格,这里只要保证“窗口〞是7格的。我们就把11号包读进了我们的缓存。进入了“待发送〞的状态。8、9号包已经变成了黄色,表示已经发送出去了。接下来的操作就是一样的了,确认包后,窗口往后移,继续将未发送的包读进缓存,把“待发送“状态的包变为〞已发送“。

丢包情况

有可能我们包发过去,对方的Ack丢了,也有可能我们的包并没有发送过去。从发送方角度看就是我们没有收到Ack。

发生的情况:一直在等Ack。如果一直等不到的话,我们也会把读进缓存的待发送的包也一起发过去。但是,这个时候我们的窗口已经发满了。所以并不能把12号包读进来,而是始终在等待5号包的Ack。

如果我们这个Ack始终不来怎么办呢?

超时重发

这时候我们有个解决办法:超时重发这里有一点要表明:这个Ack是要按顺序的。必须要等到5的Ack收到,才会把6-11的Ack发送过去。这样就保证了滑动窗口的一个顺序。

这时候可以看出5号包已经接受到Ack,后面的6、7、8号包也已经发送过去已Ack。窗口便继续向后移动。

@$DNS解析过程

DNS〔DomainNameSystem〕:因特网使用的域名系统,本质是一个分布式数据库,用于解决域名和IP地址的映射关系。

DNS解析:互联网都是通过URL来请求资源的,而URL中的域名需要解析成IP地址才能与远程主机建立连接,将域名解析成IP地址就属于DNS解析的工作范畴。

域名结构

从技术角度来看,域名是在Internet上用于解决IP地址的一种办法。一个完整的域名由2个或2个以上的局部组成,各局部之间用英文的句号“.〞来分隔,最后一个“.〞的右边局部称为顶级域名(TLD,也称为一级域名),最后一个“.〞的左边局部称为二级域名(SLD),二级域名的左边局部称为三级域名,以此类推,每一级的域名控制它下一级域名的分配。

如域名mail.cctv,其中:com为顶级域名〔top-level-domain,TLD〕,cctv为二级域名,mail为三级域名

DNS解析过程

浏览器会检查缓存中有没有这域名对应的解析过的IP地址,如果缓存中有,这个解析过程就将结束。

如果过程1中浏览器缓存中没有域名对应的ip,那么从操作系统本身去做域名解析,我们在windows中的host文件可以设置特定域名映射到特定ip。C:\Windows\System32\drivers\etc\hosts

然后在终端中pingwww.baidu,如图所示所示:

向本地域名解析效劳器〔LDNS〕发起域名解析请求

上述步骤的1、2都是在本机中完成的域名解析,如果经过1、2步骤都没有完成域名的解析,那么需要向LDNS发起域名解析,对于LDNS,Window中可以通过ipconfig/all来查询,如下所示:

LDNS一般都缓存了大局部的域名解析结果,当然缓存时间也受域名失效的时间控制,大局部的解析工作到这里就差不多结束了,LDNS负责了大局部的解析工作。

向根域名解析效劳器〔RDNS〕发起域名解析的请求

当步骤3中没有完成域名的解析,那么需要向RDNS发起域名解析的请求

根域名效劳器返回通用顶级域名解析效劳器〔gTDL〕地址

LDNS向根域名效劳器发起请求,根域名效劳器返回的是所查询的通用顶级域名〔Generictop-level-domain,gTLD〕地址,常见的通用顶级域名有、.org、.edu。

本地域名效劳器向gTLD发起解析域名请求

gTLD效劳器接收请求并返回注册的域名效劳器〔NameServer效劳器,即名称效劳器〕

当gTLD效劳器接收到本地域名效劳器发起的请求后,并根据需要解析的域名,找到该域名对应的NameServer效劳器,通常情况下,这个NameServer效劳器就是你注册的域名效劳器,则你注册的域名的效劳上的效劳器将承当起域名解析的任务。

本地域名效劳器向NameServer效劳器发起域名解析请求

NameServer效劳器会查询存储的域名和IP的映射关系表,然后返回该域名对应的ip和TTL给本地域名效劳器,本地域名效劳器进行缓存这个域名和ip的对应关系,缓存时间由TTL决定。

本地域名效劳器返回查询域名对应的ip给用户〔浏览器〕,浏览器进行缓存,缓存时间由TTL决定。

经过以上的10个步骤,就可以拿到真正的ip了,然后通过ip去对应的效劳器上请求资源。

@$CDN原理,CDN加速过程

CDN内容分发网络,全称为ContentDeliveryNetwork,通过将网络内容发布到最靠近用户的『边缘节点』,使不同地区的用户在访问相同页面、或视频时就可以就近获取。

CDN本质是一种分布式缓存系统,无需考虑数据持久化,如果缓存效劳器出现问题,在缓存集群中标记为删除即可。

CDN中实现原理:给源站域名添加CNAME别名,别名为加速节点的域名。当用户向源站发起请求时,dns效劳器解析源站域名时会发现有CNAME记录,这时dns效劳器会向CNAME域名发起请求,请求会被调度至加速节点的域名。

CDN的优点

根据用户与业务效劳器的距离,自动选择就近的cache效劳器

镜像效劳,打消运营商之间互联的瓶颈影响,保证不同网络的用户都能得到良好的访问质量

带宽优化,分担网络流量,减轻压力

域名与ip的对应关系,被称为记录(record),可分为各种类型

A记录:Address,域名指向的IP地址,A记录允许将多个域名解析到一个IP地址,但不允许将一个域名解析到多个IP上。

NS记录:NameServer,指定了特定的DNS效劳器去解析。

MX记录:MaileXchange,接受电子邮件的效劳器地址

CNAME记录:CanonicalName,别名解析,可以将指定的域名解析到其他域名上

CDN的过程

使用CDN的办法很简单,只需要修改自己的DNS解析,设置一个CNAME指向CDN效劳商即可。

用户访问未使用CDN缓存资源的过程

浏览器通过DNS解析,以得到此域名对应的IP地址;

浏览器使用所得到的IP地址,向域名的效劳主机发出数据访问请求;

效劳器向浏览器返回响应数据

使用CDN后

若您的加速域名为www.a,接入CDN网络,开始使用加速效劳后,当终端用户〔北京〕发起HTTP请求时,处理流程如图所示所示。

当终端用户〔北京〕向www.a下的指定资源发起请求时,首先向LDNS〔本地DNS〕发起域名解析请求。

LDNS检查缓存中是否有www.a的IP地址记录。如果有,那么直接返回给终端用户;如果没有,那么向授权DNS查询。

当授权DNS解析www.a时,返回域名CNAMEwww.a.tbcdn对应IP地址。

域名解析请求发送至阿里云DNS调度系统,并为请求分配最正确节点IP地址。

LDNS获取DNS返回的解析IP地址。

用户获取解析IP地址。

用户向获取的IP地址发起对该资源的访问请求。

如果该IP地址对应的节点已缓存该资源,那么会将数据直接返回给用户,示例,图中步骤7和8,请求结束。

如果该IP地址对应的节点未缓存该资源,那么节点向源站发起对该资源的请求。获取资源后,结合用户自定义配置的缓存策略,将资源缓存至节点,示例,图中的北京节点,并返回给用户,请求结束。

GET和POST区别

说道GET和POST,就不得不提HTTP协议,因为浏览器和效劳器的交互是通过HTTP协议执行的,而GET和POST也是HTTP协议中的两种办法。

HTTP全称为HyperTextTransferProtocol,中文翻译为超文本传输协议,目的是保证浏览器与效劳器之间的通信。HTTP的工作方式是客户端与效劳端之间的请求-应答协议。

HTTP协议中定义了浏览器和效劳器进行交互的不同办法,根本办法有4种,分别是GET,POST,PUT,DELETE。这四种办法可以理解为,对效劳器资源的查,改,增,删。

GET:从效劳器上获取数据,也就是所谓的查,仅仅是获取效劳器资源,不进行修改。

POST:向效劳器提交数据,这就波及到了数据的更新,也就是更改效劳器的数据。

PUT:英文含义是放置,也就是向效劳器新添加数据,就是所谓的增。

DELETE:从字面意思也能看出,这种方式就是删除效劳器数据的过程。

GET和POST区别

Get是不平安的,因为在传输过程,数据被放在请求的URL中;Post的所有操作对用户来说都是不可见的。但是这种做法也不是绝对的,大局部人的做法也是按照上面的说法来的,但是也可以在get请求加上requestbody,给post请求带上URL参数。

Get请求提交的url中的数据最多只能是2048字节,这个限制是浏览器或者效劳器给添加的,http协议并没有对url长度进行限制,目的是为了保证效劳器和浏览器能够正常运行,避免有人歹意发送请求。Post请求那么没有大小限制。

Get限制Form表单的数据集的值必须为ASCII字符;而Post支持整个ISO10646字符集。

Get执行效率比Post快。Get是form提交的默认办法。

GET产生一个TCP数据包;POST产生两个TCP数据包。

对于GET方式的请求,浏览器会把httpheader和data一并发送出去,效劳器响应200〔返回数据〕;

而对于POST,浏览器先发送header,效劳器响应100continue,浏览器再发送data,效劳器响应200ok〔返回数据〕。

Get Post

平安性 Get是不平安的,因为在传输过程,数据被放在请求的URL中 Post的所有操作对用户来说都是不可见的,相对平安

url数据大小 Get请求提交的url中的数据受浏览器和效劳器的限制,避免有人歹意发送请求 Post请求url数据没有大小限制

表单字符集 Get限制Form表单的数据集的值必须为ASCII字符 Post支持整个ISO10646字符集

TCP数据包数量 GET产生一个TCP数据包 POST产生两个TCP数据包

执行效率 Get执行效率比Post快 Post执行效率比Get慢

@$Session、Cookie和Token的主要区别

HTTP协议本身是无状态的。什么是无状态呢,即效劳器无法判断用户身份。

什么是cookie

cookie是由Web效劳器保留在用户浏览器上的小文件〔key-value格式〕,包含用户相关的信息。客户端向效劳器发起请求,如果效劳器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie。客户端浏览器会把Cookie保留起来。当浏览器再请求该网站时,浏览器把请求的网址连同该Cookie一同提交给效劳器。效劳器检查该Cookie,以此来识别用户身份。

什么是session

session是依赖Cookie实现的。session是效劳器端对象

session是浏览器和效劳器会话过程中,效劳器分配的一块储存空间。效劳器默认为浏览器在cookie中设置sessionid,浏览器在向效劳器请求过程中传输cookie包含sessionid,效劳器根据sessionid获取出会话中存储的信息,然后确定会话的身份信息。

cookie与session区别

存储位置与平安性:cookie数据寄存在客户端上,平安性较差,session数据放在效劳器上,平安性相对更高;

存储空间:单个cookie保留的数据不能超过4K,很多浏览器都限制一个站点最多保留20个cookie,session无此限制

占用效劳器资源:session一定时间内保留在效劳器上,当访问增多,占用效劳器性能,考虑到效劳器性能方面,应当使用cookie。

什么是Token

Token的引入:Token是在客户端频繁向效劳端请求数据,效劳端频繁的去数据库查询用户名和密码并进行比照,判断用户名和密码正确与否,并作出相应提示,在这样的背景下,Token便应运而生。

Token的定义:Token是效劳端生成的一串字符串,以作客户端进行请求的一个令牌,当第一次登录后,效劳器生成一个Token便将此Token返回给客户端,以后客户端只需带上这个Token前来请求数据即可,无需再次带上用户名和密码。

使用Token的目的:Token的目的是为了减轻效劳器的压力,减少频繁的查询数据库,使效劳器更加健壮。

session与token区别

session机制存在效劳器压力增大,CSRF跨站伪造请求攻击,扩展性不强等问题;

session存储在效劳器端,token存储在客户端

token提供认证和授权功能,作为身份认证,token平安性比session好;

session这种会话存储方式方式只适用于客户端代码和效劳端代码运行在同一台效劳器上,token适用于工程级的前后端别离〔前后端代码运行在不同的效劳器下〕

Servlet接口中有哪些办法及Servlet生命周期探秘

在JavaWeb程序中,Servlet主要负责接收用户请求HttpServletRequest,在doGet(),doPost()中做相应的处理,并将回应HttpServletResponse反应给用户。Servlet可以设置初始化参数,供Servlet内部使用。

Servlet接口定义了5个办法,其中前三个办法与Servlet生命周期相关:

voidinit(ServletConfigconfig)throwsServletException

voidservice(ServletRequestreq,ServletResponseresp)throwsServletException,java.io.IOException

voiddestory()

java.lang.StringgetServletInfo()

ServletConfiggetServletConfig()

生命周期:

Web容器加载Ser

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论