java进阶

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;//一条返回语句,可以省略大括号和returnTestDemo3 multiple2 = (hello, name) -> hello + name;//多条处理语句,需要大括号和returnTestDemo3 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开始支持。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值