Java IO流对象、多线程

时间:2022-12-24 05:32:06
Input(读) Output(写)操作
File类
import java.io.File;
将操作系统中的文件、目录(文件夹)、路径、封装成File对象
提供方法,操作系统中的内容。File与系统无关的类。
String pathSeparator = File.pathSeparator;
System.out.println(pathSeparator); // windows';' Linux ':' 路径分割符
String separator = File.separator;
System.out.println(separator); // 斜线 / 目录的名称分割符 linux: \
 
构造方法
File(String pathName)
 
绝对路径
在系统中具有唯一性 如:c:\users\baidawei\downloads
 
相对路径
表示的是路径之间的相对关系 如: /downloads/  不需要考虑根级目录具体盘符
 
创建文件
File file = new File("c:\\Users\\baidawei\\Downloads\\o.txt");
if(!file.exists()){
    boolean result = file.createNewFile();
    System.out.println(result);
}
Mac:
File file = new File("/Users/baidawei/Desktop/test.txt");
boolean result = file.createNewFile();
System.out.println(result);
 
创建目录
boolean result = file.mkdir();
 
删除
boolean result = file.delete();
 
获取文件名或文件夹名
String fileName = file.getName(); //test.txt
 
获取文件路径
file.getPath() // /Users/baidawei/Desktop/test.txt
 
获取文件字节长度
file.length()
 
获取绝对路径 返回字符串
file.getAbsolutePath() // /Users/baidawei/Desktop/test.txt
 
获取绝对路径 返回file类
file.getAbsoluteFile()  // /Users/baidawei/Desktop/test.txt
 
获取父路径 返回字符串
file.getParent() // /Users/baidawei/Desktop
 
获取父路径 返回File类
file.getParentFile() // /Users/baidawei/Desktop
 
文件或目录是否存在
file.exists() // boolean
 
判断是否是目录
file.isDirectory() // boolean
 
获取路径中的文件和文件夹名 返回String[]
for(String str : file.list()){
    System.out.println(str);
}
 
获取路径中的文件和文件夹名 返回File[]
for(File f : file.listFiles()){
    System.out.println(f.getName());
}
 
过滤器 
for (File f : file.listFiles(new myFilter())) {
    System.out.println(f.getName());
}
 
class myFilter implements FileFilter {
    @Override
    public boolean accept(File pathname) {
        if(pathname.isDirectory()){
            return true;
        }
        return false;
    }
}
 
递归获取全文件
public static void main(String[] args) {
File file = new File("F://");
getAllDir(file);
}
 
public static void getAllDir(File dir) {
if (dir.listFiles() != null) {
for (File f : dir.listFiles()) {
if (f.isDirectory()) {
getAllDir(f);
} else {
System.out.println(f);
}
}
}
}
 
字节流
字节输出流 OutputStream 所有输出流父类
常用方法:
write(int b) 写入1个字节
write(byte[] b) 写入字节数组
write(byte[] b,int ,int) 写入字节数组,int 开始写入的索引, int 写几个
close() 关闭流对象,释放与流相关的资源
 
FileOutputStream 写入数据文件 OutputStream的子类
FileOutputStream fos = new FileOutputStream("F://text.txt");
fos.write(97);
fos.write(98);
fos.write(99);
fos.close();
没有就创建,有就覆盖 abc
 
byte[] bytes = {65,66,67,68,69,70};
fos.write(bytes);//ABCDEF
fos.close();
 
byte[] bytes = {65,66,67,68,69,70};
fos.write(bytes,1,2);//BC
fos.close();
 
写字符串
String strs = "Hello Word \r\n my name is david"; // 换行\r\n 
byte[] bytes = strs.getBytes();
fos.write(bytes);
fos.close();
 
续写文件,FileOutputStream构造方法第二个参数设置为true 就不会覆盖了
FileOutputStream fos = new FileOutputStream("F://text.txt",true);
 
