多线程
多线程的创建方式(2)
1)定义一个类实现Runnable接口;
2)实现接口里面的run方法;—>完成耗时操作
public class MyRunnable implements Runnable{
@Override
public void run() {
for(int x = 0 ; x < 200 ; x++){
//Thread类的静态方法public static Thread currentThread()
//返回当前正在执行的线程对象的引用
System.out.println(Thread.currentThread().getName()+":"+x);
}
}
}
3)在main用户线程中创建当前这个类的实例—>“资源类对象”
4)创建线程类Thread对象,然后将3)资源类对象 作为参数传递,启动线程!
public class ThreadDemo {
public static void main(String[] args) {
//创建当前这个类对象 "资源类"
MyRunnable my = new MyRunnable() ;
//创建两个线程,同时可以给线程设置名称
//public Thread(Runnable target,String name)
//Thread类--->代理角色
Thread t1 = new Thread(my,"t1") ;
Thread t2 = new Thread(my,"t2") ;
//分别启动线程
t1.start();
t2.start();
}
}
方式一和方式二的对比
1)从代码具体的实现上来说
方式一:使用的继承,继承关系具有局限性,继承一个类的同时,将这个类的所有的方法都可以拿过来,公共的方法都可以访问(其他的方法 可能不用)
方式二:使用的接口---->Runnable接口本身就只有一个run方法 (扩展性高)
2)从内存角度考虑:是否能体现"数据共享"概念
方式一:三个栈指向三 个堆,没有数据共享概念
例题
电影院的三个窗口同时出售100张票,展示"第x个窗口正在出售第x张票"
需求:
电影院有三个窗口同时出售100张票,使用创建方式1和2分别去完这个卖票过程
方式1:继承关系 代码的实现,体现不出来"数据共享"
继承关系
public class SellTicketThread extends Thread{
//100张票
private static int tickets = 100 ;
//t1,t2,t3都要并发执行
@Override
public void run() {
//模拟一直有票
while (true){
//模拟真实场景,线程睡眠
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
//如果tickets>0
if(tickets>0){
System.out.println(getName()+"正在出售第"+(tickets--)+"张票");
}
}
}
}
public class SellTicketTest {
public static void main(String[] args) {
//创建三个线程类对象
SellTicketThread st1 = new SellTicketThread() ;
SellTicketThread st2 = new SellTicketThread() ;
SellTicketThread st3 = new SellTicketThread() ;
//线程名称
st1.setName("窗口1") ;
st2.setName("窗口2") ;
st3.setName("窗口3") ;
//启动线程
st1.start();
st2.start();
st3.start();
}
}
方式2实现
* //真实角色
public class SellTicket implements Runnable {
//100张票
private static int tickets = 100 ;
@Override
public void run() {
//模拟一直有票
while (true){
//t1,t2,,t3
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(tickets>0){
System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickets--)+"张票");
}
}
}
}
}
public class SellTicketTest {
public static void main(String[] args) {
//创建资源类对象:真实角色
SellTicket st = new SellTicket() ;
//创建三个线程,代表三个窗口
Thread t1 = new Thread(st,"窗口1") ;
Thread t2 = new Thread(st,"窗口2") ;
Thread t3 = new Thread(st,"窗口3") ;
//启动线程
t1.start() ;
t2.start() ;
t3.start() ;
}
}
Java设计模式 结构型设计—>代理模式—>静态代理
静态代理:
核心思想:让代理角色帮助真实角色完成一件事情!
真实角色专注于自己的事情!
代理角色和真实角色要实现同一个接口
动态代理:
jdk动态代理—>只要有一个接口—>反射的方式完成针对接口的对象创建
//class MyRunnable implements Runnable{}
MyRunnable my = new MyRunnable() ; ---->真实角色
Thread t1 = new Thread(my,"t1") ; ---->Thread代理角色
//结婚接口
interface Mary{
void mary() ;
}
//每一个人都需要结婚
//真实角色
class You implements Mary{
@Override
public void mary() {
System.out.println("要结婚了很开心...");
}
}
//代理角色---->婚庆公司
class WeddingCompany implements Mary{
//声明接口
private Mary mary ;
//有一个有参构造方法
public WeddingCompany(Mary mary){ //需要接口子实现类 You
this.mary = mary ;
}
@Override
public void mary() {
System.out.println("结婚之前,混穷公司布置婚礼现场!");
if(mary!=null){
mary.mary(); //核心的方法(真实角色要实现自己的事情)
}
System.out.println("婚礼现场完成之后,给婚庆公司付尾款!");
}
}
//测试类
public class StaticProxyDemo {
public static void main(String[] args) {
//创建You类对象(多态/自己new 自己)
You you = new You() ;
you.mary();
System.out.println("------------------使用静态代理-------------------------");
//创建资源类对---->真实角色
You you2 = new You() ;
//创建代理角色---->婚庆公司
WeddingCompany wc = new WeddingCompany(you2) ;
wc.mary();
}
}
使用多线程的方式完成电影院出售100张票
//真实角色
public class SellTicket implements Runnable {
//100张票
private static int tickets = 100 ;
@Override
public void run() {
//模拟一直有票
while (true){
//t1,t2,,t3
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(tickets>0){
System.out.println(Thread.currentThread().getName()+"正在出售第"+(tickets--)+"张票");
}/*else{
break;
}*/
}
/**
出现同票/负票--->线程不安全!
变量-- ---->原子性操作(线程的执行具有随机性)
* 1) 窗口1进来了 记录原始值---100 ---->100-1 =99
* 2)窗口3进来了 在打印99的时候,同时窗口2也进来了,也直接使用这个原始值 99,然后--
*/
}
}
当加入延迟效果,可能出现
1)同票出现 --------------->线程的执行的过程---原子性操作!(针对最简单,最基本的操作++,--)
2)可能出现负票!
线程不安全!
*/
public class SellTicketTest {
public static void main(String[] args) {
//创建资源类对象:真实角色
SellTicket st = new SellTicket() ;
//创建三个线程,代表三个窗口
Thread t1 = new Thread(st,"窗口1") ;
Thread t2 = new Thread(st,"窗口2") ;
Thread t3 = new Thread(st,"窗口3") ;
//启动线程
t1.start() ;
t2.start() ;
t3.start() ;
}
}
同步锁synchronized
同步方法
如果一个方法中的第一句话是一个同步代码块,可将synchronized声明到方法上
格式:权限修饰符 synchronized 返回值类型 方法名(参数列表){}
锁对象:this—代表类对象的地址值引用
如果是静态的同步方法,锁对象—>当前类名.class(字节码文件对象)
class Demo{}
public class SellTicket implements Runnable {
//100张票
private static int tickets = 100 ;
private Object obj = new Object() ; //实例变量
private Demo demo = new Demo() ;
private int x = 0 ;
@Override
public void run() {
//模拟一直有票
while (true){
//t1,t2,,t3
try {
Thread.sleep(1000);//模拟网络延迟
} catch (InterruptedException e) {
e.printStackTrace();
}
// synchronized(锁对象){
// * 多条语句对共享数据操作
// * }
//t1/t2/t3
//synchronized (new Object()){ //锁对象 ,每一个线程进来使用自己的锁对象!
//t1如果先抢占cpu执行权了
if(x % 2==0){
// synchronized (demo){ //t1,t2,t3这个三个线程使用的同一个锁
//synchronized (this){ //t1,t2,t3这个三个线程使用的同一个锁
synchronized(SellTicket.class){
if(tickets>0){
System.out.println(Thread.currentThread().getName()+
"正在出售第"+(tickets--)+"张票");
}
}
}else{
//x%2 !=0
/* synchronized (demo){ //t1,t2,t3这个三个线程使用的同一个锁
if(tickets>0){
System.out.println(Thread.currentThread().getName()+
"正在出售第"+(tickets--)+"张票");
}
}*/
SellTicket.sellTicket();
}
x++;
}
}
//定义一个方法sellTicket
/**
什么是同步方法(非静态)?如果一个方法中第一句话是一个同步代码块,可以将synchronized关键字定义在声明上
权限修饰符 synchronized 返回值类型 方法名(参数列表){
...
}
*/
/* public synchronized void sellTicket(){ //锁对象是谁? --->this
//t1,t2,t3这个三个线程使用的同一个锁
if(tickets>0){
System.out.println(Thread.currentThread().getName()+
"正在出售第"+(tickets--)+"张票");
}
}*/
public static synchronized void sellTicket(){//静态的同步方法的锁跟类相关---->当期类的字节码文件对象 类名.class
//t1,t2,t3这个三个线程使用的同一个锁
if(tickets>0){
System.out.println(Thread.currentThread().getName()+
"正在出售第"+(tickets--)+"张票");
}
}
}
线程安全—可能会出现死锁!
消费者生产者模式思想--------“信号灯法”
解决线程安全
校验多线程安全问题的标准(使用标准来看多线程环境是否存在问题以及解决方案)
1)是否是多线程环境---->是
2)是否有共享数据—>是存在的
3)是否有多条语句对共享数据操作
tickets票:多条语句同时操作
将3)多条语句多共享数据的操作使用同步代码块包起来—解决线程安全问题
Synchronized(锁对象){
多条语句对共享数据操作
}
锁对象:可以是任意Java类对象,但是多个线程必须使用同一个锁对象,否则锁不住!
自定义类,创建两个对象---作为"锁对象"
public class MyLock {
public static Object objA = new Object() ;
public static Object objB = new Object() ;
}
使用synchronized解决线程安全问题,安全问题解决了,效率低,可能死锁现象
死锁:
两个线程出现了一种互相等待的情况,A线程等待B线程释放锁,B线程等待A线程释放锁,造成死锁现象!
举例
中国人 和美国人吃饭
一双筷子 一把刀 和一个叉子
现在
中国人 美国人
一根筷子/一个叉子 一把刀和一根筷子
解决死锁问题—>线程之间的通信必须使用的同一个资源! (生产者和消费者模式思想)
资源类
*/
public class DieLock implements Runnable{
private boolean flag ; //boolen类型
public DieLock(boolean flag){
this.flag = flag ;
}
@Override
public void run() {
if(flag){
//同步代码块
synchronized (MyLock.objA){
System.out.println("if...ObjA...");
synchronized (MyLock.objB){
System.out.println("if...ObjB...");
}
}
}else {
synchronized (MyLock.objB){
System.out.println("else...ObjB...");
synchronized (MyLock.objA){
System.out.println("else...ObjA...");
}
}
}
}
}
public class DieLockDemo {
public static void main(String[] args) {
//创建资源类对象
DieLock dl1 = new DieLock(true) ;
DieLock dl2 = new DieLock(false) ;
//创建两个线程
Thread t1 = new Thread(dl1) ;
Thread t2 = new Thread(dl2) ;
//启动线程
t1.start() ;
t2.start() ;
}
}
/**
* 情况1
* if...ObjA...
* else...ObjB...
*
*
* 情况2:理想状态
* if...ObjA...
* if...ObjB...
* else...ObjB...
* else...ObjA...
*
* else...ObjB...
* else...ObjA...
* if...ObjA...
* if...ObjB...
*
*
* 情况3: 第二个线程先抢占了,等待第一个线程里面释放ObjA这个锁,第一个线程等待第二个线程释放objB锁
* else...ObjB...
* if...ObjA...
*/
解决死锁问题—>线程之间的通信必须使用的同一个资源! (生产者和消费者模式思想)
模拟生成者和消费者思想
Student类:学生数据(消费者和生成者需要使用的数据)
//学生数据
public class Student {
String name ; //姓名
int age ; //年龄
boolean flag ; //(信号灯标记) 默认false 是否有数据
}
SetData类:生成资源类
//生产者线程所在的资源类 实现Runnable接口
public class SetData implements Runnable {
//声明学生类型的变量
private Student s ;
private int x = 0 ;//统计变量
public SetData(Student s){
this.s = s ;
}
//产生一个学数据
@Override
public void run() {
while (true) { //模拟生成者一直产生数据
synchronized (s){
//如果没有数据,等待产数据
if(s.flag){
try {
s.wait();//等待会立即释放锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(x%2==0){
s.name = "高圆圆" ;
s.age = 35 ;
}else{
s.name = "文章" ;
s.age = 32 ;
}
//有数据了,唤醒
s.flag = true ;
s.notify(); //唤醒消费者线程,产数据
}
x++;
}
}
}
GetDate类:消费者资源类
//消费者线程所在的资源类 实现Runnable接口
public class GetData implements Runnable{
//声明学生类型的变量
private Student s ;
public GetData(Student s){
this.s = s;
}
//产生一个学生 数据
@Override
public void run() {
//不断的使用数据
while(true){
//Student s = new Student() ; //创建
synchronized (s){
if(!s.flag){
//如果有数据,等待将之前的数据消费掉
try {
s.wait();//调用 立即释放锁
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(s.name+"-"+s.age);
//如果没有数据了
s.flag = false ;
//通知(唤醒)生产者线程来产数据
s.notify();
}
}
}
}
按照上面格式:生成者产生数据,消费者使用数据
问题1)null—0,生产者和消费者资源类中使用的资源不是同一个对象
改进:生产者和消费者资源类中使用的资源是同一个了
但是现在生产者不断的产生数据,消费者不断地使用数据!
问题2)当我们加入了不断的产生数据,不断的使用数据,while(true)数据出现了紊乱
线程执行具有随机性-----导致多线程不安全
优化改进:需要给里面加入synchronized,将多条对共享数据操作包起来
生产者和消费者资源类都需要加入同步代码块来解决
已经优化:解决线程安全问题了,但是这个数据在控制台 打印的时候 “一次打印很多遍”,
cpu一点点时间片可以让某个线程执行多次
继续优化:循环依次打印 数据
高圆圆 35
文章 32
高圆圆 35
文章 32
Java中的等待唤醒机制里面----“信号灯法”
//有数据了,唤醒
s.flag = true ;
s.notify(); //唤醒消费者线程,消费数据
//如果没有数据了
s.flag = false ;
//通知(唤醒)生产者线程来产数据
s.notify();
为什么wait()和notify(),线程等待,线程唤醒为什么定义Object类中?
这些方法都是和锁对象有关系,而锁对象可以是任意Java类对象,而定义Object类中
调用 wait(),会立即释放锁!
//用户线程
public class ThreadMessageTest {
public static void main(String[] args) {
//创建一个学生对象---必须同一个
Student s = new Student() ;
//创建生产者资源类
SetData sd = new SetData(s) ;
//创建消费者资源类
GetData gd = new GetData(s) ;
//创建线程-分别操作生成者和消费者
Thread t1 = new Thread(sd) ;
Thread t2 = new Thread(gd) ;
//分别启动线程
t1.start();
t2.start();
}
}
throw和throws的区别
throw和throws共同点都是抛出异常;
throws:
1)抛出在发那个方法声明上
2)throws的后面跟的异常类名
3)针对throws抛出的方法处理,谁调用这个方法,谁必须处理!(处理方式—交给调用者,继续throws或者try…catch…finally)
4)throws它表示抛出异常的可能性
throw:
1)抛出在方法语句中
2)后面跟的是异常对象名 throw new xxxEception
3)throw抛出的处理----交给方法中逻辑语句处理 if语句
4)它表示抛出异常的肯定性,执行某段代码一定会有这个异常!
Jdk5以后Lock锁(接口)---->ReetrantLock可重入的互斥锁
Lock这个接口
Lock实现提供比使用synchronized方法和语句可以获得的更广泛的锁定操作
实现类:
可重入的互斥锁java.util.concurrent.locks.ReentrantLock
获取锁:指定的某个时刻 public void lock()
释放锁 : public void unlock()
Lock l = …;
l.lock();
try {
// access the resource protected by this lock
} finally {
l.unlock(); //释放锁(系统相关的资源)
}
资源类
*/
public class SellTicket implements Runnable{
//100张票
private static int tickets = 100 ;
//创建一个锁
private Object obj = new Object() ;
//jdk提供了锁Lock
private Lock lock = new ReentrantLock() ;//可重入的互斥锁
@Override
public void run() {
//模拟一直有票
while(true){
//synchronized (obj){
//使用Lock获取锁和释放锁
try{
lock.lock();
if(tickets>0){
//模拟网络延迟,线程睡眠300毫秒
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()
+"正在出售第"+(tickets--)+"张票");
}
}finally {
//当上面代码执行完毕,锁使用完了,需要释放锁
//finally代码:释放资源去使用的,一定会执行,除非一种特例(在执行finally之前,jvm退出了)
// System.out.println("finally代码块的代码一定执行!");
lock.unlock();
}
// }
}
}
}
电影院三个窗口同时出售100张票,使用方式2实现---->昨天使用synchronized同步代码块或则同步方法
使用Lock进行锁定的操作:获取锁,以及释放锁
*
*/
public class LockDemo {
public static void main(String[] args) {
//创建资源类对象
SellTicket st = new SellTicket() ;
//创建线程
Thread t1 = new Thread(st,"窗口1") ;
Thread t2 = new Thread(st,"窗口2") ;
Thread t3 = new Thread(st,"窗口3") ;
//启动线程
t1.start() ;
t2.start() ;
t3.start() ;
}
创建线程的方式3:线程池
线程池的特点:
1)降低资源销毁,因为线程使用完毕的时候,归还到线程中继续使用
2)提高线程的维护,还是借助线程池的参数
线程池:会创建一些固定的可重复使用的线程数,会在线程池中,循环使用
当某些线程使用完毕,不会被释放掉,而是归还链接池中,等待下一次再去利用!
成本比普通创建线程方式要大!
java.util.concurrent.Exceutors 工厂类
public static ExecutorService newFixedThreadPool(int nThreads)
创建固定的可重复的线程数的线程池
*java.util.concurrent ExecutorService ----->接口的具体实现类 public class ThreadPoolExecutor
Future submit(Callable task):提交异步任务,返回值就是异步任务计算的结果;
上面这个的返回值Future :异步计算的结果—如果现在只是看多个线程是否能够并发的去强转CPU执行权,并没有返回结果
这个返回值可以不用返回!
Callable:接口---->异步任务的执行 类似于 之前Runnable接口
void shutdown():关闭线程池
MyCallable 实现Callable: 就是异步任务
*/
public class MyCallable implements Callable {
@Override
public Object call() throws Exception {
//1-100之间的数据,两个线程都需要进来,能否出现并发执行
for(int x = 1; x <=100 ; x++){
System.out.println(Thread.currentThread().getName()+":"+x) ;
}
return null;
}
}
* 需求:
* 使用线程池实现两个线程的分别进行求和
* 一个线程:1-100之间的和
* 一个线程:1-200之间的和
*/
public class ThreadPoolDemo {
public static void main(String[] args) {
//创建一个线程池
//Executors
//public static ExecutorService newFixedThreadPool(int nThreads)
ExecutorService threadPool = Executors.newFixedThreadPool(2);//创建两个线程-->放在线程池中
//执行异步任务
//<T> Future<T> submit(Callable<T> task):提交异步任务,返回值就是异步任务计算的结果;
threadPool.submit(new MyCallable()); //创建异步执行任务
threadPool.submit(new MyCallable()) ;
//void shutdown():关闭线程池
//关闭线程池
threadPool.shutdown();
}
}
例题
需求:
使用线程池实现两个线程的分别进行求和
一个线程:1-100之间的和
一个线程:1-200之间的和
一个线程:1-100之间的和
*一个线程:1-200之间的和
*/
public class MyCallableTask implements Callable<Integer> {
//声明变量
private Integer number ;
public MyCallableTask(Integer number){//100/200
this.number = number ;
}
//定一个最终结果变量
int sum = 0 ;
@Override
public Integer call() throws Exception {
for(int x = 1 ; x <=number;x++){
sum+=x ;
}
return sum ;
}
}
public class ThreadPoolTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建2两个线程,通过线程池的方式
ExecutorService threadPool = Executors.newFixedThreadPool(2) ;
// <T> Future<T> submit(Callable<T> task):提交异步任务,返回值就是异步任务计算的结果;
// * 上面这个的返回值Future :异步计算的结果---如果现在只是看多个线程是否能够并发的去强转CPU执行权,并没有返回结果
// * 这个返回值可以不用返回!
Future<Integer> f1 = threadPool.submit(new MyCallableTask(100));
Future<Integer> f2 = threadPool.submit(new MyCallableTask(200));
//Future---->接口---->获取具体计算结果V get()
Integer num1 = f1.get();
Integer num2 = f2.get();
System.out.println(num1);
System.out.println(num2);
//关闭线程池
threadPool.shutdown();
}
}
线程池的优化---->参数调优(7大参数)
private volatile int corePoolSize ;核心线程数量
private volatile int maximumPoolSize;最大核心线程
private volatile long keepAliveTime ; 线程在执行期间的存活时间
TimeUnit unit—>是一个枚举—>时间的计量单位
private final BlockingQueue workQueue;阻塞队列
private volatile ThreadFactory threadFactory; 接口;创建线程的线程工厂—>DefaultThreadFactory实现类是Execytors的静态内部类,创建线程的
private volatile RejectedExecutionHandler handler;拒绝策略当线程数量已经到达最大核心线程数,通过上面的ThreadFactory创建的线程不能进入到线程池
java提供了Timer定时器—执行定时任务
java.util.Timer:定时器(可以执行一次或者重复执行某个任务)
构造方法:
Timer():创建一个计时器
成员方法
public void cancel()取消定时器
void schedule(TimerTask task, Date time) :在给定的日期时间来执行TimerTask定时任务 —>
String dataStr = “2022-11-22 18:00” ;//---->日期文本---->java.util.Date
(应用场景:引入使用io流的方式,在指定时间点上,删除指定带内容的目录里面的所有.java文件)
*
void schedule(TimerTask task, long delay) :在给定多少毫秒后(延迟时间)执行这个定时任务
public void schedule(TimerTask task,long delay,long period):在指定延迟时间(delay)执行任务,
然后每经过固定延迟时间(period)重复执行任务
*
schedule这些方法第一个参数都是定时任务:TimerTask是一个抽象类,
1)可以定义具体的子类继承自TimerTask
2)直接可以使用抽象类的匿名内部类
public class TimerDemo {
public static void main(String[] args) {
//有一个定时器
Timer timer = new Timer() ;
```
//void schedule(TimerTask task, long delay) :在给定多少毫秒后(延迟时间)执行这个定时任务
```
// timer.schedule(new MyTask(),3000);
// public void schedule(TimerTask task,long delay,long period):在指定延迟时间(delay)执行任务,
// 然后每经过固定延迟时间(period)重复执行任务
timer.schedule(new MyTask(timer),2000,3000);
}
}
//定义一个类 继承自TimerTask
class MyTask extends TimerTask{
private Timer t ;
public MyTask(Timer t){
this.t = t ;
}
//MyTask类中里面的成员方法,需要调用取消定时器的功能
//定时任务要执行的 操作
@Override
public void run() {
System.out.println("bom...");
//t.cancel();//取消定时器
}
}
引入io流
使用java.io.File来描述路径形式
描述 “D:\EE_2211\day23\code\Employee.java”
File(File parent, String child)
File(String pathname) 推荐第二个
File(String parent, String child)
public class FileDemo {
public static void main(String[] args) {
//在磁盘路径的时候 使用\或者使用//代表一个"/"
// File(File parent, String child)
File file = new File("D:\\EE_2211\\day23\\code") ;
File file2 = new File(file,"Employee.java") ;
System.out.println(file2);// D:\EE_2211\day23\code\Employee.java
System.out.println("----------------------------------") ;
// File(String pathname) 推荐第二个
File file3 = new File("D:\\EE_2211\\day23\\code\\Employee.java") ;
System.out.println(file3);
System.out.println("----------------------------------") ;
// File(String parent, String child)
File file4 = new File("D:\\EE_2211\\day23\\code","Employee.java") ;
System.out.println(file4);
}
}
基本功能:
创建文件/文件夹
public boolean createNewFile()throws IOException:创建文件,如果不存在,创建,返回true
public boolean mkdir():创建文件夹,如果存在了,则返回false;否则true
public boolean mkdirs():创建多级目录,当父目录不存在的时候创建
判断
public boolean isFile():是否是文件 使用居多
public boolean isDirectory():是否是文件夹 使用居多
public boolean isAbsolute():是否为绝对路径
public boolean exists():判断文件或者目录是否存在
删除
public boolean delete():删除由此抽象路径名表示的文件或目录 (删除目录,目录必须为空)
需求:
要将D盘下所有的.jpg结尾文件的名称输出!
public class FileDemo2 {
public static void main(String[] args) throws IOException {
// D:\EE_2211\day23\a.txt
//描述路径
File file = new File("D:\\EE_2211\\day23\\a.txt") ;
// public boolean createNewFile()throws IOException
System.out.println(file.createNewFile());
System.out.println("----------------------------------");
File file2 = new File("aaa.txt") ;//没有带盘符,默认当前项目下创建文件
System.out.println(file2.createNewFile());
System.out.println("-------------------------------------") ;
//D:\EE_2211\day23\aaa这个目录
File file3 = new File("D:\\EE_2211\\day23\\aaa") ;
System.out.println(file3.mkdir());
File file4 = new File("ccc\\ddd\\eee") ;
System.out.println(file4.mkdirs());
System.out.println(file3.isFile());
System.out.println(file3.isDirectory());
System.out.println(file3.exists());
}
}
Java.io.File---->高级功能:获取指定目录下的所有的文件夹以及文件的File数组
public File[] listFiles() : 获取指定抽象路径下的所有File数组 推荐—>使用File的功能进行判断
public String [] list(): 抽象路径名表示的目录中的文件和目录
需求:获取d盘所有的.jpg接尾文件---->输出文件名称
public class FileTest {
public static void main(String[] args) {
//1)描述磁盘上抽象路径的表示d://
File file = new File("D://") ;
//public String[] list():抽象路径名表示的目录中的文件和目录。
/* String[] strs = file.list();
for(String s:strs){
System.out.println(s) ;
}*/
//public File[] listFiles():获取指定抽象路径表示下的所有的File数组 推荐---->使用File的功能进行判断
File[] files = file.listFiles();
//遍历之前:非空判断,防止空指针异常
if(files!=null){
for(File f :files){
//f---->有文件/文件夹
//判断是文件
if(f.isFile()){
//以.jpg结尾
if(f.getName().endsWith(".jpg")){
System.out.println(f.getName());//String getName():获取File指定的路径的文件或者文件的名称
}
}
}
}
}
}
public File[] listFiles(FileFilter filter) 获取FIle数组的时候,就可以直接获取到指定文件的文件名称或者文件夹
参数是一个文件夹:文件过滤器接口
boolean accept (File pathname):
抽象路径名称所表示的路径是否放在Flie列表中,取决于返回值true,否则false,不放在列表中
public File[] listFiles(FilenameFilter filter):获取File数组的时候,就可以通过文件名称过滤器按照条件进行过滤
FilenameFilter接口
boolean accept(File dir,String name) :
参数1:指定的目录
参数2:文件名称
返回值: true,将指定指定目录下的文件放在File列表中;否则false
需求:
获取D盘下所有的以.jpg结尾文件--->输出文件名称
*/
public class FileTest2 {
public static void main(String[] args) {
//1)描述磁盘上抽象路径的表示d://
File file = new File("D://") ;
//public File[] listFiles(FileFilter filter) 获取File数组的时候,就可以直接获取到指定条件的文件名称或者文件夹
/* File[] files = file.listFiles(new FileFilter() {
@Override
public boolean accept(File pathname) {
//抽象路径名称所表示的路径是否放在File列表中,取决于返回值 true,将这个路径放在列表中,
// 否则false,不放在列表中
//获取D盘下所有的以.jpg结尾文件--->输出文件名称
// System.out.println(pathname);
return (pathname.isFile() && pathname.getName().endsWith(".jpg"));
}
});*/
//public File[] listFiles(FilenameFilter filter):获取File数组的时候,就可以通过文件名称过滤器按照条件进行过滤
File[] files = file.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
//创建File对象-->判断File是否为文件
File f = new File(dir,name) ;//文件放在指定目录中
//判断f的路径---->指定的文件 并且name文件名称必须以.jpg结尾
boolean flag1 = f.isFile();
boolean flag2 = (name.endsWith(".jpg")) ;
return flag1 && flag2 ;
}
});
//遍历File数组
if(files!=null){
for(File f:files){
System.out.println(f.getName());
}
}
}
}
递归算法
方法调用方法本身的一种现象!(并非方法嵌套)
递归的使用:
1)需要定义一个方法
2)有规律
3)有方法结束的条件(出口条件)否则"“死递归”
构造方法没有递归
例题
需求:求5的阶乘
使用求阶乘思想完成
使用递归思想
public class DiGuiDemo {
/* public DiGuiDemo(){
DiGuiDemo();
}*/
public static void main(String[] args) {
//使用求阶乘思想完成
//定义一个结果变量
int jc = 1 ;
for(int x = 2 ;x<=5 ; x ++){
jc*=x ;
}
System.out.println("5的阶乘是:"+jc);
System.out.println("-------------------------------------") ;
System.out.println("5的阶乘是:"+jieCheng(5));
}
//定义求5的阶乘的方法
private static int jieCheng(int n) {//5
if(n==1){
return 1 ;
}else {
//如果不是1
//5*调用方法名(5-1)
return n * jieCheng(n-1) ;
//5 *4*3*2*jiecheng(1)
}
}
}
面试题
有一个很有名的数学逻辑题叫做不死神兔问题。
有一对兔子,
从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如子都不死,
问第二十个月的兔子对数为多少?
规律:
兔子的对数
第一个月:1
第二个月:1
第三个月:2
第四个月:3
第五个月:5
第六个月:8
底七个月:13
....
第一个月和第二个月兔子的对数是1
从第三个月开始,每一月兔子对数是前两个月兔子对数之和!
*
*
使用a,b代表相邻两个月兔子的对数
第一个,第二个月 a=1,b=1
第二月,第三个月 a=1,b=2
第三个月,第四个月 a=2,b=3
第四个月,第五个月 a=3,b=5
*
*/
public class Test1 {
public static void main(String[] args) {
//数组的方式去实现
//创建一个数组:动态初始化
int[] arr = new int[20] ; //第二十个月
//第一个月和第二个都是1
arr[0] = 1 ;
arr[1] = 1 ;
//从第三个月开始,每一月兔子对数是前两个月兔子对数之和!
for(int x = 2 ;x<arr.length;x++){
arr[x] = arr[x-1] + arr[x-2] ;
}
//输出最后一个元素
System.out.println("第二十个月兔子对数:"+arr[19]); //6765
System.out.println("-----------------------------------------------------") ;
//递归:定义一个方法
//long time = System.currentTimeMillis() ;
System.out.println("第二十个月的兔子的对数是:"+getRabbit(20));
// long end = System.currentTimeMillis();
// System.out.println("耗时"+(end-time)+"毫秒");
}
//定义一个方法
public static int getRabbit(int n){//代表的第几个月
//第一个月和第二个月兔子的对数是1---->方法递归的出口条件
if(n==1 || n==2){
return 1;
}else {
//从第三个月开始,每一个月兔子对数是前两个月兔子对数之和!
return getRabbit(n-1)+getRabbit(n-2) ;
}
}
}
IO流的分类(输入流和输出流)
1)按流的方向划分
输入流—>读
输出流—>写
2)按流的类型分—同时按照方向划分
字节流
字节输入流:InputStream–>不能实例化—>具体的子类:针对文件的字节输入
FileInputStream
字节输出流OutputStream—>不能实例化—>具体的子类针对文件的字节输出流
FileOutoutStream
字节缓冲流(字节高效流)
字节缓冲输入流:BufferedInputStream
字节缓冲输出流:BufferedOutputStream
*需求:
在当前项目输出一个文本文本,里面同时写入内容
*字节输出流 使用
*1) 创建流对象 ---->将流对象指向"本地文件"---调用系统资源操作文件
*2)写数据
*3)释放流对象相关的资源
*
*
windows系统下通过io流的方式(字节流) 写换行--->"\r\n" 换行符号
Linux系统:"\r"
*/
public class IODemo {
public static void main(String[] args) throws IOException {
//创建字节输出流对象
//OutputStream抽象类,不能new
//public FileOutputStream(File file)
//public FileOutputStream(String pathname):推荐 直接跟路径名称
FileOutputStream fos = new FileOutputStream("fos.txt") ;//默认本目录下
//写数据
//public void write(int b) :写一个字节
fos.write(97) ;
fos.write("\r\n".getBytes());
fos.write(98) ;
/* fos.write(99) ;
fos.write(100) ;
//public void write(byte[] bytes):写一个字节数组
byte[] bytes = {65,66,67,68,69,70} ;
fos.write(bytes);*/
//释放资源
fos.close();
}
}
public FileOutputStream(String name,boolean append) throws FileNotFoundException
创建字节文件输出流对象,实现文件的末尾追加,而不将之前覆盖,第二个参数必须为true
public class IODemo2 {
public static void main(String[] args) throws IOException {
// FileOutputStream fos = new FileOutputStream("fos2.txt") ;
//public FileOutputStream(String name,boolean append) throws FileNotFoundException
FileOutputStream fos = new FileOutputStream("fos2.txt",true) ;
//for循环
for(int x = 0 ; x < 10;x++){
fos.write(("hello"+x).getBytes());
//写一次数据,换一次行
fos.write("\r\n".getBytes());
}
//释放资源
fos.close();
}
}
io流操作的时候,加入异常处理代码格式—>开发中 try…catsh…finally 使用捕获一次
public class IODemo3 {
public static void main(String[] args) {
// method1() ;
method2() ;
}
//方式2:统一try...catch...finally
public static void method2() {
FileOutputStream fos = null ;
try {
//可能出现问题的代码
fos = new FileOutputStream("fos3.txt") ;
//写数据
fos.write("worldJavaEE".getBytes());
} catch (IOException e) {
e.printStackTrace(); //交给jvm,jvm将异常信息打印控制台
} finally {
if(fos!=null){
try {
fos.close() ;
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
//方式1:分别进行try...catch... (不推荐) 阅读性差
public static void method1() {
//选中这段报红线的代码--->cltr+alt+t--->选则6 try...catch
FileOutputStream fos = null ;
try {
//创建文件字节输出流对象
fos = new FileOutputStream("fos3.txt") ;
} catch (FileNotFoundException e) {
e.printStackTrace();
}
try {
//写数据
fos.write("helloworld".getBytes());
} catch (IOException e) {
e.printStackTrace();
}
//释放资源
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
字节输入流:InputStream—>读 抽象类
提供子类:FileInputStream:针对文件操作的字节输入流
1)创建文件字节输入流对象
2)读文件
public int read() throws IOException:一次读取一个字节,返回字节数
public int read(byte[] b) throws IOException:一次读取一个字节数组
3)释放资源
public int read() throws IOException:一次读取一个字节,返回字节数
例题
需求:
将当前项目下的fis.txt文件内容读取出来,输出在控制台中
*
*
*
文件字节输入流一次读取一个字节,将文件内容输出控制台上,中文出现乱码,因为
字节流读取字节的时候---将字节--->强转成char,只考虑英文(abcdxxx)--->97---(char)97,当英文的后面有中文拼接
无法解析强转了--->乱码---->java才提供了字符流
*
什么时候使用字符流,当使用记事本打开能读懂的就使用字符;打开读不懂,用字节!(读图片文件/视频/音频)
*
*/
public class IODemo4 {
public static void main(String[] args) throws IOException {
// 1)创建文件字节输入流对象
//public FileInputStream(String name)throws FileNotFoundException 构造方法
//FileInputStream fis = new FileInputStream("fis.txt") ;
//将当前项目下的IODemo3.java文件的内容展示在控制台上
FileInputStream fis = new FileInputStream("IODemo3.java") ;
// 2)读文件
// public int read() throws IOException:一次读取一个字节,返回字节数
//第一次读
/* int len = fis.read();
System.out.println(len) ;
//将len字节---->char类型
*//* char ch = (char) len;
System.out.println(ch);*//*
System.out.println((char)len);
System.out.println("---------------------------------") ;
//第二次读取
len = fis.read() ;
System.out.println(len) ;
System.out.println((char)len);
System.out.println("---------------------------------") ;
//第三次读
len = fis.read() ;
System.out.println(len) ;
System.out.println((char)len);
System.out.println("---------------------------------") ;
//第四次读
len = fis.read() ;
System.out.println(len) ;
System.out.println((char)len);
System.out.println("-----------------------------------");*/
//第五次读
/* len = fis.read() ;
System.out.println(len) ; //-1:说明已经读取完毕
System.out.println((char)len) ;*/
//将上面代码进行优化 while循环改进 ---最终的写法:
//有一个字节数:从0开始
int len = 0 ;
while((len=fis.read())!=-1){ //read()阻塞式方法 //len赋值给流对象调用的读的方法,判断一块在之这里用
System.out.print((char)len) ;//将数据展示控制台上
}
// 3)释放资源
fis.close();
}
}
/**
传统的io流------>BIO 阻塞式流 :多个线程同时在操作的时候,一个线程在使用io流进行操作时候,其他线程处于阻塞式
NIO:非阻塞式流
*/
public int read(byte[] b) throws IOException:一次读取一个字节数组
例题
使用基本字节流一次读取一个字节数组:
public int read(byte[] b) throws IOException:一次读取一个字节数组
需求:
将当前项目下的fis2.txt文件的内容原封不动输出控制台
public class IODemo5 {
public static void main(String[] args) throws IOException {
//创建一个字节文件输入流对象
//FileInputStream fis = new FileInputStream("fis2.txt" ) ;
FileInputStream fis = new FileInputStream("IODemo3.java" ) ;
```
//一次读取一个字节数组
//public int read(byte[] b) throws IOException:一次读取一个字节数组
//创建一个字节数组
```
/* byte[] bytes = new byte[5] ;
//第一次读取
int len = fis.read(bytes) ; //第一次读取总字节数
System.out.println(len) ;
//展示内容---->将字节数组----->String
//String(byte[] bytes,int offerset,int len):每一次都是指定位置开始,读取实际字节数
System.out.println(new String(bytes,0,len));
System.out.println("------------------------------------------------") ;
//第二次读取
len = fis.read(bytes) ;
System.out.println(len) ;
System.out.println(new String(bytes,0,len));
System.out.println("-------------------------------------------------") ;
//第三次读取
len = fis.read(bytes) ;
System.out.println(len) ;
System.out.println(new String(bytes,0,len));
System.out.println("-------------------------------------------------") ;
//第四次读取
len = fis.read(bytes) ;
System.out.println(len) ;
System.out.println(new String(bytes,0,len));
System.out.println("-------------------------------------------------") ;
//第五次读取
len = fis.read(bytes) ;
System.out.println(len) ;*/
//System.out.println(new String(bytes,0,len));
//通过上面代码:重复性代码,循环改进,而且返回值字节数-1,说明已经读到末尾
//一次读取一个字节数组,字节数组长度,1024或者1024的整数倍
byte[] bytes = new byte[1024] ;
//总字节数
int len = 0 ;
while((len=fis.read(bytes))!=-1){ //赋值,判断一块使用
//将数据展示控制台上
System.out.println(new String(bytes,0,len));
}
//释放资源
fis.close() ;
}
}
使用io流进行文件的复制
例题
需求:
将当前项目下的IODemo3.java复制到D:\EE_2211\day24\Copy.java
*
使用一次读取一个字节的方式进行复制
*
使用一次读取一个字节数数组的方式进行复制
*
分析:
源文件:当前项目下的IODemo3.java
使用字节输入操作---->源文件FileInputStream(String pathname)
目的地文件:D:\EE_2211\day24\Copy.java
使用字节输出流操作---->目的地文件 FileOutputStream(String pathname)
*/
public class IOTest {
public static void main(String[] args) throws IOException {
//使用字节输入操作---->源文件FileInputStream(String pathname)
FileInputStream fis = new FileInputStream("IODemo3.java") ;
// 使用字节输出流操作---->目的地文件 FileOutputStream(String pathname)
FileOutputStream fos = new FileOutputStream("D:\\EE_2211\\day24\\Copy.java") ;
//一次读取一个字节
/* int len = 0 ;
while((len=fis.read())!=-1){//不断的再去一次读取字节
//不断写字节
fos.write(len); //写入到流对象汇总
}*/
//一次读取一个字节数组
byte[] bytes = new byte[1024] ;
int len = 0 ; //实际字节数
while((len=fis.read(bytes))!=-1){
//字节输出流写一个数组到目标文件中
fos.write(bytes,0,len);
}
//释放资源
fos.close();
fis.close();
}
}
例题2
复制视频文件,测试 基本字节流一次读取一个字节/一次读取一个字节数组的方式
将指定磁盘路径上的:D:\EE_2211\day24\a.mp4读写复制到当前项目下Copy.mp4
*
给定12M左右的视频文件--一次读取一个字节方式:共耗时:263452毫秒
给定12M左右的视频文件--一次读取一个字节方式:共耗时:345毫秒毫秒
*/
public class IOTest2 {
public static void main(String[] args) throws IOException {
long start = System.currentTimeMillis() ;
//参数1:源文件地址
// copyFile("D:\\EE_2211\\day24\\a.mp4","Copy.mp4") ;
copyFile2("D:\\EE_2211\\day24\\a.mp4","Copy.mp4") ;
long end = System.currentTimeMillis() ;
System.out.println("共耗时:"+(end-start)+"毫秒") ;
}
/**
基本字节输入流一次读取一个字节
@param srcFile 源文件
@param destFile 目的地文件
*/
public static void copyFile(String srcFile, String destFile) throws IOException {
//使用文件字节输入流操作源文件
FileInputStream fis = new FileInputStream(srcFile) ;
//使用文件字节输出流操作目的地文件
FileOutputStream fos = new FileOutputStream(destFile) ;
//一次读取一个字节
int len = 0 ;
while((len=fis.read())!=-1){
//读一个字节,给fos流对象中写一个字节,写入到目标文件中
fos.write(len) ;
}
//释放资源
fos.close();
fis.close();
}
//一次读取一个字节数组
public static void copyFile2(String srcFile,String destFile) throws IOException {
//使用文件字节输入流操作源文件
FileInputStream fis = new FileInputStream(srcFile) ;
//使用文件字节输出流操作目的地文件
FileOutputStream fos = new FileOutputStream(destFile) ;
//一次读取一个字节数组
byte[] bytes = new byte[1024] ;
//实际字节数
int len = 0 ;
while ((len=fis.read(bytes))!=-1){
//一次读取一个字节数组从字节输入流中,
//一次写一个字节数组,通过fos写入到destFile中
fos.write(bytes,0,len);
}
//释放
fos.close();
fis.close();
}
}
字节缓冲流(字节高效流)
字节缓冲输入流:BufferedInputStream/字节缓冲输出流:BufferedOutputStream
只是提供一个字节缓冲,本身就是一个字节数组,不会直接操作文件
操作具体的文件使用的都是基本字节流FileInputStream/FileOutputStream
public BufferedOutputStream(OutputStream out):创建一个字节缓冲输出流对象,默认缓冲区大小(足够大)
public BufferedInputStream(InputStream in):创建一个字节缓冲输入流对象,默认缓冲大小
* public class IODemo {
public static void main(String[] args) throws IOException {
// bufferedWrite();//使用字节缓冲流写数据
bufferedRead() ;
}
//读取当前项目下的bos.txt文件,展示控制台上
public static void bufferedRead() throws IOException {
// public BufferedInputStream(InputStream in):创建一个字节缓冲输入流对象,默认缓冲大小
BufferedInputStream bis = new BufferedInputStream(
new FileInputStream("bos.txt")) ; //缓冲大小8192长度,将读取的内容缓冲这个缓冲区中
//读取方式:一次读取一个字节
/* int by = 0 ;//实际字节数
while((by=bis.read())!=-1){
System.out.print((char)by);
}*/
//要么一次读取一个字节数组
byte[] bytes = new byte[1024] ;
int len = 0 ;
while((len=bis.read(bytes))!=-1){
System.out.println(new String(bytes,0,len));
}
bis.close();
}
public static void bufferedWrite() throws IOException {
//public BufferedOutputStream(OutputStream out)
FileOutputStream fos = new FileOutputStream("bos.txt") ;
BufferedOutputStream bos = new BufferedOutputStream(fos) ;
/**
*构造方法的原码:
* public BufferedOutputStream(OutputStream out) {
* this(out, 8192); //调用本类的带两个参数的有参构造
* }
* public BufferedOutputStream(OutputStream out, int size) {
* super(out);
* if (size <= 0) {
* throw new IllegalArgumentException("Buffer size <= 0");
* }
* buf = new byte[size]; //byte[] buf = new byte[8192] ;
* }
*/
//写数据
bos.write("hello高圆圆".getBytes());
bos.write("\r\n".getBytes());
bos.write("helloworld".getBytes());
bos.write("\r\n".getBytes());
bos.write("hellojavaEE".getBytes());
bos.write("\r\n".getBytes());
//关闭,刷新
bos.flush();
//释放资源
bos.close();
}
}
例题
针对视频文件复制
基本字节流
给定12M左右的视频文件–一次读取一个字节方式:共耗时:263452毫秒
给定12M左右的视频文件–一次读取一个字节方式:共耗时:345毫秒
字节缓冲流的方式
使用字节缓冲流的方式一次读取一个字节 共耗时:2403毫秒
使用字节缓冲流的方式一次读取一个字节数组 共耗时:195毫秒
*
*
字节流—>BufferedInputStream/BufferedOutputStream—字节高效流(使用多)
*
*/
public class CopyFileTest {
public static void main(String[] args) throws IOException {
//D:\EE_2211\day24\a.mp4---复制到 当前项目copy.mp4
long start = System.currentTimeMillis() ;
// copyFile("D:\\EE_2211\\day24\\a.mp4","copy.mp4") ;
copyFile2("D:\\EE_2211\\day24\\a.mp4","copy.mp4") ;
long end = System.currentTimeMillis() ;
System.out.println("共耗时:"+(end-start)+"毫秒");
}
//字节缓冲流一次读取一个字节数组
public static void copyFile2(String srcFile, String destFile) throws IOException {
//创建字节缓冲输入流对象
BufferedInputStream bis = new BufferedInputStream(
new FileInputStream(srcFile)) ;
//创建字节缓冲输出流对象
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream(destFile)) ;
//一次读取一个字节数组
byte[] bytes = new byte[1024] ;
int len = 0 ;
while((len=bis.read(bytes))!=-1){
bos.write(bytes,0,len);
}
bos.flush() ;//强制刷新缓冲字节 (网络中 TCP传输协议,这种场景刷新,客户端文件---传给服务器端)
//释放资源
bos.close();
bis.close();
}
//字节缓冲流一次读取一个字节
public static void copyFile(String srcFile, String destFile) throws IOException {
//创建字节缓冲输入流对象
BufferedInputStream bis = new BufferedInputStream(
new FileInputStream(srcFile)) ;
//创建字节缓冲输出流对象
BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream(destFile)) ;
//一次读取一个字节
int by = 0 ;
while((by=bis.read())!=-1){
//写一个字节
bos.write(by) ;
}
//释放资源
bos.close();
bis.close();
}
}
字符流的基本操作
基本字符流
Reader:抽象类
具体子类
public InputStreamReader(InputStream in):创建字符转换输入流,以平台默认字符集解码
public InputStreamReader(InputStream in,String charsetName):
创建字符转换输入流对象,指定字符集解码
read的功能
public int read(char[] cbuf):读取字符数组
public int read():读一个字符
//使用字符转换流读取项目下osw.txt,展示在控制台上
private static void read() throws IOException {
//public InputStreamReader(InputStream in,String charsetName):
// 创建字符转换输入流对象,指定字符集解码
/*InputStreamReader isr = new InputStreamReader(
new FileInputStream("osw.txt"),"gbk") ;//解码gbk*/
/*InputStreamReader isr = new InputStreamReader(
new FileInputStream("osw.txt"),"utf-8") ;*///解码utf-8
// public InputStreamReader(InputStream in):创建字符转换输入流,以平台默认字符集解码
InputStreamReader isr = new InputStreamReader(
new FileInputStream("osw.txt"));
//read的功能
//一次读取一个字符
int by = 0 ;//字符数
while((by=isr.read())!=-1){
//展示控制台
System.out.print((char)by);
}
//释放资源
isr.close();
}
Writer
public OutputStreamWriter (OutputStream out)
字符转换输出流(可以将基本字节输出流转换成字符输出流),平台默认的字符集编码(idea,utf-8)
public OutputStreamWriter(OutputStream out,String charsetName):
字符转换输出流 ,指定一个编码字符集
写的功能
void write(char[] cbuf)写入一个字符数组。
abstract void write(char[] cbuf, int off, int len) 写入字符数组的一部分。
void write(int c) 写一个字符
void write(String str)写一个字符串
void write(String str, int off, int len) :写入字符串的一部分
InputStreamReader/OutputStreamWriter:字符转换流弊端:代码格式复杂,不能直接操作文件!
//给当前项目下输出文件,同时写入内容--->字符缓冲输出流
private static void write() throws IOException {
//public OutputStreamWriter(OutputStream out):
OutputStreamWriter osw = new OutputStreamWriter(
new FileOutputStream("osw.txt")) ;//默认字符集编码utf-8
//写
// void write(char[] cbuf)写入一个字符数组。
char[] chs = {'a','b','c','d','e','f'} ;
osw.write(chs);
//abstract void write(char[] cbuf, int off, int len) 写入字符数组的一部分。
osw.write(chs,0,2);
//void write(int c) 写一个字符--- 将int数据类型--->ASCII码表 转换成对应的字符
osw.write(101);
//void write(String str)写一个字符串
osw.write("高圆圆") ;
//刷新
osw.flush() ;
//释放
osw.close();
}
}
字符流便捷类—简化上面的书写
public InputStreamReader(InputStream in)
public OutputStreamWriter(OutputStream out)
上面字符转换流使用的时候,无法直接直接操作具体文件,里面包装一层字节流操作,书写格式繁琐!
提供了他们具体的子类 —字符转换流的便捷类,跟idea平台默认字符集一致
FileReader(File/String pathname)
FileWriter(File/String pathname)
FileWriter(File/String pathname,boolean append):第二个参数为true,追加内容
字符流针对文件(使用高级记事本打开能读懂的),使用字符流操作
public class FileReader_WriterDemo {
public static void main(String[] args) throws IOException {
//将当前项目下的FileReader_WriterDemo.java复制到D:\EE_2211\day25\resource\copy.java
//创建FileReader操作源文件
FileReader fr = new FileReader("IODemo.java") ;
//创建Filewriter写数据
FileWriter fw = new FileWriter("D:\\EE_2211\\day25\\resource\\copy.java") ;
//一次读取字符/字符数组
char[] chs = new char[1024] ;
int len = 0 ;
while((len=fr.read(chs))!=-1){
//写字符数组,每次0开始,写入实际字符数
fw.write(new String(chs,0,len)) ;
fw.flush();
}
//关闭
fw.close() ;
fr.close() ;
}
}
高效字符流(字符缓冲流)+集合+多线程(综合使用)
BufferedReader:字符缓冲输入流
BufferedWriter:字符缓冲输出流
他们不能直接操作文件,提供缓冲区让读写效率更高,特有方式
*BufferedReader一次读取一行/可以作为键盘录入(录入一行字符串内容)
*
*
*
字符流针对文本文件(记事本打开能看懂的)–>字符流读写复制
1)字符转换流InputStreamReader/OutputStreamWriter 一次读取一个字符/一次读取一个字符数组
2)使用字符转换流的便捷类FileReader/FileWriter 可以直击操作文件 一次读取一个字符/一次读取一个字符数组
3)使用字符缓冲流BufferedReader/BufferedWriter: 一次读取一个字符/一次读取一个字符数组
4)使用字符缓冲流BufferedReader/BufferedWriter :一次读取一行特有方式(推荐!)
BufferedReader:字符缓冲输入流
//BuffereReader:字符缓冲输入流 ,将当前项目bw.txt读取出来,展示控制台
private static void read() throws IOException {
//BufferedReader(Reader in)
//创建一个字符缓冲输入流对象
BufferedReader br = new BufferedReader(new FileReader("bw.txt")) ;
//读的方式:一次读取一个字符/一次读取一个字符数组 (传统方式)
//BufferedReader的特有功能
//public String readLine() throws IOException:一次读取一行
/* //第一次读
String line = br.readLine();
System.out.println(line) ;
//第二次读
line = br.readLine() ;
System.out.println(line) ;
//第三次读
line = br.readLine() ;
System.out.println(line);
//第四次读
line = br.readLine() ;
System.out.println(line);
//第五次读
line = br.readLine() ;
System.out.println(line);*/
//一次读取一行的
String line = null ; //读取的实际内容
while((line=br.readLine())!=null){//readLine() :阻塞式方法
if("886".equals(line)){
break ;
}
//展示控制台
System.out.println(line) ;
}
BufferedWriter:字符缓冲输出流
BufferedWriter
将文本文件内容,存储到ArrList集合中,遍历信息
流最终的使用:
文件上传-------前端上传组件--------后端读取图片-------本地存储(本地磁盘)
云存储(七牛云/阿里云)
//BufferedWriter:字符缓冲输出流
private static void write() throws IOException {
//创建一个字符缓冲输出流对象
//public BufferedWriter(Writer out)
BufferedWriter bw = new BufferedWriter(new FileWriter("bw.txt")) ;
//BufferedWriter的特有功能
//写的功能---使用最多就是写public void write(String str)
bw.write("hello");
//public void newLine() throws IOException
bw.newLine();
bw.write("world") ;
bw.newLine();
bw.write("java");
bw.newLine();
bw.write("高圆圆");
bw.newLine();
//释放资源
bw.close();
}
BufferedReader(Reader in)字符缓冲输入流 ---->特有方式:一次读取一行内容 —>可以使用它键盘录入一行内容
- main()里面String[] 可以键盘录入 ,录入字符串
- Scanner以及提供的nextXXX()
- BufferedReader
public class BufferedDemo2 {
public static void main(String[] args) throws IOException {
//键盘录入---->Scanner(InputStream in) :里面参数使用字节输入流
//BufferedReader(Reader in):字符缓冲输入流
//InputStream in = System.in ; //标准输入流---等待录入
//创建字符输入流:Reader抽象类
//InputStreamReader是具体的子类 构造方法InputStreamReader(InputStream in )
// Reader reader = new InputStreamReader(in) ;
//创建一个字符缓冲输入流对象
//BufferedReader br = new BufferedReader(reader) ;
//一步走
BufferedReader br = new BufferedReader(
new InputStreamReader(System.in)) ;
//提示并录入
System.out.println("请您输入一个字符串:");
//利用BufferedReader一次读取一行
String line = br.readLine() ;
System.out.println("您录入的数据是:"+line) ;
}
}
例题
使用字符缓冲流BufferedReader/BufferedWriter :一次读取一行特有方式(推荐!)
读写复制
*/
public class Test1 {
public static void main(String[] args) throws IOException {
//创建字符缓冲输入流对象-->读
BufferedReader br = new BufferedReader(
new FileReader("IODemo.java")) ;
//创建字符缓冲输出流对象-->写
BufferedWriter bw = new BufferedWriter(
new FileWriter("D:\\Demo.java")) ;
//特有方式:一次读取一行
String line = null ;
while((line=br.readLine())!=null){
//写一行
bw.write(line) ;
bw.newLine() ;
bw.flush() ;//刷新
}
//释放资源
bw.close();
br.close();
}
}
序列化ObjectOutputStream/反序列化ObjectInoutStream
序列化:将一个Java对象(当前的类以及属性—>签名信息)—>“流数据” 前提条件就是 当前对象的所在类型必须实现
java.io.serializable序列化接口---->实现了这个接口的类才能被序列化,产生固定serialVersionUID唯一的标识ID
*
序列化:ObjectOutputStream---->将java对象—>写到序列化流中
public ObjectOutputStream(OutputStream out)
*
反序列化:ObjectInputStream---->将序列化中流数据的信息---->还原成Java对象
将一个Java对象(当前的类以及属性---签名信息)-----"流数据"前提条件是 当前的对象所在的类型
定义一个人类
*
当前这个类的信息一旦更改,里面的类的签名信息(唯一标识 序列化UID就发生变化 )
Personc.class------>已经序列化----UId=100
现在反序列化的,将Person.class---->改动了----->UId=200
*/
//将Person对象变成流数据,进行数据传输,必须实现serializable接口:里面什么都没有 ---"标记接口"
public class Person implements Serializable {
//产生固定的序列化id值
private static final long serialVersionUID = 6942368853791263306L;
private String name ;//姓名
//transient 不想一个类中某些字段不想参与序列化
transient int age ; //年龄
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
需求:
将一个Java对象 进行序列化,然在进行反序列化!
序列化:将一个Java对象(当前的类以及属性--->签名信息)--->"流数据" 前提条件就是 当前对象的所在类型必须实现
java.io.serializable序列化接口---->实现了这个接口的类才能被序列化,产生固定serialVersionUID唯一的标识ID
*
序列化:ObjectOutputStream---->将java对象--->写到序列化流中
public ObjectOutputStream(OutputStream out)
*
反序列化:ObjectInputStream---->将序列化中流数据的信息---->还原成Java对象
*/
public class IODemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// write() ;
read() ;
}
//反序列化操作:将流数据--->还原成Java对象
private static void read() throws IOException, ClassNotFoundException {
//public ObjectInputStream(InputStream in)throws IOException
//创建反序列化流对象
ObjectInputStream bis = new ObjectInputStream(
new FileInputStream("oos.txt")) ;
//将序列化的流数据--->还原成Java对象
// public final Object readObject()throws IOException, ClassNotFoundException
Person p = (Person)bis.readObject();
System.out.println(p);
}
//序列化操作
private static void write() throws IOException {
//创建一个Java对象
Person p = new Person("高圆圆",44) ;
//public ObjectOutputStream(OutputStream out)
//创建序列化流中
ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream("oos.txt")) ;
//public final void writeObject(Object obj)
oos.writeObject(p); //java.io.NotSerializableException:当前p对象所在的类型没有是实现序列化接口
//释放资源
oos.close();
}
}
java.util.Properites属性列表有自己的遍历方式—底层基于Map实现的
添加元素:
public Object setProperty(String key, String value)
遍历属性列表
public Set<String> stringPropertyNames()获取属性列表中的所有的键
public String getProperty(String key)使用此属性列表中指定的键搜索属性
public class PropertiesDemo2 {
public static void main(String[] args) {
//创建空的属性列表
Properties prop = new Properties() ;
System.out.println(prop);
// public Object setProperty(String key, String value):添加属性和属性值
prop.setProperty("张三","20") ;
prop.setProperty("文章","35") ;
prop.setProperty("姚笛","30") ;
prop.setProperty("马伊琍","44") ;
prop.setProperty("邓超","36") ;
/**
* 遍历属性列表
* public Set<String> stringPropertyNames()获取属性列表中的所有的键
* public String getProperty(String key)使用此属性列表中指定的键搜索属性
*/
Set<String> keySet = prop.stringPropertyNames();
for(String key:keySet){
String value = prop.getProperty(key);
System.out.println(key+"="+value);
}
}
}
Properties属性列表
Properties类表示一组持久的属性
Properties可以保存到流中或者从流中加载.(重要的地方)
属性列表中的每一个键以及对应的值都是一个字符串
继承Hashtable--->实现map接口--->存储数据,取出数据--->都可以使用map的方式
构造方法:
public Properties()创建空的属性列表
public class PropertiesDemo {
public static void main(String[] args) {
//public Properties():创建空的属性列表
Properties prop = new Properties() ;
//使用Map的方式添加数据
prop.put("文章","35") ;
prop.put("姚笛","30") ;
prop.put("马伊琍","44") ;
prop.put("邓超","36") ;
System.out.println(prop);
//常用的Map遍历方式
//获取所有的键的集合 Set<K> keySet()
Set<Object> keySet = prop.keySet();
for(Object key:keySet){
//通过键获取值
Object value = prop.get(key);
System.out.println(key+"---"+value) ;
}
}
}
Properties特有方式
public void load(Reader reader)throws IOException :
将指定文件中的内容加载属性集合列表中(内容键值对 key=value)
*
void store(Writer writer, String comments) :将属性列表中的内容保存指定文件中(以"键=值"元素对进行保存)
第一个参数:字符输出/使用字节输出流
第二个参数:属性列表的描述
*
相当于:
打游戏---->进度的加载
关卡的保存
*
*
*
需求:
有一个user.txt文件在当前项目下,如果文件内容里面有"lisi",将它的值改为20;将内容重写入到文件中;
zhangsan=30
lisi=40
wangwu=50
*/
public class PropertiesTest {
public static void main(String[] args) throws IOException {
//write() ;
```
read() ;
```
}
//将指定文件的内容加载到属性集合列表中
private static void read() throws IOException {
//创建空的属性列表
Properties prop = new Properties() ;
System.out.println(prop) ;
```
// public void load(Reader reader)throws IOException :将指定文件中的内容加载属性集合列表中
prop.load(new FileReader("name.txt"));
System.out.println(prop);
```
}
//有一个属性列表,里面有一些内容,将内容保存指定文件中
private static void write() throws IOException {
//创建一个属性列表
Properties prop = new Properties() ;
prop.setProperty("刘诗诗","44") ;
prop.setProperty("张佳宁","32") ;
prop.setProperty("肖战","32") ;
/**
* void store(Writer writer/OutputStream , String comments) :将属性列表中的内容保存指定文件中(以"键=值"元素对进行保存)
* 第一个参数:字符输出/使用字节输出流
* 第二个参数:属性列表的描述
*/
prop.store(new FileWriter("name.txt"),"name's list");
}
}
例题
需求:
有一个user.txt文件在当前项目下,如果文件内容里面有"lisi",将它的值改为20;将内容重写入到文件中;
zhangsan=30
lisi=40
wangwu=50
public class PropertiesTest2 {
public static void main(String[] args) throws IOException {
//创建空的属性列表
Properties prop = new Properties() ;
```
//将user.txt文件内容假造属性列表中
prop.load(new FileReader("user.txt"));
//System.out.println(prop);
//遍历属性列表中所有的键
Set<String> keySet = prop.stringPropertyNames();
for(String key :keySet){
//如果key就是"lisi"
if("lisi".equals(key)){
//修改lisi的值
prop.setProperty("lisi","20") ;
}
}
//将属性列表中的内容,保存到指定文件中,进行更新
prop.store(new FileWriter("user.txt"),"name's list") ;
System.out.println(prop);
```
}
}
如何读取src(类路径下)xxx.properties配置文件
属性配置文件它里面有颜色标记的,放在src下面的
*/
public class Test3 {
public static void main(String[] args) throws IOException {
//就需要将src下面的xxx.properties内容加载属性列表汇总
```
//创建属性列表Properties
Properties prop = new Properties() ;
System.out.println(prop);
//如何读src下面的xx.properties
//1)获取当前类的字节码文件对象 --->Class 正在运行的java类对象
```
// Class clazz = Test3.class ;
//System.out.println(clazz);//class com.qf.properties_01.Test3
//2)Class类---获取类加载器---解析这个里面所有成员(变量/方法..校验)
//public ClassLoader getClassLoader()
// ClassLoader classLoader = clazz.getClassLoader();
```
//3)ClassLoader---->public InputStream getResourceAsStream(String name):参数名称:就是src下面配置文件名称
//获取资源文件所在输入流对象--->将资源文件的内容读取到了字节输入流中
```
// InputStream inputStream = classLoader.getResourceAsStream("name.properties");
```
//一步走
//前提:配置文件必须在src下面
InputStream inputStream = Test3.class.getClassLoader().
getResourceAsStream("name.properties");
```
//4)将输入流对象的内容加载属性列表中
prop.load(inputStream);
//通过key获取value
String value = prop.getProperty("高圆圆");
System.out.println(value);
String value2 = prop.getProperty("王宝强");
System.out.println(value2);
//完成自己的业务操作
System.out.println(prop);
}
}