[TOC]
IO(Input&&Output)
字符流、字节流
字节和字符
Bit最小的二进制单位,是计算机的操作部分,取值0或者1
Byte(字节)是计算机操作数据的最小单位由8位bit组成 取值(-128~127)
Char(字符)是用户可读的最小单位,在Java里面由16位bit组成 取值(0~65535)
字节流
操作byte类型数据,主要操作类是OutputStream、InputStream的子类;不用缓冲区,直接对文件本身进行操作
字节流
操作字符类型数据,主要操作类是Reader、Writer的子类;使用缓冲区缓冲字符,不关闭流就不会输出任何内容
相互转换
整个IO包实际上分为字节流和字符流;除了这两个流之外还存在一组字节流-字符流的转换类。
OutputStreamWriter:是Writer的子类,将输入的字符流变为字节流,即将一个字符流的输出对象变为以一个字节流输出对象。
public static void m1() throws IOException{
//字符流转化为字节流输出
File file=new File("test.txt");
OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream(file));
osw.write("我是字符流转化为字节流输出的");
osw.close();
}
InputStreamReader:是Reader的子类,将输入的字节流变为一个字符流,即将一个字节流的输入对象变为字符流的输入对象
public static void m2() throws IOException{
File file=new File("test.txt");
InputStreamReader isr=new InputStreamReader(new FileInputStream(file));
char[] buf=new char[1024];
int len=isr.read(buf);
System.out.println(new String(buf));
isr.close();
}
File类
java.io.File
路径问题
pathSeparator 路径分隔符 等价于字符串书写的;(windows) :(linux)
String path="D:/aaa.txt"+File.pathSeparator+"G:/bbb.txt"
separator 文件分隔符 等价于字符串写的\\效果
String name="D:"+File.separator+"aaa.txt";
String name2="D://aaa.txt";
String name3="D:\aaa.txt";
windows中的目录分隔符\
**使用java代码写的时候,因为\就是转义字符,所以应该写成 \\ 或者也可以写成/ **
Linux中的目录分隔符是/
构造方法
File(File parent,String child)
File(String pathname)
File(String parent,String child)
File(URL uri)
常用的功能
创建删除目录和文件
创建文件 createNewFile()
1、创建过程中如果没有中间的目录就会报错
2、如果文件已经存在了,那么调用方法后的返回值就是false。但是不会报错
3、如果不指定后缀名默认还是创建问价,不会创建文件夹
File file=new File(".\\Demo8_17\\java");
//./java //G:\codes\javaSE\java
//./Demo8_17/java //G:\codes\javaSE\Demo8_17\java
boolean isCraete=file.createNewFile();
创建文件夹功能 mkdir()
1、只能创建一级文件夹,多级目录会报错
2、如果要想创建多级目录需要使用file.mkdirs();
删除文件或者文件夹 delete()
1、如果有中间目录删除会失败,但是并不报错
2、删除文件夹,必须保证该文件夹下没有文件和子文件夹,会删除失败
File的获取功能
**file.getName()**通过路径的split(“/“)数组的的最后一个元素
**file.getPath()**得到创建file的路径,创建的时候是啥就得到什么
**file.getAbsolutePath()**获取绝对路径
**file.getAbsoluteFile()**返回的是文件 返回值类型是File
file.length()获取的是文件的字节数,返回值:long类型**
file.getParent() 获取的是父目录,返回值类型是String
file.getParentFile() 获取的是父目录,返回值类型是File类型
File file=new File(".\\java"); //./java //G:\codes\javaSE\java //./Demo8_17/java //G:\codes\javaSE\Demo8_17\java boolean isCraete=file.createNewFile(); //boolean isdelete =file.delete(); System.out.println("+++++++++++++"); System.out.println(file.getName());//java System.out.println(file.getPath());//.\java System.out.println(file.getAbsolutePath());//G:\codes\javaSE\.\java System.out.println(file.getAbsoluteFile());//G:\codes\javaSE\.\java System.out.println(file.length());//0 System.out.println(file.getParent());//. System.out.println(file.getParentFile());//.
File判断功能
**file.exists()**判断路是否存在
file.isDirectory() 如果文件夹路径存在,并且路径表示的是文件夹的时候才会返回true
file.isFile() 如果文件路径存在,并且是文件才会返回true
//File判断功能 java文件没有创建 //判断路径是否存在 File file1=new File(".//java"); boolean exists=file.exists(); System.out.println(exists);//true //判断是不是文件夹 boolean isDirectory=file.isDirectory(); System.out.println(isDirectory);//false //判断是否是文件 boolean isFile=file.isFile(); System.out.println(isFile);//true
遍历目录
//遍历目录 返回字符串数组 .
File file2=new File("G:\\codes\\javaSE");
String[] list=file2.list();
System.out.println(Arrays.toString(list));// Demo8_17,Demo8_9 输出该文件加目录下的所有子目录的名字
//遍历目录 返回File数组
File[] files=file2.listFiles();
System.out.println(Arrays.toString(files));//返回从磁盘目录开始的地址
//获取磁盘根目录
File[] files1=file2.listRoots();
System.out.println(Arrays.toString(files1));//返回本地电脑所有的磁盘号
文件过滤器
通过设置可以获取指定类型的文件
//文件过滤器
//通过重写抽象方法 FileFilter 当返回值为true的时候,添加进数组,实现文件过滤的功能
//原理: file.listFiles()每次获取到一个文件夹都会调用对应接口的accept方法
//accept()方法如果返回值为true,就将此文件或者文件夹放入对应的数组中,所以我们可以用我们的代码进行控制函数的返回值是true还是false
File file3=new File("G:\\codes\\javaSE");
file.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
return false;
}
});
输入流和输出流
输出流和输入流是相对于程序而言。当需要将磁盘中的文件读入程序中,这种流这就是—-输入流;当需要将数据从程序写入到文件这种流就是—-输出流
输出流 OutputStream
OutputStream叫做字节输出流,每次操作的是1个字节(8位)
OutputStream可以写入任何文件
OutputStream抽象类是所有输出字节流类的超类
操作的数据都是字节
FileOutputStream
文件输出流,用于将程序当中的数据写入File的输出流
构造方法
append 设置为true/false,表示是否附加写入的数据
参数解释 | |
---|---|
FileOutputStream(File file) |
创建文件输出流以写入由指定的 File 对象表示的文件。 |
FileOutputStream(File file, boolean append) |
创建文件输出流以写入由指定的 File 对象表示的文件。 |
FileOutputStream(FileDescriptor fdpj) | 创建文件输出流以写入指定的文件描述符 |
FileOutputStream(String name) | 创建文件输入流以指定的名称写入文件 |
FileOutputStraem(String name,boolean append) | 创建文件输出流以指定的名称写入文件 |
写入方式
System.out.println("-------------输出流");
FileOutputStream fos=new FileOutputStream("G:\\codes\\javaSE\\Demo8_17\\src\\demo\\demo.txt",true);
fos.write(123);
fos.write("\r\n".getBytes());
fos.write(new byte[]{65,66,67});
fos.write("\r\n".getBytes());
fos.write(new byte[]{68,69,70},0,2);
fos.write("\r\n".getBytes());
fos.write("大家好".getBytes());
//如果需要多次写入并且进行换行操作的话需要添加 \r\n
InputStream
跟OutputStream一样,每次操作的是一个字节(8位)
InputStream可以读取任何文件
InputStream 抽象类,是表示输入字节流的所有类的超类
基本共性功能方法
int read():读取一个字节并且返回,没有字节返回-1
int read(byte[] );读取一定量的字节数,并且存储到字节数组中
void close(): 关闭输入流
FileInputStream
文件输入流,用于数据读取到程序的输入流,用于数据读取到程序的输入流
构造方法
FileInputStream(File file) | 打开与实际文件的连接创建一个FileInputStream,传入一个File对象 |
FileInputStream(String name) | 传入一个路径名name |
读取方法
//读取单个字节
// read的返回值代表读取的内容文件末尾的值是-1
File file=new File("./Demo8_17/src/demo/demo.txt");
System.out.println(file.exists());
FileInputStream fis=new FileInputStream(file);
int len=0;
while((len = fis.read()) != -1){ System.out.print((char)len); }
fis.close();
//////////////
//读取到字节数组
//返回值代表读取到了多少个有效的字节
读取的内容在byte里边
File file1 = new File("./Demo8_17/src/demo/demo.txt");
FileInputStream fis1 = new FileInputStream(file);
byte [] b = new byte[2];
len = 0;
while((len = fis.read(b)) != -1){
String str = new String(b, 0, len);
System.out.print(str);
}
字符流
字符流操作的单位也是以字节为单位,其主要就是为了解决文本文件的输入和输出
Writer
字符输出流,和字节流的主要区别在与字符流只能操作文本文件
abstract void | close() 关闭流,先刷新 |
abstract void | flush() 刷新流 |
void | write(char[] cbuf) 写入一个字符数组 |
abstract void | write(char[] cbuf,int off,int len)写入字符数组的一部分 |
void | write(int c)写一个字符 |
void | write(String str) 写入一个字符串 |
void | write(String str,int off ,int len) 写入一字符串的一部分 |
FileWrite
Write的实现类
构造方法
append的作用 true/false 设置为false,表示从头开始写
FileWriter(File file) | 给一个File对象构造一个FileWriter对象 |
FileWriter(File file,boolean append) | |
FileWriter(String name) | |
FileWriter(String name,boolean append) | |
FileWrite(FileDescriptor fd) | 构造与文件描述符关联的FileWrite对象 |
注意
它中的构造方法都是super(new FileOutputStream(fileName));
Reader
字符输入流,和字节流的区别在与字符流只能操作文本文件
方法
read()读取一个字符 ;read(char[] cbuf) 将字符读入数组
FileReader
构造方法
FileReader(File file) | |
FileReader(String fileName) | |
FileReader(FileDescriptor fd) |
使用方法
public class HelloWorld {
public static void main(String [] args) throws IOException {
FileReader fr = new FileReader("./Demo8_18/src/com/lyz/onclass/test.txt");
FileWriter fw = new FileWriter("./Demo8_18/src/com/lyz/onclass/newtest.txt");
char [] ch = new char[1024];
int len = 0;
while((len = fr.read(ch)) != -1){
fw.write(ch, 0, len);
}
fr.close();
fw.close();
}
}
test.txt | newtest.txt |
---|---|
注意
使用字符流,不能去读取不是文本的文件
转换流
正常情况下idea的开发环境的编码为UTF-8的时候,那么当我们使用字节流或者字符进行写入文件的时候,文件编码也还是UTF-8,如果用字节流或者字符流进行文件的读取的操作的时候,默认也是UTF-8格式
如果文件时GBK的话,就会出现乱码情况
转换流的出现,就是为了解决写入和读取文本文件的时候指定具体的编码格式
OutputStreamWriter
OutputStreamWriter是字符流通向字节流的桥梁,可使用指定的字符编码表,将要写入流中的字符编码成字节
- 创建一个字节输出流 FileOutputStream fos=new FileOutputStream(“”)
- 创建转换输出流,绑定指定的字节输出流,并指定编码格式OutputStreamWriter osw =new OutputStreamWriter(fos,”gbk”)
- 调用write方法写入 osw.write(“你好”)
InputStreamReader
InputStreamReader是字节流通向字符流的桥梁,它使用指定的编码表读取字节并将其解码为字符
- 创建一个字节输入流FileInputStream fis=new FileInputStream(“”)
- 创建转换输入流,绑定字节输入流,并指定编码格式IntputStreamReader isr=new InputStreamReader(fis,”UTF-8”)
- 调用read方法进行读取
补充: 字符编码
缓冲流
1、缓冲流 :BufferReader BufferWriter BufferInputStream BufferOutputStream
2、作用:提高读取和写入的速度
提高读取和写入的速度的原因:内部提供了一个大小为8192的缓冲区
3、处理流,就是套接在已有流的基础上
BufferInputStream BufferOutputStream
//缓冲流处理流的一种,提高流的读取和写入的速度
//主要是在内部提供了一个缓冲区
public static void bufferTest1() {
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
//1、造文件
File srcPath = new File("./src/古典雅致美女打着纸伞3840_2160.jpg");
File destPath = new File("./src/01.jpg");
//2、造流
FileInputStream fis = new FileInputStream(srcPath);
FileOutputStream fos = new FileOutputStream(destPath);
//造处理流 即缓冲流
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(fos);
//复制的操作 读取和写入
byte[] buffer = new byte[10];
int len;
while ((len = bis.read(buffer)) != -1) {
bos.write(buffer);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//资源关闭
//要求,先关闭外层的流,再关闭内层的流
if(bis!=null){
try {
bis.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if(bos!=null){
try {
bos.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
//关闭外层流的同时,内层流也会自动的关闭
//关闭内层的代码可以省略
/*fis.close();
fos.close();*/
}
}
public static void FileInputOutputStreamTest(String src,String dest) {
File srcPath = new File(src);
File destPath = new File(dest);
FileInputStream fis = null;
FileOutputStream fos=null;
try {
fis = new FileInputStream(srcPath);
fos = new FileOutputStream(destPath);
byte[] b = new byte[10];
int len;
while ((len = fis.read(b)) != -1) {
fos.write(b);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
if(fos!=null) {
try {
fos.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
public static void main(String[] args) {
String src="./src/古典雅致美女打着纸伞3840_2160.jpg";
String dest="./src/02.jpg";
long start1=System.currentTimeMillis();
bufferTest1();
long start2=System.currentTimeMillis();
FileInputOutputStreamTest(src,dest);
long end=System.currentTimeMillis();
System.out.println("前"+(start2-start1)+" 后"+(end-start2));
}
//前55 后2294
**BufferReader BufferWriter **
为了提高字符流读写的效率,引入了缓冲机制,进行字符批量的读写,提高了单个字符读写的效率。BufferedReader用于加快读取字符的速度,BufferedWriter用于加快写入的速度
BufferedReader和BufferedWriter类各拥有8192个字符的缓冲区。当BufferedReader在读取文本文件时,会先尽量从文件中读入字符数据并放满缓冲区,而之后若使用read()方法,会先从缓冲区中进行读取。如果缓冲区数据不足,才会再从文件中读取,使用BufferedWriter时,写入的数据并不会先输出到目的地,而是先存储至缓冲区中。如果缓冲区中的数据满了,才会一次对目的地进行写出。
public static void testBufferedReaderBufferedWriter() throws IOException{
BufferedReader br=new BufferedReader(new FileReader(new File("./src/demo.txt")));
BufferedWriter bw=new BufferedWriter(new FileWriter(new File("./src/demo_copy.txt")));
/*
//方式一,和之前一样使用 char[]数组
char[] ch=new char[1024];
int len;
while((len=br.read(ch))!=-1){
bw.write(ch);
}
*/
//还有一种方式 使用String raedLine
String data;
while((data=br.readLine())!=null){
System.out.println(data);
bw.write(data);
}
br.close();
bw.close();
}
BufferedReader中的两种读取方法的对比
源文件 | String data br.readLine(data)!=null | char[] ch (len=br.read(ch))!=-1 |
---|---|---|
中间没有不读取换行符号,遇到换行符号就直接返回读取到的一行数据 | 能读取到换行符 |
注意: 在BufferedReader 和BufferedWriter中有一个flush()方法,调用时,不管缓冲区有多少内容都直接返回出来,默认是缓冲区满和没有了才返回
Other
1、标准的输入输出流
1.1
System.in:标准的输入流,默认从键盘输入 类型是InputStream
System.out:标准的输出流,默认从控制台输出
1.2:重定向
System类的setIn(InputStream is)/setOut(PrintStream ps)方式重新指定输入和输出的
2、打印流
3、数据流
1单纯使用System.in实现程序的输入
public static void m1() throws IOException {
//输入 使用 Scanner实现,调用next()返回一个参数
// 使用System.in实现 System.in --->BufferedReader的readLine();
InputStreamReader is=new InputStreamReader(System.in);
BufferedReader br=new BufferedReader(is);
while(true){
String data=br.readLine();
if("e".equalsIgnoreCase(data)||"exit".equalsIgnoreCase(data)){
System.out.println("程序输入即将结束");
break;
}
System.out.println(data.length()+"|-------|"+data);
}
}
Properties
Properties类表示一个持久的属性集。Properties可以保存在流中或从流中加载。属性列表中每一个键即其对应的值都是一个字符串。
特点
- Hashtable(和HashMap不同的是,Hashtable是一个线程安全的)的子类,map集合中的方法都是可以用的
- 该集合没有泛型,键和值都只能是字符串
- 他是一个可以持久化的属性集
- 它主要使用getProperty没有设置synchronized , setProperty设置类synchronized获取和改变他的值
常用方法
- void load(InputStream inStream) 从输入字节流读取属性列表
- void load(Reader reader) 从输入字符流读取属性列表(关键字和元素对)
- void store(OutputStream out,String comments) 将此属性列表(key和value)写入Properties表中,亦适用于使用load(InputStream)方法加载到Properties表中的格式输出流
- void store(Writer writer,String comments) 使用load(Reader)方法的格式输出到输出字符流
多线程
方式一:
进程概念
进程:进程值正在运行的程序。确切的说,当一个程序加载到内存中运行的时候,即变成了一个进程,进程时处于运行过程中的程序,具有一定的独立功能
线程: 线程时进程的一个执行单元,负责当前进程中程序的运行 一个进程至少有一个线程,可以有多个线程的
总结: 一个程序至少有一个进程,一个进程中可以包含多个线程
多线程开发
线程的调度
有两种调度模式:分时调度模型和抢占式调度模型
分时调度:所有线程轮流使用cpu的使用权,平均分配每一个线程占用的CPU的时间
抢占式调度:优先让优先级高的线程使用CPU
通过继承Thread类来实现多线程
不能直接调用线程中的run()方法,要调用线程的start()函数,才能实现多线程
一个线程只能启动一次,要不然就会报错误IllegalThreadStateException();
实例:
class MyThread extends Thread{
private String title;
public MyThread(String title){
this.title=title;
}
@Override
public void run() {
for(int i=0;i<10;i++){
System.out.println(this.title+"i的值是"+i);
}
}
}
基于Runable接口实现多线程
Thread runable1=new Thread(new MyThread_R("线程R1"));
runable1.start();
利用lambda表达式来创建线程,在以后的开发对于多线程的实现,优先考虑的就是Runable接口的实现,
for(int x=0;x<3;x++){
String title="线程对象-"+x;
new Thread(()->{
for(int y=0;y<10;y++){
System.out.println( title+"lambda表达式"+y);
}
}).start();
}
Thread与Runable关系
使用Runable是最方便的,可以避免单继承的局限,也可以更好的对功能进行扩充
但是从结构上来看,之前继承Thread的时候,还是实现Runable接口中的run()方法
通过 Thread 类的构造方法传递了一个 Runnable 接口对象的时候,那么该接口对象将被 Thread 类中的 target 属性所保存,在 start() 方法执行的时候会调用Thread类中的 run() 方法,而这个 run() 方法去调用 Runnable 接口子类被覆写过的 run() 方法。
多线程开发的本质上是多个线程可以进行同一资源的抢占
public class Thread implements Runnable
Callable接口实现多线程
Runable接口有一个问题,就是线程结束之后无法获取一个返回值,jdk1.5之后就提出了java.util.concurrent.Callable,就解决了这一问题
Callable定义的时候可以设置一个泛型,此泛型的类型就是返回数据的类型,这样的好处就是 可以避免一些安全隐患
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* 为什么说Callable接口的方式创建多线程比实现Runable接口创建多线程强大?
* 1、call() 可以有返回值
* 2、call() 可以抛出异常,被外面的操作捕获,获取异常信息
* 3、call()是支持泛型的,指明泛型的时候:1、重写的类需要明确泛型,2、创建的FutureTask对象也需要指明泛型
*/
public class ThreadNew {
public static void main(String[] args) {
MyCallMyThread thread=new MyCallMyThread();
FutureTask futureTask = new FutureTask(thread);
new Thread(futureTask).start();
try {
//get()返回值为FutureTask构造器参数Callable实现类重写的call()的返回值。
//如果对call的返回值不感兴趣或者说,返回值并没有什么实际效果,那就不用调用get()方法
int sum=(int) futureTask.get();
System.out.println(sum);
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (ExecutionException e) {
throw new RuntimeException(e);
}
}
}
class MyCallMyThread implements Callable{
@Override
public Object call() throws Exception {
int sum=0;
for(int i=1;i<=100;i++){
sum+=i;
}
return sum;
}
}
线程池
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
/**
* 创建线程的方式四:使用线程池
*
* 好处:
* 1、提高响应速度(减少创建新线程的时间)
* 2、减低资源消耗(重复利用线程池中的线程,不需要每次都创建
* 3、便于线程管理
* corePoolSize():核心池的大小
* maximumPoolSize():最大线程数
* keepAliveTime():线程没有任务时最多保持多长时间后会终止
*/
class NumberThread implements Runnable{
@Override
public void run() {
for(int i=0;i<100;i++){
if(i%2==0){
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
}
}
class NumberThread1 implements Runnable{
@Override
public void run() {
for(int i=0;i<100;i++){
if(i%2!=0){
System.out.println(Thread.currentThread().getName()+" "+i);
}
}
}
}
public class ThreadPool {
public static void main(String[] args) {
//1、提供指定线程数量的线程池
ExecutorService service=Executors.newFixedThreadPool(10);
//设置线程池的属性
//ExecutorService 是一个线程
System.out.println(service.getClass());
ThreadPoolExecutor service1 = (ThreadPoolExecutor) service;
//service1.set
//2、执行指定的线程操作、需要提供实现Runable接口或Callable接口实现类的对象
service.execute(new NumberThread());//适合使用于Runable;
service.execute(new NumberThread1());//适合使用于Runable;
//service.submit();//适合使用于Callable;
//3、关闭连接池
service.shutdown();
}
}
序列化
序列化操作的对象必须实现Serializable接口
读取末尾 抛出EOFException
序列化流没有续写::增删改必须 全部读取,修改后,再覆盖
重点
Runable和Callable的区别
1、Runable是在jdk1.0的时候提出的多线程的实现接口,而Callable实在jdk1.5之后提出的
2、java.long.Runable接口中只提供了一个run()方法,并且没有返回值,就无法判断该线程是否已经结束
3、java.util.concurrent.Callable接口提供了call()方法,可以有返回值
创建多线程有几种方式 四种方法
1、继承Thread类
2、基于Runable接口实现
3、Callable接口实现多线程
4、使用线程池