字节输入流 InputStream 所有字节输入流的父类
读取任意文件,每次只读取1个字节。
read(int b) 读取1个字节
read(byte[] b) 读取字节数组
close() 关闭流对象
 
FileInputStream 读取数据文件 InputStream子类
 
读取字节:
public static void main(String[] arg) throws IOException {
FileInputStream fis = new FileInputStream("F://text.txt");
int len = 0;
while((len=fis.read()) != -1){ // -1就是没了
System.out.println(len); //输出字节 len代表执行一次
}
fis.close();
}
 
读取字节数组(字符串):
 
public static void main(String[] arg) throws IOException {
FileInputStream fis = new FileInputStream("F://text.txt");
byte[] b = new byte[1024];
int len = 0;
while((len=fis.read(b)) != -1){
System.out.print(new String(b,0,len)); //调用String构造方法 参数 字节数组
}
 
fis.close();
}
 
复制文件
字节复制 效率低
FileInputStream fis = new FileInputStream("F:\\text.txt");
FileOutputStream fos = new FileOutputStream("F:\\copy\\text.txt");
 
int len = 0;
while((len=fis.read()) != -1){
fos.write(len);
}
fis.close();
fos.close();
 
字节数组赋值 提高效率
FileInputStream fis = null;
FileOutputStream fos = null;
 
try{
fis = new FileInputStream("F:\\text.txt");
fos = new FileOutputStream("F:\\copy\\text.txt");
 
int len = 0;
byte[] b = new byte[1024];
while((len=fis.read(b)) != -1){
fos.write(len);
}
}catch(Exception ex){
System.out.print(ex.getMessage());
}finally {
fis.close();
fos.close();
}
 
FileWriter类 字符输出流 写文本
write(int c) 写1个字符
write(char[] c) 写字符数组
write(char[] c,int ,int)字符数组,开始索引,多少个
write(String s) 写字符串
 
FileWriter fw = new FileWriter("F:\\text.txt");
//写1个字符
fw.write(100);
//最好写一次刷一次,释放内存
fw.flush();
//写1个字符数组
fw.write(new char[]{99,98,97,96});
//写1个字符数组 1开始 3个
fw.write(new char[]{99,98,97,96},1,3);
//写字符串
fw.write("有一个姑娘,他有一些嚣张");
//刷新 write一次 刷新一次
fw.flush();
//关闭
fw.close();
续写 构造方法第二个参数true
FileWriter fw = new FileWriter("F:\\text.txt",true);
 
FileReader累 字符输入流 读取文本
FileReader fr = new FileReader("F:\\text.txt");
//读1个字节
int len = 0;
while((len=fr.read()) != -1){
System.out.print((char)len);
}
fr.close();
 
读取字符数组
char[] c = new char[1024];
int len = fr.read(c);
System.out.println(new String(c,0,len));
fr.close();
 
转换流
解决乱码问题
因为写文件时会默认本机编码表,GBK,美国机器打开就会乱码。
 
使用OutputStreamWriter(OutputStream out) 读取文件可以指定编码类型
FileOutputStream fos = new FileOutputStream("/Users/baidawei/Desktop/gbk.txt");
//使用转换流指定编码表
OutputStreamWriter osw = new OutputStreamWriter(fos,"GBK"); //默认GBK可以不写
osw.write("你好");
osw.flush();
osw.close();
 
使用UTF-8编码表
OutputStreamWriter osw = new OutputStreamWriter(fos,"UTF-8");
 
UTF-8 编码表中 一个汉字3个字节。
 
使用InputStreamReader类写入文件指定编码类型。
//如果编码格式不一致则会乱码
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("/Users/baidawei/Desktop/utf.txt");
InputStreamReader isr = new InputStreamReader(fis,"UTF-8");
int len = 0;
while((len=isr.read()) != -1){
System.out.println((char)len);
}
fis.close();
}
 
缓冲流
提高读写速度、效率。
 
