Java API 的基础学习: Object, Objects, Arrays, String, 包装类, 集合, Stream流, File, IO流, System, Runtime, BigInteger, 正则表达式
API
API: 应用程序编程接口
, 即已经写好的东西, 可以直接使用
String
| String name = "abc"; |
| name = "def"; |
| |
| |
| String s = "abc"; |
| |
| String s = new String(); |
| |
| String s = new String("abc"); |
| |
| char[] chs = {'a', 'b', 'c'}; |
| String s = new String(chs); |
| |
| byte[] bytes = {97, 98, 99, 100}; |
| String s = new String(bytes); |
- 使用双引号直接赋值时, 如果该字符串在
串池
中已存在, 会复用
字符串
| String s1 = "abc"; |
| String s2 = "abc"; |
| |
| |
- 使用
new
创建时, 每次 new
会在堆内存
中开辟一个新空间, 不会 复用
| char[] chs = {'a', 'b', 'c'}; |
| String s1 = new String(chs); |
| String s2 = new String(chs); |
| |
| |
StringBuilder
可以看成一个容器, 创建之后里面的内容可以改变
因此拼接字符串时, 不会每次产生一个新的字符串, 提高效率
| StringBuilder s = new StringBuilder("abc"); |
| |
| s.append("def"); |
| |
| s.reverse(); |
| |
| int len = s.length(); |
| |
| String str = s.toString(); |
StringJoiner
| StringJoiner s = new StringJoiner(间隔符号, 开始符号, 结束符号); |
| |
| e.g. StringJoiner s = new StringJoiner("---", "(", ")"); |
| |
| s.add("abc"); |
| |
| int len = s.length(); |
| |
| String str = s.toString(); |
System
| System.exit(0); |
| |
| long l = System.currentTimeMillis(); |
| |
| |
| System.arraycopy(arr1, 0, arr2, 0, 10); |
| |
| |
| |
Runtime
| Runtime r1 = Runtime.getRuntime(); |
| |
| r1.exit(0); |
| |
| int cpuProcessors = r1.availableProcessors(); |
| |
| long maxM = r1.maxMemory(); |
| |
| long totM = r1.totalMemory(); |
| |
| long freeM = r1.freeMemory(); |
| |
| r1.exac("写 cmd 命令"); |
Object
toString
| Student stu = new Student(); |
| String s = stu.toString(); |
| sout(s); |
| |
| |
equals
| Student s1 = new Student(); |
| Student s2 = new Student(); |
| boolean result = s1.equals(s2); |
| |
| |
| |
| |
| |
clone
clone
需要重写, 并让类实现 Cloneable
接口
-
e.g. User
| public class User implements Cloneable { |
| |
| |
| @Override |
| public String toString() { |
| return name + ", " + id; |
| } |
| |
| @Override |
| protected Object clone() throws CloneNotSupportedException { |
| |
| return super.clone(); |
| } |
| } |
-
Cloneable
这个接口中没有抽象方法, 表示当前接口是一个标记行接口
-
实现 Cloneable
表示当前类的对象可以被克隆
-
e.g.
| public static void main(String[] args) throws CloneNotSupportedException { |
| User u1 = new User("zhangsan", "123"); |
| User u2 = (User)u1.clone(); |
| |
| |
| System.out.println(u1); |
| System.out.println(u2); |
| } |
-
浅克隆
- 基本数据类型: 完全拷贝数据值
- 引用数据类型: 完全拷贝地址值
Object.clone()
是浅克隆
-
深克隆
- 基本数据类型: 完全拷贝数据值
- 字符串: 复用
- 引用数据类型: 重新创建新的
- 重写
clone
以深克隆 e.g.
| |
| @Override |
| protected Object clone() throws CloneNotSupportedException { |
| int[] data = this.data; |
| int[] newData = new int[data.length]; |
| |
| for (int i = 0; i < data.length; i++) { |
| newData[i] = data[i]; |
| } |
| |
| User u = (User)super.clone(); |
| u.data = newData; |
| |
| return u; |
| } |
- 实际开发中, 一般使用序列化再反序列化
- 使用第三方工具如
Jackson
或者 Gson
, 将对象转换为 JSON
字符串, 再从 JSON
字符串重建新的对象
Objects
一个对象工具类, 注意不是 Object
, 因为有 s
| boolean flag = Objects.isNull(对象); |
| boolean flag = Objects.nonNull(对象); |
| boolean flag = Objects.equals(对象1, 对象2); |
isNull
与 nonNull
相反
equals
会先作非空判断, 再比较两个对象
BigInteger
BigInteger 是 Java 提供的高精度计算类, 可以很方便地解决高精度问题。
初始化
| import java.io.PrintWriter; |
| import java.math.BigInteger; |
| |
| class Main { |
| static PrintWriter out = new PrintWriter(System.out); |
| public static void main(String[] args) { |
| |
| |
| BigInteger a = new BigInteger("12345678910"); |
| out.println(a); |
| |
| |
| BigInteger b = new BigInteger("1E", 16); |
| out.println(b); |
| |
| |
| BigInteger c = new BigInteger(31, new Random()); |
| out.println(c); |
| |
| |
| |
| BigInteger d = BigInteger.valueOf(100); |
| out.println(d); |
| |
| out.close(); |
| } |
| } |
对象一旦创建, 内部记录的值不能发生改变
基本运算
以下均用 this 代替当前 BigIntger :
函数名 |
功能 |
abs() |
返回 this 的绝对值 |
negate() |
返回 - this |
add(BigInteger val) |
返回 this + val |
subtract(BigInteger val) |
返回 this - val |
multiply(BigInteger val) |
返回 this * val |
divide(BigInteger val) |
返回 this / val |
remainder(BigInteger val) |
返回 this % val |
divideAndRemainder(BigInteger val) |
返回一个数组, 包含 this / val 和 this % val |
mod(BigInteger val) |
返回 this mod val |
pow(int e) |
返回 this^e |
and(BigInteger val) |
返回 this & val |
or(BigInteger val) |
返回 this |
not() |
返回 ~ this |
xor(BigInteger val) |
返回 this ^ val |
shiftLeft(int n) |
返回 this << n |
shiftRight(int n) |
返回 this >> n |
max(BigInteger val) |
返回 this 与 val 的较大值 |
min(BigInteger val) |
返回 this 与 val 的较小值 |
bitCount() |
返回 this 的二进制中不包括符号位的 1 的个数 |
bitLength() |
返回 this 的二进制中不包括符号位的长度 |
getLowestSetBit() |
返回 this 的二进制中最右边的位置 |
compareTo(BigInteger val) |
比较 this 和 val 值大小 |
toString() |
返回 this 的 10 进制字符串表示形式 |
toString(int radix) |
返回 this 的 raidx 进制字符串表示形式 |
intValue(BigInteger val) |
转为 int 类型, 超出范围数据出错 |
BigDecima
正则表达式
- 校验字符串是否满足规则
- 在一段文本中查找满足要求的内容
如何书写
字符类
代码 |
范围 |
[abc] |
a,b,c |
[^abc] |
除 a,b,c |
[a-zA-z] |
a-z, A-Z |
[a-d[m-p]] |
a-d, m-p |
[a-z&&[def]] |
d,e,f |
[a-z&&[^bc]] |
a, d-z |
[a-z&&[^m-p]] |
a-l, q-z |
预定义字符(只匹配一个字符)
字符 |
范围 |
. |
任意字符 |
\d |
数字 |
\D |
非数字 |
\s |
一个空白字符 |
\S |
非空白字符 |
\w |
英文, 数字, 下划线 |
\W |
一个非单词字符(非"英文, 数字, 下划线") |
练习
[[D:/java_code/regex/Test.java|手机,座机,邮箱]]
插件
any-rule
, 支持 idea
, vscode
爬虫
基础爬取和有条件的爬取
| String str = "Java有Java8,Java11,还有Java17."; |
| |
| |
| { |
| |
| Pattern p = Pattern.compile("Java\\d{0,2}"); |
| |
| |
| Matcher m = p.matcher(str); |
| |
| |
| boolean b = m.find(); |
| System.out.println(b); |
| |
| |
| String s = m.group(); |
| System.out.println(s); |
| |
| |
| b = m.find(); |
| System.out.println(b); |
| |
| |
| s = m.group(); |
| System.out.println(s); |
| |
| |
| while (m.find()) { |
| System.out.println(m.group()); |
| } |
| } |
| |
| |
| |
| |
| { |
| Pattern p = Pattern.compile("(?i)Java(?=8|11|17)"); |
| Matcher m = p.matcher(str); |
| |
| while (m.find()) { |
| System.out.println(m.group()); |
| } |
| } |
| |
| |
| { |
| Pattern p = Pattern.compile("(?i)Java(?:8|11|17)"); |
| Matcher m = p.matcher(str); |
| |
| while (m.find()) { |
| System.out.println(m.group()); |
| } |
| } |
| |
| |
| { |
| Pattern p = Pattern.compile("(?i)Java(?!8|11|17)"); |
| Matcher m = p.matcher(str); |
| |
| while (m.find()) { |
| System.out.println(m.group()); |
| } |
| } |
贪婪爬取与非贪婪爬取
捕获分组和非捕获分组
时间相关类
包装类
用一个对象, 把基础数据类型包起来
| byte -> Byte |
| short -> Short |
| char -> Character |
| int -> Integer |
| long -> Long |
| float -> Float |
| double -> Double |
| boolean -> Boolean |
创建
| public static void main(String[] args) { |
| Integer i1 = new Integer(1); |
| Integer i2 = new Integer("1"); |
| |
| Integer i3 = Integer.valueOf(123); |
| Integer i4 = Integer.valueOf("123"); |
| Integer i5 = Integer.valueOf("123", 8); |
| sout(i5); |
| |
| |
| Integer i6 = Integer.valueOf(127); |
| Integer i7 = Integer.valueOf(127); |
| sout(i6 == i7); |
| } |
JDK5 以后可以自动装箱, 自动拆箱
, 赋值和运算直接写即可
| Integer i1 = 10; |
| Integer i2 = 20; |
| Integer i3 = i1 + i2; |
| int i4 = i1; |
Integer 成员方法
| String str1 = Integer.toBinaryString(100); |
| String str2 = Integer.toOctalString(100); |
| String str3 = Integer.toHexString(100); |
| |
| int i = Integer.parseInt("123"); |
| |
Arrays
操作数组的工具类
| int[] a = {1, 2, 3}; |
| |
| String s = Arrays.toString(a); |
| int pos = Arrays.binarySearch(a, 2); |
| int[] b = Arrays.copyOf(a, 10); |
| int[] c = Arrays.copyOfRange(a, 1, 2); |
| Arrays.fill(a, 0); |
| Arrays.sort(a); |
| Arrays.sort(a, cmp); |
cmp 举例
| Integer[] arr = {2, 3, 1, 5, 6, 7, 8, 4, 9}; |
| |
| |
| |
| |
| |
| |
| |
| Arrays.sort(arr, new Comparator<Integer>(){ |
| @Override |
| public int compare(Integer o1, Integer o2) { |
| return o1 - o2; |
| } |
| |
| |
| |
| |
| |
| }); |
集合
单列集合: Collection
, 双列集合: Map
Java 集合对应 C++ STL
集合类不能直接存储基础数据类型, 因为其设计用于存储对象
, 只能存储对应的包装类
| List<Integer> a = new ArrayList<>(); |
| List<Integer> a = new LinkedList<>(); |
| Set<Integer> s = new HashSet<>(); |
| Set<Integer> s = new TreeSet<>(); |
| Map<String, Interger> mp = new HashMap<>(); |
| Map<String, Interger> mp = new TreeMap<>(); |
| Stack<Integer> st = new Stack<>(); |
| Queue<Integer> q = new LinkedList<>(); |
| Deque<Integer> q = new ArrayDeque<>(); |
| PriorityQueue<Integer> q = new PriorityQueue<>(); |
Collection
单列集合的祖宗接口(是个接口, 不能直接创建其对象, 只能创建实现类的对象)
| Collection<String> coll = new ArrayList<>(); |
| coll.add("123"); |
| coll.clear(); |
| coll.remove("123"); |
| bool flag1 = coll.contains("123"); |
| bool flag2 = coll.isempty(); |
| int size = coll.size(); |
遍历
迭代器遍历
| Collection<String> coll = new ArrayList<>(); |
| Iterator<String> it = coll.iterator(); |
| while (it.hasNext()) { |
| String str = it.next(); |
| sout(str); |
| } |
- 迭代器遍历时, 不能使用集合的增加或删除操作, 但可以
it.remove()
在遍历时删除元素
增强 for 遍历
| for(String s : coll) { |
| s = "xxx"; |
| } |
| |
| |
Lambda 遍历
| |
| |
| |
| coll.forEach(new Consumer<String>() { |
| @Override |
| public void accept(String s) { |
| sout(s); |
| } |
| }); |
| |
| |
| |
| coll.forEach(s -> sout(s)); |
List
| List<Integer> list = new ArrayList<>(); |
| |
| list.add(1); |
| |
| list.remove(0); |
| |
| Integer i = Integer.valueOf(1); |
| list.remove(i); |
| |
| |
| List<String> list = new ArrayList<>(); |
| list.add("abc"); |
| String s = list.set(0, "123"); |
| s = list.get(0); |
泛型
泛型: 可以在编译阶段约束操作的数据类型, 并进行检查
格式: <数据类型>
注意: 泛型只支持引用数据类型
- 如果没有泛型, 可以给集合添加任意类型的元素, 默认为
Object
, 不能使用类的特有行为
- Java 的泛型是伪泛型
- 指定泛型的具体类型后, 可以传入其子类类型
泛型类
编写一个类, 如果不确定类型, 可以定义为泛型类
修饰符 class 类名<类型> {}
比如
| |
| |
| public class MyArrayList<E> { |
| Object[] obj = new Object[10]; |
| int size; |
| |
| public boolean add(E e) { |
| obj[size] = e; |
| size++; |
| return true; |
| } |
| |
| public E get(int index) { |
| return (E)obj[index]; |
| } |
| |
| @Override |
| public String toString() { |
| return Arrays.toString(obj); |
| } |
| } |
泛型方法
修饰符 <类型> 返回值类型 方法名(类型 变量名) {}
比如
| public static <E> void addAll(ArrayList<E> list1, ArrayList<E> list2) { |
| if (list2 == null) { |
| return; |
| } |
| list2.forEach(e -> list1.add(e)); |
| } |
| public static <E> void addAll(ArrayList<E> list, E...e) { |
| for (E element : e) { |
| list.add(element); |
| } |
| } |
泛型接口
修饰符 interface 接口名<类型> {}
比如
| public interface List<E> extends Collection<E> { |
| |
| } |
注意
泛型不具备继承性, 但是数据具备继承性
(泛型里面写什么类型, 那么只能传递什么类型的数据)
通配符
如何不确定类型, 但又想限定类型的一定 范围, 需要使用通配符
| |
| public static void method(ArrayList<? extends xxx> list) { |
| |
| } |
| |
| |
| public static void method(ArrayList<? super xxx> list) { |
| |
| } |
Hash
hashCode
- 哈希值是根据
Objrct.hashCode()
计算得到的 int
类型整数
- 默认使用地址值计算
- 一般要重写
hashCode
, 使用对象内部的属性值计算哈希值
| @Override |
| public int hashCode() { |
| return Objects.hash(name, age); |
| } |
- 哈希表存储自定义对象要重写
equals
和 hashCode
LinkedHashSet
相较 HashSet
, 可以保证数据存取有序(底层使用双链表记录添加顺序)
Tree
默认排序
- 默认排序/自然排序:
Javabean
类实现 Comparable
接口, 重写里面的抽象方法, 指定比较规则
| public class Student implements Comparable<Student> { |
| private int age; |
| |
| public Student() { |
| |
| } |
| |
| public Student(int age) { |
| this.age = age; |
| } |
| |
| public int getAge() { |
| return age; |
| } |
| |
| public void setAge(int age) { |
| this.age = age; |
| } |
| |
| @Override |
| public iint compareTo(Student o) { |
| return this.getAge() - o.getAge(); |
| } |
| |
| |
| |
| |
| |
| |
| |
| } |
比较器排序
- 比较器排序: 创建 TreeSet 对象时, 传递比较器
Comparator
指定规则
| |
| |
| TreeSet<String> ts = new TreeSet<>(new Comparator<String>() { |
| @Override |
| public int compare(String o1, String o2) { |
| int diff = o1.length() - o2.length(); |
| return diff == 0 ? o1.compareTo(o2) : diff; |
| } |
| }); |
| |
| |
| TreeSet<String> ts = new TreeSet<>((o1, o2) -> { |
| int diff = o1.length() - o2.length(); |
| return diff == 0 ? o1.compareTo(o2) : diff; |
| }); |
Map
键值对: Entry
| Map<String, String> mp = new HashMap<>(); |
| |
| mp.put("1", "b"); |
| mp.put("1", "c"); |
| |
| mp.remove("1"); |
| |
| mp.clear(); |
| |
| boolean flag1 = mp.containsKey("1"); |
| boolean flag2 = mp.containsValue("2"); |
| |
| boolean tag = mp.isEmpty(); |
| |
| int size = mp.size(); |
| |
| Set<String> keys = mp.keySet(); |
| for (String key : keys) { |
| String value = mp.get(key); |
| } |
| |
| |
| Set<Map.Entry<String, String>> entries = map.entrySet(); |
| for (Map.Entry<String, String> entry : entries) { |
| String key = entry.getKey(); |
| String value = entry.getValue(); |
| } |
| |
| |
| mp.forEach(new BiConsumer<String, String>() { |
| @Override |
| public void accept(String key, String value) { |
| sout(key + "=" + value); |
| } |
| }); |
| |
| mp.forEach((key, value) -> sout(key + "=" + value)); |
HashMap
LinkedHashMap
相较 HashMap
, 可以保证数据存取有序(底层使用双链表记录添加顺序)
TreeMap
Collections
集合工具类
| addAll, shuffle, sort, binarySearch, copy, fill, max/min, swap |
不可变集合
可变参数: 底层是一个数组, 只不过不需要我们创建
| public static int getSum(int...args) { |
| int sum = 0; |
| for (int i : args) { |
| sum += i; |
| } |
| return sum; |
| } |
- 方法的形参中, 只能写一个可变参数
- 如果有可变参数, 还有其他的参数, 可变参数要写在最后
不可变集合: 不能添加, 不能删除, 不能修改, 只能查询
| |
| |
| List<String> list = List.of("abc", "def"); |
| Set<String> set = Set.of("abc", "def"); |
| Map<String, String> map = Map.of("a", "1", "b", "2"); |
Stream 流
| ArrayList<String> list = new ArrayList<>(); |
| |
| |
| list.stream().filter(name -> name.startswith("a")).filter(name -> name.length() == 3).forEach(name -> sout(name)); |
- 先获取一条流, 把数据放上去
- 利用 Stream 流的 API 进行操作
获取
- 单列集合
| ArrayList<String> list = new ArrayList<>(); |
| Collectinos.addAll(list, "a", "b", "c"); |
| Stream<String> streamList = list.stream(); |
| streamList.forEach(s -> sout(s)); |
- 双列集合
| mp.keySet().stream().forEach(s -> sout(s)); |
| mp.entrySet().stream().forEach(s -> sout(s)); |
- 数组
| int[] arr = {1, 2, 3}; |
| Arrays.stream(arr).forEach(s -> sout(s)); |
- 一堆同类型的零散数据
| Stream.of(1, 2, 3).forEach(s -> sout(s)); |
| |
| |
| |
| |
中间方法
filter
- 过滤
limit
- 获取前几个元素
skip
- 跳过前几个元素
distinct
- 元素去重, 依赖 hashCode, equals
concat(Stream a, Stream b)
- 合并两个流为一个流
Stream<R> map(Functino<T, R> mapper)
- 转换流中的数据类型
- 注意
- 中间方法返回新的 Stream 流, Stream 流只能用一次, 建议链式编程
- 修改 Stream 流中的数据, 不会影响原来集合或数组中的数据
终结方法
| list.stream().forEach(s -> sout(s)); |
| long cnt = list.stream.count(); |
-
收集
| Object[] arr = list.stream().toArray(); |
| list.stream().toArray(new IntFunction<? extends Object[]>() { |
| @Override |
| public Object[] apply(int value) { |
| return new Object[0]; |
| } |
| }); |
| |
| |
| |
| String[] arr = list.stream().toArray(new IntFunction<String[]>() { |
| @Override |
| public String[] apply(int value) { |
| return new String[value]; |
| } |
| }); |
| |
| |
| |
| String[] arr = list.stream().toArray(value -> new String[value]); |
-
collect()
| List<String> newList = list.stream() |
| .filter(s -> "男".equals(s.split("-")[1])) |
| .collect(Collectors.toList()); |
| Map<String, Integer> map = list.stream() |
| .filter(s -> "男".equals(s.split("-")[1])) |
| .collect(Collectors.toMap( |
| |
| |
| new Function<String, String>() { |
| @Override |
| public String apply(String s) { |
| return s.split("-")[0]; |
| } |
| }, |
| |
| |
| new Function<String, Integer>() { |
| @Override |
| public Integer apply(String s) { |
| return Integer.parseInt(s.split("-")[2]); |
| } |
| })); |
| |
| |
| Map<String, Integer> map = list.stream() |
| .filter(s -> "男".equals(s.split("-")[1])) |
| .collect(Collectors.toMap( |
| s -> s.split("-")[0], |
| s -> Integer.parseInt(s.split("-")[2]) |
| )); |
File
-
File 对象表示路径, 可以是文件, 也可以是文件夹
-
路径可以存在, 也可以不存在
-
绝对路径带盘符, 相对路径默认在当前项目下找
-
三种构造方法
| String path = "D:\\temp\\temp.txt"; |
| File f = new File(path); |
| sout(f); |
| String parent = "D:\\temp"; |
| String child = "temp.txt"; |
| File f = new File(parent, child); |
| sout(f); |
| File parent = new File("D:\\temp"); |
| String child = "temp.txt"; |
| File f = new File(parent, child); |
| sout(f); |
判断, 获取
| isDirectory - 判断是否为文件夹 |
| isFile - 判断是否为文件 |
| exists - 判断是否存在 |
| length - 返回文件大小 (字节数量) |
| getAbsolutePath - 返回绝对路径 |
| getPath - 返回定义文件时使用的路径 |
| getName - 返回文件的名称, 带后缀 |
| lastModified - 返回文件的最后修改时间 (时间毫秒值) |
创建, 删除
| createNewFile - 创建一个新的空的文件 |
| mkdir - 创建单极文件夹 |
| mkdirs - 创建多级文件夹 |
| delete - 删除文件或空文件夹 (不走回收站) |
获取并遍历
public File[] listFiles()
获取当前路径下所有内容, 包含文件, 文件夹和隐藏文件
- 路径不存在/路径是文件/需要权限才能访问的文件夹, 返回
null
- 空文件夹 - 返回长度为 0 的数组
| File[] arr = File.listRoots(); |
| String[] arrString = f.list(); |
| File[] arrFile = f.listFiles(); |
| String[] arrString = f.list(new FilenameFilter() { |
| @Override |
| public boolean accept(File dir, String name) { |
| File src = new File(dir, name); |
| return src.isFile() && name.endsWith(".txt"); |
| } |
| }); |
| |
| File[] arrFile = f.listFiles(new FileFilter() { |
| @Override |
| public boolean accept(File pathname) { |
| return pathname.isFile() && pathname.getName().endsWith(".txt"); |
| } |
| }); |
| |
| File[] arrFile = f.listFiles(new FilenameFilter() { |
| @Override |
| public boolean accept(File dir, String name) { |
| File src = new File(dir, name); |
| return src.isFile() && name.endsWith(".txt"); |
| } |
| }); |
IO 流
用于读写文件中的数据(可以读写文件, 或网络中的数据...)
- 分类
- 方向
- 操作文件
- 字节流 - 所有类型文件
- 字符流 - 纯文本文件
- word, excel 不是纯文本文件
- txt, md 是纯文本文件
字节流
OutputStream
- FileOutputStream
- 操作本地文件的字节输出流, 可以把程序中的数据写到本地文件
- 步骤
| public static void main(String[] args) throws IOException { |
| |
| FileOutputStream fos = new FileOutputStream("flow\\temp\\a.txt"); |
| fos.write(97); |
| fos.close(); |
| } |
| FileOutputStream fos = new FileOutputStream("flow\\temp\\a.txt"); |
| FileOutputStream fos = new FileOutputStream(new File("flow\\temp\\a.txt")); |
- 如果文件不存在会创建一个新的文件, 但要保证父级路径存在
- 如果文件已存在, 会清空文件
- write 写入整数参数在 ASCII 上对应的字符
| |
| fos.write(97); |
| |
| |
| fos.write(57); |
| fos.write(55); |
- 不释放资源, Java 会占用文件, 无法删除
- 写出数据
| byte[] bytes = {97, 98, 99, 100}; |
| fos.write(bytes); |
| |
| byte[] bytes = {97, 98, 99, 100}; |
| fos.write(bytes, 2, 2); |
| String str = "abc"; |
| byte[] arr = str.getBytes(); |
| String str1 = "abc"; |
| byte[] arr1 = str1.getBytes(); |
| fos.write(arr1); |
| |
| |
| |
| |
| |
| String wrap = "\r\n"; |
| byte[] arrWrap = wrap.getBytes(); |
| fos.write(arrWrap); |
| |
| String str2 = "123"; |
| byte[] arr2 = str2.getBytes(); |
| fos.write(arr2); |
| |
| fos.close(); |
| FileOutputStream fos = new FileOutputStream("flow\\temp\\a.txt", true); |
- FileInputStream
- 操作本地文件的字节输入流, 可以把本地文件中的数据读取到程序中来
- 步骤
| public static void main(String[] args) throws IOException { |
| |
| FileInputStream fis = new FileInputStream("flow\\temp\\a.txt"); |
| int b1 = fis.read(); |
| System.out.println((char)b1); |
| fis.close(); |
| } |
- 如果文件不存在, 直接报错
- 一次读一个字节, 读出来的是数据对应 ASCII 码
- 如果读不到, read 会返回 -1
- 循环读取
| FileInputStream fis = new FileInputStream("flow\\temp\\a.txt"); |
| |
| int b; |
| while ((b = fis.read()) != -1) { |
| sout((char)b); |
| } |
| |
| fis.close(); |
文件拷贝
| FileInputStream fis = new FileInputStream("D:\\temp\\movie.mp4"); |
| FileOutputStream fos = new FileOutputStream("flow\\copy.mp4"); |
| |
| int b; |
| while ((b = fis.read()) != -1) { |
| fos.write(b); |
| } |
| |
| |
| fos.close(); |
| fis.close(); |
注意: 先开的后关闭
| FileInputStream fis = new FileInputStream("D:\\temp\\movie.mp4"); |
| FileOutputStream fos = new FileOutputStream("flow\\copy.mp4"); |
| |
| int len; |
| byte[] bytes = new byte[1024 * 1024 * 5]; |
| while ((len = fis.read(bytes)) != -1) { |
| fos.write(bytes, 0, len); |
| } |
| |
| |
| fos.close(); |
| fis.close(); |
IO 流中捕获异常
需要执行 close
, 但可能 close
前面的代码就有异常, 跳转到 catch 导致 close
没有执行
| try { |
| FileOutputStream fos = new FileOutputStream("xxx"); |
| |
| } catch () { |
| |
| } finally { |
| |
| |
| fos.close(); |
| } |
| FileInputStream fis = null; |
| FileOutputStream fos = null; |
| |
| try { |
| fis = new FileInputStream("D:\\temp\\movie.mp4"); |
| fos = new FileOutputStream("flow\\cpoy.mp4"); |
| |
| int len; |
| byte[] bytes = new byte[1024 * 1024 * 5]; |
| while ((len = fis.read(bytes)) != -1) { |
| fos.write(bytes, 0, len); |
| } |
| } catch (IOException e) { |
| |
| } finally { |
| if (fos != null) { |
| try { |
| fos.close(); |
| } catch (IOException e) { |
| e.printStackTrace(); |
| } |
| } |
| |
| if (fis != null) { |
| try { |
| fis.close(); |
| } catch (IOException e) { |
| e.printStackTrace(); |
| } |
| } |
| } |
- 简化方案
| |
| try (FileInputStream fis = new FileInputStream("D:\\temp\\movie.mp4"); |
| FileOutputStream fos = new FileOutputStream("flow\\copy.mp4")) { |
| |
| 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(); |
| } |
| FileInputStream fis = new FileInputStream("D:\\temp\\movie.mp4"); |
| FileOutputStream fos = new FileOutputStream("flow\\copy.mp4"); |
| |
| try (fis; fos) { |
| 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 字符集中一个英文占一个字节
| a -> 97 (0110 0001), 一个字节 8 位 |
| public byte[] getBytes() 默认方式编码 UTF-8 |
| public byte[] getBytes(String charsetName) 指定方法编码 |
| String(byte[] bytes) 默认方式解码 |
| String(byte[] bytes, String charsetName) 指定方式解码 |
字符流
字符流 = 字节流 + 字符集
输入流: 一次读一个字节, 遇到中文一次读多个字节
输出流: 底层把数据按照指定的编码方式编码, 变成字节再写到文件中
Reader
- FileReader
public int read()
读取之后, 底层会进行解码转成十进制, 并返回这个十进制; 十进制表示在字符集上的数字
| FileReader fr = new FileReader("xxx"); |
| |
| int ch; |
| while ((ch = fr.read()) != -1) { |
| System.out.print((char)ch); |
| } |
| |
| fr.close(); |
public int read(char[] buffer)
一次读取多个, 然后解码并强转, 返回字符到字符数组中
| char[] chars = new char[2]; |
| |
| int len; |
| while ((len = fr.read(chars)) != -1) { |
| System.out.print(new String(chars, 0, len)); |
| } |
| |
| fr.close(); |
Writer
- FileWriter
| void write(int c) |
| void write(String str) |
| void write(String str, int off, int len) |
| void write(char[] cbuf) |
| void write(char[] cbuf, int off, int len) |
练习 - 拷贝文件夹
| |
| |
| private static void copydir(File src, File dest) throws IOException { |
| dest.mkdirs(); |
| |
| File[] files = src.listFiles(); |
| |
| if (files == null) { |
| |
| } |
| |
| for (File file : files) { |
| if (file.isFile()) { |
| FileInputStream fis = new FileInputStream(file); |
| FileOutputStream fos = new FileOutputStream(new File(dest, file.getName())); |
| |
| byte[] bytes = new byte[1024]; |
| int len; |
| while ((len = fis.read(bytes)) != -1) { |
| fos.write(bytes, 0, len); |
| } |
| |
| fos.close(); |
| fis.close(); |
| } else { |
| copydir(file, new File(dest, file.getName())); |
| } |
| } |
| } |
缓冲流
缓冲流自带长度为 8192 的缓冲区
Bufferedxxx
把基本流 xxx 包装成高级流, 提高读取数据的性能
字节缓冲流
| public BufferedInputStream(InputStream is) |
| public BufferedOutputStream(OutputStream os) |
| BufferedInputStream bis = new BufferedInputStream(new FileInputStream("xxx")); |
| BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("xxx")); |
| |
| |
| int b; |
| while ((b = bis.read()) != -1) { |
| bos.write(b); |
| } |
| |
| |
| byte[] bytes = new byte[1024]; |
| int len; |
| while ((len = bis.read()) != -1) { |
| bos.write(bytes, 0, len); |
| } |
| |
| bos.close(); |
| bis.close(); |
字符缓冲流
| public BufferedReader(Reader r) |
| public BufferedWriter(Writer w) |
- BufferedReader 特有方法
public String readLine() // 读一行至回车停止, 但不会读入回车
- BufferedWriter 特有方法
public void newLine() // 跨平台的换行
转换流
字符流和字节流之间的桥梁
-
利用转换流按照指定字符编码读取
| InputStreamReader isr = new InputStreamReader(new FileInputStream("xxx"), "GBK"); |
| |
| int ch; |
| while ((ch = isr.read()) != -1) { |
| System.out.print((char)ch); |
| } |
| |
| isr.close(); |
| |
| |
| FileReader fr = new FileReader("xxx", Charset.forName("GBK")); |
| |
| int ch; |
| while ((ch = fr.read()) != -1) { |
| System.out.print((char)ch); |
| } |
| |
| fr.close(); |
| |
| |
-
将本地 GBK 文件转成 UTF-8
| InputStreamReader isr = new InputStreamReader(new FileInputStream("flow\\temp\\a.txt"), "GBK"); |
| OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("flow\\temp\\a.txt"), "UTF-8"); |
| |
| int b; |
| while ((b = isr.read()) != -1) { |
| osw.write(b); |
| } |
| |
| osw.close(); |
| isr.close(); |
| |
| |
| FileReader fr = new FileReader("flow\\temp\\a.txt", Charset.forName("GBK")); |
| FileWriter fw = new FileWriter("flow\\temp\\a.txt", Charset.forName("UTF-8")); |
| |
| int b; |
| while ((b = fr.read()) != -1) { |
| fw.write(b); |
| } |
| |
| fw.close(); |
| fr.close(); |
序列化流/对象操作输出流
把 Java 中的对象写到本地文件中
| |
| public ObjectOutputStream(OutputStream out) |
| |
| |
| public final void writeObject(Object obj) |
- 用对象输出流把对象保存到文件会出现
NotSerializableException
异常
- 需要让
Javabean类
实现 Serializalbe
接口
| |
| |
| public class Student implements Serializable { |
| private String name; |
| private int age; |
| } |
反序列化流/对象操作输入流
把序列化到本地文件中的对象读取到程序中来
| |
| public ObjectInputStream(InputStream in) |
| |
| |
| public Object readObject() |
序列化流和反序列化流细节
(1) 对 Student
修改后 (比如加了一个 private String ID
), Javabean类
的版本号改变, 如果反序列化修改前的序列化的对象就会出错
解决方案: 固定版本号
- 自己设定版本号
| public class Student implements Serializable { |
| private static final long serialVersionUID = 1L; |
| |
| private String name; |
| private int age; |
| } |
- 设置
idea
以自动生成版本号
(2) 有的信息不想序列化
解决方案: transient
关键字
| public class Student implements Serializable { |
| private static final long serialVersionUID = 1L; |
| |
| private String name; |
| private int age; |
| |
| |
| |
| private transient String address; |
| } |
(3) 序列化流写道文件中的数据不能修改, 一旦修改就无法再次读回来了
打印流
- 只操作文件目的地, 不操作数据源
- 特有的写出方法可以实现
数据原样写出
- 特有的写出方法可以实现
自动刷新, 自动换行
字节打印流
| PrintStream ps = new PrintStream(new FileOutputStream("xxx"), true, Charset.forName("UTF-8")); |
| ps.println(97); |
| ps.println(true); |
| ps.print("%s和%s", "a", "b"); |
| ps.close(); |
字符打印流
| PrintWriter pw = new PrintWriter(new FileWriter("xxx"), true); |
| pw.println(97); |
| pw.println(true); |
| pw.print("%s和%s", "a", "b"); |
| pw.close(); |
特殊的打印流
| PrintStream ps = System.out; |
| |
| |
| ps.println("123"); |
解压缩流
压缩包中的每一个文件在 Java 中为一个 ZipEntry 对象, 解压的本质就是把每一个 ZipEntry 按照层级拷贝到本地另一个文件夹里
| public static void main(String[] args) throws IOException { |
| |
| File src = new File("D:\\temp.zip"); |
| File dest = new File("D:\\"); |
| unzip(src, dest); |
| } |
| |
| public static void unzip(File src, File dest) throws IOException { |
| |
| ZipInputStream zip = new ZipInputStream(new FileInputStream(src)); |
| ZipEntry entry; |
| |
| while ((entry = zip.getNextEntry()) != null) { |
| if (entry.isDirectory()) { |
| File file = new File(dest, entry.toString()); |
| file.mkdirs(); |
| } else { |
| FileOutputStream fos = new FileOutputStream(new File(dest, entry.toString())); |
| int b; |
| while ((b = zip.read()) != -1) { |
| fos.write(b); |
| } |
| fos.close(); |
| |
| zip.closeEntry(); |
| } |
| } |
| |
| zip.close(); |
| } |
压缩流
| public static void toZip(File src, File dest) throws IOException { |
| ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(new File(dest, "a.zip"))); |
| ZipEntry entry = new ZipEntry("a.txt"); |
| zos.putNextEntry(entry); |
| |
| FileInputStream fis = new FileInputStream(src); |
| int b; |
| while ((b = fis.read()) != -1) { |
| zos.write(b); |
| } |
| fis.close(); |
| |
| zos.closeEntry(); |
| zos.close(); |
| } |
| public static void main(String[] args) throws IOException { |
| File src = new File("D:\\aaa"); |
| File destParent = src.getParentFile(); |
| File dest = new File(destParent, src.getName() + ".zip"); |
| |
| ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(dest)); |
| |
| toZip(src, zos, src.getName()); |
| |
| zos.close(); |
| } |
| |
| public static void toZip(File src, ZipOutputStream zos, String name) throws IOException { |
| File[] files = src.listFiles(); |
| |
| for (File file : files) { |
| if (file.isFile()) { |
| ZipEntry entry = new ZipEntry(name + "\\" + file.getName()); |
| zos.putNextEntry(entry); |
| |
| FileInputStream fis = new FileInputStream(file); |
| int b; |
| while ((b = fis.read()) != -1) { |
| zos.write(b); |
| } |
| fis.close(); |
| |
| zos.closeEntry(); |
| } else { |
| toZip(file, zos, name + "\\" + file.getName()); |
| } |
| } |
| } |
Commons-io
链接
有关 IO 操作的工具包, 作用是提高 IO 流的开发效率
- 官网下载
commons-io-2.18.0-bin.zip
(二进制文件), 并解压
- 打开文件夹, 找到
commons-io-2.18.0.jar
- 在
JAVA PROJECTS
中打开 java_code
, 找到 Referenced Libraries
, 添加 commons-io-2.18.0.jar
| |
| |
| { |
| File src = new File("flow\\temp\\a.txt"); |
| File dest = new File("flow\\temp\\copy.txt"); |
| FileUtils.copyFile(src, dest); |
| } |
| |
| { |
| File src = new File("D:\\aaa"); |
| File dest = new File("D:\\bbb"); |
| FileUtils.copyDirectory(src, dest); |
| } |
| |
| { |
| File src = new File("D:\\aaa"); |
| File dest = new File("D:\\bbb"); |
| FileUtils.copyDirectoryToDirectory(src, dest); |
| } |
| |
| { |
| File src = new File("D:\\aaa"); |
| FileUtils.deleteDirectory(src); |
| } |
| |
| { |
| File src = new File("D:\\aaa"); |
| FileUtils.cleanDirectory(src); |
| } |
| |
| |
官网

下载 jar
链接
| |
| { |
| File file = FileUtil.file("D:\\", "aaa", "bbb", "a.txt"); |
| System.out.println(file); |
| |
| File touch = FileUtil.touch(file); |
| System.out.println(touch); |
| } |
| |
| |
| { |
| ArrayList<String> list = new ArrayList<>(); |
| Collections.addAll(list, "aaa", "bbb", "ccc"); |
| File file = FileUtil.writeLines(list, "D:\\a.txt", "UTF-8"); |
| System.out.println(file); |
| } |
| |
| |
| { |
| ArrayList<String> list = new ArrayList<>(); |
| Collections.addAll(list, "aaa", "bbb", "ccc"); |
| File file = FileUtil.appendLines(list, "D:\\a.txt", "UTF-8"); |
| System.out.println(file); |
| } |
| |
| |
| { |
| List<String> list = FileUtil.readLines("D:\\a.txt", "UTF-8"); |
| System.out.println(list); |
| } |
练习
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现