【JavaSE基础】10-IO流

【JavaSE基础】10-IO流,第1张

文章目录
    • 异常
      • 异常和错误
        • Throwable
        • Error
        • Exception
          • 未检查异常
          • 已检查异常
          • 异常处理
            • JVM 默认的处理方法
            • 程序员自定义处理
          • 捕获异常
            • 单个异常
            • 多个异常
            • 异常处理原理
            • 抛出\声明异常
            • finally
            • 面试题
    • File
      • 0:Class Summary
      • 1:Constractors Summary
      • 2:Methods Summary
      • FileUtils.class
    • I/O Stream
      • 0:Class Summary
      • 1:Constractors Summary
      • 2:Methods Summary
      • 3:如何实现
        • 输出流
        • 输入流
        • 创建流对象
        • 实现换行
        • 中文字符
        • 乱码问题
        • 编码表
        • String 类中编码相关的方法
      • Legacy class - Properties
        • 0:Class Summary
        • 1:Constractors Summary
        • 2:Methods Summary
      • IOUtils.class
      • 面试题
    • 小总结
      • 子类不可以比父类更坏
      • 如何访问 src 目录下的资源文件
        • 传统的资源加载方式
        • 类加载器加载资源

异常 异常和错误 Throwable
  1. Throwable 是所有Error和Exception的超类。
  2. 只有Throwable的直接或间接子类对象才可以被 JAVA 虚拟机或 JAVA throw 抛出。
  3. 同理,也必须是这样的对象才可以作为 catch 的参数。

实用方法:

  1. public String getMessage() 返回 throwable 的详细消息
  2. public String getLocalizedMessage() 返回throwable的本地化描述。子类重写:生成特定的语言环境的消息;子类不重写:默认调用的是 getMessage()
  3. public String toString() 返回 throwablb的简短描述。返回的内容组成:对象的name + ": "[冒号和空格] + 对象的 getLocalizedMessage(),如若 getLocalizedMessage() 返回 null,则只返回 对象的name
  4. public void printStackTrace() 将此 throwable 及其 堆栈追踪信息 输出到标准的错误流。[System.err]。返回的内容组成:第一行:对象的toString() ,之后所有行是 堆栈追踪信息 []fillStackTrace() 记录的数据]
  5. public void printStackTrace(PrintStream s) 输出到指定的字节输出流
  6. public void printStackTrace(PrintStream s) 输出到指定的字符输出流
Error
  1. 指的是合理程序不应该捕获的严重问题。
  2. Error进行描述的严重问题,一般不编写代码处理,而是修正,由 JAVA 虚拟机 抛出。
Exception
  1. 指的是合理程序应该捕获的条件
  2. 分类:RuntimeException以及其子类,运行时异常,编译可通过,不需要显示处理,也可以和编译时异常一同处理,否则留下安全隐患。例如:ArithmeticException ,在遇到 /0 除以 0。

P.S:如果出现了RuntimeException 就一定是你的问题!

  1. 分类:非RuntimeException,编译异常,必须进行处理,否则编译不通过。
未检查异常

Java 语言规范中,将派生于 Error 或 RuntimeEception 的所有异常,称为 未检查[Unchecked] 异常 【所说的未检查 大概是 不会被编译器检查,也不会被 throw 抛出,最终是遗留在运行时的 隐患 】

方法定义时,应尽量修正,或者遇到重大的错误时应该 去抛出。

已检查异常

除了未检查异常 之外的所有异常称之为 已检查[Checked] 异常 。如:编译时异常

方法定义时,不仅仅要声明返回值,也要给出会抛出的异常。应该在首部抛出所有可能抛出的 已检查异常。

异常处理 JVM 默认的处理方法

main主方法收到异常,抛给 JAVA 虚拟机进行处理,JVM对其进行描述,封装成对应的异常,当指定的异常发生时,JVM 将其包装成对应异常的 对象。并抛给 调用者-- main主方法。

程序员自定义处理

通过 两种方式进行自定义处理:

  1. try...catch...finally 推荐单独使用try...catchtry...finally
  2. throws 在方法声明中将异常抛出,留给调用者进行处理。

P.S:切记不可在在不必要的 使用,在不必要的方法中使用 try...catch,将会导致异常被吃掉,正确的做法:将异常上抛给调用者

捕获异常 单个异常
try{
	// code java
} catch(ExceptionType e){
	// handle for this type
}
  1. try中任何代码抛出了在catch中说明的异常类。try中异常点(抛出异常的代码语句)之后的代码将不会被执行,而转去执行 catch 块中代码。
  2. try中没有抛出任何的异常,catch将不会执行。
  3. try中出现异常,catch中没有对应捕获,由 JVM 抛出异常,并终止程序的运行。
多个异常

1、 e1、e2、e3 等平级关系时,可直接定义,顺序无关

try{
	// code java
} catch(ExecptionType e1) {
	// handle for this type
} catch(ExecptionType e2) {
	// handle for this type
} catch(ExecptionType e3) {
	// handle for this type
}