字节输出流的缓冲流 BufferedOutputStream(OutputStream out)
FileOutputStream fos = new FileOutputStream("/Users/baidawei/Desktop/gbk.txt");
//使用缓冲流
BufferedOutputStream bos = new BufferedOutputStream(fos);
bos.write("加班中...".getBytes());
bos.close();
 
字节输入流的缓冲流 BufferedInputStream(InputStream input)
FileInputStream fis = new FileInputStream("/Users/baidawei/Desktop/gbk.txt");
//使用缓冲流
BufferedInputStream bis = new BufferedInputStream(fis);
int len = 0;
byte[] b = new byte[1024];
while((len=fis.read(b)) != -1){
System.out.print(new String(b,0,len));
}
 
字符输出流缓冲流BufferedWriter
public static void main(String[] args) throws IOException {
String url = "/Users/baidawei/Desktop/test.txt";
FileWriter fw = new FileWriter(url);
BufferedWriter bfw = new BufferedWriter(fw);
bfw.write("测试bufferedWriter输出流缓冲流");
bfw.flush();
//特有换行方法
bfw.newLine();
bfw.write("newline好使么?");
bfw.flush();
bfw.close();
}
 
字符输入流缓冲流BufferedReader
public static void main(String[] args) throws IOException {
String url = "/Users/baidawei/Desktop/test.txt";
FileReader fr = new FileReader(url);
BufferedReader br = new BufferedReader(fr);
 
String line = null;
 
while((line = br.readLine()) != null){
//特有方法 读取一整行 返回字符串 不包含\r\n换行符号
System.out.println(line);
}
}
 
复制
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new FileReader("/Users/baidawei/Desktop/test.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("/Users/baidawei/Desktop/test_bak.txt"));
 
String line = null;
 
while((line = br.readLine()) != null){
bw.write(line);
bw.newLine();
}
br.close();
bw.close();
}
 
Properties类
Properties类是Hashtable的子类,实现Map接口,Hashtable是线程不安全的被Hashmap替换。
键值对 都是字符串,可以和IO对象结合使用,实现持久化存储(可以存储到硬盘、u盘、光盘)上。
Properties pp = new Properties();
//存入 等同于put 区别只能字符串
pp.setProperty("key","value");
pp.setProperty("yu","datou");
System.out.println(pp);
 
//通过key获取value
String val = pp.getProperty("yu");
System.out.println(val);
 
//获取所有key
for(String s :pp.stringPropertyNames()){
System.out.println(pp.getProperty(s));
}
 
load() 加载方法
Properties pp = new Properties();
//.properties 后缀为 键值对配置文件
FileReader fr = new FileReader("/Users/baidawei/desktop/test.properties");
pp.load(fr);
System.out.println(pp);
fr.close();
 
test.properties文件格式固定 必须这样 不能加空格 分号
name=zhagnsan
age=18
#email=853020304@qq.com
#后面是注视
 
store() 保存方法
Properties pp = new Properties();
pp.setProperty("firstname","zhagn");
pp.setProperty("lastname","san");
pp.setProperty("email","83@qq.com");
FileWriter fw = new FileWriter("/Users/baidawei/desktop/test.properties");
pp.store(fw,"yuanyin buneng zhognwen"); //写中文会转换为unicode编码表存储
fw.close();
对象的序列化
对象中的数据new Person();以流的形式写入到文件中保存的过程称为写出对象。
ObjectOutputStream 写出对象 对象的序列化
//类需要实现Serializable接口
public class Person extends Object implements Serializable {
private String name;
private int age;
 
Person(String name){
this.name = name;
}
 
Person(String name,int age){
this(name);
this.age = age;
}
 
@Override
public String toString(){
return "Person[name" + name + ", age =" + age +"]";
}
}
 
Person p = new Person("name",18);
FileOutputStream fos = new FileOutputStream("/Users/baidawei/desktop/oos.text");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(p);
oos.close();
fos.close();
 
