Java基础知识总结(五)

多线程

多线程的创建方式(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,,t3try {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,,t3try {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() {@Overridepublic 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, ClassNotFoundExceptionPerson 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);
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值