Java个人学习之旅(第十七天)

NIO:

Java NIO (New IO,Non-Blocking IO)是从Java 1.4版本开始引入的一套新的IO API,可以替代标准的Java IO 。

NIO与原来的IO同样的作用和目的,但是使用的方式完全不同,NIO支持面向缓冲区的(IO是面向流的)、基于
通道的IO操作。
NIO将以更加高效的方式进行文件的读写操作。

1. Buffer(缓冲区):

属性:

  1. limit 缓冲区大小限制,默认指向最后一的元素的下一位

  2. position 指针位置,默认指向当前元素的下一位

  3. capacity 缓冲区容量,在初始化缓冲区的时候可以设置缓冲区大小

清理,翻转和倒带

  1. clear() 使缓冲区准备好信道读取或相对放置操作的一个新的序列:它设置了limit和position为零。

  2. flip() 使缓冲区准备好新的通道写入或相对获取操作序列:它将limit设置为当前位置,然后将position设置为零。

  3. rewind()使缓冲区准备好重新读取已经包含的数据:它保持limit不变,并将position设置为零。

一些方法:

  • mark() 将此缓冲区的当前position做个标记

  • reset() 使position返回到mark()标记的位置

  • remaining() 返回当前position和limit之间的元素数。

  • hasRemaining() 返回当前position和limit之间的是否有元素。

  • get() 相对获取,从position处开始读取数据,然后position自增

  • get (int index),绝对获取,不会影响position

总结:
0<=mark<=position<=limit<=position

直接缓冲区和非直接缓冲区

直接缓冲区:通过allocateDirect()方法分配直接缓冲区,将缓冲区建立在物理内存中。可以提高效率。

非直接缓冲区:通过allocate()方法分配缓冲区,将缓冲区建立在JVM的内存中。

allocateDirect(int capacity)分配一个新的直接字节缓冲区
allocate(int capacity)分配一个新的非直接字节缓冲区。

isDirect()判断这个字节缓冲区是否是直接缓冲区。

代码示例:

public class NIOTest {
	
	@Test
	public void test01() {
		//初始化缓冲区并且规定容量
		CharBuffer cb = CharBuffer.allocate(10);
		
		//往缓冲区加入元素
		cb.put('a');
		cb.put("123");
		cb.put('b');
		cb.put('c');
		
		System.out.println("capaticy : " + cb.capacity());
		System.out.println("limit : " + cb.limit());
		System.out.println("position : " + cb.position());
		
		cb.flip();  //使缓冲区准备好读取和写入操作,limit指向指针位置,指针指向0
		
		System.out.println(cb.get());
		System.out.println(cb.get());
		System.out.println(cb.get());
		
		System.out.println("limit : " + cb.limit());
		System.out.println("position : " + cb.position());
	}
}

运行结果:
在这里插入图片描述

2. Channel(通道):

Channel和IO中的Stream(流)是差不多一个等级的。只不过Stream是单向的,而Channel是双向的,既可以用来进行读操作,又可以用来进行写操作

Channel本身不能直接访问数据,而只能与Buffer进行交互(可以将channel想象成轨道,而把buffer想象成装载数据的地铁)

Channel接口的主要实现类:

  1. FileChannel 本地文件传输通道

  2. SocketChannel/ServerSocketChannel TCP协议数据传输通道

  3. DatagramChannel UDP协议传输通道

使用 channel+直接缓冲区(物理内存映射文件) 完成文件的复制:

通道之间的数据传输 (直接缓冲区)

  1. transferFrom() :从源通道传输数据

  2. transferTo():传输数据到目标通道

代码示例:

public class ChannelTest {
	
