大多数人,都低估了编程学习的难度,而高估了自己的学习能力和毅力。
当前系列: Java语法 修改讲义

复习:文件:清理资源 / flush / 序列化


File

Java的IO操作包都需要导入:
import java.io.*;

Java不区分文件(file)和文件夹(directory),即文件夹仍然被当做文件处理(同Linux)

实例化一个File类得到File对象,但必须在构造函数中指定其

文件路径

斜杠可以是正斜杠(同Linux),也可以是反斜杠(同Windows)

String path = "D:\\sample.txt";    //@想一想@:为什么是两个\\?
String path = "D:/sample.txt";
如果表示文件夹的话,建议不要忘记最后以斜杠结尾:
String dir = "D:\\17bang\\yz\\dfg"; 
String dir = "D:/17bang/yz/dfg";
可以是绝对路径(如上),也可以是相对路径:
String path = "sample.txt";

演示:相对路径的当前文件夹就是项目所在文件夹。

增删改查

拿到文件对象之后:

File file = new File(path);   //或者
File file = new File(dir);

就可以调用File对象的内置方法:

  • 创建文件:createNewFile()
    System.out.println(file.createNewFile());
  • 创建最末端的文件夹,如果其父目录不存在的话,
    • mkdir():无法创建,返回false
      System.out.println("file.mkdir():" + file.mkdir());
    • mkdirs():创建其父目录
      System.out.println("file.mkdirs():" + file.mkdirs());
    PS:mkdir,make directory,和DOS命令相同
  • 删除delete():
    file.delete();
  • 检查:
    • 是否存在:exists()
      System.out.println(file.exists());
    • 能不能(can):
      System.out.println("file.canExecute():" + file.canExecute());    //执行
      System.out.println("file.canRead():" + file.canRead());    //读
      System.out.println("file.canWrite():" + file.canWrite());    //写
    • 是不是(is):
      System.out.println("file.isFile():" + file.isFile());    //文件
      System.out.println("file.isDirectory():" + file.isDirectory());    //目录
      System.out.println("file.isHidden():" + file.isHidden());    //隐藏文件/目录
      System.out.println("file.isAbsolute():" + file.isAbsolute());    //绝对路径
  • 显示目录下所有子目录/文件
    String[] chidren = file.list();
    for (int i = 0; i < chidren.length; i++) {
    	System.out.println(chidren[i]);
    }
  • 修改文件属性:
    file.setReadOnly();    //设为只读
    //设置最后修改时间,参数为时间戳
    file.setLastModified(1212121);     


Stream

注意:此(java.io.)Stream非彼(java.util.)Stream

但Java没有统一的Stream基类/接口。而是:

  • 为字符操作定义了Reader/Writer
  • 为二进制内容定义了OutputStream/InputStream

