JDK1.8新特性
-
Lambda表达式
-
函数式接口
-
*方法引用和构造器调用
-
Stream API
stream.collect()
-
接口中的默认方法和静态方法
-
新时间日期API
Lambda表达式:
lambda表达式本质上是一段匿名内部类,也可以是一段可以传递的代码
Lambda允许函数作为参数传递到方法中。有点像js,这属于是匿名函数
//入参为空
TestDemo no_param = () -> "hi, no param";
TestDemo no_param2 = () -> { return "hi, no param"; };
System.out.println(no_param.hi());
//单个参数
TestDemo2 param = name -> name;
TestDemo2 param2 = name -> { return name;};
System.out.println(param.hei("hei, grils"));
//多个参数
TestDemo3 multiple = (String hello, String name) -> hello + " " + name;
//一条返回语句,可以省略大括号和return
TestDemo3 multiple2 = (hello, name) -> hello + name;
//多条处理语句,需要大括号和return
TestDemo3 multiple3 = (hello, name) -> {
System.out.println("进入内部");
return hello + name;
};
假设这样一个场景,在商城浏览商品信息时,经常会有条件的进行筛选浏览,例如要选颜色为红色的、价格小于8000千的….
// 筛选颜色为红色
public List<Product> filterProductByColor(List<Product> list){
List<Product> prods = new ArrayList<>();
for (Product product : list){
if ("红色".equals(product.getColor())){
prods.add(product);
}
}
return prods;
}
我们发现实际上这些过滤方法的核心就只有if语句中的条件判断,其他均为模版代码,每次变更一下需求,都需要新增一个方法,然后复制黏贴,假设这个过滤方法有几百行,那么这样的做法难免笨拙了一点。如何进行优化呢?
优化一:使用设计模式
定义一个MyPredicate接口
如果想要筛选颜色为红色的商品,定义一个颜色过滤类
定义过滤方法,将过滤接口当做参数传入,这样这个过滤方法就不用修改,在实际调用的时候将具体的实现类传入即可。
例如,如果想要筛选价格小于8000的商品,那么新建一个价格过滤类既可
这样实现的话可能有人会说,每次变更需求都需要新建一个实现类,感觉还是有点繁琐呀,那么再来优化一下
优化二:使用匿名内部类
定义一个MyPredicate接口
定义过滤方法,将过滤接口当做参数传入,这样这个过滤方法就不用修改,在实际调用的时候将具体的实现类传入即可。
调用过滤方法的时候:使用匿名内部类,就不需要每次都新建一个实现类,直接在方法内部实现。看到匿名内部类,不禁想起了Lambda表达式。
优化三:使用lambda表达式
定义一个MyPredicate接口
定义过滤方法,将过滤接口当做参数传入,这样这个过滤方法就不用修改,在实际调用的时候将具体的实现类传入即可。
使用lambda表达式进行过滤
在jdk1.8中还有更加简便的操作 Stream API
优化四:使用Stream API
甚至不用定义过滤方法,直接在集合上进行操作
函数式接口
函数式接口的提出是为了给Lambda表达式的使用提供更好的支持。
简单来说就是只定义了一个抽象方法的接口(Object类的public方法除外),就是函数式接口,并且还提供了注解:@FunctionalInterface
常见的四大函数式接口
- Consumer 《T》:消费型接口,有参无返回值
- Supplier 《T》:供给型接口,无参有返回值
- Function 《T,R》::函数式接口,有参有返回值
- Predicate《T》: 断言型接口,有参有返回值,返回值是boolean类型
总结:函数式接口的提出是为了让我们更加方便的使用lambda表达式,不需要自己再手动创建一个函数式接口,直接拿来用就好了
方法引用
若lambda体中的内容有方法已经实现了,那么可以使用“方法引用”
也可以理解为方法引用是lambda表达式的另外一种表现形式并且其语法比lambda表达式更加简单
反射
什么是反射?
反射: 动态获取类的所有信息以及调用对象的方法的功能称为java语言的反射机制,通过非new的方式拿到对象。 可以理解为对类的解,剖。只是获得它的字节码(class对象)
我们能new对象 是因为知道类的名字, 但是比如tomcat运行servlet.java , 由于tomcat是早就已经固定写好的,tomcat无法知道我们写出的servlet的类信息,无法通过人工new对象的这种方式来创建对象,就需要用到反射去获取类的信息
反射的缺点速度慢。达到了毫秒级的开销,说自己做了几万次for循环测试出来的,
反射遍历的过程
gson的内部原理: 序列化和反序列化底层都是反射,拿到内部类的结构后,会去内存进行遍历,然后把值取出来
遍历是如何遍历的呢?内存再给每个类型分配的时候都是有严格边界的,比如int类型四字节,long类型8字节,引用类型也是4字节,引用类型存储着地址4字节,然后顺着地址找到相对应的对象的地址,然后去对象地址里面,内存再给对象里面的属性再去分配空间,int类型4字节,long类型8字节,引用类型存的是地址4字节,然后再沿着对象的地址遍历到下一个对象,然后遍历对象的结构…
new一个对象就申请一片空间,底层c++中malloc函数会去进行一个空间的分配,比如new一个cat对象,里面有int类型、long类型和Person对象(地址4字节),4+8+4=16,一共需要申请16字节的空间。所以**遍历的时候,当gson遍历到一个对象的时候,并不知道这个对象是什么类型,**但是因为是值传递,我们可以找到要遍历的对象的地址,然后对对象头遍历,JVM学过,因为对象的头部存储着这个对象的类型指针,就是类型的指向,我们可以类型指向可以找到我们需要的对象是什么类型的数据,假设是个cat类型的对象,然后我们再去遍历里面都存储着什么类型的数据,然后把值读取出来 ( int类型的的"12",int类型的:“18”,Person{“size”:“12”,“height”:“18”}),然后同时可以算出我们需要分配多大的内存了
(c++中malloc函数,申请空间,*(int*) mal(sizeof(int)10) mal(sizeof(int)*10)表示申请了10个int类型的空间,(int*) 表示转换成int类型的数组)
获取Class类实例化对象的三种方式
1、Class.forName(“全类名”)
如果你想让一个类的静态代码块执行-----》就用这种方式,因为这种方式会让类加载,然后会执行静态代码块
2、类名.class
多用于参数的传递。
3、对象.getClass()
多用于对象的获取字节码文件的方式。
暴力反射
反射能不能获取到私有的信息?
可以,设置setAccessible()方法后就可以了
如何忽略安全访问修饰符的安全检查
忽略安全访问修饰符的安全检查 setAccessible(true)
涉及到暴力反射 —》private
反射优缺点
答:优点(1)能够运行时动态获取类的实例,提高灵活性;(2)与动态编译结合Class.forName(‘com.mysql.jdbc.Driver.class’);//加载MySQL的驱动类
缺点:使用反射性能较低,需要解析字节码,将内存中的对象进行解析。
其解决方案是:通过setAccessible(true)关闭JDK的安全检查来提升反射速度;多次创建一个类的实例时,有缓存会快很多;ReflflectASM工具类,通过字节码生成的方式加快反射速度。
哪里用到反射机制
JDBC中,利用反射动态加载了数据库驱动程序。
tomcat利用反射调用Servlet获取信息
Eclispe等开发工具利用反射动态刨析对象的类型与结构,动态提示对象的属性和方法。
很多框架都用到反射机制,注入属性,调用方法,如Spring。
tomcat利用反射调用Servlet获取信息
tomcat利用反射调用Servlet获取信息的原因:
tomcat借助Socket拿到了HTTP协议,(HTTP协议本身就是个字符串,能一行行显示出来,是因为里面有换行符)
ajax传输的是HTTP协议,Socket接收的是TCP或者UDP协议,因为HTTP协议是基于TCP协议写的,所以Socket也能接收HTTP协议
大致流程:
tomcat通过Socket向端口取浏览器传给端口的数据,把获取的http数据放入到创建的request和response两个对象,然后利用反射获取servlet类信息,创建servlet对象,通过反射最终要调用doGet和doPost这俩非静态方法方法
通过Class cl = Class.forName(全类名)获取servlet类信息,然后cl.newInstance出servlet对象,反射出来的对象的方法没有办法直接调用,此时需要用代理对象,然后cl.getMethod(“doGet”,参数).invoke(“对象”,参数)和cl.getMethod(“doPost”,“参数”).invoke(“对象”,参数)调用doGet和doPost方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HN71O1CW-1670078399851)(…/img/image-20220820152616337.png)]
Java反射创建对象效率高还是通过new创建对象的效率高?
答:通过new创建对象的效率比较高。通过反射时,先找查找类资源,使用类加载器创建,过程比较繁琐,所以效率较低。
通过反射机制调用对象的方法
反射出来的对象的方法没有办法直接调用,只能通过代理去使用
调用静态的 类.class.getMethod(“方法”,类.class)
调用非静态的,用invoke方法
Class cl = Class.forName(“”);
Object y = cl.newInstance();
cl.getMethod(“方法”,参数类型.class).invoke(y,参数值)
反射获取注解信息、Field、 Method 和 Constructor
注解信息、Field、 Method 和 Constructor
1、获取注解信息
类上面、属性上面、方法上面都可以加注解
注解的意思就是在类中给属性、方法加标记
根据官方的命名规则,可以总结出以下几条:
getDeclaredAnnotationXXXX:只可以获取直接存在的注解信息,即直接修饰在某个元素(类、属性和方法)上的注解。
getXXXXByType:可以获取间接存在的注解信息
getAnnotationXXX:可以获取继承的注解信息
2、Field获取成员变量类信息
getField(String **) //需要传参 指定一个变量的接收 获取指定public类型的全局变量
getFields() //获取所有的public类型的全局变量的信息 需要用数组类型获取
getDeclareField() //Declare 忽略修饰访问符 获取类当中指定的全局变量
getDeclareFields() //获取类当中所有全局变量的信息 需要用数组类型获取 包括public private protect和不加任何修饰符的
3、Method:获取类方法的信息
4、Constructor:获取构造方法的信息
反射获取修饰符、返回值、参数
获取DemoXX2类的字节码信息
Class cl = Class.forName(“com.反射.DemoXX2”);
获取父类
Class supercl = cl.getSuperclass();
获取类、方法、构造方法、属性的修饰符
String modifiers = Modifier.toString(cl.getModifiers());
获取名称
getName()
获取参数
Class[] paramTypes = constructor.getParameterTypes(); //由此构造方对象表示的构造函数的形式参数类型
获取参数类型
Class[] paramTypes = method.getParameterTypes(); //获取参数类型
获取返回值类型
Class Type = field.getType();
代理
为什么要使用代理:
假设我要进行转账,但是不能直接转账,我们想给它添加一个识别信息的功能,但是不对原代理进行修改的情况下添加新的功能,就需要用到代理
就是在不改变原有对象代价的基础之上,对原有功能进行增强
静态代理:
静态代理:一个人工写好的类,去进行代理执行
静态代理就是给目标类添加功能,首先目标类实现接口中的核心方法,然后代理类也实现接口的核心方法,在代理类中定义目标类对象,在代理类的核心方法中,目标类对象也调用核心方法,然后就可以在方法前后添加想增强的功能了
就比如tomcat调用controller层,就用到了静态代理,利用反射class.forName获取controller层的类实例,然后获取它的方法,然后调用invoke方法,传入对象和参数,进行的静态代理
静态代理的弊端:对于目标类的每一个方法,都得重新编辑同时加入自己的附加功能,目标类方法过多时,效率太低。
静态代理
1)定义一个接口:ITeacherDao
//接口
public interface ITeacherDao {
void teach();
}
2)目标对象TeacherDao实现接口ITeacherDao
//目标类
public class TeacherDao implements ITeacherDao {
public void teach() {
System.out.println(" 老师授课中... ");
}
}
3)用静态代理方式,就需要在代理对象TeacherDaoProxy中也实现ITeacherDao
//代理对象,静态代理
public class TeacherDaoProxy implements ITeacherDao {
private ITeacherDao target;//目标对象,通过接口来聚合
public TeacherDaoProxy(ITeacherDao target) {
this.target = target;
}
public void teach() {
System.out.println("开始代理,完成某些操作");
target.teach();
System.out.println("提交...");
}
}
4)调用的时候通过调用代理对象的方法来调用目标对象
public class Client {
public static void main(String[] args) {
//创建目标对象
TeacherDao teacherDao = new TeacherDao();
//创建代理对象,同时将被dialing对象传递给代理对象
TeacherDaoProxy teacherDaoProxy = new TeacherDaoProxy(teacherDao);
//通过代理对象,调用到被代理对象的方法
teacherDaoProxy.teach();
}
}
动态代理
JDK的动态代理:实现接口
反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
1.实现InvocationHandler接口,重写invoke()
2.使用Proxy.newProxyInstance()产生代理对象
3.被代理的对象必须要实现接口
CGlib动态代理继承,子类:没有这个类,CGlib动态生成模板类,然后再去通过代理生成对象
比如mybatis在 dao层在编译期间的时候只有接口,在运行期间先通过cglib生成他的实现类,以这些实现类为模板,再通过代理生成实现类的对象,调用invoke()进行使用
接口需要实现类来实现其中的方法,
用到了CGlib套用ASM框架生成实现类,作为模板,给代理使用,(和Mybatis反向生成dao层、entity层、mapper层一样,只是Mybatis生成的是代码文件,能存在磁盘上,CGlib只在内存中存在,不生成类文件)
你需要用一个接口里面的方法,但是接口不能创建实例对象,所以需要一个实现类来实现接口中的方法,这时就出现了代理类去实现指定的接口。
动态代理:一个代理类生成很多代理对象,一个代理对象对应一个目标对象
动态代理的引入就是在静态代理的基础上,添加了一个中间处理类,当调用目标类的方法时,优先进入中间处理类,自定义的附加功能放在中间处理类中。
中间处理类实现了InvocationHandler接口中的invoke方法,进行功能的增强
//先定义一个接口
public interface ByClothes {
public void clothes(String size);
}
//定义目标类
public class ClothesFactory implements ByClothes{
public void clothes(String size){
System.out.println("我给制作了一件"+size+"的衣服");
}
}
定义一个代理类去实现InvocationHandler接口,然后就必须实现InvocationHandler接口中的invoke方法,这个方法需要三个参数,Object invoke(Object proxy, Method method, Object[] args)
//代理类Handler
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class Handler implements InvocationHandler {
private Object target = null;//目标对象
public Handler(Object target){
this.target = target;
}
/**
* invoke -----------jdk提供的,表示我们要求代理对象要完成的功能
* @param proxy ----------》jdk提供的代理对象
* @param method ----------》目标对象当中的核心方法
* @param args ------------》方法中的参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("售前服务");
method.invoke(target,args);
System.out.println("售后服务");
return null;
}
}
然后创建Proxy代理对象,
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) {
//获取中间处理类
Handler handler = new Handler(new ClothesFactory());
ByClothes byClose = (ByClothes) Proxy.newProxyInstance(ByClothes.class.getClassLoader(),new Class[]{ByClothes.class},handler);
byClose.clothes("xxxxL");
}
}
/*proxy对象:然后就需要创建一个代理类对象
要想创建一个代理对象, 需要使用 Proxy 类的 newProxylnstance 方法。 这个方法有三个
参数:
1、一个类加载器(class loader。) 作为 Java 安全模型的一部分, 对于系统类和从因特网
上下载下来的类,可以使用不同的类加载器。目前, 用 null 表示使用默认的类加载器。
2、一个 Class 对象数组, 每个元素都是需要实现的接口。
3、一个调用处理器(就是咱们定义的代理类)。*/
compareTo()和Arrays.sort()
知识补充:Arrays.binarySearch() 二分查找
**Arrays.sort(),**不仅可以对基本类型排序,还可以对引用类型的数组排序
要想使用Arrays.sort()对引用类型排序,就要实现Comparable接口,并且重写**compareTo()**方法,
为什么重写了compareTo()方法就可以通过Arrays.sort()来进行排序了?
因为Arrays.sort()的内部实现用到了compareTo()方法,
public class Person1 implements Comparable<Person1>{
private int age;
private String name;
public Person1(int a,String b) {
age =a;
name = b;
}
@Override
public String toString() {
return "Person1 [age=" + age + ", name=" + name + "]";
}
@Override
public int compareTo(Person1 o) {
// TODO Auto-generated method stub
return age-o.age;
}
}
仿写Arrays.sort 排序的基础是因为能实现交换,其中sort()方法在比较的时候用到了compareTo
public class Array3 {
public static void sort(Object[] arr) {
Comparable[] arr2 = (Comparable[]) arr;
for(int i =0;i<arr2.length;i++) {
for(int j =0;j<arr2.length-1;j++) {
if (arr2[j].compareTo(arr[j+1])>0) {
Comparable temp = arr2[j];
arr2[j] = arr2[j+1];
arr2[j+1] = temp;
}
}
}
}
}
public class Test {
public static void main(String[] args) {
Person1 person1 = new Person1(10, "66");
Person1 person2 = new Person1(18, "66");
Person1 person3 = new Person1(20, "66");
Person1[] arr = {person1,person2,person3};
Array3.sort(arr);
for(Person1 person12:arr) {
System.out.println(person12);
}
}
}
四种内部类
1、普通内部类
在一个类里面作为类的一个字段直接定义就可以了
public class InnerClassTest {
public class InnerClassA {//普通内部类
}
}
2、静态内部类
需要注意的是,静态内部类的对象无法访问外部类的非静态成员,静态内部类本身就是独立于外部类对象以外的,且一个类里面只能有一个public修饰符
public class InnerClassTest {
static class InnerClassA {//静态内部类
}
}
3、匿名内部类
没有名字的类,在创建对象的时候直接通过对象.方法来调用它,一般匿名内部类存在于java文件中,和其他的类没有从属关系,
class Test{
Public static void main(String [args]){
new Sever(){
//这里就是new了一个接口的对象,但是该对象没有名字,在里面可以直接重写接口方法
}
public void eat(){
System.out.println("真好吃");
}
}
}
class Test1{
new Test().eat();//不把这个Test对象赋名也可以直接使用它的eat方法
}
interface Sever{
void Method();
}
4、局部内部类
存在于方法里面
public class Outer{
public void method(){
class inner{
//inner就是一个局部内部类
}
}
}
异常
1、产生异常就会中断线程然后报错,不会执行程序
try catch了之后会尝试执行一次,不会中断线程
2、什么时候最好加上try catch呢?
除了CPU和内存 其他不可控的都可能会出现异常
下面这样的需要加上try catch,链接这网络IO或者磁盘IO
3、什么时候加上 throws Expection,当可能会产生异常时
throw和throws
throw
1、throw使用在函数内,抛出的是异常对象。一旦执行throw,就一定抛出了某种异常对象,跳转到调用者,并将具体的问题对象抛给调用者
throw new Exception 抛出异常 ,所以调用它所在的方法一定会出现异常
2、通过throw new Exception 能给其他方法传递信息
其他方法可以通过 e.getMessage()获取信息
3、throw后 下面的代码不会执行了
throws
throws表示出现异常的一种可能性,并不一定会发生这些异常,使用在方法上,抛出的是异常类,可以抛出多个,用逗号隔开。
hrows Exception会一直向上抛出异常,直到有一个环节选择了 try catch
异常体系
异常的根类是java.lang.Throwable
错误Error:
Error表示严重的问题,一般不应该试图捕获
Error及其子类被视为未经检查的异常,用于编译时检查异常
Exception
1.运行时异常(非受检异常),RuntimeException及其子类,一般不解决
2.编译时异常(受检异常),Checked异常,一般需要在方法或构造函数的throws子句中声明或用try-catch处理
泛型
就是将类型参数化,用到的时候再去确定类型
不需要每次都再强转,而且还可以保障类型的安
1、泛型是怎么实现的?
内部实现原理依托反射,就是去遍历内存的方法区来实现
2、自定义泛型
2、泛型:广泛的代表任意类型
3、泛型数组的定义
4、泛型不能放基本类型 只能放引用类型
如果不指定类型,一律按照Object类型处理,指定的类型也必须是Object下的子类,所以不能使用基本类型,需要使用基本类型的包装类
5、反射常用泛型类
Class
Constructor
5、父类是泛型,子类泛型要和父类一致
子类不是泛型,父类要明确泛型
6、泛型接口
实现类是泛型类,要和接口泛型类型一直
实现类不是泛型类,接口要明确数据类型
7、泛型方法可以脱离泛型类中的定义,是自己独立存在的
泛型实现队列
public class Queue<T> {
private T[] arr;
private int front = 0;//入队列的游标,指向了数组的最开始
private int rear = 0;//出队列的游标,指向了数组的最开始,出队列的时候就从第一个数开始出
public Queue(int size){ //定义队列长度
arr = (T[])new Object[size];
}
/**
* 插入数据
* @param value
*/
public void add(T value){
if(front - rear == arr.length){ //当头-尾=队列长度的时候,说明队列满了
T[] brr = (T[]) new Object[arr.length *2]; //队列的扩容
for(int i = rear;i<front;i++){
brr[i] = arr[i % arr.length];
}
arr = brr; //arr再指向brr,arr长度就扩容了,brr只是一个过渡用的数组
}
arr[front % arr.length] = value; //如果入队列的中途有一个下标为0的数据出队列了,就是当队列没满的时候,那么这时需要
// 让入队列的下标回到0位置,这时用front对数组长度求余就可以实现这个操作,
//但是需要注意的是,我们只是让插入的数放到队列中空的地方,front的值是一直在++的
front++;
}
/**
* 输出数据
* @return
*/
public T remove(){
if(rear == front){ //入队列的指针front和出队列的指针rear相等的时候,
System.out.println("队列空了"); //就说明队列的里面的数据全部让出队列的指针rear循环完了
return null;
}
T value = arr[rear % arr.length];//和入队列一样,当循环出队列的时候,假如出队列的回到了数组的第一个,用rear对数组
rear++; //长度求余的时候也可以实现这个操作,
return value; //但是需要注意的是,rear的值也是一直在++的
}
}
public class QueueTest {
public static void main(String[] args) {
Queue<Integer> queue = new Queue(5);
queue.add(1);
queue.add(2);
queue.add(3);
queue.add(4);
queue.add(5);
queue.add(5);
queue.add(5);
queue.add(5);
System.out.println(queue.remove());
System.out.println(queue.remove());
System.out.println(queue.remove());
System.out.println(queue.remove());
System.out.println(queue.remove());
System.out.println(queue.remove());
System.out.println(queue.remove());
System.out.println(queue.remove());
System.out.println(queue.remove());
}
}
泛型实现栈
package com.泛型;
public class Stack<T> {
//声明一个数组
private T[] arr;
//游标
private int flag;
public Stack(int size){
arr = (T[])new Object[size]; //定义栈(数组)的大小
}
public void add(T value){
if (flag==arr.length){//如果添加的数据到达了栈的大小,也就是数组的长度,会报错 数组长度越界
T[] brr = (T[]) new Object[arr.length*2];//栈扩容二倍
for (int i =0;i<arr.length;i++){
brr[i] = arr[i]; //把arr数组的数据 填到brr中
}
arr=brr;
}
arr[flag] = value;
flag=flag+1;
}
/**
* 栈数据的输出
* @return
*/
public T get(){
if (flag==0){ //flag--到没有数据的时候
System.out.println("栈空");
return null;
}
flag=flag-1;
return arr[flag];
}
}
package com.泛型;
public class StackTest {
public static void main(String[] args) {
Stack<Integer> stack = new Stack<>(5);
stack.add(2);
stack.add(3);
stack.add(4);
stack.add(5);
stack.add(6);
stack.add(7);
stack.add(8);
System.out.println(stack.get());
System.out.println(stack.get());
System.out.println(stack.get());
System.out.println(stack.get());
System.out.println(stack.get());
System.out.println(stack.get());
System.out.println(stack.get());
System.out.println(stack.get());
System.out.println(stack.get());
}
}
IO流
两个元件之间互相传输的操作就叫IO操作,比如CPU到内存,内存到磁盘、从网络下载数据到磁盘
IO操作是跨零件的,损耗多,最快的还是CPU和内存之间的消耗,CPU运行一次叫一个时钟周期,一个时钟周期需要0.2ns。CPU和内存之间的IO操作也需要100个时钟周期,所以单次读取操作也需要20ns
内存和磁盘消耗的时间更多,单次最小读取5ms,是0.2ns的2500万倍。平均下来也需要几十毫秒。 磁盘分为 :固态盘有读写数量限制 机械盘没有
内存和网络的请求时间消耗都是几十到上百毫秒,网络分为内网和外网,内网要快一些
所以IO开销大一般指内存和磁盘的IO操作、内存和内网、外网的IO操作
常用流
BIO、NIO、AlO的区别?
, BIO: 同步并阻塞,在服务器中实现的模式为-一个连接-一个线程。也就是说,客户端有连接请求
的时候,服务器就需要启动一个线程进行处理,BIO一般适用于连接数目小且固定的架构,这种方式对
于服务器资源要求比较高,而且并发局限于应用中,是JDK1.4之 前的唯-选择,但好在程序直观简
单,易理解。
●NIO: 同步并非阻塞,在服务器中实现的模式为一个请求一个线程,也就是说,客户端发送的连
接请求都会注册到多路复用器上,多路复用器轮询到有连接IO请求时才会启动一个线程进行处理。
NIO- - 般适用于连接数目多且连接比较短(轻操作)的架构,并发局限于应用中,编程比较复
杂,从JDK1.4开始支持。‘
●AlO: 异步并非阻塞,在服务器中实现的模式为一个有效请求一个线程,也就是说,客户端的I0
**请求都是通过操作系统先完成之后,再通知服务器应用去启动线程进行处理。**AIO-般适用于连接
数目多且连接比较长(重操作)的架构,充分调用操作系统参与并发操作,编程比较复杂,从
两个元件之间互相传输的操作就叫IO操作,比如CPU到内存,内存到磁盘、从网络下载数据到磁盘
IO操作是跨零件的,损耗多,最快的还是CPU和内存之间的消耗,CPU运行一次叫一个时钟周期,一个时钟周期需要0.2ns。CPU和内存之间的IO操作也需要100个时钟周期,所以单次读取操作也需要20ns
内存和磁盘消耗的时间更多,单次最小读取5ms,是0.2ns的2500万倍。平均下来也需要几十毫秒。 磁盘分为 :固态盘有读写数量限制 机械盘没有
内存和网络的请求时间消耗都是几十到上百毫秒,网络分为内网和外网,内网要快一些
所以IO开销大一般指内存和磁盘的IO操作、内存和内网、外网的IO操作
常用流
BIO、NIO、AlO的区别?
, BIO: 同步并阻塞,在服务器中实现的模式为-一个连接-一个线程。也就是说,客户端有连接请求
的时候,服务器就需要启动一个线程进行处理,BIO一般适用于连接数目小且固定的架构,这种方式对
于服务器资源要求比较高,而且并发局限于应用中,是JDK1.4之 前的唯-选择,但好在程序直观简
单,易理解。
●NIO: 同步并非阻塞,在服务器中实现的模式为一个请求一个线程,也就是说,客户端发送的连
接请求都会注册到多路复用器上,多路复用器轮询到有连接IO请求时才会启动一个线程进行处理。
NIO- - 般适用于连接数目多且连接比较短(轻操作)的架构,并发局限于应用中,编程比较复
杂,从JDK1.4开始支持。‘
●AlO: 异步并非阻塞,在服务器中实现的模式为一个有效请求一个线程,也就是说,客户端的I0
**请求都是通过操作系统先完成之后,再通知服务器应用去启动线程进行处理。**AIO-般适用于连接
数目多且连接比较长(重操作)的架构,充分调用操作系统参与并发操作,编程比较复杂,从
JDK1.7开始支持。