Java语言基础
IO流
字节流
可以读写任何类型的文件,比如:音频、视频 、文本文件
- 字节流的抽象基类:
字节输入流:InputStream
字节输出流:OutputStream - 我们发现InputStream、OutputStream是抽象类,我们不能对其进行直接实例化,而我们需要使用子类对其进行实例化:
- 子类1:FileOutputStream<------>FileInputStream
FileOutputStream:文件输出流,用于将数据写入 File 或 FileDescriptor 的输出流;
构造方法:
FileOutputStream(File file) :创建一个向指定 File 对象表示的文件中写入数据的文件输出流
FileOutputStream(String name) :创建一个向具有指定名称的文件中写入数据的输出文件流
方法摘要:
pubilc void close():关闭此文件输出流并释放与此流有关的所有系统资源
public void write(int b):写一个字节 超过一个字节 砍掉前面的字节
public void write(byte[] b):写一个字节数组
public void write(byte[] b,int off,int len):写一个字节数组的一部分,从off开始到len结束;
FileOutputStream写出数据在windows下的换行符是:\r\n
FileOutputStream写出数据如何实现数据的追加写入:
FileOutputStream fos = new FileOutputStream(“a.txt” , true) ; //在a.txt文档中 完成追加写入
注意事项:
1.创建字节输出流对象了做了几件事情?
a:调用系统资源创建a.txt文件
b:创建了一个fos对象
c:把fos对象指向这个文件
2.为什么一定要有close()?
a: 通知系统释放关于管理a.txt文件的资源
b: 让Io流对象变成垃圾,等待垃圾回收器对其回收
FileInputStream:文件输入流,从文件系统中的某个文件中获得输入字节;
构造方法:
FileInputStream(File file):通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的 File 对象 file 指定
FileInputStream(String name):通过打开一个到实际文件的连接来创建一个 FileInputStream,该文件通过文件系统中的路径名 name 指定
方法摘要:
pubilc void close():关闭此文件输入流并释放与此流有关的所有系统资源
int read():从此输入流中读取一个数据字节,如果没有数据返回的就是-1
int read(byte[] b):从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中
int read(byte[] b, int off, int len):从此输入流中将最多 len 个字节的数据读入一个 byte 数组中
//用字节流一次读写一个字节复制一个.mp3的文件到电脑的桌面上
public class Demo {
public static void main(String[] args) throws IOException {
File file = new File("大王叫我来巡山.mp3");
FileInputStream in = new FileInputStream(file);
FileOutputStream out = new FileOutputStream("C:\\Users\\MyComputer\\Desktop\\" + file.getName());
//频繁的读写操作
int len=0;//用来记录读取到的字节
while ((len=in.read())!=-1){
out.write(len);
}
in.close();
out.close();
}
}
我们会发现一次读写一个字节效率低,复制速度很慢,所以我们选择读取数据一次一个字节数组;
public class Demo {
public static void main(String[] args) throws IOException {
File file = new File("大王叫我来巡山.mp3");
FileInputStream in = new FileInputStream(file);
FileOutputStream out = new FileOutputStream("C:\\Users\\MyComputer\\Desktop\\" + file.getName());
//定义一个字节数组,充当缓冲区
byte[] bytes = new byte[1024 * 8];
int len = 0;
while ((len = in.read(bytes)) != -1) {//一次读取一个字节数组[1024*8]
out.write(bytes, 0, len);
}
//释放资源
in.close();
out.close();
}
}
- 子类2:BufferedOutputStream<------>BufferedInputStream
缓冲思想:
字节流一次读写一个数组的速度明显比一次读写一个字节的速度快很多,这是加入了数组这样的缓冲区效果,java本身在设计的时候,也考虑到了这样的设计思想即装饰设计模式,所以提供了字节缓冲区流;
BufferedOutputStream:该类实现缓冲的输出流;
构造方法:
BufferedOutputStream(OutputStream out):创建一个新的缓冲输出流,以将数据写入指定的底层输出流
BufferedOutputStream(OutputStream out, int size):创建一个新的缓冲输出流,以将具有指定缓冲区大小的数据写入指定的底层输出流
方法摘要:
void close():关闭此输入流并释放与该流关联的所有系统资源
void flush():刷新此缓冲的输出流。
void write(byte[] b, int off, int len):将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此缓冲的输出流
void write(int b):将指定的字节写入此缓冲的输出流
BufferedInputStream:该类实现缓冲的输入流;
构造方法:
BufferedInputStream(InputStream in):创建一个 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。
BufferedInputStream(InputStream in, int size):创建具有指定缓冲区大小的 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用
方法摘要:
void close():关闭此输入流并释放与该流关联的所有系统资源
int read():从此输入流中读取数据
int read(byte[] b, int off, int len):从此字节输入流中给定偏移量处开始将各字节读取到指定的 byte 数组中
//复制一个.mp3的文件到电脑的桌面上
public class Demo {
public static void main(String[] args) throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("大王叫我来巡山.mp3"), 1024);
BufferedOutputStream bos= new BufferedOutputStream(new FileOutputStream("C:\\Users\\MyComputer\\Desktop\\大王叫我来巡山.mp3"));
int len = 0;//用来记录读取到的字节
long start = System.currentTimeMillis();
byte[] bytes = new byte[1024 * 8];
while ((len = bis.read(bytes)) != -1) {
bos.write(bytes,0,len);
bos.flush();
}
bos.close();
bis.close();
}
}
字符流
- 字符流出现的原因:因为一个中文汉字占两个字节,如果使用字节流操作,那么我们就需要对这个汉字进行拆分和合并比较麻烦;那么java为了方便的操作中文就给我们提供了一个字符流,但是呢?我们都知道,数据在计算机上的存储的最小单位就是字节,那么要操作数据,本质应该还是对最小单位的操作,于是这个字符流底层应该还是使用字节流那么要使用字符流,就需要对这个中文汉字进行拆分和合并,那么按照什么进行拆分和合并,要按照编码表进行拆分
- 字符流: 字符流 = 字节流 + 编码表
- 编码表:
ASCII
GB2312
GBK
UNICODE(USC-2, u3C-4)
UTF-8 - 字符流的抽象基类:
Reader:字符输入流
Writer:字符输出流 - String类中的编码和解码问题:
编码: 就是把字符串转换成字节数组,把一个字符串转换成一个字节数组
public byte[] getBytes():使用平台的默认字符集将此 String编码为 byte 序列,并将结果存储到一个新的 byte 数组中。
public byte[] getBytes(String charsetName) :使用指定的字符集将此 String 编码为 byte 序列,并将结果存储到一个新的 byte 数组中
解码: 把字节数组转换成字符串
public String(byte[] bytes): 通过使用平台的默认字符集解码指定的 byte 数组,构造一个新的 String。
public String(byte[] bytes, String charsetName): 通过使用指定的 charset 解码指定的 byte 数组,构造一个新的 String
默认的字符集是GBK的
String(byte[] bytes, String charsetName):通过指定的字符集解码字节数组
byte[] getBytes(String charsetName):使用指定的字符集合把字符串编码为字节数组
编码:就是把看得懂的变成看不懂的: String – byte[]
解码:就是把看不懂的变成看得懂的: byte[] – String
- 子类1:转换流OutputStreamWriter<------>InputStreamReader
OutputStreamWriter:是字符流通向字节流的桥梁;可使用指定的 charset 将要写入流中的字符编码成字节,它使用的字符集可以由名称指定或显式给定,否则将接受平台默认的字符集。
构造方法:
OutputStreamWriter(OutputStream out):根据默认编码(GBK)把字节流的数据转换为字符流
OutputStreamWriter(OutputStream out,String charsetName):根据指定编码把字节流数据转换为字符流
方法摘要:
void close():关闭此流,但要先刷新它。
void flush() :刷新该流的缓冲
public void write(int c): 写一个字符
public void write(char[] cbuf): 写一个字符数组
public void write(char[] cbuf,int off,int len) :写一个字符数组的 一部分
public void write(String str): 写一个字符串
public void write(String str,int off,int len): 写一个字符串的一部分
InputStreamReader :是字节流通向字符流的桥梁;它使用指定的 charset 读取字节并将其解码为字符,它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。
构造方法:
InputStreamReader(InputStream is):用默认的编码(GBK)读取数据
InputStreamReader(InputStream is,String charsetName):用指定的编码读取数据
方法摘要:
public int read():一次读取一个字符
public int read(char[] cbuf): 一次读取一个字符数组 如果没有读到 返回-1
void close():关闭该流并释放与之关联的所有资源
往文件中写入数据,字符流记得要刷,void flush()
//使用字符流来复制文本文件,一次读取一个字符,写一个字符来复制
public class MyTest {
public static void main(String[] args) throws IOException {
InputStreamReader in = new InputStreamReader(new FileInputStream("MyTest.txt"));
OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream("C:\\Users\\MyComputer\\Desktop\\a.txt"));
int len=0;//定义一个变量,来记录读取的字符
while ((len=in.read())!=-1){
out.write(len);
out.flush();//刷新
}
//释放资源
in.close();
out.close();
}
}
一次读写一个字节效率低,复制速度很慢,所以我们选择读取数据一次一个字符数组
public class MyTest3 {
public static void main(String[] args) throws IOException {
InputStreamReader in = new InputStreamReader(new FileInputStream("MyTest.txt"));
OutputStreamWriter out = new OutputStreamWriter(new FileOutputStream("C:\\Users\\MyComputer\\Desktop\\b.java"));
//定义一个字符数组,来充当缓冲区
char[] chars = new char[1000];
//定义一个变量,用来记录读取到的有效字符个数
int len=0;
while ((len=in.read(chars))!=-1){
out.write(chars,0,len);
out.flush();
}
//释放资源
in.close();
out.close();
}
}
- 子类2:FileWriter<------>FileReader
转换流的名字比较长,而我们常见的操作都是按照本地默认编码实现的,所以,为了简化我们的书写,转换流提供了对应的子类
例如:
BufferedReader br = new BufferedReader(new FileReader(“MyTest.txt”)) ;
BufferedWriter bw = new BufferedWriter(new FileWriter(“c.txt”)) ; - 子类3:BufferedReader<------>BufferedWriter
BufferedWriter:写出数据 ,高效的字符输出流
BufferedReader:读取数据 ,高效的字符输入流
BufferedWriter的构造方法: public BufferedWriter(Writer w)
BufferedReader的构造方法: public BufferedReader(Reader e)
字符缓冲流的特殊功能:
BufferedWriter: public void newLine():根据系统来决定换行符 具有系统兼容性的换行符
BufferedReader: public String readLine():一次读取一行数据 是以换行符为标记的 读到换行符就换行 没读到数据返回null,包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回 null
//用字符缓冲流的特殊功能复制文本文件
public class MyTest {
public static void main(String[] args) {
BufferedReader bfr = new BufferedReader(new InputStreamReader(new FileInputStream("MyTest.txt")));
BufferedWriter bfw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("C:\\Users\\MyComputer\\Desktop\\b.java")));
//定义一个变量来记录读取到的每一行
String line = null;
while ((line = bfr.readLine()) != null) { //注意一行一行的读取,读取不到返回null
bfw.write(line);
bfw.newLine();//写个换号符
bfw.flush();
}
bfr.close();
bfw.close();
}
}
案例演示:键盘录入3名学生信息按照总分排序并写入文本文件,产生一个随机数作为文档名,便于我们每次产生新的文本
public class Student {
private String name;
private int chineseScore;
private int mathScore;
private int englishScore;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getChineseScore() {
return chineseScore;
}
public void setChineseScore(int chineseScore) {
this.chineseScore = chineseScore;
}
public int getMathScore() {
return mathScore;
}
public void setMathScore(int mathScore) {
this.mathScore = mathScore;
}
public int getEnglishScore() {
return englishScore;
}
public void setEnglishScore(int englishScore) {
this.englishScore = englishScore;
}
//获取总分的方法
public int getTotalScore(){
return chineseScore+mathScore+englishScore;
}
}
public class MyTest {
public static void main(String[] args) throws IOException {
TreeSet<Student> students = new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
int num = s1.getTotalScore() - s2.getTotalScore();
int num2=num==0?s1.getName().compareTo(s2.getName()):num;
return -num2;
}
});
for (int i = 1; i <= 3; i++) {
Scanner sc = new Scanner(System.in);
Student student = new Student();
System.out.println("请输入第"+i+"个学生的姓名");
String name = sc.nextLine();
student.setName(name);
System.out.println("请输入第" + i + "个学生的语文成绩");
int yw = sc.nextInt();
student.setChineseScore(yw);
System.out.println("请输入第" + i + "个学生的数学成绩");
int sx = sc.nextInt();
student.setMathScore(sx);
System.out.println("请输入第" + i + "个学生的英语成绩");
int yy= sc.nextInt();
student.setEnglishScore(yy);
//把每个学生放到集合中
students.add(student);
}
//遍历集合中的学生信息,保存到文本文件中
long l = System.currentTimeMillis();//定义当前的时间
Date date = new Date(l);
String time = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss").format(date);//将当前的时间转换位字符串形式
BufferedWriter writer = new BufferedWriter(new FileWriter(time+"studentScore.txt"));
writer.write("序号 \t 姓名\t 语文\t 数学\t 英语\t 总分");
writer.newLine();
writer.flush();
int i=1;
for (Student student : students) {
writer.write((i++)+"\t\t"+student.getName()+" \t"+student.getChineseScore()
+" \t"+student.getMathScore()+" \t"+student.getEnglishScore()+" \t"+student.getTotalScore());
writer.newLine();
writer.flush();
}
writer.close();
}
}
//下篇再见…谢谢