基础知识
硬件:处理器( C pU )、存储器(内存)、 l/O:输入设备( Input Device )和 输出设备( Output Device )
CPU 只能和内存做直接的数据交换
lnput Device :现实中的物理信号(光信号 e.g.摄像头、电信号、波信号 e.g.声音)一>数字格式.存储在内存中
Output Device :数字一>物理信号
IO设备:显示器( O )、触屏显示器( IO )、鼠标( l )、键盘( l )、摄像头( I )、麦克风( l ) 、器声扬( O ) 、 硬盘( IO )、网卡( IO )
变量、对象本质上是编程语言对硬件中内存的抽象
内存(Memory)又被称为RAM(Random Access Memory),支持以O(1)时间复杂度根据下标(内存地址)访问的存储
各个存储访问速度的直观感受
硬盘(Hard Disk Drive, HDD)的实现
-
磁盘:利用磁性实现的一种方式, 硬盘(机械硬盘)一般都是指磁盘
-
固态硬盘(Solid State Disk, SSD)
-
闪存 (Flash Memoty)
磁盘更适合做连续数据的读写,而不是和做随机数据的读写。磁盘寻址步骤:
- 旋转转片找到对应扇区
- 找到对应磁道
软件层面认识硬盘中的数据:
文件是对硬盘中数据的抽象概念
认识文件
狭义上的文件(file):针对硬盘这种持久化存储的I/O设备,当我们想要进行数据保存时,往往不是保存成一个整体,而是独立成一个个的单位进行保存,这个独立的单位就被抽象成文件的概念,就类似办公桌上的一份份真实的文件一般。即,文件是对硬盘中数据的抽象概念(因此硬盘数据的读写 ==> 文件的读写问题)
-
文件由OS +文件系统( Filesystem )统一管理
-
文件的读写问题:文件由于被操作系统进行了管理,所以根据不同的用户,会赋予用户不同的对待该文件的权限,一般地可以认为有可读、可写、可执行权限。)
-
文件数据 = 内容数据 + 元信息
- 文件的元信息:指文件名、文件类型、文件大小等,并不作为文件的数据而存在的信息。
很多操作系统为了实现接口的统一性,将所有的 I/O 设备都抽象成了文件的概念,使用这一理念最为知名的就是 Unix、Linux 操作系统 —— 万物皆文件。
Windows 操作系统上,还有一类文件比较特殊,就是平时我们看到的快捷方式(shortcut),这种文件只是对真实文件的一种引用而已。其他操作系统上也有类似的概念,例如,软链接(soft link)等。
树型结构组织 和 目录
我们看到的文件的基本知识
1.文件被以树形结构(数据结构学过的树,但不是二叉树)进行管理,文件都是树上的结点(node)
2.文件可以大体分成两大类
1)普通文件:就是俗称的文件,是用于存储数据的文件
-
普通文件又可分为:
-
文本文件(被字符集编码的文本,如:.txt)
-
不是文本的文件(按照标准格式保存的非被字符集编码过的文件,二进制文件,如:.class、.gpn)
-
-
文件后缀( file suffiex ):也称文件扩展名,在 Windows OS 下,以文件后缀来标记出这个文件文存储的内容是什么内容,如:*. txt 普通文本 、 * . docx Word 文档。文件扩展名可以被显示或隐藏
2)目录/文件夹( directory,dir/folder ) :用于管理树形结构组织数据的文件
-
以 / 结尾,代表这个结点是目录(当Windows 是个目录时,C : /Windows 等价于 C :/Windows / )
-
无论是目录或文件都只是树上的一个节点,一个节点可以有多个孩子
3.文件树只是一个逻辑结构,而不是硬盘上的物理结构
作为软件开发工程师大部分场景只需要关注这个逻辑结构即可,不需要管理物理结构
文件路径(Path)
关于文件的路径( path ) : 根据一个规则,从文件树上唯一确定一个位置一定对应到某个结点,但是这个结点可以不存在
路径分类
-
绝对路径( a bsolute path abs path ) :从一棵树的根结点出发描述的路径 。(无论‘我们‘身在何处,总是唯一确定一个位置)
-
相对路径( relative path ) :从‘我们’所在的当前位置出发,描述的路径。(“我们“的位置一变,路径就可能失效了)
我们所在的位置一定是一个目录,我们是不能处于一个文件上的。
首先找到我的父结点( Windows ) ,再去到父亲结点( C :)、找到它的叫做 a . txt 的孩子
最基本的相对路径:
什么叫做“我们”所在的位置:每个进程都有一个当前工作目录( current working directory,cwd ) ,一般一个进程的启动目录,就是当前工作目录
进程区行时的工作目录在哪里? current working diretory cwd
JVM下,通常就是进程启动时的目录! ! 但这个目录可以改变[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y7JqDRne-1657515798775)(img/image-20220708215849154.png)]
注意,原则上是启动时的目录,而和代码/工程所在目录无关。
因此,使用文件的相对路径时,要在前边加上 … 用以返回文件真正的父节点
真实中怎么去描述路径 (重点)
windows 下,先以绝对路径为例:
D :\ 课程 \ 2022 - 06 - 27 - 2022 火箭班 \ 板书.png
-
\ : 俗称路径分隔符( path separator )
-
C:、 D :、 E :代表的就是根。所以,这个以根开头的路径一定是个绝对路径 , 由 ‘文件名+文件名+文件名 … 组成
-
步骤:
- 从根 D : 开始
- 找到 “ D : ” 的孩子“课程”
- 找到 “课程” 的孩子 2022 …
- 找到 ”2022 … “ 的孩子 板书.png
当我们身处 “ D:\课程 ” 时,下面这个相对路径和上面的绝对路径描述的是同一个结点:
2022 - 06 - 27 - 2022 火箭班 \ 板书.png (不是由根开始,所以是相对路径)
路径的表示
Windows 使用\作为路径分隔符, Linux( Unix / XOS )使用/作为路径分隔符。好在我们写的是 Java , Java 号称跨平台,所以帮我们处理这个问题我们代码中写” / ”或者” \ \ ”都可以。由于” / ”不用转义,所以我一般使用” / ”为主。
例,在代码的字符串中如何表示这个路径:
String path = " D : \课程\2022 - 06 - 27 - 2022 火箭班 - IO "
在java中直接这么写的错误的,因为字符串中反斜杠( \ ) ,表示转义的意思,比如:\ n 、\ t …
应该写作 :
- String path =” D : \\ 课程 \\ 2022 - 06 - 27 - 2022 火箭班 - IO ”,使用 \\ 转义后表示\ 。
- String path = " D : / 课程 / 2022 - 06 - 27 - 2022 火箭班 - IO ” 同样是正确的
路径表示中的两个特殊的符号一个点( " . " )和两个点(”…” ) 。其中:
- " . ”表示在当前位置(目录)不同
- ”…”表示回到当前位置的父结点(目录)上
举例说明:
-
C : /Windows / Users / hello . jpg
-
C : /Windows/./users / hello.jpg :这里的“.”表示还是在当前位置没动
-
C : / Windows /…/Windows / Users / hello.jpg: 这里的“…”表示回到父目录 C盘
-
C : /…/…/…/…/Windows / Users / hello.jpg: 根再回到双亲也还是根
-
C : / . / . / . / . / Windows / Users / hello.jpg
这两个符号一般在相对路径上使用较多,例如,我现在在 C : / Windows / Users 下:
-
…/…/ a . txt : 表示 C : / a . txt
-
. / hello.jpg :表示 C : / Windows / Users / hello.jpg
-
hello.jpg :表示 C : /Windows / Users / hello.jpg
关于文件的路径总结
1 .路径是树上找到一个结点的位置
2 .路径并不表示文件一定存在
3 .路径分为绝对路径 or 相对路径
4 .当前位置二进程的当前工作目录
5 .文件路径” / ”或者” \ \ ”表示路径分隔符, Java 下使用哪个都可以
6 . " . ”和 “…” 的含义
文件的结构
树的结点分为:叶子节点 和 非叶子节点
-
普通文件:叶子节点
-
目录文件:叶子节点(空目录) 和 非叶子节点
对树结构来说,对结点的操作更容易、子树的操作更复杂,因此文件系统中,以结点为单元进行操作(在代码层面)
-
文件移动操作(文件重命名、文件剪切+粘贴):结点的移动(重命名 or 移动到其他结点下)
-
文件复制操作(复制+粘贴):新建结点+内容的复制
-
目录移动操作(目录重命名、目录剪切+粘贴):以该结点为根的一棵子树的移动
-
目录复制操作:以该结点为根的一裸子树的复制
-
删除:默认情况下只能删除普通文件 or 空目录。只能删除结点,不能删除子树
-
删除非空目录:对整棵树的删除
而对树的操作,则需要转换成对结点的操作,以目录的复制为例,深度 or 广度遍历整棵树,然后对其中的每个结点进行复制.
通过 Java 代码操作文件( FiIe )
文件数据=元数据(属性数据)+内容数据
接下来,我们只关注涉及元数据的操作,暂时不涉及内容数据的读写。如:
- 对文件系统树的操作:判断是否存在、判断是否是目录、创建、删除、重命名 …
File概述
FiIe 对象描述一个文件(这是一个结点,但结点对应的文件是否存在并不一定)
代码要求:
-
SE+ DS:要求我们学过的所有代码,闭卷考试的要求:可以脱离IDEA环境熟练地编写和使用,熟悉常见类的常见方法因为最终是为 OJ 服务的。(OJ一般不允许在IDEA上编写)
-
DB 之后的所有代码,是开卷考试的要求:对常见类有个印象,可以结合IDEA工具,查网络资料编写出来即可
-
所有类必须放在某个包( package )下,习惯上包的命名习惯: com . io . Demo1…
构造方法
方法
创建一个新文件
- 返回true:创建成功
- 返回false:创建失败(文件已经存在)
- 抛出IOException:发生了IO异常(最常见的的是文件所在的目录现在还不存在)
// 1. 绝对路径方式创建文件对象
// (仅创建了对象,并未真正在电脑上创建文件,还需要调用createNewFile方法)
// 1.1 直接传入字符串路径
File file1 = new File("E:\\JavaStudy\\Bite比特\\课件\\课堂程序\\hello.txt");
// 1.2 传入父路径+子路径
File file2 = new File("E:\\JavaStudy\\Bite比特\\课件\\课堂程序","hello.txt");
// 1.3 以File对象形式传入parent
File parent = new File("E:\\JavaStudy\\Bite比特\\课件\\课堂程序");
File file3 = new File(parent,"hello.txt");
// 2. 常用方法
// 2.1 compareTo: 比较文件路径,负数:file1<file2 正数:file1>file2 0:file1==file2
int comp1 = file1.compareTo(file2);
int comp2 = file2.compareTo(file3);
System.out.println(comp1);
System.out.println(comp2);
// 2.2 creatNewFile创建文件
File file4 = new File("E:\\JavaStudy\\Bite比特\\课件\\课堂程序\\IO2\\hello.txt");
file4.createNewFile(); // 文件目录IO2不存在,抛出java.io.IOException异常
// 异常的知识点:Java 中的异常分为 受查异常(checked exception) or 非受查异常(uncheck exception)
// 方法中如果抛出受查异常,有语法要求
// 要么,我们自己 try catch,不再抛出受查异常
// 要么,在方法签名中通过 throws 声明有受查异常可能被抛出
// 故 java.io.IOException 是受查异常
// java.lang.RuntimeException 及其子类是非受查异常
File file5 = new File("E:\\JavaStudy\\Bite比特\\课件\\课堂程序\\IO\\hello.txt");
boolean r1 = file5.createNewFile();
System.out.println(r1); // 文件尚未被创建,所以是 true
r1 = file5.createNewFile();
System.out.println(r1); // 文件被创建过了,所以是 false
删除一个文件
delete ( ) vs deleteOnExit ( )
delete():立即删除,只能操作叶子节点(普通文件或空目录)
- 返回true:删除成功
- 返回false:对应文件不存在导致的删除失败,对非空目录进行操作导致的失败
- 抛出IOException:其他情况失败(如:文件被其他进程正在使用打开)
- delete ( )是不进入回收站的删除,是真正的删除 ;windows 上的回收站的删除,实际上只是把文件剪切到一个固定目录
deleteOnExit():在 JVM 退出时才真正删除,可以操作普通文件或空目录
删除非空目录
-
先遍历(深度优先 ― 递归),删除这个目录下的所有子孙,让这个目录变成空目录
-
删除该目录
import java.io.File; import java.io.IOException; public class Demo2 { public static void main(String[] args) throws IOException { // 1. 删除普通文件 File file = new File("E:\\JavaStudy\\Bite比特\\课件\\课堂程序\\IO\\hello.txt"); file.delete(); // 2. 删除非空目录 File file1 = new File("E:\\JavaStudy\\Bite比特\\课件\\课堂程序\\IO"); // 1) 删除非空目录file1下的所有子孙文件 traversal(file1); // 2) 删除该目录 file1.delete(); } /** * 利用广度优先的方式删除一个非空目录 * @param dir * @throws IOException */ private static void traversal(File dir) throws IOException { File[] files = dir.listFiles(); // 查看这个目录下的所有孩子(不是子孙) for (File file : files) { // 若这个孩子也是目录,继续深度优先进行遍历,删除这个孩子的子孙文件 if(file.isDirectory()){ System.out.println("目录:"+file.getCanonicalPath()+"\\"); // 删除这个目录下的文件,使该目录变为空目录,可以使用delete方法 traversal(file); file.delete(); }else { // 这个孩子是普通文件,直接删除 // 得到文件的一个标准的路径(去除一切无意义的 . 和 ..) System.out.println("文件:"+file.getCanonicalPath()); file.delete(); } } } }
delete方法删除情况:
-
普通文件删除(存在): true
-
普通文件删除(不存在):false
-
空目录删除(存在):true
-
非空目录删除:false(必须递归解决)
其他常用方法
-
isDirectory ( ): 文件存在&&文件是文件夹
isFile ( ) 文件存在&&文件是普通文件
-
例:
File file = new File("..\\hello-world.txt");
getName ( ) :得到文件名 —> hello-world.txt
getParent ( ) :得到父目录 —> …
isAbsolute():判断对象是根据相对路径还是绝对路径(有根节点)创建,false相对,true绝对 —> false
getPath():得到传入对象的路径(相对或绝对)—> …\hello-world.txt
getAbsolutePath():absolute path 一般的绝对路径 —> D:\代码练习\文件示例1\…\hello-world.txt
getCanonicalPath():canonical path 标准的绝对路径(去掉了无意义的 . 和 …) —> D:\代码练习\hello-world.txt
注意:使用文件的相对路径时,要在前边加上 … 用以返回文件真正的父节点。例:
// IDEA 配置的默认启动位置就是工程目录下
File file5 = new File("hello.txt");
System.out.println(file5.getCanonicalPath()); // 得到的是工程目录+文件名,不是文件的真正路径
File file6 = new File("..\\hello.txt");
System.out.println(file6.getCanonicalPath()); // 先返回了根目录,得到的是文件的真正路径
使用绝对路径就没有这个问题
File file1 = new File("E:\\JavaStudy\\Bite比特\\课件\\课堂程序\\IO\\hello.txt");
System.out.println(file1.exists());
System.out.println(file1.isDirectory());
System.out.println(file1.isFile());
File file2 = new File("E:\\..\\.\\..\\JavaStudy\\Bite比特\\课件\\课堂程序\\IO\\");
String absolutePath = file1.getAbsolutePath();
String canonicalPath = file1.getCanonicalPath();
System.out.println(absolutePath);
System.out.println(canonicalPath);
System.out.println(file2.getName());
System.out.println(file2.getParent()); System.out.println(file2.getParentFile().getCanonicalPath());
IastModified ( ): 得到该文件最后一次被修改的时间(以时间戳 timestamp 形式表现)
System.currentTimeMillis():得到当前时间的时间戳timestamp,代表从某个时刻(固定值)到某个时间经过的秒数\毫秒数
long s = System.currentTimeMillis(); // 当前时间的时间戳,单位:毫秒
Thread.sleep(1000);
long e = System.currentTimeMillis();
long passMs = e - s; // 经过的毫秒数
System.out.println(passMs);
File[] listFiles():返回目录下的所有孩子,常用于做树的遍历(多个孩子的树:深度优先遍历——数组,广度优先遍历——队列)
File[] files1 = file1.listFiles(); // 叶子节点对象,返回null
File[] files2 = file2.listFiles(); // 空目录对象,返回空数组: []
System.out.println(Arrays.toString(files1));
System.out.println(Arrays.toString(files2));
createNewFile、mkdir、mkdirs方法的区别
createNewFile ( ) :创建是个普通文件
mkdir ( ) :make directory创建是个目录文件,失败原因:文件已经存在、路径上的祖先不存在
mkdirs():创建是个目录文件,可以一并把路径中目前不存在的目录都创建出来。
// a b c 是未创建的目录,若是创建\\c\\hello.txt,则将hello.txt作为目录名创建一个新文件
File file3 = new File("E:\\JavaStudy\\Bite比特\\课件\\课堂程序\\IO\\a\\b\\c");
System.out.println(file3.mkdirs());
boolean renameTo ( File dest ):重命名,可实现 1.文件修改名称 2.剪切+粘贴
File file4 = new File("E:\\JavaStudy\\Bite比特\\课件\\课堂程序\\IO\\hello.txt");
// 1.文件修改名称
File dest1 = new File("E:\\JavaStudy\\Bite比特\\课件\\课堂程序\\IO\\bye.txt");
file4.renameTo(dest1);
// 2.剪切+粘贴
File dest2 = new File("E:\\JavaStudy\\Bite比特\\课件\\课堂程序\\IO\\a\\hello.txt");
dest1.renameTo(dest2);