上述这些类共同实现的是:

  • Closeable,所以可以使用try(closable实例)的语法
    try(FileWriter writer = new FileWriter(file, true)) {
    这样无论如何(比如{}中代码出现异常),JVM都会调用writer.close()方法关闭流。类似于:
    FileWriter writer = new FileWriter(file, true)
    try {
    } finally {
    	writer.close();
    }
  • Flushable,所以可以调用flush()方法:
    writer.flush();
    强制清空缓冲区。

Reader/Writer

为了便于演示,我们使用FileReader和FileWriter

writer构造函数中传入File对象:

Writer writer = new FileWriter(file);

可以指示是覆盖还是后缀添加(append)

Writer writer = new FileWriter(file, true);    //第二个参数指示append

注意这里是比较奇怪的,writer对象可以调用两个方法write()和append(),你可能会根据方法名推测:

  • append()是在文件末尾追加
  • write()是覆盖整个文件内容重写

然鹅不是这样的。究竟是覆盖重写(write)还是追加(append),靠的是Writer如何构造。

关于换行:不同的操作系统又不同的换行符:

  • Unix和Linux:\n
  • Mac:\r
  • Windows:\r\n

如果文件只是为了阅读,我们可以直接使用“\r\n”(Linux也可以识别),但如果是脚本文件啥的,就得自行判断注意了。

PS:C#提供了writeLine()以及Envirionment.NewLine等自动解决方案

reader

  • 可以直接存放到char[]中,
    char[] container = new char[100];
    reader.read(container);
  • 也可以存放到CharBuffer(字符缓存区)中
    //100是长度/容积大小,单位是字符
    CharBuffer charBuffer = CharBuffer.allocate(100); 
    reader.read(charBuffer);
    //读写指针指到buffer头部,
    //并且设置其读取长度=读出字符内容(而不是整个缓存的容量大小)
    System.out.println(charBuffer.flip().toString());  

OutputStream/InputStream

如果是二进制文件,使用FileInputStream和FileOutputStream,调用read()和write()方法,和reader/writer非常类似。

演示:System.out和System.in都是PrintStream


Serializable

演示:File实现了Serializable,但是Serializable里面啥都没有,啥意思啊?

试试把Teacher类序列化成一个文件?

// ObjectOutputStream:对象输出流,将对象序列化成流
try (ObjectOutputStream stream = new ObjectOutputStream(
		//构造函数参数:指定对象生成的流,传递到何处
		new FileOutputStream("D:\\fg"))) {
	stream.writeObject(fg);

结果报异常:

Exception in thread "main" java.io.NotSerializableException: Teacher

因为Teacher没有实现Serializable:

public class Teacher /* implements Serializable */  {
根据堆栈信息找到:
} else if (obj instanceof Serializable) {
所以,Serializable接口也仅仅只是做一个标记用。它用于辨别对象是否实现了Serializable接口,真正的序列化由JVM自动完成:
writeOrdinaryObject(obj, desc, unshared);
演示:序列化后的文件用记事本打开是乱码,因为Java中的序列化是二进制的。


绘图

首先需要导入awt(Abstract Window Toolkit,抽象窗口工具集)包:

import java.awt.*;
import java.awt.image.*;

它是由sun提供的GUI(Graphics User Interface,图形用户界面)内置工具库。

然后,通过BufferedImage得到一个画布

// 创建宽200,高50像素的图片(画布)对象
BufferedImage image = new BufferedImage(200, 50,
		BufferedImage.TYPE_4BYTE_ABGR); //4 byte的agrb
再基于图像,生成画笔
//基于图片对象开始绘图(画笔)
Graphics2D graphics = image.createGraphics(); 
设置底色的变通方法:画一个和图片一样大小的矩形,填充颜色
graphics.setColor(Color.LIGHT_GRAY);
graphics.fillRect(0, 0, image.getWidth(), image.getHeight());

注意我们这里使用的是fillRect()方法,是drawRect():

  • drawRect():画一个只有边框的矩形
  • fillRect():画一个只有填充色的矩形

如果要矩形同时具有边框和填充色,要计算好他们的坐标和大小,以免覆盖:

graphics.setColor(Color.black);
graphics.drawRect(2, 2, image.getWidth()-5, image.getHeight()-5);

graphics.setColor(Color.LIGHT_GRAY);
//填充用矩形比边框小一个像素
graphics.fillRect(3, 3, image.getWidth()-6, image.getHeight()-6);


关于颜色,我们既可以使用现成的Color.XXX,也可以指定AGRB:

//不指定透明度,使用int指定RGB标号
new Color(175, 90, 182)
//指定透明度,使用float指定GRB比例和透明度
new Color(0.3f, 0.7f, 0.8f, 0.8f)

绘制字符串

// 指定字体样式大小等
graphics.setColor(Color.cyan);
graphics.setFont(new Font("微软雅黑", Font.ITALIC, 40));
// 指定内容和起始位置
graphics.drawString("abcd", 20, 45);

绘制直线

graphics.drawLine(3, 5, 170, 65);
无法直接画,画一个长度为0的线条
graphics.drawLine(100, 25, 100, 25);

最后的处理:

//和close()类似,释放资源
graphics.dispose();
//将其保存到D盘,png格式,名为captcha.png
ImageIO.write(image, "png", new File("d:\\captcha.png"));


作业

见:J&C:文件:清理资源 / flush / 序列化 / 绘图
学习笔记
源栈学历
大多数人,都低估了编程学习的难度,而高估了自己的学习能力和毅力。

作业

觉得很 ,不要忘记分享哟!

任何问题,都可以直接加 QQ群:273534701

在当前系列 Java语法 中继续学习:

多快好省!前端后端,线上线下,名师精讲

  • 先学习,后付费;
  • 不满意,不要钱。
  • 编程培训班,我就选源栈

更多了解 加:

QQ群:273534701

答疑解惑,远程debug……

B站 源栈-小九 的直播间

写代码要保持微笑 (๑•̀ㅂ•́)و✧

公众号:源栈一起帮

二维码