一、异常
异常是程序中可能出现的问题,它的父类是Exception。异常分为两类,编译时异常、运行时异常。
- 编译时异常:没有继承RuntimeException的异常,直接继承于Exception。编译阶段就会错误提示。
- 运行时异常:RuntimeException本身和子类。编译阶段没有错误提示,运行时有异常会提示。
JVM默认处理异常的方式:
1.把异常的名称,异常原因及异常出现的位置等信息输出在了控制台
2.程序停止执行。异常下面的代码不会再执行。
public class ExceptionDemo6 {
public static void main(String[] args) {
/*
自己处理(捕获异常)
格式:
try {
可能出现异常的代码;
} catch(异常类名 变量名) {
异常的处理代码;
}
好处:可以让程序继续往下执行,不会停止
*/
int[] arr = {1, 2, 3, 4, 5, 6};
try{
//可能出现异常的代码;
System.out.println(arr[10]);//此处出现了异常,程序就会在这里创建一个ArrayIndexOutOfBoundsException对象
//new ArrayIndexOutOfBoundsException();
//拿着这个对象到catch的小括号中对比,看括号中的变量是否可以接收这个对象
//如果能被接收,就表示该异常就被捕获(抓住),执行catch里面对应的代码
//当catch里面所有的代码执行完毕,继续执行try...catch体系下面的其他代码
}catch(ArrayIndexOutOfBoundsException e){
//如果出现了ArrayIndexOutOfBoundsException异常,我该如何处理
System.out.println("索引越界了");
}
System.out.println("看看我执行了吗?");
}
}
关于异常的四个问题:
1.如果try中没有遇到问题,怎么执行?
会把try里面所有的代码全部执行完毕,不会执行catch里面的代码
注意: 只有当出现了异常才会执行catch里面的代码
2.如果try中可能会遇到多个问题,怎么执行?
会写多个catch与之对应
细节: 如果我们要捕获多个异常,这些异常中如果存在父子关系的话,那么父类一定要写在下面
在JDK7之后,我们可以在catch中同时捕获多个异常,中间用|进行隔开
表示如果出现了A异常或者B异常的话,采取同一种处理方案
3.如果try中遇到的问题没有被捕获,怎么执行?
相当于try…catch的代码白写了,最终还是会交给虚拟机进行处理。
4. 如果try中遇到了问题,那么try下面的其他代码还会执行吗?
下面的代码就不会执行了,直接跳转到对应的catch当中,执行catch里面的语句体,但是如果没有对应catch与之匹配,那么还是会交给虚拟机进行处理
异常中的常见方法
- public String getMessage() 返回此 throwable 的详细消息字符串
- public String toString() 返回此可抛出的简短描述
- public void printStackTrace() 在底层是利用System.err.println进行输出
把异常的错误信息以红色字体输出在控制台
细节:仅仅是打印信息,不会停止程序运行
int[] arr = {1, 2, 3, 4, 5, 6};
try {
System.out.println(arr[10]);
} catch (ArrayIndexOutOfBoundsException e) {
String message = e.getMessage();
System.out.println(message);//Index 10 out of bounds for length 6
String str = e.toString();
System.out.println(str);//java.lang.ArrayIndexOutOfBoundsException: Index 10 out of bounds for length 6
e.printStackTrace();
}
System.out.println("看看我执行了吗?");
//正常的输出语句
//System.out.println(123);
//错误的输出语句(而是用来打印错误信息)
//System.err.println(123);
}
}
抛出异常
- throws:写在方法定义处,表示声明一个异常。告诉调用者,使用本方法可能会有哪些异常。
- throw :写在方法内,结束方法。手动抛出异常对象,交给调用者。方法中下面的代码不再执行了。
public class ExceptionDemo12 {
public static void main(String[] args) {
/*
需求:
定义一个方法求数组的最大值
*/
int[] arr = null;
int max = 0;
try {
max = getMax(arr);
} catch (NullPointerException e) {
System.out.println("空指针异常");
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("索引越界异常");
}
System.out.println(max);
}
public static int getMax(int[] arr)/* throws NullPointerException,ArrayIndexOutOfBoundsException*/{
if(arr == null){
//手动创建一个异常对象,并把这个异常交给方法的调用者处理
//此时方法就会结束,下面的代码不会再执行了
throw new NullPointerException();
}
if(arr.length == 0){
//手动创建一个异常对象,并把这个异常交给方法的调用者处理
//此时方法就会结束,下面的代码不会再执行了
throw new ArrayIndexOutOfBoundsException();
}
System.out.println("看看我执行了吗?");
int max = arr[0];
for (int i = 1; i < arr.length; i++) {
if(arr[i] > max){
max = arr[i];
}
}
return max;
}
}
二、文件File
FIle概述和构造方法
1.File对象表示路径,可以是文件、也可以是文件夹。这个路径可以是存在的,也可以是不存在的
2.绝对路径带盘符,相对路径不带盘符,默认到当前项目下去找
public class FileDemo1 {
public static void main(String[] args) {
/*
public File(String pathname) 根据文件路径创建文件对象
public File(String parent, String child) 根据父路径名字符串和子路径名字符串创建文件对象
public File(File parent, String child) 根据父路径对应文件对象和子路径名字符串创建文件对象
C:\Users\alienware\Desktop
\:转义字符
*/
//1.根据字符串表示的路径,变成File对象
String str = "C:\\Users\\alienware\\Desktop\\a.txt";
File f1 = new File(str);
System.out.println(f1);//C:\Users\alienware\Desktop\a.txt
//2.父级路径:C:\Users\alienware\Desktop
//子级路径:a.txt
String parent = "C:\\Users\\alienware\\Desktop";
String child = "a.txt";
File f2 = new File(parent,child);
System.out.println(f2);//C:\Users\alienware\Desktop\a.txt
File f3 = new File(parent + "\\" + child);
System.out.println(f3);//C:\Users\alienware\Desktop\a.txt
//3.把一个File表示的路径和String表示路径进行拼接
File parent2 = new File("C:\\Users\\alienware\\Desktop");
String child2 = "a.txt";
File f4 = new File(parent2,child2);
System.out.println(f4);//C:\Users\alienware\Desktop\a.txt
}
}
File的成员方法
public class FileDemo2 {
public static void main(String[] args) throws IOException{
/*
public boolean isDirectory() 判断此路径名表示的File是否为文件夹
public boolean isFile() 判断此路径名表示的File是否为文件
public boolean exists() 判断此路径名表示的File是否存在
*/
//1.对一个文件的路径进行判断
File f1 = new File("D:\\aaa\\a.txt");
System.out.println(f1.isDirectory());//false
System.out.println(f1.isFile());//true
System.out.println(f1.exists());//true
System.out.println("--------------------------------------");
//2.对一个文件夹的路径进行判断
File f2 = new File("D:\\aaa\\bbb");
System.out.println(f2.isDirectory());//true
System.out.println(f2.isFile());//false
System.out.println(f2.exists());//true
System.out.println("--------------------------------------");
//3.对一个不存在的路径进行判断
File f3 = new File("D:\\aaa\\c.txt");
System.out.println(f3.isDirectory());//false
System.out.println(f3.isFile());//false
System.out.println(f3.exists());//false
//---------------------------------------------------------------------
/*
public long length() 返回文件的大小(字节数量)
public String getAbsolutePath() 返回文件的绝对路径
public String getPath() 返回定义文件时使用的路径
public String getName() 返回文件的名称,带后缀
public long lastModified() 返回文件的最后修改时间(时间毫秒值)
*/
//1.length 返回文件的大小(字节数量)
//细节1:这个方法只能获取文件的大小,单位是字节
//如果单位我们要是M,G,可以不断的除以1024
//细节2:这个方法无法获取文件夹的大小
//如果我们要获取一个文件夹的大小,需要把这个文件夹里面所有的文件大小都累加在一起。
File f1 = new File("D:\\aaa\\a.txt");
long len = f1.length();
System.out.println(len);//12
File f2 = new File("D:\\aaa\\bbb");
long len2 = f2.length();
System.out.println(len2);//0
System.out.println("====================================");
//2.getAbsolutePath 返回文件的绝对路径
File f3 = new File("D:\\aaa\\a.txt");
String path1 = f3.getAbsolutePath();
System.out.println(path1);
File f4 = new File("myFile\\a.txt");
String path2 = f4.getAbsolutePath();
System.out.println(path2);
System.out.println("====================================");
//3.getPath 返回定义文件时使用的路径
File f5 = new File("D:\\aaa\\a.txt");
String path3 = f5.getPath();
System.out.println(path3);//D:\aaa\a.txt
File f6 = new File("myFile\\a.txt");
String path4 = f6.getPath();
System.out.println(path4);//myFile\a.txt
System.out.println("====================================");
//4.getName 获取名字
//细节1:
//a.txt:
// a 文件名
// txt 后缀名、扩展名
//细节2:
//文件夹:返回的就是文件夹的名字
File f7 = new File("D:\\aaa\\a.txt");
String name1 = f7.getName();
System.out.println(name1);
File f8 = new File("D:\\aaa\\bbb");
String name2 = f8.getName();
System.out.println(name2);//bbb
System.out.println("====================================");
//5.lastModified 返回文件的最后修改时间(时间毫秒值)
File f9 = new File("D:\\aaa\\a.txt");
long time = f9.lastModified();
System.out.println(time);//1667380952425
//---------------------------------------------------------------------
/*
public boolean createNewFile() 创建一个新的空的文件
public boolean mkdir() 创建单级文件夹
public boolean mkdirs() 创建多级文件夹
public boolean delete() 删除文件、空文件夹
*/
//1.createNewFile 创建一个新的空的文件
//细节1:如果当前路径表示的文件是不存在的,则创建成功,方法返回true
// 如果当前路径表示的文件是存在的,则创建失败,方法返回false
//细节2:如果父级路径是不存在的,那么方法会有异常IOException
//细节3:createNewFile方法创建的一定是文件,如果路径中不包含后缀名,则创建一个没有后缀的文件
/*File f1 = new File("D:\\aaa\\ddd");
boolean b = f1.createNewFile();
System.out.println(b);//true*/
//2.mkdir make Directory,文件夹(目录)
//细节1:windows当中路径是唯一的,如果当前路径已经存在,则创建失败,返回false
//细节2:mkdir方法只能创建单级文件夹,无法创建多级文件夹。
/* File f2 = new File("D:\\aaa\\aaa\\bbb\\ccc");
boolean b = f2.mkdir();
System.out.println(b);*/
//3.mkdirs 创建多级文件夹
//细节:既可以创建单级的,又可以创建多级的文件夹
File f3 = new File("D:\\aaa\\ggg");
boolean b = f3.mkdirs();
System.out.println(b);//true
//---------------------------------------------------------------------
/*
public boolean delete() 删除文件、空文件夹
细节:
如果删除的是文件,则直接删除,不走回收站。
如果删除的是空文件夹,则直接删除,不走回收站
如果删除的是有内容的文件夹,则删除失败
*/
//1.创建File对象
File f1 = new File("D:\\aaa\\eee");
//2.删除
boolean b = f1.delete();
System.out.println(b);
//public File[] listFiles() 获取当前该路径下所有内容
//1.创建File对象
File f = new File("D:\\aaa");
//2.listFiles方法
//作用:获取aaa文件夹里面的所有内容,把所有的内容放到数组中返回
File[] files = f.listFiles();
for (File file : files) {
//file依次表示aaa文件夹里面的每一个文件或者文件夹
System.out.println(file);
}
//---------------------------------------------------------------------
/*
public static File[] listRoots() 列出可用的文件系统根
public String[] list() 获取当前该路径下所有内容
public String[] list(FilenameFilter filter) 利用文件名过滤器获取当前该路径下所有内容
(掌握)public File[] listFiles() 获取当前该路径下所有内容
public File[] listFiles(FileFilter filter) 利用文件名过滤器获取当前该路径下所有内容
public File[] listFiles(FilenameFilter filter) 利用文件名过滤器获取当前该路径下所有内容
*/
/* //1.listRoots 获取系统中所有的盘符
File[] arr = File.listRoots();
System.out.println(Arrays.toString(arr));
//2.list() 获取当前该路径下所有内容(仅仅能获取名字)
File f1 = new File("D:\\aaa");
String[] arr2 = f1.list();
for (String s : arr2) {
System.out.println(s);
}*/
//3.list(FilenameFilter filter) 利用文件名过滤器获取当前该路径下所有内容
//需求:我现在要获取D:\\aaa文件夹里面所有的txt文件
File f2 = new File("D:\\aaa");
//accept方法的形参,依次表示aaa文件夹里面每一个文件或者文件夹的路径
//参数一:父级路径
//参数二:子级路径
//返回值:如果返回值为true,就表示当前路径保留
// 如果返回值为false,就表示当前路径舍弃不要
String[] arr3 = f2.list(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
File src = new File(dir,name);
return src.isFile() && name.endsWith(".txt");
}
});
System.out.println(Arrays.toString(arr3));
}
}
//---------------------------------------------------------------------
/*
(掌握)public File[] listFiles() 获取当前该路径下所有内容
public File[] listFiles(FileFilter filter) 利用文件名过滤器获取当前该路径下所有内容
public File[] listFiles(FilenameFilter filter) 利用文件名过滤器获取当前该路径下所有内容
*/
//1.创建File对象
File f = new File("D:\\aaa");
//2.需求:打印里面所有的txt文件
File[] arr = f.listFiles();
for (File file : arr) {
//file依次表示aaa文件夹里面每一个文件或者文件夹的路径
if(file.isFile() && file.getName().endsWith(".txt")){
System.out.println(file);
}
}
//---------------------------------------------------------------------
//创建File对象
File f = new File("D:\\aaa");
//调用listFiles(FileFilter filter)
File[] arr1 = f.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
return pathname.isFile() && pathname.getName().endsWith(".txt");
}
});
//调用listFiles(FilenameFilter filter)
File[] arr2 = f.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
File src = new File(dir, name);
return src.isFile() && name.endsWith(".txt");
}
});
System.out.println(Arrays.toString(arr2));
三、I/O流(字节流、字符流)
I/O流是指存储和读取数据的解决方案:I表示input,O表示output。
I/O流用于读写数据,如本地文件、网络等。
输出流是从程序到文件,输入流是从文件到程序
I/O流分为字节流和字符流。
字节流:可以操作所有类型的文件
字符流:只能操作纯文本文件(用windows系统自带的记事本打开并且能读懂的文件,如txt文件,md文件,xml文件,lrc文件等)
字节输出流
字节输出流的基本用法
public class ByteStreamDemo1 {
public static void main(String[] args) throws IOException {
/*
* 演示:字节输出流FileOutputStream
* 实现需求:写出一段文字到本地文件中。(暂时不写中文)
*
* 实现步骤:
* 创建对象
* 写出数据
* 释放资源
* */
//1.创建对象
//写出 输出流 OutputStream
//本地文件 File
FileOutputStream fos = new FileOutputStream("myio\\a.txt");
//2.写出数据
fos.write(97);
//3.释放资源
fos.close();
}
}
字节输出流的细节
public class ByteStreamDemo2 {
public static void main(String[] args) throws IOException {
/*
字节输出流的细节:
1.创建字节输出流对象
细节1:参数是字符串表示的路径或者是File对象都是可以的
细节2:如果文件不存在会创建一个新的文件,但是要保证父级路径是存在的。
细节3:如果文件已经存在,则会清空文件
2.写数据
细节:write方法的参数是整数,但是实际上写到本地文件中的是整数在ASCII上对应的字符
‘9’
‘7’
3.释放资源
每次使用完流之后都要释放资源
*/
//1.创建对象
FileOutputStream fos = new FileOutputStream("myio\\a.txt");
//2.写出数据
fos.write(57);
fos.write(55);
//3.释放资源
fos.close();
while(true){}
}
}
字节输出流写出数据三种方式
public class ByteStreamDemo3 {
public static void main(String[] args) throws IOException {
/*
void write(int b) 一次写一个字节数据
void write(byte[] b) 一次写一个字节数组数据
void write(byte[] b, int off, int len) 一次写一个字节数组的部分数据
参数一:
数组
参数二:
起始索引 0
参数三:
个数 3
*/
//1.创建对象
FileOutputStream fos = new FileOutputStream("myio\\a.txt");
//2.写出数据
//fos.write(97); // a
//fos.write(98); // b
byte[] bytes = {97, 98, 99, 100, 101};
/* fos.write(bytes);*/
fos.write(bytes,1,2);// b c
//3.释放资源
fos.close();
}
}
换行和续写
public class ByteStreamDemo4 {
public static void main(String[] args) throws IOException {
/*
换行写:
再次写出一个换行符就可以了
windows: \r\n
Linux: \n
Mac: \r
细节:
在windows操作系统当中,java对回车换行进行了优化。
虽然完整的是\r\n,但是我们写其中一个\r或者\n,
java也可以实现换行,因为java在底层会补全。
建议:
不要省略,还是写全了。
续写:
如果想要续写,打开续写开关即可
开关位置:创建对象的第二个参数
默认false:表示关闭续写,此时创建对象会清空文件
手动传递true:表示打开续写,此时创建对象不会清空文件
*/
//1.创建对象
FileOutputStream fos = new FileOutputStream("myio\\a.txt",true);
//2.写出数据
String str = "kankelaoyezuishuai";
byte[] bytes1 = str.getBytes();
fos.write(bytes1);
//再次写出一个换行符就可以了
String wrap = "\r\n";
byte[] bytes2 = wrap.getBytes();
fos.write(bytes2);
String str2 = "666";
byte[] bytes3 = str2.getBytes();
fos.write(bytes3);
//3.释放资源
fos.close();
}
}
字节输入流
字节输入流的基本用法
public class ByteStreamDemo1 {
public static void main(String[] args) throws IOException {
/*
* 演示:字节输入流FileInputStream
* 实现需求:读取文件中的数据。(暂时不写中文)
*
* 实现步骤:
* 创建对象
* 读取数据
* 释放资源
* */
//1.创建对象
FileInputStream fis = new FileInputStream("myio\\a.txt");
//2.读取数据
int b1 = fis.read();
System.out.println((char)b1);
int b2 = fis.read();
System.out.println((char)b2);
int b3 = fis.read();
System.out.println((char)b3);
int b4 = fis.read();
System.out.println((char)b4);
int b5 = fis.read();
System.out.println((char)b5);
int b6 = fis.read();
System.out.println(b6);//-1
//3.释放资源
fis.close();
}
}
字节输入流的细节
public class ByteStreamDemo2 {
public static void main(String[] args) throws IOException {
/*
字节输入流的细节:
1.创建字节输入流对象
细节1:如果文件不存在,就直接报错。
Java为什么会这么设计呢?
输出流:不存在,创建
把数据写到文件当中
输入流:不存在,而是报错呢?
因为创建出来的文件是没有数据的,没有任何意义。
所以Java就没有设计这种无意义的逻辑,文件不存在直接报错。
程序中最重要的是:数据。
2.写数据
细节1:一次读一个字节,读出来的是数据在ASCII上对应的数字
细节2:读到文件末尾了,read方法返回-1。
3.释放资源
细节:每次使用完流之后都要释放资源
*/
//1.创建对象
FileInputStream fis = new FileInputStream("myio\\b.txt");
//2.读取数据
int b1 = fis.read();
System.out.println((char)b1);
//3.释放资源
fis.close();
}
}
字节输入流循环读取
public class ByteStreamDemo3 {
public static void main(String[] args) throws IOException {
/*
字节输入流循环读取
*/
//1.创建对象
FileInputStream fis = new FileInputStream("myio\\a.txt");
//2.循环读取
int b;
while ((b = fis.read()) != -1) {
System.out.println((char) b);
}
//3.释放资源
fis.close();
/* *//*
*
* read :表示读取数据,而且是读取一个数据就移动一次指针
*
* *//*
FileInputStream fis = new FileInputStream("myio\\a.txt");
//2.循环读取
while ((fis.read()) != -1) {
System.out.println(fis.read());//98 100 -1
}
//3.释放资源
fis.close();*/
}
}
文件拷贝基本代码
public class ByteStreamDemo4 {
public static void main(String[] args) throws IOException {
/*
* 练习:
* 文件拷贝
* 把D:\movie.mp4拷贝到当前模块下。
*
* 注意:
* 选择一个比较小的文件,不要太大。
*
*
*
* 课堂练习:
* 要求统计一下拷贝时间,单位毫秒
* */
long start = System.currentTimeMillis();
//1.创建对象
FileInputStream fis = new FileInputStream("D:\\itheima\\movie.mp4");
FileOutputStream fos = new FileOutputStream("myio\\copy.mp4");
//2.拷贝
//核心思想:边读边写
int b;
while((b = fis.read()) != -1){
fos.write(b);
}
//3.释放资源
//规则:先开的最后关闭
fos.close();
fis.close();
long end = System.currentTimeMillis();
System.out.println(end - start);
}
}
文件拷贝弊端和解决方案
将数据读到字节数组byte[]中,将byte[]转成字符串一定要带着后面的0,len两个参数。
public class ByteStreamDemo5 {
public static void main(String[] args) throws IOException {
/*
public int read(byte[] buffer) 一次读一个字节数组数据
*/
//1.创建对象
FileInputStream fis = new FileInputStream("myio\\a.txt");
//2.读取数据
byte[] bytes = new byte[2];
//一次读取多个字节数据,具体读多少,跟数组的长度有关
//返回值:本次读取到了多少个字节数据
int len1 = fis.read(bytes);
System.out.println(len1);//2
String str1 = new String(bytes,0,len1);
System.out.println(str1);
int len2 = fis.read(bytes);
System.out.println(len2);//2
String str2 = new String(bytes,0,len2);
System.out.println(str2);
int len3 = fis.read(bytes);
System.out.println(len3);// 1
String str3 = new String(bytes,0,len3);
System.out.println(str3);// ed
//3.释放资源
fis.close();
}
}
文件拷贝改写
public class ByteStreamDemo6 {
public static void main(String[] args) throws IOException {
/*
* 练习:
* 文件拷贝
* 把D:\movie.mp4 (16.8 MB) 拷贝到当前模块下。
*
* */
long start = System.currentTimeMillis();
//1.创建对象
FileInputStream fis = new FileInputStream("D:\\movie.mp4");
FileOutputStream fos = new FileOutputStream("myio\\copy.mp4");
//2.拷贝
int len;
byte[] bytes = new byte[1024 * 1024 * 5];
while((len = fis.read(bytes)) != -1){
fos.write(bytes,0,len);
}
//3.释放资源
fos.close();
fis.close();
long end = System.currentTimeMillis();
System.out.println(end - start);
}
}
I/O流不同版本捕获异常的方式
基本做法
public class ByteStreamDemo7 {
public static void main(String[] args) {
/*
*
* 利用try...catch...finally捕获拷贝文件中代码出现的异常
*
*
* */
//1.创建对象
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream("D:\\movie.mp4");
fos = new FileOutputStream("myio\\copy.mp4");
//2.拷贝
int len;
byte[] bytes = new byte[1024 * 1024 * 5];
while((len = fis.read(bytes)) != -1){
fos.write(bytes,0,len);
}
} catch (IOException e) {
//e.printStackTrace();
} finally {
//3.释放资源
if(fos != null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(fis != null){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
JDK7方案
public class ByteStreamDemo8 {
public static void main(String[] args) {
/*
*
* JDK7:IO流中捕获异常的写法
*
* try后面的小括号中写创建对象的代码,
* 注意:只有实现了AutoCloseable接口的类,才能在小括号中创建对象。
* try(){
*
* }catch(){
*
* }
*
* */
try (FileInputStream fis = new FileInputStream("D:\\movie.mp4");
FileOutputStream fos = new FileOutputStream("myio\\copy.mp4")) {
//2.拷贝
int len;
byte[] bytes = new byte[1024 * 1024 * 5];
while ((len = fis.read(bytes)) != -1) {
fos.write(bytes, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
JDK9方案
public class ByteStreamDemo9 {
public static void main(String[] args) throws FileNotFoundException {
/*
*
* JDK9:IO流中捕获异常的写法
*
*
* */
FileInputStream fis = new FileInputStream("D:\\movie.mp4");
FileOutputStream fos = new FileOutputStream("myio\\copy.mp4");
try (fis;fos) {
//2.拷贝
int len;
byte[] bytes = new byte[1024 * 1024 * 5];
while ((len = fis.read(bytes)) != -1) {
fos.write(bytes, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
字符集
在计算机中,任何数据都是以二进制的形式来存储
ASCII字符集中,一个英文占一个字节
GBK字符集中,一个英文占一个字节,二进制第一位是0,一个中文占两个字节,二进制高位字节的第一位是1
Unicode字符集的UTF-8编码格式,一个英文占一个字节,二进制第一位是0,一个中文占三个字节,二进制第一位是1。
有乱码的原因
- 读取数据时未读完整个汉字
- 编码和解码的方式不统一
如何不产生乱码
- 不要用字节流读取文本文件
- 编码解码时用一个码表,同一个编码方式
编码和解码
public class CharSetDemo3 {
public static void main(String[] args) throws UnsupportedEncodingException {
/*
Java中编码的方法
public byte[] getBytes() 使用默认方式进行编码
public byte[] getBytes(String charsetName) 使用指定方式进行编码
Java中解码的方法
String(byte[] bytes) 使用默认方式进行解码
String(byte[] bytes, String charsetName) 使用指定方式进行解码
*/
//1.编码
String str = "ai你哟";
byte[] bytes1 = str.getBytes();
System.out.println(Arrays.toString(bytes1));
byte[] bytes2 = str.getBytes("GBK");
System.out.println(Arrays.toString(bytes2));
//2.解码
String str2 = new String(bytes1);
System.out.println(str2);
String str3 = new String(bytes1,"GBK");
System.out.println(str3);
}
}
字符输入流
字符流=字节流+字符集
输入流:一次读一个字节,遇到中文时,一个读多个字节
输出流:底层会把数据按照指定的编码方式进行编码,变成字节再写到文件中
使用场景为纯文本文件读写操作
无参read方法讲解
public class CharStreamDemo1 {
public static void main(String[] args) throws IOException {
/*
第一步:创建对象
public FileReader(File file) 创建字符输入流关联本地文件
public FileReader(String pathname) 创建字符输入流关联本地文件
第二步:读取数据
public int read() 读取数据,读到末尾返回-1
public int read(char[] buffer) 读取多个数据,读到末尾返回-1
第三步:释放资源
public void close() 释放资源/关流
*/
//1.创建对象并关联本地文件
FileReader fr = new FileReader("myio\\a.txt");
//2.读取数据 read()
//字符流的底层也是字节流,默认也是一个字节一个字节的读取的。
//如果遇到中文就会一次读取多个,GBK一次读两个字节,UTF-8一次读三个字节
//read()细节:
//1.read():默认也是一个字节一个字节的读取的,如果遇到中文就会一次读取多个
//2.在读取之后,方法的底层还会进行解码并转成十进制。
// 最终把这个十进制作为返回值
// 这个十进制的数据也表示在字符集上的数字
// 英文:文件里面二进制数据 0110 0001
// read方法进行读取,解码并转成十进制97
// 中文:文件里面的二进制数据 11100110 10110001 10001001
// read方法进行读取,解码并转成十进制27721
// 我想看到中文汉字,就是把这些十进制数据,再进行强转就可以了
int ch;
while((ch = fr.read()) != -1){
System.out.print((char)ch);
}
//3.释放资源
fr.close();
}
}
有参read方法讲解
public class CharStreamDemo2 {
public static void main(String[] args) throws IOException {
/*
第一步:创建对象
public FileReader(File file) 创建字符输入流关联本地文件
public FileReader(String pathname) 创建字符输入流关联本地文件
第二步:读取数据
public int read() 读取数据,读到末尾返回-1
public int read(char[] buffer) 读取多个数据,读到末尾返回-1
第三步:释放资源
public void close() 释放资源/关流
*/
//1.创建对象
FileReader fr = new FileReader("myio\\a.txt");
//2.读取数据
char[] chars = new char[2];
int len;
//read(chars):读取数据,解码,强转三步合并了,把强转之后的字符放到数组当中
//空参的read + 强转类型转换
while((len = fr.read(chars)) != -1){
//把数组中的数据变成字符串再进行打印
System.out.print(new String(chars,0,len));
}
//3.释放资源
fr.close();
}
}