ObjectInputStream 读取对象 对象的反序列化
FileInputStream fis = new FileInputStream("/Users/baidawei/desktop/oos.text");
ObjectInputStream ois = new ObjectInputStream(fis);
Object obj = ois.readObject();
System.out.println(obj);
ois.close();
 
属性值静态的 static 不能序列化
属性值 加transient关键字 阻止序列化
 
Serializable接口 没有抽象方法 只是一个标记
 
打印流
只负责数据目的输出
PrintStream
接收File类型,字符串文件名,OutputStream输出流
PrintWriter
//输出目的FIle
public static void printStream_file() throws FileNotFoundException {
File file = new File("/Users/baidawei/desktop/myTest.txt");
PrintWriter pw = new PrintWriter(file);
pw.println("啥意思啊"); //原样输出
pw.println("啥意思啊嗯?");
pw.flush();
pw.close();
}
 
//输出String文件名
public static void printWriter_fileName() throws FileNotFoundException{
PrintWriter pw = new PrintWriter("/Users/baidawei/desktop/myTest2.txt");
pw.println("hahahehehhe");
pw.println("hahahhaha");
pw.flush();
pw.close();
}
 
//输出OutputStream 输出流对象可以 开启自动刷新
public static void printWriter_outputStream() throws FileNotFoundException{
PrintWriter pw = new PrintWriter(new FileOutputStream("/Users/baidawei/desktop/myTest3.txt"),true);
pw.println("333");
pw.println("444");
pw.flush();
pw.close();
}
 
commons-io工具类
简化File操作类
下载后 在Idea中选择File->Project Structure->Dependencies->加号->JARs or directories->ok->External Libraries中就出现了
FilenameUtils类
String url = "/Users/baidawei/Desktop/myTest.txt";
 
//获取文件扩展名
String ext = FilenameUtils.getExtension(url);
System.out.println(ext); //txt
 
//获取文件名
String fileName = FilenameUtils.getName(url);
System.out.println(fileName); ///myTest.txt
 
//是否是txt文件
boolean isExtens = FilenameUtils.isExtension(url,"txt");
System.out.println(isExtens);
 
FileUtils类
String url = "/Users/baidawei/Desktop/haha.txt";
 
//读取文件 返回String
String s = FileUtils.readFileToString(new File(url));
System.out.println(s);
 
//写String到文件
FileUtils.writeStringToFile(new File("/Users/baidawei/Desktop/haha.txt"),"字符串 要写的内容");
 
//复制文件
FileUtils.copyFile(new File("/Users/baidawei/Desktop/haha.txt"),new File("/Users/baidawei/Desktop/new.txt"));
 
//复制文件夹 macnew
FileUtils.copyDirectory(new File("/Users/baidawei/Downloads/mac"),new File ("/Users/baidawei/Downloads/macnew"));
 
//复制到目标文件里面去 macnew/mac
FileUtils.copyDirectoryToDirectory(new File("/Users/baidawei/Downloads/mac"),new File ("/Users/baidawei/Downloads/macnew"));
 
多线程
进程:当一个程序进入内存运行,就变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能。 如 迅雷软件运行中
线程:线程是进程的一个执行单元,负责当前继承中的程序执行,一个进程中至少有一个线程,可以有多个线程。 如迅雷软件中 同时下载3个任务
 
程序运行原理
分时调度:所有线程轮流使用CPU的使用权,平均分配每个线程占用CPU的时间。
抢占式调度:优先让优先级高的线程使用CPU,如果线程优先级相同,那么会随机选择一个,Java使用的为抢占式调度。
 
Main方法是主线程
public static void main(String[] args) {
SayHi();
System.out.println("===========================单线程======================");
}
 
public static void SayHi() {
for (int i = 0; i < 100000; i++) {
System.out.println(i);
}
}
需要等待10万次执行结束后 才能执行 最下面的输出。
 