	@Test
	public void test() {
		
		try {
			//文件输入通道
			//操作方式只读
			FileChannel fr = FileChannel.open(Paths.get("abc.rar"), StandardOpenOption.READ);
			
			//文件输出通道
			//操作方法,读写创建
			FileChannel fw = FileChannel.open(Paths.get("c.rar"),StandardOpenOption.READ,
					StandardOpenOption.WRITE,StandardOpenOption.CREATE);
			
			//fr输出数据到fw
			//fr.transferTo(0, fr.size(), fw);
			
			//fw从fr接受数据
			fw.transferFrom(fr, 0, fr.size());
			
			fr.close();
			fw.close();
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

运行结果(先生成a.rar,再生成b.rar):
在这里插入图片描述

3. Path:

Path用替换原有的File类

实例化
Paths类提供静态的get()方法用来获取Path对象

  1. static Path get(String first, String …more) : 用于将多个字符串成路径

  2. static Path get(URI uri) : 返回指定uri对应的path路径

常用方法:
在这里插入图片描述

4. Files工具类

操作文件或文件目录的工具类

在这里插入图片描述

在这里插入图片描述

复制方法代码举例:

@Test
	public void test() throws FileNotFoundException, IOException {
		
		Files.copy(new FileInputStream("a.txt"), Paths.get("b.txt"),StandardCopyOption.REPLACE_EXISTING);
		
	}

判断是否为隐藏文件

@Test
	public void test() throws FileNotFoundException, IOException {

		//判断文件是否为隐藏文件
		Path path = Paths.get("a.txt");
		System.out.println("该文件是隐藏文件" + ":"  + Files.isHidden(path));
	}

5.Charset

NIO提供了Charset用于字符编码和解码

输出支持的字符集

利用字符集编码和解码

不太重要,直接上代码:

@Test
	public void test() throws CharacterCodingException {
		//初始化转码对象
		Charset forname = Charset.forName("UTF-8");
		//初始化一个字符缓冲区对象,设置容量为10
		CharBuffer cb = CharBuffer.allocate(10);
		
		//放置字符到缓冲区
		cb.put("你好NIO");
		
		//编码
		CharsetEncoder encoder = forname.newEncoder();
		
		//进入读取模式
		cb.flip();
		
		//编码之后得到byteBuffer
		ByteBuffer bb = encoder.encode(cb);
		//遍历byteBuffer
		for(int i=0;i < bb.limit();i++){
			System.out.println(bb.get());
		}
		
		//初始化解码对象
		CharsetDecoder decoder = forname.newDecoder();
		bb.flip();
		
		//解码
		CharBuffer cbf = decoder.decode(bb);
		
		System.out.println(cbf.toString());

	}

运行结果:
在这里插入图片描述

多线程:

理解说明:

  1. 程序:是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码。

  2. 进程:资源分配的基本单位,程序的一次执行过程,或是正在运行的一个程序。

  3. 线程:线程作为调度和执行的单位,每个线程拥独立的运行栈和程序计数器(pc),进程可进一步细化为线程,是一个程序内部的一条执行路径。

并行与并发的理解:

并行:多个CPU同时执行多个任务。比如:多个人同时做不同的事。
并发:一个CPU(采用时间片)同时执行多个任务。比如:秒杀、多个人做同一件事

创建线程类

一. 继承Thread

 创建一个继承于Thread类的子类
 重写Thread类的run() --> 将此线程执行的操作声明在run()中
 创建Thread类的子类的对象
 通过此对象调用start():①启动当前线程 ② 调用当前线程的run()

代码示例:

public class MyThread extends Thread{

	@Override
	public void run() {
		for(int i=0;i < 100;i++){
			System.out.println(getName() + ":" + i);
		}
	}
}

	@Test
	public void test01() {
		
		//初始化两个线程对象
		MyThread thread1 = new MyThread();
		MyThread thread2 = new MyThread();

		for (int i = 0; i < 100; i++) {

			System.out.println(Thread.currentThread().getName());

			if (i == 20) {
				thread1.start();
				thread2.start();
			}
		}
	}

部分运行结果:
在这里插入图片描述
可以看到,两个线程对象打印的顺序是未知的,因为线程会抢占CPU的时间片,谁抢的时间片多谁就有运行得久一点

二. 实现Runnable接口的方式:

创建一个实现了Runnable接口的类
实现类去实现Runnable中的抽象方法:run()
创建实现类的对象
将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象
通过Thread类的对象调用start()

代码示例:

public class MyThread2 implements Runnable{

	@Override
	public void run() {
		// TODO Auto-generated method stub
		for(int i=0;i < 30;i++){
			System.out.println(Thread.currentThread().getName() + " : " + i);
		}
	}
}

	@Test
	public void test02() {

		MyThread2 mt2 = new MyThread2();

		Thread t1 = new Thread(mt2, "线程1");
		t1.start();

		Thread t2 = new Thread(mt2, "线程2");
		t2.start();
	}
}

部分运行结果:
在这里插入图片描述
同样是乱序

两种方式的对比:
开发中:优先选择:实现Runnable接口的方式

  1. 实现的方式没类的单继承性的局限性

  2. 实现的方式更适合来处理多个线程共享数据的情况

相同点:
两种方式都需要重写run(),将线程要执行的逻辑声明在run()中。
目前两种方式,要想启动线程,都是调用的Thread类中的start()。

大部分笔记都转载于尚硅谷,有兴趣想自学Java得朋友可以去看一看:Java零基础入门教程

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值