File
- File 仅仅封装一个路径名,这个路径可以存在也可以不存在
- 构造方法
- File(String pathName) pathName指定路径,可以是文件也可以是文件夹
- File(String parent, String child) parent + child 组成文件夹,两个参数中间的分隔符会自动加上,在任一参数下加分隔符也都可以
- File(File parent, String child) 父路径是一个File对象
- 常用方法
- 创建功能
- boolean createNewFile() 创建一个新的空文件
- 如果文件存在,那么创建失败返回false
- 如果文件不存在,那么创建成功返回true
- 不管创建的目标有没有后缀,都只会创建文件
- boolean mkdir() 创建一个新的空文件夹
- 必须保证父文件夹存在的情况下才能创建单级文件夹
- 不管创建的目标有没有后缀,都只会创建文件夹
- boolean mkdirs() 创建一个多级的空文件夹
- 没有父文件夹,就会把父文件夹也创建出来
- 不管创建的目标有没有后缀,都只会创建文件夹
- boolean createNewFile() 创建一个新的空文件
- 删除方法
- booelan delete() 文件和文件夹都可以删除
- 不走回收站
- 文件直接删除,文件夹只能删除空文件夹
- 文件夹有内容则删除失败
- booelan delete() 文件和文件夹都可以删除
- 判断和获取功能
- boolean isDirectory() 判断是不是文件夹
- boolean isFile() 判断是不是文件
- boolean exists() 判断是否存在
- String getName() 获取文件(文件名和后缀)或文件夹的名称
- String getAbsolutePath() 获取File对象的绝对路径名字符串
- String getPath() == toString() 构造器传入的路径字符串
- File[] listFiles() 返回File文件夹中的所有子文件或目录的File数组描述
- 当调用对象不存在时,返回null
- 当调用对象是一个文件时,返回null
- 当调用对象是一个空文件夹时,返回一个长度为0的数组
- 当调用对象是一个有内容的文件夹时,返回子文件和文件夹(包括隐藏)
- 当调用对象是一个需要权限才能进入的文件夹时,返回null
- public String[] list() 返回File文件夹中的所有子文件(隐藏的也在)或目录的Stirng数组描述,仅仅自己这层,孙子文件或文件夹不包括在内
- public File[] listFiles(FileFilter filter) 根据过滤条件返回遍历内容
- java.io.FileFilter 接口,过滤文件的接口
- public boolean accept(File pathname)
- 参数pathname: 当前正在遍历的File对象,可能是文件也可能是文件夹
- public File[] listFiles(FilenameFilter filter) 根据过滤条件返回遍历内容
- java.io.FilenameFilter 接口,过滤文件的接口
- public boolean accept(File dir, String name)
- 参数dir:当前正在遍历的File对象的父目录,也就是构造方法中传进去的File对象,也是调用listFile的那个对象本身
- 参数name:当前正在遍历的File对象的名字
- 创建功能
- 构造方法
字节流
- 所有的文件数据,都可以用字节流传输
- 字节输出流(OutputStream)
- java.io.OutputStream 抽象类,是所有字节输出流实现类的超类
- 基本方法
- void close() 关闭流,关闭资源
- void flush() 刷新此输出流并强制所有缓冲的输出字节被写出
- void write(byte[] b) 将b字节写入到输出流
- void write(byte[] b, int off, int len) 将b字节从off开始,写入len个长度到输出流
- off,从0开始
- len,off位置的那个字节是第一个,一直数到len结束
- void write(int i) 将i转byte后,写入一个字节到输出流
- 基本方法
- java.io.FileoutputStream extends OutputStream 文件字节输出流,把内存中的数据写到硬盘的文件中
- 构造方法,创建一个文件输出流就会创建对应的文件
- FileOutputStream(String fileName) 创建一个向指定名称的文件中写入数据的文件输出流
- FileOutputStream(File file) 创建一个向指定file对象的文件中写入数据的文件输出流
- FileOutputStream(String fileName, boolean append) 是否续写
- FileOutputStream(File file, boolean append) 是否续写
- 基本使用
- 一次写入多个字节
- void write(byte[] b)
- void write(byte[] b, int off, int len)
- 如果第一个字节是整数,显示的时候会查询ASCII表
- 如果第一个字节是负数,那第一个字节就会和第二个字节组成中文显示,查询系统默认编码表(GBK)(UTF-8三个字节表示一个中文)
- 一次写入一个字节
- void write(int i) 写入后,记事本会把每个字节翻译成字符显示
- 写字符串:把字符串getBytes()转换为byte[]即可
- 写换行
- win: \r\n
- linux: \n
- mac: \r
- 一次写入多个字节
- 构造方法,创建一个文件输出流就会创建对应的文件
- java.io.OutputStream 抽象类,是所有字节输出流实现类的超类
- 字节输入流(InoutStream)
- java.io.inputStream 抽象类,是所有字节输入流实现类的超类
- 基本方法
- void close() 关闭流,关闭资源
- abstract int read() 从输入流读取数据一个字节
- int read(byte[] b) 从输入流中读取多个字节存放到b中,返回读取到的有效字节数
- 基本方法
- java.io.FileInputStream extends InputStream 文件字节输入流,把文件中的数据读取到内存中
- 构造方法
- FileInputStream(File file) 创建一个向指定file对象的文件中读取数据的文件输入流
- FileInputStream(String fileName) 创建一个向指定名称的文件中读取数据的文件输入流
- 基本使用
- 读取一个字节:int read() 读取到文件末尾会返回-1
- 读取多个字节:int read(byte[] b) 读取到文件末尾会返回-1
- 构造方法
- java.io.inputStream 抽象类,是所有字节输入流实现类的超类
String类中的编码和节码问题
- 编码
- byte[] getBytes() 使用平台的默认字符集将该String编码为字节存储到数组中
- byte[] gteBytes(String charsetName) 指定编码集的编码
- 解码
- String(byte[] bytes) 通过平台的默认字符集解码指定的字节数组来构造新的String
- String(byte[] bytes, String charsetName) 指定编码集的解码
字符流
- 使用字节流读取文本文件时,如果遇到中文字符,正好把一个中文的多个字节拆开读了,那么中文可能会出问题。所以Java提供了字符流专门用于处理文本文件。同字节流一样与一次读取一个字节,但是碰到负数时会把整个字符的所有字节都读出来
- 字符输入流(Reader)
- java.io.Reader 抽象类,是所有字符输入流实现类的超类
- 基本方法
- public void close() 关闭流,关闭资源
- public int read() 从输入流中读取一个字符
- public int read(char[] cbuf) 从输入流中读取多个字符放到cbuf中,返回读取到的有效字符数
- 基本方法
- java.io.FileReader extends InputStreamReader extends Reader 文件字符输入流
- 构造方法
- FileReader(String fileName) 创建一个向指定file对象的文件中读取数据的文件输入流
- FileReader(File file) 创建一个向指定名称的文件中读取数据的文件输入流
- 基本使用
- 读取一个字符:int read() 读取到文件末尾会返回-1
- 读取多个字符:int read(char[] cbuf) 读取到文件末尾会返回-1
- 构造方法
- java.io.Reader 抽象类,是所有字符输入流实现类的超类
- 字符输出流(Writer)
- java.io.Writer 抽象类,是所有字符输出流实现类的超类
- 基本方法
- public void close() 关闭流,关闭资源
- public void flush() 刷新此输出流并强制所有缓冲的输出字符被写出
- public void write(char[] cbuf) 将c字符写入到输出流
- public void write(char[] cbuf, int off, int len) 将c字符从off开始,写入len个长度到输出流
- off,从0开始
- len,off位置的那个字节是第一个,一直数到len结束
- public void write(int c) 将i转char后,写入一个字符到输出流
- public void write(String str) 写入字符串
- public void write(String str, int off, int len) 写入字符串的一部分
- 基本方法
- java.io.FileWriter extends OutputStreamWriter extends Writer 文件字符输出流
- 构造方法,创建一个文件输出流就会创建对应的文件
- FileWriter(String fileName) 创建一个向指定名称的文件中写入数据的文件输出流
- FileWriter(File file) 创建一个向指定file对象的文件中写入数据的文件输出流
- FileWriter(String fileName, boolean append) 是否续写
- FileWriter(File file, boolean append) 是否续写
- 基本使用:方式基本与OutputStream相同,只是字符流需要用到flush()强制写出缓冲区
- 构造方法,创建一个文件输出流就会创建对应的文件
- java.io.Writer 抽象类,是所有字符输出流实现类的超类
- 字节流和字符流的区别
- OutputStream没有使用close()方法,内容依然可以正常输出,Writer则不行(可以使用flush()强制刷新)
- 字节流在处理的时候并不会使用到缓冲区,字符流会使用
基础概念
- 并发与并行
- 并发:同一时间段内,交替执行,给人的感觉是在同一时间段内同时进行
- 并行:同一时间点,同时执行,真正意义上的同时执行
- 线程与进程
- 进程:一个内存中运行的应用程序,每个进程都有一个独立的内存空间,进程也是程序的一次执行过程,是系统运行程序的基本单位,进程之间共享数据不容易
- 线程:线程是进程中的一个执行单元,一个进程中至少有一个线程,线程之间共享进程的数据
- 线程调度
- 分时调度:所有线程轮流使用CPU,平均分配每个线程占用的时间
- 抢占式调度:优先级高的优先使用CPU,如果都一样就随机(Java采用这种方式)
- 主线程:执行主方法的线程即main()方法
创建多线程的两种方式
- 继承Thread(java.lang.Thread)类 -> 重写run()方法 -> 调用start()方法启动线程
- Thread中的方法
- 构造方法
- public Thread() 分配一个新的线程对象
- Public Thread(String name) 分配一个指定名字的新的线程对象
- public Thread(Runnable target) 分配一个带有指定目标新的线程对象
- public Thread(Runnable target, String name) 分配一个带有执行目标新的线程对象并指定名字
- 常用方法
- public String getName() 获取线程名字
- public void start() 启动执行线程,JVM调用线程的run()方法
- public void run() 线程要执行的任务在此处定义
- public static void sleep(long millis) 当前正在执行的线程暂停执行指定毫秒数
- public static Thread currentThread() 返回当前正在执行的线程对象的引用
- 构造方法
- Thread中的方法
- 实现Runnable(jaba.lang.Runnable)接口 -> 重写run()方法 -> 创建Thread对象 -> 调用start()方法启动线程
- 两者区别
- Runnable避免了但继承的局限性
- Runnable增强了程序的扩展性,把设置线程任务和开启线程进行了分离
- 线程池只能放实现Runnable或Callable类线程,不能直接放入继承Thread的类
同步技术,解决线程安全问题
- 线程安全问题:多个线程同时操作修改一份数据,出现非法数据的现象
- 同步代码块
// 同步代码块中的锁对象(同步锁、对象监视器),可以是任意对象 // 多个线程使用的锁对象必须是同一个 // 锁对象可以保证只有一个线程在执行同步代码块的内容 synchronized(同步锁) {可能会出现线程安全问题的代码块} // 线程执行到同步代码块部分,会检查同步锁是否被占有 // 1. 如果被其他线程占有,则等待其他线程执行完毕释放锁,再占有锁,执行同步代码块 // 2. 如果没有,则占有该同步锁,执行同步代码块 // 程序频繁的判断锁、获取锁、释放锁,导致运行效率降低
- 同步方法
public synchronized 返回值类型 方法名(参数...) {可能会出现线程安全问题的代码}
- 锁机制(Lock(java.util.concurrent.locks.Lock接口)锁,JDK1.5之后)
- Lock 提供了比synchronized更广泛的锁定操作
- 接口中的方法
- void lock() 获取锁
- void unlock() 释放锁
- 使用步骤
- java.util.concurrent.locks.ReentrantLock 实现了 Lock 接口
- 在类的成员位置创建一个 ReentrantLock 对象
- 在可能出现线程安全问题的代码前调用Lock接口中的lock()方法获取锁
- 在可能出现线程安全问题的代码后调用Lock接口中的unlock()方法释放锁
Lock.lock(); 可能会出现线程安全问题的代码 // unlock写在finally最佳 Lock.unlock();
线程状态
-
在Thread类中,有一个Thread.State内部类描述了线程的状态
状态英文 状态中文 说明 NEW 新建 创建线程对象,但一直没有调用启动的线程 RUNNABLE 运行 正在JVM中执行的线程 BLOCKED 阻塞 等待其他线程释放监视器锁的线程 WAITING 永久等待、无限等待 调用Object.wait(),线程等待唤醒后才可以继续执行操作 TIMED_WAITING 计时等待、休眠、睡眠 调用Object.wait(long time)、sleep(long time),线程在指定时间后自动唤醒 TERMINATED 死亡 run()方法结束、调用stop()、执行出现异常的线程
线程通信,线程之间的等待和唤醒
- 几个方法
- wait(long time) 在毫秒值后没有被唤醒,就会自动醒来,注意和sleep(long time)的区别,会释放锁
- wait() 需要手动唤醒才可以醒来,会释放锁
- notify() 唤醒等待的线程,如果有多个则随机唤醒一个,不会释放锁,会继续执行下去
- notifyAll() 唤醒所有等待的线程,不会释放锁
- 生产者、消费者模型
public static void main(String[] args) { // 生产者和消费者是单独的两个线程,一定要消费者先执行 // 两个线程用一个同步锁进行通信,同一个同步锁控制两个线程的等待和唤醒 Object obj = new Object(); // 消费者线程 new Thread() { @Override public void run() { while (true) { synchronized (obj) { System.out.println("C:what kind of product i need"); try { // 等待生产者生产 obj.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("C:thanks it`s good"); System.out.println("-----------------------"); // 消费结束,唤醒生产者生产 obj.notify(); } } } }.start(); new Thread() { @Override public void run() { while (true) { synchronized (obj) { System.out.println("P:done"); // 生产结束,唤醒消费者消费 obj.notify(); try { // 等待消费者消费 obj.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } }.start(); }
线程池(JDK1.5后,内置了线程池)
- 容纳多个线程的容器,其中的线程可以反复使用,省去了频繁创建线程对象的操作,不需要反复创建线程而消耗过多的资源
- 线程池的使用
- java.util.concurrent.Executors(类),用来生产线程池的工厂类
- public static ExecutorService newFixedhreadPool(int nThreads) 创建一个可重用固定线程数的线程池,参数nThread代表该线程池的最大线程数
- java.util.concurrent.ExecutorService(接口),线程池接口
- public Future submit(Runnable task) 提交一个Runnable任务用于执行,获取一个线程并执行
- public void shutdown() 关闭/销毁线程池的方法,调用后程序也会停止
ExecutorService threadpool = Executors.newFixedThreadPool(5); threadpool.submit(new Runnable() { @Override public void run() { // 即使没有死循环,程序也不会停止,因为线程池没有关闭 System.out.println(Thread.currentThread().getName() + "is running"); } });
- java.util.concurrent.Executors(类),用来生产线程池的工厂类
- Java中线程池对象 java.util.concurrent.ThreadPoolExecutor
- 构造方法参数
- 核心线程数量 不能小于0
- 最大线程数 不能小于等于0,最大数量>=核心线程数量
- 空闲线程最大存活时间 不能小于0
- 时间单位 时间单位TimeUnit类
- 任务队列 不能为null 任务在队列中等着,线程空闲了再取出执行
- 创建线程工厂 不能为null 创建线程的方式
- 任务的拒绝策略 不能为null 有四个值,当提交的任务 > 池子中最大线程数 + 队列容量时会触发任务的拒绝策略
- AbortPolicy: 丢弃任务并抛出RejectedExcutionException异常(默认策略)
- DiscardPolicy 丢弃任务,但是不抛出异常
- DiscardOldestPolicy 抛弃队列中等待最久的任务,然后把当前任务加入到队列中
- CallerRunsPolicy 调用任务的run()方法绕过线程池直接执行
- 构造方法参数
volatile 强制线程在每次使用变量的时候,都去共享区域查看最新值
- 在Java中堆内存只有一块,而每一个线程都独有一块栈空间
原子性
- volatile只能保证每次在使用共享数据的时候时最新值
- 不能保证原子性
悲观锁和乐观锁
- 悲观锁,synchronized,总是从最坏的角度出发,认为每次获取数据的时候,别人都有可能修改,所以每次操作共享数据之前,都会上锁
- cas,原子类的操作方式。假设每次获取数据别人都不会修改,所以不会上锁。只不过在修改共享数据的时候,会检查一下,别人有没有修改过数据。
- 如果别人修改过,那么再次获取最新的值
- 如果没有被人修改,那么直接修改当前共享数据的值