Thread类
Thread类是程序中的线程类,允许应用程序并发地运行多个执行线程。
创建执行线程有两种方法,
1:继承Thread类,重写run方法 创建对象,开启线程,run方法相当于其他线程的main方法。
2:实现Runnable接口,重写run方法。创建runnable的子类对象,传入到某个线程的构造方法中,开启线程。
方法一继承Thread类:
public class SubThread extends Thread {
@Override
public void run() {
for(int i = 1;i<50;i++){
System.out.println(i+"A");
}
}
}
//并发执行 一起循环
public class Main {
public static void main(String[] args) {
SubThread st = new SubThread();
st.start();//运行子类 run方法
for (int i = 0; i < 50; i++) {
System.out.println(i);
}
}
}
 
获取线程的名字
public class SubThread extends Thread {
@Override
public void run() {
for(int i = 1;i<50;i++){
//获取线程的名字 父类的方法
System.out.println(getName());
System.out.println(i+"A");
}
}
}
 
Thread.currentThread().getName() //当前线程的名字
程序主线程名字:main,自定义的线程:Thread-0、Thread-1数字顺延 也可以使用setName()自定义线程名称
sleep()方法 睡眠多少毫秒在执行
 
方法二:实现Runnable接口
public class SubThread implements Runnable {
@Override
public void run() {
for(int i = 1;i<50;i++){
System.out.println(i+"A");
}
}
}
public static void main(String[] args) {
SubThread st = new SubThread();
Thread th = new Thread(st);
th.start();
for (int i = 0; i < 50; i++) {
System.out.println(i);
}
}
 
线程池
线程池是一个容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。
实现Runnable接口
public class ThreadPoolRunnable implements Runnable {
public void run(){
System.out.println(Thread.currentThread().getName());
}
}
//创建线程池对象 包含2个线程对象
ExecutorService es = Executors.newFixedThreadPool(2);
//从线程池创建对象
es.submit(new ThreadPoolRunnable());
es.submit(new ThreadPoolRunnable());
 
多线程 安全问题
//并发问题 同时判断为true 执行了多次
SubThread st = new SubThread();
 
Thread t0 = new Thread(st);
Thread t1 = new Thread(st);
Thread t2 = new Thread(st);
 
t0.start();
t1.start();
t2.start();
 
public class SubThread implements Runnable {
 
private int count = 1;
 
@Override
public void run() {
while(true){
if(count > 0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "出售第:" + --count);
}
}
}
}
 
解决线程安全:
1.同步代码块 synchronized(锁对象)
用于解决线程安全问题,同步执行,第一个线程执行完后再执行下一个线程。多个线程时,使用同一个锁对象才能够保证线程安全。
//lock是定义的一个object 对象
synchronized (lock){
if(count > 0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "出售第:" + --count);
}
}
 
2.同步方法 synchronized 不需要写锁对象 是本类对象this
public synchronized void method(){
//可能会产生线程安全问题的代码
}
 
Lock接口
Lock实现了比使用synchronized方法和语句获得更广泛的锁定操作。
实现类 ReentrantLock
lock() 锁
unlock() 释放锁
 
public void run() {
while (true) {
lock.lock(); //锁住
if (count > 0) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "出售第:" + --count);
}
lock.unlock(); //释放
}
}
}
 
线程的死锁
当线程任务出现了多个同步(多个锁)时,如果同步中潜逃了其他的同步,这时容易引发一种现象:程序出现无限等待,这种现象称之为死锁。
synchronzied(A锁){
synchronized(B锁){
 
}
}
 
线程等待与唤醒
wait() :等待,将正在执行的线程释放其执行资格 和 执行权,并存储到线程池中。
notify():唤醒,唤醒线程池中被wait()的线程,一次唤醒一个,而且是任意的。
notifyAll(): 唤醒全部:可以将线程池中的所有wait() 线程都唤醒。
这些方法都是在 同步中才有效。同时这些方法在使用时必须标明所属锁,这样才可以明确出这些方法操作的到底是哪个锁上的线程。