JDK 7 特性 (平级情况)

try{
	// code java
} catch(ExceptionType | ... |ExceptionType e ){
	//handle for this type
}

简单 高效,在字节码文件总仅仅存在一个对应公共的 catch子句的代码块。注:捕获多个异常时,e 隐含为 final 变量

2、e1、e2、e3 存在着子父级关系时,catch 先捕获子异常,再捕获父异常,防止:父异常吃掉子异常。

异常处理原理

try 里面抛出的异常,JVM会生成一个 描述问题的异常对象,然后将其抛出,由catch进行匹配,如果JVM的异常对象是某个 catch 后的异常实例,则执行catch后的处理语句块,否则就继续上抛给方法,或main主方法直接抛出。终止程序运行。

抛出\声明异常

throws ExceptionType,ExceptionType,ExceptionType... 抛出多个异常时,使用“,”分割。
不可声明异常:未检查异常
1、 java内部错误
2、RuntimeException及其子类

如何抛出一个异常?
1、找到一个合适的异常类
2、创建这个类的一个对象
3、将对象抛出

/** 运行时异常 */
if(target == null){
	throw new NullPointerException([);
}

/** 非运行时异常[有时称为编译时异常] */
// 必须在方法上进行 throws FileNotFoundException 声明
if(!file.exists){
	throw new FileNouFoundException();
}

注意:
1、运行时异常: throw new ExceptionType(); 即可
2、非运行时异常: throw new ExceptionType(); 并且必须同时在 方法的声明中 throws ExecptionType
总之: 一个方法的生命中应该抛出所有的已检查异常,而未检查异常要么不可控制,要么就应该尽力修正,避免发生。若没有声明已检查异常,则编译错误。

finally

1、 finally 语句块一定会执行,无论异常捕获与否
2、用于释放资源的时候最为常见【JDBC和IO流
3、如果执行 finally 之前 JVM退出,则不执行 finally语句块。例如:System.exit(0);finally之前被执行

清理资源也可能有异常

if(target!=null){
	try{
		terget.close();
	} catch(IOException e) {
		// do nothing!  静默关闭
	}
}

警告: try…finally 中 若 try 和 finally 中都存在着 return 语句,则以 finally 中定义的为准。(覆盖了 try 中的return语句。)

JDK 7 的新特性

JDK 7之前的垃圾回收太过于繁琐。因此在JDK 7之后提供了一种新的资源释放方法:try...with...resource 其实际是一种语法糖。

class Demo7{
	public static void main(String[] args){
		int a = 1;
		int b = 0;
		String str = null;
		try{
			int c = a/b;
			boolean isEquals = str.equals("");
		} catch(NullPointerException | ArthimentException e){
			// handle for this type
		}
	}
}
面试题

throw 和 throws 的区别?
解答:

  1. 所在位置不同
  2. 处理者不同
  3. 对象不同
  4. 个数不同
  5. 发生可能行不同
  • throw:定义在方法体,针对只可以是一个异常类的对象,表示抛出异常,有方法体语句进行处理,throw 是抛出异常,异常确实发生。
  • throws:定义在方法声明上,可以针对多个异常类,中间是用 “,” 隔开,声明抛出的异常,由方法调用者处理,throws 表示的是异常发生的一种可能性,并不一定发生。
  • 配合使用:当需要抛出 非运行时异常【编译时异常】时,在方法中需要 throw 具体的异常类对象,方法声明上也需要进行 声明 throws 具体异常类名。

2、final、fianally、finalize三者的区别?
解答:
1、final 最终的意思,可修饰变量、方法、类。 修饰类,则不可以被继承;修饰方法,则不可以被重写;修饰变量,则值不可以更改【例如数组,是指数组的地址值不可修改,但是数组的项可以被修改】
2、 finally 异常处理的一部分,用于释放资源。一般来说,其中的代码一定会执行,除非子这之前JVM退出。例如:Sysem.exit(0)
3、finalize Object类中的方法,用于垃圾回收(当垃圾回收器确定不存在对该对象的有效引用时自动调用)

3、catch 中存在 return 语句,finally中的语句是否执行,如果是,在catch之前还是之后?
解答:
finally 会执行,在之前。 准确的说:是在中间执行。但finally 对 return之后的变量的值的改变不会生效。因此时,return 后面实际上是一个常量。

class Demo{
	public static void main(String[] args){
		System.out.println(heh());
	}

	public static int heh(){
		int c = 12;		
		try{
			c = 1/0;
		} catch(Exception e){
			return c;
		} finally {
			c = 100
		}
		return c;
	}
}
// 结果是: 12
public static int heh()
{
	int i = 12;
	i = 1 / 0;
	i = 100;
	break MISSING_BLOCK_LABEL_27;
	Exception exception;
	exception;
	int j = i;
	i = 100;
	return j;
	Exception exception1;
	exception1;
	i = 100;
	throw exception1;
	return i;
}
File 0:Class Summary

public class File extends Object implements Comparanle

File 是文件和文件夹的抽象表示。(目录路径名)
File 类的实例是不可变的,一旦创建,File对象的抽象路径名将永不可变。

1:Constractors Summary

底层一致的三个构造:
1、public File(String pathname)
2、public File(File parent,String child)
3、public File(String parent,String child)

2:Methods Summary

1、创建功能

  • boolean createNewFile() 成功创建文件返回 true,已存在或失败 返回false
  • boolean mkdir() 创建单级路径[目录],例如:D:\a\b\c,必须a、b已经存在,成功创建 c ,则返回 true,否则返回 false
  • boolean mkdirs() 创建多级目录,例如:D:\a\b\c,同时创建路径中所有不存在的目录,返回true,否则返回false

2、 删除功能

  • boolean delete() 删除文件或文件夹,不走回收站,相当于是 Shift + Delete。如果是文件夹,则必须为空,否则false

3、重命名功能

  • boolean renameTo(File dest) 路径名相同,重命名;路径名不同,剪切并重命名。

4、判断功能

  • boolean isFile() 判断是否为文件
  • boolean isDirectory() 判断是否为文件夹
  • boolean exists() 判断是否存在

5、获取功能

  • public String getAbsolutePath() 获取绝对路径
  • public String getPath() 获取创建时使用的全路径
  • public String getName() 获取文件名(文件包含扩展名)
  • public String lastModified 最后一次修改的时间

6、高级获取功能

  • public static File[] listRoots() 获取所有可用的盘符

  • public String[] list() 获取路径下所有的文件和文件夹的名称

  • public String[] list(FilenameFilter filter) 获取路径下指定文件名的名称

  • public File[] listFiles()获取路径下所有的文件和文件名的File 实例

  • public String[] list(FilenameFilter filter)

  • public String[] list(FileFilter filter)

FilenameFilter 文件名过滤器

public interface FilenameFilter{
	public abstract boolean accept(File dir,String name);
	// dir 被找到的文件所在路径
	// name 被找到文件的名称
	// 当且仅当该名称在指定的文件列表时,返回 true
}

FileFilter 文件名过滤器

public interface FilenameFilter{
	public abstract boolean accept(File pathname);
}

案例1: 获取指定文件夹下的所有的.jpg文件

File file = new File("D://photo");
String[] subfiles = file.list(new FilenameFilter(){
	@Override
	public abstract accept(File dir,String name){
		return (new File(dir,file).isFile())&&(name.endsWith(".jpg"));
	}
});

案例2: 批量修改文件名

递归思想 重新复习一下该思想。

案例3: 获取指定目录及其各级子目录下的.ppt文件

// FileUtils.list("D://bb","ppt");
public static String[] list(File tar, String extension) {
	if (tar == null || extension == null) {
		throw new NullPointerException("Most not be null");
	}
	if (!tar.exists()) {
		throw new IllegalArgumentException("Dir should be directory");
	}
	if (extension.length() == 0) {
		throw new IllegalArgumentException("");
	}
	if (tar.isFile()) {
		if (tar.getName().endsWith(extension)) {
			return new String[] { tar.getPath() };
		} else {
			return null;
		}
	}
	return traverse(tar, extension, new StringBuilder());
}

private static String[] traverse(File dir, String extension,
		StringBuilder sb) {
	File[] files = dir.listFiles();
	for (File subfile : files) {
		if (subfile.isFile()) {
			// 记得忽略 后缀名的大小写
			if (subfile.getName().toLowerCase()
					.endsWith(extension.toLowerCase())) {
				sb.append(subfile.getPath()).append("<>");
			}
		} else {
			traverse(subfile, extension, sb);
		}
	}
	return sb.toString().split("<>");
}

案例4: 删除指定目录(可能包含子目录)

FileUtils.delete();
FileUtils.class

package com.io.exec;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class FileUtils {

	private static final int DEFAULT_BUFFER_SIZE = 8192;

	public static void delete(File target) throws FileNotFoundException {
		if (target == null) {
			throw new NullPointerException("Directory most not be null");
		}
		if (!target.exists()) {
			throw new FileNotFoundException("srcDir cannot be find");
		}
		if (target.isFile()) {
			// 如果是文件的话直接删除,就结束了
			System.out.println("\t" + target.getName() + "    "
					+ target.delete());
			return;
		}
		// 获取目录下的所用 文件、文件夹
		File[] files = target.listFiles();
		for (File subfile : files) {
			if (subfile.isFile()) {
				// 如果是 文件的话,就直接删除
				System.out.println("\t" + subfile.getName() + "    "
						+ subfile.delete());
			} else {
				delete(subfile);
			}
		}
		// 删除空文件夹 for 循环中已经实现了删除所有的文件
		System.out.println(target.getName() + "    " + target.delete());
	}

	public static void copyDir(File srcDir, File destDir) throws IOException {
		copyDir(srcDir, destDir, DEFAULT_BUFFER_SIZE);
	}

	public static void copyDir(File srcDir, File destDir, int bufferSize)
			throws IOException {
		if (srcDir == null || destDir == null) {
			throw new NullPointerException("srcDir most not be null");
		}
		if (!srcDir.exists()) {
			throw new FileNotFoundException("srcDir cannot be find");
		}
		if (!srcDir.isDirectory()) {
			throw new IllegalArgumentException("srcDir should be a directory");
		}
		File[] files = srcDir.listFiles();
		for (File subfile : files) {
			File src = subfile;
			File dest = new File(destDir, subfile.getName());
			if (subfile.isFile()) {
				copyFile(src, dest, bufferSize);
			} else {
				copyDir(src, dest, bufferSize);
			}
		}
	}

	public static void moveAndRename(File srcDir, File destDir, String srcEnds,
			String destEnds) throws IOException {
		moveAndRename(srcDir, destDir, srcEnds, destEnds, DEFAULT_BUFFER_SIZE);
	}

	public static void moveAndRename(File srcDir, File destDir, String srcEnds,
			String destEnds, int bufferSize) throws IOException {
		if (srcDir == null || destDir == null) {
			throw new NullPointerException("srcDir most not be null");
		}
		if (!srcDir.exists()) {
			throw new FileNotFoundException("srcDir cannot be find");
		}
		if (!srcDir.isDirectory()) {
			throw new IllegalArgumentException("srcDir should be a directory");
		}
		File[] files = srcDir.listFiles();
		for (File subfile : files) {
			File src = subfile;
			File dest;
			if (subfile.isFile()) {
				if (subfile.getName().endsWith(srcEnds)) {
					dest = new File(destDir, subfile.getName().replace(srcEnds,
							destEnds));
					copyFile(src, dest, bufferSize);
				}
			} else {
				dest = new File(destDir, subfile.getName());
				copyDir(src, dest, bufferSize);
			}
		}
	}

	public static void copyFile(File srcFile, File destFile) throws IOException {
		copyFile(srcFile, destFile, DEFAULT_BUFFER_SIZE);
	}

	public static void copyFile(File srcFile, File destFile, int bufferSize)
			throws IOException {
		if (srcFile == null || destFile == null) {
			throw new NullPointerException("srcFile most not be null");
		}
		if (!srcFile.exists()) {
			throw new FileNotFoundException("srcFile cannot be find");
		}
		if (!srcFile.isFile()) {
			throw new IllegalArgumentException("srcFile should be a file");
		}
		File destDir = destFile.getParentFile();
		if (!destDir.exists()) {
			destDir.mkdirs();
		}
		InputStream in = null;
		OutputStream out = null;
		try {
			in = new FileInputStream(srcFile);
			out = new FileOutputStream(destFile);
			byte[] bys = new byte[bufferSize];
			int len;
			while ((len = in.read(bys)) >= 0) {
				out.write(bys, 0, len);
			}
		} finally {
			IOUtils.closeQuietly(out);
			IOUtils.closeQuietly(in);
		}
	}

	public static String[] list(File tar, String extension) {
		if (tar == null || extension == null) {
			throw new NullPointerException("Most not be null");
		}
		if (!tar.exists()) {
			throw new IllegalArgumentException("Dir should be directory");
		}
		if (extension.length() == 0) {
			throw new IllegalArgumentException("");
		}
		if (tar.isFile()) {
			if (tar.getName().endsWith(extension)) {
				return new String[] { tar.getPath() };
			} else {
				return null;
			}
		}
		return traverse(tar, extension, new StringBuilder());
	}

	private static String[] traverse(File dir, String extension,
			StringBuilder sb) {
		File[] files = dir.listFiles();
		for (File subfile : files) {
			if (subfile.isFile()) {
				// 记得忽略 后缀名的大小写
				if (subfile.getName().toLowerCase()
						.endsWith(extension.toLowerCase())) {
					sb.append(subfile.getPath()).append("<>");
				}
			} else {
				traverse(subfile, extension, sb);
			}
		}
		return sb.toString().split("<>");
	}
}
I/O Stream 0:Class Summary
  1. IO流用来处理设备间的数据传输:上传和下载
  2. JAVA对数据的 *** 作是通过流实现的
  3. JAVA用于 *** 作流的对象都在 java.io包下

所有具体使用的子类 均派生自此四基类,并且以此为后缀。

1:Constractors Summary

1、构造对象时,强烈推荐使用父类、接口引用指向子类的对象
父类、接口中提供一个无参构造方法,供具体子类初始化的时候使用

  • public InputStream(){}
    public OutputStream(){}
  • InputStream in = new FileInputStream(File file)
    InputStream in = new FileInputStream(String pathname)
  • Reader & Writer、FileReader & FileWriter 同上

2、基本流对应的高效缓冲流,包装了基本流,提供了一个缓冲区

  • BufferedInputStream buffIn = new BufferedInputStream(InputStream in)
    BufferedInputStream buffIn = new BufferedInputStream(InputStream in,int size)
    默认的 缓冲区大小是 8192 字节,可自己指定,推荐使用 1M,但是具体按照自己的电脑性能来指定,本人电脑亲测 128 K 最佳
  • Reader & Writer、FileReader & FileWriter 同上

3、转换流,包装字节流得到字符流,并且可指定编码方式,不指定默认平台编码方式

  • Reader reader = new InputStreamReader(InputStream in)
    Reader reader = new InputStreamReader(InputStream in,String charsetname)
    Reader reader = new InputStreamReader(InputStream in,Charset charset)

案例: 建立高效字符缓冲流的方式,实现文本文件的copy

InputStream in = null;
Reader reader = null;
BufferedReader buffReader = null;

OutputStream out = null;
Writer writer = null;
BufferedWriter = null;

try{
	in = new FileInputStream("D://joian.info");
	reader = new InputStreamReader(in);
	buffReader = newq BufferedReader(reader);

	out = new FileOutputStream("D://Sun.info");
	writer = new OutputStream(out);
	buffWriter = new BufferedWriter(writer);
	
	// Each line while reading
	String line;
	while((line = buffReader.readLine())!=null){
		// handle each line. write...
		buffWriter.write(line);
		buffWriter.newLine();
		buffWriter.flush();
	}
} catch(FileNotFoundException e){
	System.out.println("File not found...:"+e.getMessaqge());
} catch(IOException e){
	System.out.println("Errors while readint file...:"+e.getMessaqge());
} finally {
	// 关闭的原则是:
		// 从输出流到输入流,“无关紧要”
		// 从依赖项到被依赖项顺序
	IOUtils.closeQuietly(buffWriter);
	IOUtils.closeQuietly(writer);
	IOUtils.closeQuietly(out);

	IOUtils.closeQuietly(buffReader);
	IOUtils.closeQuietly(reader);
	IOUtils.closeQuietly(in);
}
2:Methods Summary

读写默认时是按单个字节或单个字符进行的,使用字节数组或字符数组可以指定每次的每次读写的字节或字符的个数,从而减少向底层的命令请求,减少和系统底层的交互,提高了读写效率。

1、读取功能:输入流方法,实现从外部向程序写入

  • 字节流的读方法:InputStream 、FileInputStream、BufferedInputStream
  • 字符流的读方法:Reader、InputStreamReader
  • 高效缓冲字符流的读方法: BufferedReader

2、写入功能:输出流方法,实现从程序向外部写出

  • 字节流的写方法:OutputStream 、FileOutputStream、BufferedOutputStream
  • 字符流的写方法:Writer、OutputStreamWriter
  • 高效缓冲字符流的写方法: BufferedWriter

3、追加功能:进行写出的时候,指定构造器的第二个参数 append 为 true,即可实现追加,默认是 false:清空,写入。

4、关闭功能

  • implements Closeable
    public interface Closeable extends AutoCloseable since JDK 5
    仅仅提供了一个抽象的 void close() throws IOException 方法,供具体子类去实现。如果已经使用该方法实现了关闭的 *** 作,再次进行调用的时候没有任何的效果。IO 流的 *** 作,不论读写都需要在 *** 作的最后执行资源的关闭,释放有关的系统资源,否则会大大降低执行效率,影响安全性。Close主要做两件事情:1.让流对象成为垃圾,可悲俩及回收器回收;2.通知系统释放与流相关的所有资源

案例: 简单实现文件的读写 [使用字节流]

InputStream in = null;
OutputStream out = null;
try{
	in = new FileInputStream("D://Joian.info");
	out = new FileOutputStream("D://Sun.info");
	
	// 建立数组缓冲
	byte[] bys = new byte[128*1024]; // 1024*1024
	int len;
	while((len = in.read(bys))>=0) // 变成的偏执,不想相信 -1
	{
		out.write(bys,0,len);
		// out.write(len);
		// 推荐使用 三个参数的 write 方法
		// 缓冲区最后一次读进去的字符长度为len
		// [len,bys.length-1] 的区间内容是上次的一流内容
		// 已经写入到文件在,在进行写的话,导致重复。
	}
} catch(FileNotFoundException e){
	System.out.println("File not found...:"+e.getMessaqge());
} catch(IOException e){
	System.out.println("Errors while readint file...:"+e.getMessaqge());
} finally {
	// 关闭原则
	IOUtils.closeQuietly(out);
	IOUtils.closeQuietly(in);
}
3:如何实现 输出流

1、创建输出流对象

  • 调用系统功能去创建文件(有,则不创建)
    如果在路径中出现了 不存在的目录,则触发异常 FileNotFoundException
  • 创建 输出流的具体子类(FileOutputStream fos)对象
  • 将 fos 与文件关联

2、调用流方法 write() 实现写入
3、关闭流对象

输入流
  1. 创建输入流
    • 将创建的输入流对象和系统文件进行关联,如果不存在指定文件的话么就直接抛出异常 FileNotFoundException
  2. 调用流方法 read() 实现读取
  3. 关闭流对象
创建流对象

1、创建流对象 指定为 null 初始化

  • 构造器创建流对象时,可能抛出 FileNotFoundException 异常
  • try 子句中的语句可能都不会执行,但是在finally需要对流对象进行关闭 *** 作,就必须进行先进行初始化

2、父类\接口 引用 指向子类对象 – 多态

实现换行
  1. Windows 系统 换行是 “\n”
  2. Linux\Unix 系统 换行是 “\r\n”
  3. Windows 系统 换行是 “\r”
中文字符

中文字符在计算机中占有两个字节长度,可使用字符 char 类型进行存储:GBK 编码 使用2个字节

  • 第一个字节是负数(定)(主导)
  • 第二个字节常见为负数,也可能会正数(变)

编码时,按照不同的编码方式实现的编码字节长度不同:

  • ASCII编码:1个字节
  • GBK编码:2个字节
  • UTF-8编码:3个字节
  • Unicode编码:默认是 UTF-16,4个字节

字符流依赖字节流实现原理:每次读取两个字节长度,按照给定或默认编码方式 转换为 字符。

// 自定义字节流 正确读取中文字符
// 当发现最后读入的字节为负数的时候,再读取一个字节
// 只是简单的实现,可能在不同的编码下会出现问题
乱码问题

使用相同编码进行读写...

编码表

编码表:代码说明表格,帮助用户明确无解释数据和字符代码的含义。由现实世界的字符和对应的数值组成

计算机编码:电脑呢不代表字符和数字的方式。最小单位是 字节 bit ,机器语言的单位是 byte; 1 byte = 8 bits

1、ANSI:由2个字节来表示一个字符的各种汉字延伸编码方式

  • 简体中文 *** 作系统中 ANSI 代表的是 GB2312 | GBK
  • 日为 *** 作系统中 ANSI 代表的是 JISI 编码

2、ASCII:美国信息互换标准代码,表示的范围是 “-128~127”,由8位(单字节)组成,最高位是符号位,其余7为是数值位

3、ISO-8859-1:欧洲码表、拉丁码表,表示的范围是“0~255”,由8位(单字节)组成,均为数值位

4、GB2312:简体中文字符集,99.75%覆盖。双字节

  • 高位字节:0xA1~0xF7 (01~87区)
  • 低位字节:0xA1~0xFE (01~94位)
  • 例如:“啊” 对应的是 16 区 01 位,表示为 :0xB0A1(16进制)

5、GBK:字符集扩展了 GB2312,提供繁体中文支持

6、GB18030:采用单字节、双字节、四字节对字符进行编码

  • 单字节对应的是 ASCII 码表
  • PS: GB2312、GBK、GB18030 统称为 双字节字符集(DBCS)
  • GB18030-2000 最新的齐全的汉字编码标准

7、Big-5码:通用于台湾、香港的繁体字编码方案 “大五码”

8、Unicode: JAVA 的编码方式。国际标准码,最新版是 Unicode 8.0,是 通用多八位编码字符集 的简称。为每种语言中的每个字符设定了统一且唯一的编码二进制编码,以满足跨语言、跨平台进行文本转换的处理需求。

Unicode 十六进制编码 “U+”,固定使用16bits表示一个字符,标准的Unicode称为 UTF-16。后来为了使得双字节的Unicode能在现存的处理单字节的系统上传输,出现了 UTF-8,使用类似 MBCS(Multiple-Byte Character System,GB2312)的方式对 Unicode进行编码。

9、UTF-8 最多使用三个字节来表示一个字符,定义了区间规则,最大程度的兼容了ASCII

  • 单字节:00000000 - 0000007F
  • 双字节:00000080 - 000007FF
  • 三字节:00000800 - 0000FFFF
String 类中编码相关的方法

不提供编码时,使用平台默认的编码方式

  1. public String(byte[] bys,Charset cs)
  2. public String(byte[] bys,String cs)
  3. public byte[] getBytes(Charset cs)
  4. public byte[] getBytes(String cs)
Legacy class - Properties

public class Properties extends Hashtable

0:Class Summary

1、属性集合类,可与IO结合使用,具有持久性,可保存在流中或从流中加载。键以及对应的值均为 字符串
2、此类是线程安全的多线程可以共享 一个 Properties 对象,不必进行外部的同步。

1:Constractors Summary

1、public Properties()
2、public Properties(Properties defaults) 使用 受保护的 字段值 Properties defaults 构造属性集合对象

2:Methods Summary

继承自 Hashtable 的方法(基本上不使用,仅供了解)。
特有功能:

  • public Object setProperty(String key, String value) 调用 Hashtable 的 put 方法实现。返回的是指定键的旧值,若该键不存在则返回的是 null
  • public String getProperty(String key) 返回指定键的值,不存在指定键,则返回 null
  • public Set stringPropertyNames() 返回键的集合

Properties和IO结合:

  • public void load(InputStream in) throw IOException
  • public void load(Reader reader) throw IOException
    面向行格式从输入字符流中读取属性列表,把文件中的数据(键值对)读取到集合中,集合特指是 属性集合 Properties 对象。行数据的格式应该是以下三种之一:(分割符号是 =、: 、- )
  1. key = value
  2. key : value
  3. key - value
  • public void store(OutputStream out, String comments) throw IOException
  • public void store(Writer writer, String comments) throw IOException
    将集合中的数据按照**“key=value”**格式存储到文件中,comments为 属性列表的描述。
    • comments 为 null:则只是添加 # + new Date()到首行中
    • comments 非 null:则需要在# + new Date()之前加入一行 # + comments

IOUtils.class

package com.io.exec;

import java.io.BufferedReader;
import java.io.Closeable;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.Reader;
import java.io.Writer;
import java.net.URL;

public class IOUtils {

	// 因为 流类定义中实现了 Closable
	// JDK 1.5 之后 Closeable 继承自 AutoCloseable
	public static void closeQuietly(Closeable target) {
		// public static void closeQuietly(AutoCloseable target) {
		if (target != null) {
			try {
				target.close();
			} catch (IOException e) {
				// do nothing ! 静默关闭
			}
		}
	}

	/**
	 * 提供一个简单的copy 文件的方法
	 */
	public static void copy(Reader reader, Writer writer, int bufferSize)
			throws IOException {
		// 程序出现的异常 抛出 给调用者,让调用者去处理。
		// 做一个简单的检验 工作
		if (reader == null || writer == null) {
			throw new NullPointerException("Stream cannot be null!");
		}
		if (bufferSize <= 0) {
			throw new IllegalArgumentException(
					"BufferSize should be positive readerteger!");
		}
		int len;
		char[] chs = new char[bufferSize];
		while ((len = reader.read(chs)) > 0) {
			writer.write(chs, 0, len);
			writer.flush();
		}
	}

	public static void copy(Reader reader, Writer writer) throws IOException {
		copy(reader, writer, 1024 * 1024);
	}

	public static void copy(InputStream in, OutputStream out, int bufferSize)
			throws IOException {
		// 程序出现的异常 抛出 给调用者,让调用者去处理。
		// 做一个简单的检验 工作
		if (in == null || out == null) {
			throw new NullPointerException("Stream cannot be null!");
		}
		if (bufferSize <= 0) {
			throw new IllegalArgumentException(
					"BufferSize should be positive integer!");
		}
		int len;
		byte[] b = new byte[bufferSize];
		while ((len = in.read(b)) > 0) {
			out.write(b, 0, len);
		}
	}

	public static void copy(InputStream in, OutputStream out)
			throws IOException {
		copy(in, out, 1024 * 1024);
	}

	/***
	 * 实现 按照平台给定的编码方式从url路径下载文件并读取
	 * 
	 * @param url
	 *            给定的下载使用的 url 路径
	 * @return 按照指定编码方式读取到的文本内容的字串
	 * @throws IOException
	 */
	public static String downloadString(URL url) throws IOException {
		/** 将异常上抛给 调用者进行处理... */
		// 使用 从系统配置文件中读取 编码方式,默认使用的是和平台相同的编码方式
		// return downloadString(url, System.getProperty("file.encoding"));
		return downloadString(url, System.getProperty("sun.jnu.encoding"));
	}

	/***
	 * 实现 按照指定编码格式从url路径下载文件并读取
	 * 
	 * @param url
	 *            给定的下载使用的 url 路径
	 * @param charset
	 *            指定解析的编码方式
	 * @return 按照指定编码方式读取到的文本内容的字串
	 * @throws IOException
	 */
	public static String downloadString(URL url, String charset)
			throws IOException {
		/** 因为 高效缓存字符流依赖于 字符流,字符流依赖于字节流,一次创建 */
		/** 使用 接口引用 具体子类对象的方式 进行定义 */
		InputStream in = null;
		Reader reader = null;
		BufferedReader buffReader = null;

		// 因为读取文本文件或网页源码时,需要进行多次的字符串的拼接 *** 作, 所以使用 StringBuilder 单线程高效的字符串创建器
		StringBuilder sb = new StringBuilder();
		try {
			/** 调用 url 对象的 openStream 将指定的 url 地址转换为 字节输入流 */
			in = url.openStream();
			/** 使用具体的读取转换流,并给定编码方式 进行字符流的读取 */
			/** 字符流 可以看作是 字节流 + 编码表 */
			reader = new InputStreamReader(in, charset);
			/** 构建高效字符流 */
			buffReader = new BufferedReader(reader);
			/** 创建字符串 用于读取每行文本 */
			String line;
			/** 循环获取 文件中的每行内容,为null表示 读到文本的末尾 */
			while ((line = buffReader.readLine()) != null) {
				/** 使用 StringBuilder 的append 添加方法进行添加 */
				sb.append(line).append("\n");
			}
		} finally {
			/** 关闭顺序:先关闭上层依赖性,再关闭下层的被依赖项 */
			IOUtils.closeQuietly(buffReader);
			IOUtils.closeQuietly(reader);
			IOUtils.closeQuietly(in);
		}
		/** 使用 StringBuilder 的 toString 方法实现返回值的返回 */
		return sb.toString();
	}

	/**
	 * 实现 按照指定编码方式读取文本文件
	 * 
	 * @param file
	 *            需要进行的读取的文本文件
	 * @param charset
	 *            指定的读取方式
	 * @return 返回按照指定编码方式读取到的文本内容的字符串
	 */
	public static String readTextFile(String file, String charset)
			throws IOException {
		/** 因为读取文本文件或网页源码时,需要进行多次的字符串的拼接 *** 作, 所以使用 StringBuilder 单线程高效的字符串创建器 */
		StringBuilder sb = new StringBuilder();
		InputStream fis = null;
		Reader reader = null;
		BufferedReader buffReader = null;
		try {
			/** 使用给定的文件路径进行 字节输入流的创建 */
			fis = new FileInputStream(file);
			/** 使用具体的读取转换流,并给定编码方式 进行字符流的读取 */
			/** 字符流 可以看作是 字节流 + 编码表 */
			reader = new InputStreamReader(fis, charset);
			/** 构建高效字符流 */
			buffReader = new BufferedReader(reader);
			/** 创建字符串 用于读取每行文本 */
			String line;
			/** 循环获取 文件中的每行内容,为null表示 读到文本的末尾 */
			while ((line = buffReader.readLine()) != null) {
				/** 使用 StringBuilder 的append 添加方法进行添加 */
				sb.append(line).append("\n");
			}
		} finally {
			/** 关闭顺序:先关闭上层依赖性,再关闭下层的被依赖项 */
			IOUtils.closeQuietly(buffReader);
			IOUtils.closeQuietly(reader);
			IOUtils.closeQuietly(fis);
		}
		/** 使用 StringBuilder 的 toString 方法实现返回值的返回 */
		return sb.toString();
	}

	/**
	 * 实现 按照默认编码方式读取文本文件
	 * 
	 * @param file
	 *            需要进行的读取的文本文件
	 * @return 返回按照默认编码方式读取到的文本内容的字符串
	 */
	public static String readTextFile(String file) throws IOException {
		/** 将异常上抛给 调用者进行处理... */
		// 使用 从系统配置文件中读取 编码方式,默认使用的是和平台相同的编码方式
		// return readTextFile(file, System.getProperty("file.encoding"));
		return readTextFile(file, System.getProperty("sun.jnu.encoding"));
	}
}
面试题

close和flush方法的区别?
解答:自行百度

小总结 子类不可以比父类更坏
  1. 子类重写父类方法时,必须抛出相同异常或父类异常的子异常
  2. 父类多个异常,子类可以相同,但不可抛出父类没有的异常
  3. 父类没有异常,则子类不可抛出异常,子类存在异常的话,只可以 try 不可 throws。
如何访问 src 目录下的资源文件 传统的资源加载方式

使用 当前类的 class 属性,调用 getResource(String path); 方法实现

URL url = 
	Demo.class.getResource("/a.txt");
// 或者
	Demo.class.getResource("/com/rupeng/a.txt")
类加载器加载资源

使用类加载器 加载资源的时候,切记不可加入 ‘/’ 开头。

URL url = 
	Demo.class.getClassLoader().getResource("a.txt");
// 或者
	Demo.class.getClassLoader().getResource("com/rupeng/bin/a.txt");

综合案例:

package com.io.src;

import java.io.InputStream;
import java.io.IOException;
import java.net.URL;

public class Demo1 {
	public static void main(String[] args) {
		// 访问 src 目录下的资源文件
		/** 当资源文件在 src 文件夹下的时候 */
		// URL url = Demo1.class.getResource("/a.txt");
		URL url = Demo1.class.getClassLoader().getResource("a.txt");
		/** 当资源文件在 和类在同一个包 (文件夹)下的时候 */
		// URL url = Demo1.class.getResource("/com/io/src/a.txt");
		// URL url =
		// Demo1.class.getClassLoader().getResource("com/io/src/a.txt");
		/** 直接将资源转换为 字节流传入 */
		InputStream inStream = Demo1.class.getClassLoader()
				.getResourceAsStream("a.txt");
		/**
		 * 使用 class 属性的 getResource 方法获取时,必须可加“/” 使用 类加载器 的方法 getResource 方法或
		 * getResourceAsStream 获取时,不可加“/”
		 */
		// 具体原因是: 使用 Demo1.class.getClassLoader().getResource(""); 就可得知
		System.out.println(Demo1.class.getClassLoader().getResource(""));
		// file:/C:/JavaProject/26_IO/bin/
		System.out.println(Demo1.class.getResource(""));
		// file:/C:/JavaProject/26_IO/bin/com/io/src/
		try {
			// InputStream in = url.openStream();
			InputStream in = inStream;

			byte[] bys = new byte[10];
			StringBuilder sb = new StringBuilder();
			while (in.read(bys) >= 0) {
				sb.append(new String(bys));
			}
			System.out.println(sb);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/798657.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-06
下一篇 2022-05-06

发表评论

登录后才能评论

评论列表(0条)

保存