学习记录
前言
学习过程中一些不知道的类和方法的记录
一、Spring之ClassPathResource
ClassPathResource是用于Spring中加载资源文件的类
1.引入库
代码如下(示例):
import org.springframework.core.io.ClassPathResource;
2.读取资源文件
代码如下(示例):
@Test
public void testClassPathResource() throws IOException {
Resource res = new ClassPathResource("resource/ApplicationContext.xml");
InputStream input = res.getInputStream();
Assert.assertNotNull(input);
}
3.源码解读
内部源码:
public ClassPathResource(String path) {
this(path, (ClassLoader) null);
}
public ClassPathResource(String path, ClassLoader classLoader) {
Assert.notNull(path, "Path must not be null");
String pathToUse = StringUtils.cleanPath(path);
if (pathToUse.startsWith("/")) {
pathToUse = pathToUse.substring(1);
}
this.path = pathToUse;
this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
}
public ClassPathResource(String path, Class<?> clazz) {
Assert.notNull(path, "Path must not be null");
this.path = StringUtils.cleanPath(path);
this.clazz = clazz;
}
/**
* 读取资源文件
* This implementation opens an InputStream for the given class path resource.
* @see java.lang.ClassLoader#getResourceAsStream(String)
* @see java.lang.Class#getResourceAsStream(String)
*/
@Override
public InputStream getInputStream() throws IOException {
InputStream is;
if (this.clazz != null) {
is = this.clazz.getResourceAsStream(this.path);
}
else if (this.classLoader != null) {
is = this.classLoader.getResourceAsStream(this.path);
}
else {
is = ClassLoader.getSystemResourceAsStream(this.path);
}
if (is == null) {
throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist");
}
return is;
}
该类获取资源文件有两种方法:通过class获取和classLoader获取
@Test
public void testResouce() {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
System.out.println(loader.getResource("").getPath());
System.out.println(this.getClass().getResource("").getPath());
System.out.println(this.getClass().getResource("/").getPath());
System.out.println(System.getProperty("user.dir"));
}
输出结果:
/home/sunny/workspace/spring-01/target/test-classes/
/home/sunny/workspace/spring-01/target/test-classes/com/me/spring/spring_01/
/home/sunny/workspace/spring-01/target/test-classes/
/home/sunny/workspace/spring-01
Class.getResource("")
获取的是相对于当前类的相对路径
Class.getResource("/")
获取的是classpath的根路径
ClassLoader.getResource("")
获取的是classpath的根路径
在创建ClassPathResource对象时,我们可以指定是按Class的相对路径获取文件还是按ClassLoader来获取。
二、FileUtils
1.文件操作工具类
官方API文档:http://commons.apache.org/proper/commons-io/apidocs/org/apache/commons/io/FileUtils.html
maven依赖:
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.8.0</version>
</dependency>
2.方法使用
一、拷贝文件
static void | copyFile(File srcFile, File destFile) |
---|---|
static void | copyFile(File srcFile, File destFile, boolean preserveFileDate) |
static long | copyFile(File input, OutputStream output) |
static void | copyFileToDirectory(File srcFile, File destDir) |
… | … |
1)拷贝文件到文件: copyFile(File srcFile, File destFile)
File file = new File("E:\\java\\file01\\abc雪.jpg");
String destFilePath = "E:\\java\\file02";
String destFileName = "abc雪02.jpg";
try {
FileUtils fileUtils = new FileUtils();
//文件拷贝到新的位置并保存文件的日期。
fileUtils.copyFile(file, new File(destFilePath,destFileName));
System.out.println("文件拷贝成功");
} catch (IOException e) {
e.printStackTrace();
System.out.println(e.getMessage());
}
2)拷贝文件到字节输出流。:copyFile(File input, OutputStream output)
String destFileName = "abc雪03.jpg";
//从文件copy to an字节输出流。
FileUtils.copyFile(file, new FileOutputStream(new File(destFilePath,destFileName)));
3)拷贝文件到文件的目录保存文件的日期:copyFileToDirectory(File srcFile, File destDir)
拷贝的文件名无法自定义,和原文件名一样
//拷贝文件到文件的目录保持文件的日期。
FileUtils.copyFileToDirectory(file, new File(destFilePath));
二、拷贝目录及文件
static void | copyDirectory(File srcDir, File destDir) |
---|---|
static void | copyDirectory(File srcDir, File destDir, boolean preserveFileDate) |
static void | copyDirectory(File srcDir, File destDir, FileFilter filter) |
static void | copyDirectory(File srcDir, File destDir, FileFilter filter, boolean preserveFileDate) |
… | … |
1)将整个目录拷贝到新位置,并保持原文件日期:copyDirectory(File srcDir, File destDir)
其包含文件及子目录文件并保持原文件日期
File file = new File("E:\\java\\file01");
String destFilePath = "E:\\java\\file03";
try {
//将整个目录复制新位置,并保持原文件日期。
FileUtils.copyDirectory(file, new File(destFilePath));
System.out.println("文件目录拷贝成功");
} catch (IOException e) {
e.printStackTrace();
System.out.println(e.getMessage());
}
2)将已筛选的目录拷贝的新位置:copyDirectory(File srcDir, File destDir, FileFilter filter)
文件过滤器筛选 其包含文件及子目录文件 拷贝,并保持原文件日期。
String destFilePath = "E:\\java\\file04";
//将已筛选的目录复制,并保持原文件日期的新位置。
FileUtils.copyDirectory(file, new File(destFilePath), new FileFilter() {
@Override
public boolean accept(File pathname) {
if(pathname.isDirectory()) return true;
else {
boolean b1 = pathname.getName().endsWith(".txt");
boolean b2 = pathname.getName().endsWith(".jpg");
return b1 || b2;
}
}
});
三、删除目录及文件
static void | deleteDirectory(File directory) |
---|---|
static boolean | deleteQuietly(File file) |
static void | forceDelete(File file) |
… | … |
1)删除指定文件,从不引发异常:deleteQuietly(File file)
File file = new File("E:\\java\\file04\\abc雪.jpg");
//删除指定文件,从不引发异常。
FileUtils.deleteQuietly(file);
2)删除指定文件,不存在报异常:forceDelete(File file)
File file = new File("E:\\java\\file04\\abc雪.jpg");
try {
FileUtils.forceDelete(file);
System.out.println("操作成功");
} catch (IOException e) {
e.printStackTrace();
System.out.println(e.getMessage());
}
3)递归删除目录:deleteDirectory(File directory)
删除其包含文件及子目录文件
File file = new File("E:\\java\\file04\\abc雪.jpg");
//递归删除目录。
try {
FileUtils.deleteDirectory(new File(destFilePath));
System.out.println("操作成功");
} catch (IOException e) {
e.printStackTrace();
System.out.println(e.getMessage());
}
四、清除目录:cleanDirectory(File directory)
static void cleanDirectory(File directory)
Cleans a directory without deleting it.
清除该目录下的文件及子目录文件而不删除该目录文件夹。该目录不存在会报错
String destFilePath = "E:\\java\\file04";
try {
FileUtils.cleanDirectory(new File(destFilePath));
System.out.println("操作成功");
} catch (IOException e) {
e.printStackTrace();
System.out.println(e.getMessage());
}
五、将字符串写入文件中,writeStringToFile
writeStringToFile一共有四个参数
- file (File) : file 指定数据存放在什么文件下,会默认创建文件夹和文件
- data (String):指定写入的数据
- encoding (String):指定写入时编码格式 (参数可选)
- append (boolean):指定插入时是覆盖插入还是追加插入 默认为false false是覆盖插入 true为追加插入 (参数可选)
String writeData="FileUtils test";
try {
FileUtils.writeStringToFile(new File("e:/file/fileTest.txt"),writeData,"UTF-8",false);
System.out.println("写入成功");
} catch (IOException e) {
e.printStackTrace();
}
六、将指定的文件夹中的内容读取出来以String的形式来显示:readFileToString
readFileToString 二个参数
- file (File) : file 指定数据存放在什么文件下,会默认创建文件夹和文件
- encoding (String):指定写入时编码格式 (参数可选)
public static void main(String[] args) {
String readData=null;
try {
readData= FileUtils.readFileToString(new File("e:/file/fileTest.txt"),"UTF-8");
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("读取的数据为:"+readData);
}
三、TypeReference
1.获取泛型参数
对进行泛型的反序列化,使用TypeReference可以明确的指定反序列化的类型,
代码如下(示例):
//后台java代码 String planJson = request.getParameter("planJson");
if (!Tools.isEmpty(planJson)) {
Plan plan = JSON.parseObject(planJson, new TypeReference<Plan>() {
});
modelMap.addAttribute("plan", plan);
}
JSON.parseObject(planJson, new TypeReference<Plan>(){})
是把字符串planjson转化为相应的JSONObject对象,“键值对”形式,然后通过new TypeReference<Plan>(){ }
匿名内部类来吧planjson的JSONObject转化为Plan对象,注意一点,前台在做 $("#addForm").serializeJson()
处理的时候,form表单里还有许多隐藏域,不属于plan对象本身的属性,通过TypeReference只把属于plan对象属性的参数和值组装成plan对象
四、ClassPathResource、FileUtils和TypeReference
读取路径中的json文件并保存
代码如下:
private ConcurrentHashMap<String, List<Info>> cache;
private ClassPathResource resource;
public void load(String filename) {
// 从文件中加载缓存到map中
try {
//获得资源文件
resource = new ClassPathResource(filename);
//读取资源文件
String str2Cache = FileUtils.readFileToString(resource.getFile(), StandardCharsets.UTF_8);
cache = new ConcurrentHashMap<>();
//将文件内容保存在cache这个currentmap中。
cache = JSON.parseObject(str2Cache, new TypeReference<ConcurrentHashMap<String,List<Info>>>(){});
} catch (IOException e) {
log.error("从文件中加载Cache失败.{}", e.getMessage());
throw new RuntimeException(e.getMessage());
}
}
private void save() {
// 将map中的数据写入到文件中
try {
String cache2String = JSON.toJSONString(cache);
FileUtils.writeStringToFile(resource.getFile(), cache2String, StandardCharsets.UTF_8);
} catch (IOException e) {
log.error("写入文件失败.{}", e.getMessage());
}
}
五、BufferedReader
1.接收控制台信息
代码如下:
BufferedReader input = new BufferedReader(new InputStreamReader(System.in));
2.读取文件
代码如下:
BufferedReader in = new BufferedReader(new FileReader(“foo.in”));
3.BufferedReader读取资源文件
代码如下:
//读文件
public static String readFiles(String fileName) {
String jsonstr = "";
File jsonfile = new File(fileName);
BufferedReader reader = null;
if (!jsonfile.exists()) {
System.out.println("not found "+fileName);
return null;
}
try {
reader = new BufferedReader(new FileReader(jsonfile));
String readstr = null;
int line = 1;
while ((readstr = reader.readLine()) != null) {
jsonstr += readstr;
line++;
}
return jsonstr;
} catch (IOException e) {
e.printStackTrace();
return null;
} finally {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
六.BufferedWriter
1.API
(1) 写一个字符到字符缓冲区中
/**
* 写入一个字符,并存入到字符缓冲区中
*
* @exception IOException If an I/O error occurs
*/
public void write(int c) throws IOException {
synchronized (lock) {
ensureOpen();
if (nextChar >= nChars)
flushBuffer();
cb[nextChar++] = (char) c; //将单个字符存入到
}
}
(2)写一个字符数组的一部分
* 写一个字符数组的一部分
*
*/
public void write(char cbuf[], int off, int len) throws IOException {
synchronized (lock) {
ensureOpen();
if ((off < 0) || (off > cbuf.length) || (len < 0) ||
((off + len) > cbuf.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return;
}
if (len >= nChars) {
//如果请求长度超过输出缓冲区的大小则直接进行输出,而不用字符缓冲区做缓冲
flushBuffer();
out.write(cbuf, off, len); //实际是调用的OutputStreamWriter的write方法本质是调用StreamEncoder的write方法执行的
return;
}
int b = off, t = off + len;
//此循环的目的就是保证len个字符务必被复制到字符缓冲区中区
while (b < t) {
int d = min(nChars - nextChar, t - b); //判断字符缓冲区中的剩余位置和要写入的字符长度最小值,若字符缓冲区空间充足,则复制len个字符,若不充足,则复制剩余位置个字符
System.arraycopy(cbuf, b, cb, nextChar, d); //从目标数组cbuf的下标b中复制d个字符到字符缓冲区
b += d;
nextChar += d;
if (nextChar >= nChars) //判断字符缓冲区是否已满?若满了,则调用flushBuffer方法把缓冲区中的内容全部输出,然后按道理此时字符缓冲区应该就是空了的?
flushBuffer();
}
}
}
源码分析:
本质是调用OutputStreamWriter的write(cbuf, off, len)方法而OutputStreamWriter的write(cbuf, off, len)方法实际是调用StreamEncoder的write方法执行的
1、传入字符数组cbuf,字符数组的偏移点off,以及要写入的字符个数len--代表要从字符数组cbuf中下标off开始写入len个字符
2、 前期条件判断避免出现RuntimeException异常所以必须抛出
3、 当要写入的字符个数len大于字符缓冲区的长度时则意味着就算字符缓冲区cb是空的也无法载入写入字符,那么就没必有调用字符缓冲区了,直接调用write(cbuf, off, len)方法,out变量是OutputStreamWriter的对象,而OutputStreamWriter内的方法都是StreamEncoder类的方法执行。因此实际调用的是StreamEncoder的write方法。在StreamEncoder中直接把字符数组cbuf通过编码器编码到StreamEncoder的字节缓冲区中
4、当要写入的字符个数len小于字符缓冲区的长度时,通过 System.arraycopy的方法把字符数组cbuf内要写入的字符复制到字符缓冲区cb中,其中while循环的作用就是保证符数组cbuf内要写入的字符全部复制到字符缓冲区cb中
2.写资源文件
代码如下(示例):
//写文件
public static void writeFiles(String content, String fileName) {
BufferedWriter bufferedWriter = null;
try {
File file = new File(fileName);
if (!file.exists()) {
file.createNewFile();
}
FileWriter fileWriter = new FileWriter(fileName, true);
bufferedWriter = new BufferedWriter(fileWriter);
bufferedWriter.write(content);
//newLine(),换行
//bufferedWriter.newLine();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
bufferedWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
3.BufferedWriter流与OutputStreamWriter的关系
1)前面OutputStreamWriter的API说明中提到过:为了获得最高效率,请考虑在BufferedWriter中包装OutputStreamWriter,以避免频繁的转换器调用这句话,理解如下:
当我们直接使用OutputStreamWriter进行输出时,不管是一个字符,还是一个数组,都会调用字符编码器进行转换,因为其是直接调用的StreamEncoder的implWrite方法,若存在for循环100次,每次调用write(int c)方法写入一个字符,那么最终就会调用100次的implWrite方法,就会调用100次的字符编码器进行转换
但是当我们使用BufferedWriter包装OutputStreamWriter时,每次调用write(int c)方法时,都只是将字符写入到BufferedWriter类的字符缓冲区中。到最后调用flush方法或者close方法时,才会调用一次StreamEncoder的implWrite方法,也就是调用一次字符编码器
因此BufferedWriter可以提高字符输出的效率,提高的关键点在于:存在字符缓冲区用于存储字符