Fork me on GitHub

设计模式之单例模式

设计模式之单例模式

核心作用

单例模式属于创建类型的一种常用的软件设计模式,可以保证一个类只有一个实例并提供一个访问该实例的全局访问点。

应用场景

常见应用场景

  • Windows的Task Manager(任务管理器)就是很典型的单例模式。
  • Windows的Recycle Bin(回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。
  • 项目中,读取配置文件的类,一般也只有一个对象。没有必要每次使用配置文件数据,每次new一个对象去读取。
  • 网站的计数器,一般也是采用单例模式实现,否则难以同步。
  • 应用程序的日志应用,一般都使用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。
  • 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。
  • 操作系统的文件系统,也是大的单例模式实现的具体例子,一个操作系统只能有一个文件系统。
  • Application 也是单例的典型应用(Servlet编程中会涉及到)。
  • Spring中,每个Bean默认就是单例的,这样做的优点是Spring容器可以管理。
  • Servlet编程中,每个Servlet也是单例。
  • SpringMVC框架/Struts1框架中,控制器对象也是单例。

优点

  • 由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存的方式来解决。
  • 单例模式可以在系统设置全局的访问点,优化环共享资源访问,例如可以设计一个单例类,负责所有数据表的映射处理。

常见的五种单例模式实现方式

  • 主要:
    • 饿汉式(线程安全,调用效率高。 但是,不能延时加载。)
    • 懒汉式(线程安全,调用效率不高。 但是,可以延时加载。)
  • 其他:
    • 双重检测锁式(由于JVM底层内部模型原因,偶尔会出问题。不建议使用)
    • 静态内部类式(线程安全,调用效率高。 但是,可以延时加载)
    • 枚举式(线程安全,调用效率高,不能延时加载)

饿汉式

示例代码如下:

package com.msl.singleton;
/**
 * 测试饿汉式单例模式
 * @author Senley
 *
 */
public class SingletonDemo1 {
    
    //类初始化时,立即加载这个对象(没有延时加载的优势)。加载类时,天然的是线程安全的!
    private static SingletonDemo1 instance = new SingletonDemo1();
    
    private SingletonDemo1() {
    }
    
    //方法没有同步,调用效率高!
    public static SingletonDemo1 getInstance() {
        return instance;
    }
    
}

懒汉式

示例代码如下:

package com.msl.singleton;

/**
 * 测试懒汉式单例模式
 * @author Senley
 *
 */
public class SingletonDemo2 {
    
    //类初始化时,不初始化这个对象(延时加载,真正用的时候再创建)。
    private static SingletonDemo2 instance;  
    
    private SingletonDemo2(){ //私有化构造器
    }
    
    //方法同步,调用效率低!
    public static  synchronized SingletonDemo2  getInstance(){
        if(instance==null){
            instance = new SingletonDemo2();
        }
        return instance;
    }
    
}

双重检测锁式

示例代码如下:

package com.msl.singleton;

/**
 * 双重检查锁实现单例模式
 * @author Senley
 *
 */
public class SingletonDemo3 { 

  private static SingletonDemo3 instance = null; 

  public static SingletonDemo3 getInstance() { 
    if (instance == null) { 
      SingletonDemo3 sc; 
      synchronized (SingletonDemo3.class) { 
        sc = instance; 
        if (sc == null) { 
          synchronized (SingletonDemo3.class) { 
            if(sc == null) { 
              sc = new SingletonDemo3(); 
            } 
          } 
          instance = sc; 
        } 
      } 
    } 
    return instance; 
  } 

  private SingletonDemo3() { 

  } 
    
}

静态内部类式

示例代码如下:

package com.msl.singleton;

/**
 * 测试静态内部类实现单例模式
 * 这种方式:线程安全,调用效率高,并且实现了延时加载!
 * @author Senley
 *
 */
public class SingletonDemo4 {
    
    private static class SingletonClassInstance {
        private static final SingletonDemo4 instance = new SingletonDemo4();
    }
    
    private SingletonDemo4(){
    }
    
    //方法没有同步,调用效率高!
    public static SingletonDemo4  getInstance(){
        return SingletonClassInstance.instance;
    }
    
}

枚举式

示例代码如下:

package com.msl.singleton;

/**
 * 测试枚举式实现单例模式(没有延时加载)
 * @author Senley
 *
 */
public enum SingletonDemo5 {
    
    //这个枚举元素,本身就是单例对象!
    INSTANCE;
    
    //添加自己需要的操作!
    public void singletonOperation(){
    }
    
}

基于TCP实现多用户登录

基于TCP实现多用户登录

  • 创建服务器
  1. 指定端口 使用ServerSocket创建服务器
  2. 阻塞式等待连接 accept
  3. 操作: 输入输出流操作
  4. 释放资源
  • 创建客户端
  1. 建立连接: 使用Socket创建客户端 +服务的地址和端口
  2. 操作: 输入输出流操作
  3. 释放资源

Server

package com.msl.tcp;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * 模拟登录 多个客户端请求
 * 创建服务器
 * 1、指定端口 使用ServerSocket创建服务器
 * 2、阻塞式等待连接 accept
 * 3、操作: 输入输出流操作
 * 4、释放资源 
 * @author Senley
 *
 */
public class LoginMultiServer {
    public static void main(String[] args) throws IOException {
        System.out.println("-----Server-----");
        // 1、指定端口 使用ServerSocket创建服务器
        ServerSocket server =new ServerSocket(8888);
        boolean isRunning =true;;
        // 2、阻塞式等待连接 accept
        while(isRunning) {
            Socket  client =server.accept(); 
            System.out.println("一个客户端建立了连接");
            new Thread(new Channel(client)).start();
        }
        server.close();
    }
    //一个channel就代表一个客户端
    static class Channel implements Runnable{
        private Socket client;
        //输入流
        private DataInputStream dis;
        //输出流
        private DataOutputStream dos;
        public Channel(Socket  client) {
            this.client = client;
            try {
                //输入
                dis = new DataInputStream(client.getInputStream());
                //输出
                dos =new DataOutputStream(client.getOutputStream());    
            } catch (IOException e) {
                e.printStackTrace();
                release();
            }
        }
        
        //接收数据
        private String receive() {
            String datas ="";
            try {
                datas = dis.readUTF();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return datas;
        }
        //释放资源
        private void release() {
            // 4、释放资源 
            try {
                if(null != dos) {
                    dos.close();                    
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if(null != dis) {
                    dis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if(null != client) {
                    client.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        //发送数据
        private void send(String msg) {
            try {
                dos.writeUTF(msg);
                dos.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        @Override
        public void run() {                        
            // 3、操作: 输入输出流操作                    
            String uname ="";
            String upwd ="";
            //分析
            String[] dataArray = receive().split("&");
            for(String info:dataArray) {
                    String[] userInfo =info.split("=");
                    if(userInfo[0].equals("uname")) {
                        System.out.println("你的用户名为:"+userInfo[1]);
                        uname = userInfo[1];
                    }else if(userInfo[0].equals("upwd")) {
                        System.out.println("你的密码为:"+userInfo[1]);
                        upwd = userInfo[1];
                    }                
            }                    
            if(uname.equals("senley") && upwd.equals("123")) { //成功
                send("登录成功,欢迎回来");
            }else { //失败
                send("用户名或密码错误");
            }        
            release();        
        }    
    }
}

Client

package com.msl.tcp;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.UnknownHostException;

/**
 * 模拟登录  多个客户端请求
 * 创建客户端
 * 1、建立连接: 使用Socket创建客户端 +服务的地址和端口
 * 2、操作: 输入输出流操作
 * 3、释放资源 
 * @author Senley
 *
 */
public class LoginMultiClient {
    public static void main(String[] args) throws UnknownHostException, IOException {        
        System.out.println("-----Client-----");
        //1、建立连接: 使用Socket创建客户端 +服务的地址和端口
        Socket client =new Socket("localhost",8888);
        //2、操作: 输入输出流操作  先请求后响应
        new Send(client).send();
        new Receive(client).receive();        
        client.close();
    }
    //发送
    static class Send{
        private Socket client;
        private DataOutputStream dos;
        private BufferedReader console ;
        private String msg;
        public Send(Socket client) {            
            console=new BufferedReader(new InputStreamReader(System.in));
            this.msg =init();
            this.client = client;
            try {
                dos=new DataOutputStream(client.getOutputStream());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        private String init() {                    
            try {
                System.out.print("请输入用户名:");
                String uname =console.readLine();
                System.out.print("请输入密码:");
                String upwd =console.readLine();
                return "uname="+uname+"&"+"upwd="+upwd;
            } catch (IOException e) {
                e.printStackTrace();
            }
            return "";            
        }
        
        public void send() {
            try {
                dos.writeUTF(msg);
                dos.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    //接收
    static class Receive{
        private Socket client;
        private DataInputStream dis;
        public Receive(Socket client) {
            this.client = client;
            try {
                dis=new DataInputStream(client.getInputStream());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }        
        public void receive() {
            String result;
            try {
                result = dis.readUTF();
                System.out.println(result);
            } catch (IOException e) {
                e.printStackTrace();
            }        
        }
    }
}

示例结果如下:

LoginMultiServer

多线程可重入锁的原理实现

多线程可重入锁的原理实现

可重入锁

锁作为并发共享数据保证一致性的工具,大多数内置锁都是可重入的,也就是说,如果某个线程试图获取一个已经由它自己持有的锁时,那么这个请求会立刻成功,并且会将这个锁的计数值加1,而当线程退出同步代码块时,计数器将会递减,当计数值等于0时,锁释放。如果没有可重入锁的支持,在第二次企图获得锁时将会进入死锁状态。

可重入锁随处可见,如synchronized 和ReentrantLock。

原理实现

手动实现一个可重入锁,代码如下:

package com.msl.others;

import java.util.concurrent.locks.ReentrantLock;

/**
 * 可重入锁: 锁可以延续使用 + 计数器
 * 
 * @author Senley
 *
 */
public class LockTest {
    ReentrantLock lock = new ReentrantLock();
    public void a() throws InterruptedException {
        lock.lock();
        System.out.println(lock.getHoldCount());
        doSomething();
        lock.unlock();
        System.out.println(lock.getHoldCount());
    }
    //不可重入
    public void doSomething() throws InterruptedException {
        lock.lock();
        System.out.println(lock.getHoldCount());
        //...................
        lock.unlock();
        System.out.println(lock.getHoldCount());
    }
    public static void main(String[] args) throws InterruptedException {
        LockTest04 test = new LockTest04();
        test.a();            
        Thread.sleep(1000);        
        System.out.println(test.lock.getHoldCount());
    }

}

结果如下:

1
2
1
0
0

多线程并发协作模型-生产者消费者模式

多线程并发协作模型-生产者消费者模式

生产者消费者模式

应用场景

  • 假设仓库中只能存放一件产品,生产者将生产出来的产品放入仓库,消费者将仓库中产品取走消费。

  • 如果仓库中没有产品,则生产者将产品放入仓库,否则停止生产并等待,直到仓库中的产品被消费者取走为止。

  • 如果仓库中放有产品,则消费者可以将产品取走消费,否则停止消费并等待,直到仓库中再次放入产品为止。

    Producer-consumer-problem

分析

这是一个线程同步问题,生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件。

  • 对于生产者,没有生产产品之前,要通知消费者等待。而生产了产品之后,又需要马上通知消费者消费。

  • 对于消费者,在消费之后,要通知生产者已经消费结束,需要继续生产新产品以供消费。

  • 在生产者消费者问题中,仅有synchronized是不够的。

    • synchronized可阻止并发更新同一个共享资源,实现了同步。
    • synchronized不能用来实现不同线程之间的消息传递(通信)。

解决方式

  • 并发协作模型-生产者消费者模式—>管程法
  • 并发协作模型-生产者消费者模式—>信号灯法

示例

管程法

示例代码如下:

package com.msl.cooperation;
/**
 * 协作模型:生产者消费者实现方式一:管程法
 * 借助缓冲区
 * 
 * @author Senley
 *
 */
public class CoTest01 {
    public static void main(String[] args) {
        SynContainer container = new SynContainer();
        new Productor(container).start();
        new Consumer(container).start();
    }
}
//生产者
class Productor extends Thread{
    SynContainer container  ;    
    public Productor(SynContainer container) {
        this.container = container;
    }

    public void run() {
        //生产
        for(int i=0;i<100;i++) {
            System.out.println("生产-->"+i+"个馒头");
            container.push(new Steamedbun(i) );
        }
    }
}
//消费者
class Consumer extends Thread{
    SynContainer container  ;    
    public Consumer(SynContainer container) {
        this.container = container;
    }
    public void run() {
        //消费
        for(int i=0;i<100;i++) {
            System.out.println("消费-->"+container.pop().id+"个馒头");
        }
    }
}
//缓冲区
class SynContainer{
    Steamedbun[] buns = new Steamedbun[10]; //存储容器
    int count = 0; //计数器
    //存储 生产
    public synchronized void push(Steamedbun bun) {
        //何时能生产  容器存在空间
        //不能生产 只有等待
        if(count == buns.length) {
            try {
                this.wait(); //线程阻塞  消费者通知生产解除
            } catch (InterruptedException e) {
            }
        }
        //存在空间 可以生产
        buns[count] = bun;
        count++;
        //存在数据了,可以通知消费了
        this.notifyAll();
    }
    //获取 消费
    public synchronized Steamedbun pop() {
        //何时消费 容器中是否存在数据
        //没有数据 只有等待
        if(count == 0) {
            try {
                this.wait(); //线程阻塞  生产者通知消费解除
            } catch (InterruptedException e) {
            }
        }
        //存在数据可以消费
        count --;
        Steamedbun bun = buns[count] ;        
        this.notifyAll(); //存在空间了,可以唤醒对方生产了
        return bun;
    }
}
//馒头
class Steamedbun{
    int id;
    public Steamedbun(int id) {
        this.id = id;
    }
    
}

结果如下:

生产-->0个馒头
生产-->1个馒头
生产-->2个馒头
生产-->3个馒头
生产-->4个馒头
生产-->5个馒头
生产-->6个馒头
生产-->7个馒头
生产-->8个馒头
生产-->9个馒头
生产-->10个馒头
生产-->11个馒头
消费-->9个馒头
消费-->10个馒头
消费-->11个馒头
消费-->8个馒头
生产-->12个馒头
生产-->13个馒头
消费-->7个馒头
生产-->14个馒头
消费-->13个馒头
生产-->15个馒头
消费-->14个馒头
生产-->16个馒头
消费-->15个馒头
生产-->17个馒头
消费-->16个馒头
生产-->18个馒头
消费-->17个馒头
生产-->19个馒头
生产-->20个馒头
生产-->21个馒头
消费-->18个馒头
消费-->20个馒头
生产-->22个馒头
消费-->21个馒头
消费-->22个馒头
生产-->23个馒头
消费-->19个馒头
生产-->24个馒头
消费-->23个馒头
生产-->25个馒头
消费-->24个馒头
生产-->26个馒头
生产-->27个馒头
生产-->28个馒头
消费-->25个馒头
消费-->27个馒头
生产-->29个馒头
生产-->30个馒头
消费-->28个馒头
消费-->29个馒头
生产-->31个馒头
生产-->32个馒头
消费-->30个馒头
消费-->31个馒头
生产-->33个馒头
生产-->34个馒头
消费-->32个馒头
消费-->33个馒头
生产-->35个馒头
生产-->36个馒头
消费-->34个馒头
消费-->35个馒头
生产-->37个馒头
消费-->36个馒头
生产-->38个馒头
消费-->37个馒头
生产-->39个馒头
消费-->38个馒头
生产-->40个馒头
消费-->39个馒头
生产-->41个馒头
消费-->40个馒头
生产-->42个馒头
消费-->41个馒头
消费-->42个馒头
消费-->26个馒头
消费-->12个馒头
生产-->43个馒头
消费-->6个馒头
消费-->43个馒头
消费-->5个馒头
消费-->4个馒头
消费-->3个馒头
消费-->2个馒头
消费-->1个馒头
消费-->0个馒头
生产-->44个馒头
生产-->45个馒头
消费-->44个馒头
生产-->46个馒头
消费-->45个馒头
生产-->47个馒头
消费-->46个馒头
生产-->48个馒头
消费-->47个馒头
生产-->49个馒头
消费-->48个馒头
生产-->50个馒头
消费-->49个馒头
生产-->51个馒头
消费-->50个馒头
生产-->52个馒头
消费-->51个馒头
生产-->53个馒头
消费-->52个馒头
生产-->54个馒头
消费-->53个馒头
消费-->54个馒头
生产-->55个馒头
生产-->56个馒头
消费-->55个馒头
生产-->57个馒头
消费-->56个馒头
生产-->58个馒头
消费-->57个馒头
生产-->59个馒头
消费-->58个馒头
生产-->60个馒头
消费-->59个馒头
生产-->61个馒头
消费-->60个馒头
生产-->62个馒头
消费-->61个馒头
生产-->63个馒头
消费-->62个馒头
生产-->64个馒头
消费-->63个馒头
消费-->64个馒头
生产-->65个馒头
生产-->66个馒头
消费-->65个馒头
生产-->67个馒头
消费-->66个馒头
生产-->68个馒头
消费-->67个馒头
生产-->69个馒头
消费-->68个馒头
消费-->69个馒头
生产-->70个馒头
生产-->71个馒头
消费-->70个馒头
生产-->72个馒头
消费-->71个馒头
消费-->72个馒头
生产-->73个馒头
生产-->74个馒头
生产-->75个馒头
生产-->76个馒头
消费-->73个馒头
消费-->76个馒头
消费-->75个馒头
消费-->74个馒头
生产-->77个馒头
生产-->78个馒头
消费-->77个馒头
消费-->78个馒头
生产-->79个馒头
消费-->79个馒头
生产-->80个馒头
生产-->81个馒头
消费-->80个馒头
消费-->81个馒头
生产-->82个馒头
生产-->83个馒头
消费-->82个馒头
消费-->83个馒头
生产-->84个馒头
生产-->85个馒头
消费-->84个馒头
消费-->85个馒头
生产-->86个馒头
生产-->87个馒头
消费-->86个馒头
生产-->88个馒头
消费-->87个馒头
生产-->89个馒头
消费-->88个馒头
消费-->89个馒头
生产-->90个馒头
生产-->91个馒头
消费-->90个馒头
消费-->91个馒头
生产-->92个馒头
生产-->93个馒头
消费-->92个馒头
生产-->94个馒头
消费-->93个馒头
生产-->95个馒头
消费-->94个馒头
消费-->95个馒头
生产-->96个馒头
生产-->97个馒头
消费-->96个馒头
消费-->97个馒头
生产-->98个馒头
生产-->99个馒头
消费-->98个馒头
消费-->99个馒头

信号灯法

示例代码如下:

package com.msl.cooperation;
/**
 * 协作模型:生产者消费者实现方式二:信号灯法
 * 借助标志位
 * 
 * @author Senley
 *
 */
public class CoTest02 {
    public static void main(String[] args) {
        Tv tv  =new Tv();
        new Player(tv).start();
        new Watcher(tv).start();
    }
}
//生产者 演员
class Player extends Thread{
    Tv tv;    
    public Player(Tv tv) {
        this.tv = tv;
    }

    public void run() {
        for(int i=0;i<20;i++) {
            if(i%2==0) {
                this.tv.play("欢乐集结号");
            }else {
                this.tv.play("怕上火 喝加多宝");
            }
        }
    }
}
//消费者 观众
class Watcher extends Thread{
    Tv tv;    
    public Watcher(Tv tv) {
        this.tv = tv;
    }

    public void run() {
        for(int i=0;i<20;i++) {
            tv.watch();
        }
    }
}
//同一个资源 电视
class Tv{
    String voice;
    //信号灯
    //T 表示演员表演 观众等待
    //F 表示观众观看 演员等待
    boolean flag = true;
    
    //表演
    public  synchronized void play(String voice) {
        //演员等待
        if(!flag) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }        
        //表演
        System.out.println("表演了:"+voice);
        this.voice = voice;
        //唤醒
        this.notifyAll();
        //切换标志
        this.flag =!this.flag;
    }
    //观看
    public synchronized  void watch() {
        //观众等待
        if(flag) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //观看
        System.out.println("听到了:"+voice);
        //唤醒
        this.notifyAll();
        //切换标志
        this.flag =!this.flag;
    }
}

结果如下:

表演了:欢乐集结号
听到了:欢乐集结号
表演了:怕上火 喝加多宝
听到了:怕上火 喝加多宝
表演了:欢乐集结号
听到了:欢乐集结号
表演了:怕上火 喝加多宝
听到了:怕上火 喝加多宝
表演了:欢乐集结号
听到了:欢乐集结号
表演了:怕上火 喝加多宝
听到了:怕上火 喝加多宝
表演了:欢乐集结号
听到了:欢乐集结号
表演了:怕上火 喝加多宝
听到了:怕上火 喝加多宝
表演了:欢乐集结号
听到了:欢乐集结号
表演了:怕上火 喝加多宝
听到了:怕上火 喝加多宝
表演了:欢乐集结号
听到了:欢乐集结号
表演了:怕上火 喝加多宝
听到了:怕上火 喝加多宝
表演了:欢乐集结号
听到了:欢乐集结号
表演了:怕上火 喝加多宝
听到了:怕上火 喝加多宝
表演了:欢乐集结号
听到了:欢乐集结号
表演了:怕上火 喝加多宝
听到了:怕上火 喝加多宝
表演了:欢乐集结号
听到了:欢乐集结号
表演了:怕上火 喝加多宝
听到了:怕上火 喝加多宝
表演了:欢乐集结号
听到了:欢乐集结号
表演了:怕上火 喝加多宝
听到了:怕上火 喝加多宝

多线程synchronized实现简单的影院购票功能

多线程synchronized实现简单的影院购票功能

由于同一进程的多个线程共享同一块存储空间,在带来方便的同时,也带来了访问冲突的问题。解决该问题的机制是synchronized关键字,它包括两种用法:synchronized 方法和 synchronized 块。

synchronized 方法

通过在方法声明中加入 synchronized关键字来声明,语法如下:

public synchronized void accessVal(int newVal);

synchronized 方法控制对“对象的类成员变量”的访问:每个对象对应一把锁,每个 synchronized 方法都必须获得调用该方法的对象的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。

synchronized 块

synchronized 方法的缺陷:若将一个大的方法声明为synchronized 将会大大影响效率。

Java 为我们提供了更好的解决办法,那就是 synchronized 块。 块可以让我们精确地控制到具体的“成员变量”,缩小同步的范围,提高效率。

synchronized 块:通过 synchronized关键字来声明synchronized 块,语法如下:

synchronized(syncObject)
   { 
   //允许访问控制的代码 
   }

实现简单的影院购票功能

代码如下:

package com.msl.syn;

import java.util.ArrayList;
import java.util.List;

/**
 * 实现简单的影院购票功能 - 快乐影院
 * @author Senley
 *
 */
public class HappyCinema2 {
    public static void main(String[] args) {
        //可用位置
        List<Integer> available = new ArrayList<Integer>();
        available.add(1);
        available.add(2);
        available.add(5);
        available.add(6);
        available.add(7);
        //顾客需要的位置
        List<Integer> seats1 = new ArrayList<Integer>();
        seats1.add(1);
        seats1.add(2);
        List<Integer> seats2 = new ArrayList<Integer>();
        seats2.add(3);
        seats2.add(6);
        
        MslCinema m = new MslCinema(available, "Happy Cinema");
        new Thread(new HappyCustomer(m, seats1),"张三").start();
        new Thread(new HappyCustomer(m, seats2),"李四").start();
    }
    
}

//影院
class MslCinema{
    List<Integer> available; //可用的位置
    String name; //名称
    
    public MslCinema(List<Integer> available, String name) {
        this.available = available;
        this.name = name;
    }
    
    //购票
    public boolean bookTickets(List<Integer> seats) {
        System.out.println("欢迎光临"+this.name+",可用位置为:"+available);
        List<Integer> copy = new ArrayList<Integer>();
        copy.addAll(available);
        //相减
        copy.removeAll(seats);
        //判断大小
        if(available.size()-seats.size()!=copy.size()) {
            return false;
        }
        //成功
        available = copy;
        return true;
    }
}

//顾客
class HappyCustomer implements Runnable{
    MslCinema cinema;
    List<Integer> seats;

    public HappyCustomer(MslCinema cinema, List<Integer> seats) {
        super();
        this.cinema = cinema;
        this.seats = seats;
    }

    @Override
    public void run() {
        synchronized(cinema) {
            boolean flag = cinema.bookTickets(seats);
            if(flag) {
                System.out.println("出票成功--"+Thread.currentThread().getName()+"--位置为"+seats);
            }else {
                System.out.println("出票失败--"+Thread.currentThread().getName()+"--位置不够");        
            }
        }
    }
}

结果如下:

欢迎光临Happy Cinema,可用位置为:[1, 2, 5, 6, 7]
出票成功--张三--位置为[1, 2]
欢迎光临Happy Cinema,可用位置为:[5, 6, 7]
出票失败--李四--位置不够

“synchronized (cinema)” 意味着线程需要获得cinema对象的“锁”才有资格运行同步块中的代码。cinema对象的“锁”也称为“互斥锁”,在同一时刻只能被一个线程使用。A线程拥有锁,则可以调用“同步块”中的代码;B线程没有锁,则进入cinema对象的“锁池队列”等待,直到A线程使用完毕释放了cinema对象的锁,B线程得到锁才可以开始调用“同步块”中的代码。

多线程的实现方式及简单模拟

多线程的实现方式及简单模拟

多线程的实现方式

  1. 继承Thread类
    • 1、创建:继承Thread + 重写run
    • 2、启动:创建子类对象 + start
  2. Runnable接口(避免单继承的局限性,优先使用接口
    • 1、创建:实现Runnable + 重写run
    • 2、启动:创建实现类对象 + Thread对象 + start
  3. Callable接口
    • 1、创建目标对象
    • 2、创建执行服务
    • 3、提交执行
    • 4、获取结果
    • 5、关闭服务

多线程的简单模拟

模拟12306

代码如下:

package com.msl.thread;
/**
 * 模拟12306
 * @author Senley
 *
 */
public class Web12306 implements Runnable {
    //票数
    private int ticketNums = 99;
    
    @Override
    public void run() {
        while(true) {
            if(ticketNums<0) {
                break;
            }
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"-->"+ticketNums--);
        }
    }
    
    public static void main(String[] args) {
        //一份资源
        Web12306 web = new Web12306();
        System.out.println(Thread.currentThread().getName());
        //多个代理
        new Thread(web,"码畜").start();
        new Thread(web,"码农").start();
        new Thread(web,"码蟥").start();
    }
}

结果如下:

main
码畜-->99
码蟥-->97
码农-->98
码农-->96
码蟥-->96
码畜-->96
码畜-->95
码农-->95
码蟥-->95
码畜-->94
码农-->94
码蟥-->93
码蟥-->92
码农-->91
码畜-->90
码畜-->89
码农-->88
码蟥-->88
码蟥-->87
码畜-->86
码农-->85
码蟥-->84
码农-->84
码畜-->83
码畜-->82
码蟥-->82
码农-->82
码蟥-->80
码农-->81
码畜-->81
码畜-->79
码蟥-->78
码农-->79
码农-->77
码畜-->77
码蟥-->77
码畜-->76
码农-->74
码蟥-->75
码农-->73
码蟥-->72
码畜-->73
码农-->71
码畜-->71
码蟥-->70
码农-->69
码蟥-->68
码畜-->69
码蟥-->66
码农-->67
码畜-->65
码蟥-->62
码农-->63
码畜-->64
码畜-->61
码蟥-->60
码农-->59
码畜-->58
码蟥-->57
码农-->58
码农-->56
码畜-->56
码蟥-->56
码蟥-->55
码农-->55
码畜-->54
码蟥-->53
码农-->51
码畜-->52
码畜-->50
码蟥-->50
码农-->50
码农-->48
码蟥-->49
码畜-->47
码畜-->44
码农-->45
码蟥-->46
码畜-->43
码农-->41
码蟥-->42
码农-->39
码畜-->40
码蟥-->40
码畜-->38
码农-->38
码蟥-->38
码畜-->37
码农-->37
码蟥-->37
码蟥-->35
码农-->35
码畜-->36
码畜-->34
码蟥-->34
码农-->34
码农-->33
码畜-->33
码蟥-->33
码蟥-->32
码农-->32
码畜-->32
码畜-->31
码农-->31
码蟥-->31
码蟥-->30
码畜-->30
码农-->30
码农-->29
码蟥-->28
码畜-->29
码畜-->27
码农-->26
码蟥-->27
码畜-->25
码农-->25
码蟥-->25
码蟥-->24
码农-->23
码畜-->23
码蟥-->22
码农-->22
码畜-->22
码畜-->21
码蟥-->19
码农-->20
码蟥-->18
码畜-->18
码农-->17
码蟥-->16
码农-->15
码畜-->14
码畜-->13
码农-->13
码蟥-->13
码蟥-->12
码农-->11
码畜-->12
码农-->10
码畜-->9
码蟥-->8
码农-->7
码畜-->6
码蟥-->7
码畜-->5
码农-->3
码蟥-->4
码蟥-->2
码农-->2
码畜-->2
码畜-->0
码农-->1
码蟥-->1

模拟龟兔赛跑

代码如下:

package com.msl.thread;
/**
 * 模拟龟兔赛跑
 * @author Senley
 *
 */
public class Racer implements Runnable{
    private String winner;//胜利者
    @Override
    public void run() {
        for(int steps=1;steps<=100;steps++) {
            //模拟休息
            if(Thread.currentThread().getName().equals("rabbit")&&steps%10==0) {
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName()+"-->"+steps);
            //比赛是否结束
            boolean flag = gameOver(steps);
            if(flag) {
                break;
            }
        }
    }
    
    private boolean gameOver(int steps) {
        if(winner!=null) {//存在胜利者
            return true;
        }else {
            if(steps==100) {
                winner = Thread.currentThread().getName();
                System.out.println("winner-->"+winner);
                return true;
            }
        }
        return false;
    }
    
    public static void main(String[] args) {
        Racer racer = new Racer();
        new Thread(racer,"tortoise").start();
        new Thread(racer,"rabbit").start();
    }
}

结果如下:

tortoise-->1
rabbit-->1
rabbit-->2
rabbit-->3
rabbit-->4
rabbit-->5
rabbit-->6
rabbit-->7
rabbit-->8
rabbit-->9
tortoise-->2
tortoise-->3
tortoise-->4
tortoise-->5
tortoise-->6
tortoise-->7
tortoise-->8
tortoise-->9
tortoise-->10
tortoise-->11
tortoise-->12
tortoise-->13
tortoise-->14
tortoise-->15
tortoise-->16
tortoise-->17
tortoise-->18
tortoise-->19
tortoise-->20
tortoise-->21
tortoise-->22
tortoise-->23
tortoise-->24
tortoise-->25
tortoise-->26
tortoise-->27
tortoise-->28
tortoise-->29
tortoise-->30
tortoise-->31
tortoise-->32
tortoise-->33
tortoise-->34
tortoise-->35
tortoise-->36
tortoise-->37
tortoise-->38
tortoise-->39
tortoise-->40
tortoise-->41
tortoise-->42
tortoise-->43
tortoise-->44
tortoise-->45
tortoise-->46
tortoise-->47
tortoise-->48
tortoise-->49
tortoise-->50
tortoise-->51
tortoise-->52
tortoise-->53
tortoise-->54
tortoise-->55
tortoise-->56
tortoise-->57
tortoise-->58
tortoise-->59
tortoise-->60
tortoise-->61
tortoise-->62
tortoise-->63
tortoise-->64
tortoise-->65
tortoise-->66
tortoise-->67
tortoise-->68
tortoise-->69
tortoise-->70
tortoise-->71
tortoise-->72
tortoise-->73
tortoise-->74
tortoise-->75
tortoise-->76
tortoise-->77
tortoise-->78
tortoise-->79
tortoise-->80
tortoise-->81
tortoise-->82
tortoise-->83
tortoise-->84
tortoise-->85
tortoise-->86
tortoise-->87
tortoise-->88
tortoise-->89
tortoise-->90
tortoise-->91
tortoise-->92
tortoise-->93
tortoise-->94
tortoise-->95
tortoise-->96
tortoise-->97
tortoise-->98
tortoise-->99
tortoise-->100
winner-->tortoise
rabbit-->10

模拟龟兔赛跑(使用Callable)

代码如下:

package com.msl.thread;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * 模拟龟兔赛跑
 * @author Senley
 *
 */
public class CRacer implements Callable<Integer>{
    private String winner;//胜利者
    @Override
    public Integer call() throws Exception {
        for(int steps=1;steps<=100;steps++) {
            //模拟休息
            if(Thread.currentThread().getName().equals("pool-1-thread-1")&&steps%10==0) {
                Thread.sleep(100);
            }
            System.out.println(Thread.currentThread().getName()+"-->"+steps);
            //比赛是否结束
            boolean flag = gameOver(steps);
            if(flag) {
                return steps;
            }
        }
        return null;
    }
    
    private boolean gameOver(int steps) {
        if(winner!=null) {//存在胜利者
            return true;
        }else {
            if(steps==100) {
                winner = Thread.currentThread().getName();
                System.out.println("winner-->"+winner);
                return true;
            }
        }
        return false;
    }
    
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        CRacer racer = new CRacer();
        //创建执行服务: 
        ExecutorService  ser=Executors.newFixedThreadPool(2);
        //提交执行: 
        Future<Integer> result1 =ser.submit(racer) ;
        Future<Integer> result2 =ser.submit(racer) ;
        //获取结果:  
        Integer r1 =result1.get();
        Integer r2 =result2.get();
        System.out.println(r1+"-->"+r2);
        //关闭服务:  
        ser.shutdownNow();
    }
}

结果如下:

pool-1-thread-2-->1
pool-1-thread-1-->1
pool-1-thread-1-->2
pool-1-thread-1-->3
pool-1-thread-1-->4
pool-1-thread-1-->5
pool-1-thread-1-->6
pool-1-thread-1-->7
pool-1-thread-1-->8
pool-1-thread-1-->9
pool-1-thread-2-->2
pool-1-thread-2-->3
pool-1-thread-2-->4
pool-1-thread-2-->5
pool-1-thread-2-->6
pool-1-thread-2-->7
pool-1-thread-2-->8
pool-1-thread-2-->9
pool-1-thread-2-->10
pool-1-thread-2-->11
pool-1-thread-2-->12
pool-1-thread-2-->13
pool-1-thread-2-->14
pool-1-thread-2-->15
pool-1-thread-2-->16
pool-1-thread-2-->17
pool-1-thread-2-->18
pool-1-thread-2-->19
pool-1-thread-2-->20
pool-1-thread-2-->21
pool-1-thread-2-->22
pool-1-thread-2-->23
pool-1-thread-2-->24
pool-1-thread-2-->25
pool-1-thread-2-->26
pool-1-thread-2-->27
pool-1-thread-2-->28
pool-1-thread-2-->29
pool-1-thread-2-->30
pool-1-thread-2-->31
pool-1-thread-2-->32
pool-1-thread-2-->33
pool-1-thread-2-->34
pool-1-thread-2-->35
pool-1-thread-2-->36
pool-1-thread-2-->37
pool-1-thread-2-->38
pool-1-thread-2-->39
pool-1-thread-2-->40
pool-1-thread-2-->41
pool-1-thread-2-->42
pool-1-thread-2-->43
pool-1-thread-2-->44
pool-1-thread-2-->45
pool-1-thread-2-->46
pool-1-thread-2-->47
pool-1-thread-2-->48
pool-1-thread-2-->49
pool-1-thread-2-->50
pool-1-thread-2-->51
pool-1-thread-2-->52
pool-1-thread-2-->53
pool-1-thread-2-->54
pool-1-thread-2-->55
pool-1-thread-2-->56
pool-1-thread-2-->57
pool-1-thread-2-->58
pool-1-thread-2-->59
pool-1-thread-2-->60
pool-1-thread-2-->61
pool-1-thread-2-->62
pool-1-thread-2-->63
pool-1-thread-2-->64
pool-1-thread-2-->65
pool-1-thread-2-->66
pool-1-thread-2-->67
pool-1-thread-2-->68
pool-1-thread-2-->69
pool-1-thread-2-->70
pool-1-thread-2-->71
pool-1-thread-2-->72
pool-1-thread-2-->73
pool-1-thread-2-->74
pool-1-thread-2-->75
pool-1-thread-2-->76
pool-1-thread-2-->77
pool-1-thread-2-->78
pool-1-thread-2-->79
pool-1-thread-2-->80
pool-1-thread-2-->81
pool-1-thread-2-->82
pool-1-thread-2-->83
pool-1-thread-2-->84
pool-1-thread-2-->85
pool-1-thread-2-->86
pool-1-thread-2-->87
pool-1-thread-2-->88
pool-1-thread-2-->89
pool-1-thread-2-->90
pool-1-thread-2-->91
pool-1-thread-2-->92
pool-1-thread-2-->93
pool-1-thread-2-->94
pool-1-thread-2-->95
pool-1-thread-2-->96
pool-1-thread-2-->97
pool-1-thread-2-->98
pool-1-thread-2-->99
pool-1-thread-2-->100
winner-->pool-1-thread-2
pool-1-thread-1-->10
10-->100

装饰器设计模式的理解与实现

装饰器设计模式的理解与实现

设计模式的分类

总体来说设计模式分为三大类:

创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

装饰器设计模式的定义与特点

装饰(Decorator)模式的定义:指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式,它属于对象结构型模式。

装饰(Decorator)模式的主要优点有:

  • 装饰器是继承的有力补充,比继承灵活,在不改变原有对象的情况下,动态地给一个对象扩展功能,即插即用。
  • 通过使用不用装饰类及这些装饰类的排列组合,可以实现不同效果。
  • 装饰器模式完全遵守开闭原则。

其主要缺点是:

装饰模式会增加许多子类,过度使用会增加程序复杂性。

装饰器设计模式的结构与实现

模式结构

  • 抽象构件(Component)角色:定义一个抽象接口以规范准备接收附加责任的对象。
  • 具体构件(ConcreteComponent)角色:实现抽象构件,通过装饰角色为其添加一些职责。
  • 抽象装饰(Decorator)角色:继承抽象构件,并包含具体构件的实例,可以通过其子类扩展具体构件的功能。
  • 具体装饰(ConcreteDecorator)角色:实现抽象装饰的相关方法,并给具体构件对象添加附加的责任。

模式实现

代码如下:

package com.msl.io;
/**
 * 模拟咖啡
 * 1、抽象组件:需要装饰的抽象对象(接口或抽象父类)
 * 2、具体组件:需要装饰的对象
 * 3、抽象装饰类:包含了对抽象组件的引用以及装饰者共有的方法
 * 4、具体装饰类:被装饰的对象
 * @author Senley
 *
 */
public class DecorateTest {
    public static void main(String[] args) {
        Drink coffer = new Coffer();
        Drink suger = new Suger(coffer);//装饰
        System.out.println(suger.info()+"-->"+suger.cost());
        Drink milk = new Milk(coffer);//装饰
        System.out.println(milk.info()+"-->"+milk.cost());
        milk = new Milk(suger);//装饰
        System.out.println(milk.info()+"-->"+milk.cost());
    }
}

//抽象组件
interface Drink{
    double cost();//费用
    String info();//说明
}

//具体组件
class Coffer implements Drink{
    private String name ="原味咖啡";
    @Override
    public double cost() {
        return 10;
    }

    @Override
    public String info() {
        return name;
    }
}

//抽象装饰类
abstract class Decorate implements Drink{
    //对抽象组件的引用
    private Drink drink;
    public Decorate(Drink drink) {
        this.drink = drink;
    }
    @Override
    public double cost() {
        return this.drink.cost();
    }

    @Override
    public String info() {
        return this.drink.info();
    }
}

//具体装饰类
class Milk extends Decorate{
    public Milk(Drink drink) {
        super(drink);
    }
    @Override
    public double cost() {
        return super.cost()*4;
    }

    @Override
    public String info() {
        return super.info()+"加入了牛奶";
    }
}

class Suger extends Decorate{
    public Suger(Drink drink) {
        super(drink);
    }
    @Override
    public double cost() {
        return super.cost()*2;
    }

    @Override
    public String info() {
        return super.info()+"加入了蔗糖";
    }    
}

结果如下:

原味咖啡加入了蔗糖-->20.0
原味咖啡加入了牛奶-->40.0
原味咖啡加入了蔗糖加入了牛奶-->80.0

IO文件拷贝与对接流

IO文件拷贝与对接流

IO文件拷贝

IO文件拷贝:文件字节输入输出流。

  1. 创建源
  2. 选择流
  3. 操作
  4. 释放资源

代码如下:

package com.msl.io;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * 文件拷贝:文件字节输入、输出流
 * 1、创建源
 * 2、选择流
 * 3、操作(写出内容)
 * 4、释放资源
 * @author Senley
 *
 */
public class Copy {

    public static void main(String[] args) {
        copy("src/com/msl/io/Copy.java","copy.txt");
    }
    /**
     * 文件的拷贝
     * @param srcPath
     * @param destPath
     */
    public static void copy(String srcPath,String destPath) {
        //1、创建源
        File src = new File(srcPath);//源头
        File dest = new File(destPath);//目的地
        //2、选择流
        InputStream is = null;
        OutputStream os = null;
        try {
            is = new FileInputStream(src);
            os = new FileOutputStream(dest);
            //3、操作
            byte[] flush = new byte[1024];//缓冲容器
            int len = -1;//接收长度
            while((len=is.read(flush))!=-1) {
                os.write(flush,0,len);
            }
        }catch(FileNotFoundException e){
            e.printStackTrace();
        }catch(IOException e){
            e.printStackTrace();
        }finally {
            //释放资源 分别关闭 先打开的后关闭
            try {
                if (null != os) {
                    os.close();
                } 
            } catch (Exception e) {
                e.printStackTrace();
            }
            
            try {
                if(null!=is) {
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

IO对接流

  1. 图片读取到字节数组

    1)图片到程序

    2)程序到字节数组

  2. 字节数组写出到文件

    1)字节数组到程序

    2)程序到文件

代码如下:

package com.msl.io;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * 1、图片读取到字节数组
 * 2、字节数组写出到文件
 * @author Senley
 *
 */
public class IOTest {

    public static void main(String[] args) {
        //图片转成字节数组
        byte[] datas = fileToByteArray("p.png");
        System.out.println(datas.length);
        byteArrayToFile(datas, "p-byte.png");
    }
    
    /**
     * 1、图片读取到字节数组
     * 1)、图片到程序 FileInputStream
     * 2)、程序到字节数组 ByteArrayOutputStream
     */
    public static byte[] fileToByteArray(String filePath) {
        //1、创建源与目的地
        File src = new File(filePath);
        byte[] dest = null;
        //2、选择流
        InputStream is = null;
        ByteArrayOutputStream baos = null;
        try {
            is = new FileInputStream(src);
            baos = new ByteArrayOutputStream();
            //3、操作(分段读取)
            byte[] flush = new byte[1024*10];//缓冲容器
            int len = -1;//接收长度
            while((len=is.read(flush))!=-1) {
                baos.write(flush,0,len); //写出到字节数组中
            }
            baos.flush();
            return baos.toByteArray();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //4、释放资源
            try {
                if(null!=is) {
                    is.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }
    /**
     * 2、字节数组写出到文件
     * 1)、字节数组到程序 ByteArrayInputStream
     * 2)、程序到文件 FileOutputStream
     */
    public static void byteArrayToFile(byte[] src,String filePath) {
        //1、创建源
        File dest = new File(filePath);
        //2、选择流
        InputStream is = null;
        OutputStream os = null;
        try {
            is = new ByteArrayInputStream(src);
            os = new FileOutputStream(dest);
            //3、操作(分段读取)
            byte[] flush = new byte[5];//缓冲容器
            int len = -1;//接收长度
            while((len=is.read(flush))!=-1) {
                os.write(flush,0,len);//写出到文件
            }
            os.flush();
        }catch (IOException e) {
            e.printStackTrace();
        }finally {
            //4、释放资源
            try {
                if (null != os) {
                    os.close();
                } 
            } catch (Exception e) {
            }
        }
    }
}

使用递归及面向对象思想统计文件夹大小

使用递归及面向对象思想统计文件夹大小

IO介绍

对于任何程序设计语言而言,输入输出(Input/Output)系统都是非常核心的功能。程序运行需要数据,数据的获取往往需要跟外部系统进行通信,外部系统可能是文件、数据库、其他程序、网络、IO设备等等。外部系统比较复杂多变,那么就有必要通过某种手段进行抽象、屏蔽外部的差异,从而实现更加便捷的编程。

输入(Input)指的是:可以让程序从外部系统获得数据(核心含义是“读”,读取外部数据)。常见的应用:

  • 读取硬盘上的文件内容到程序。例如:播放器打开一个视频文件、word打开一个doc文件。
  • 读取网络上某个位置内容到程序。例如:浏览器中输入网址后,打开该网址对应的网页内容;下载网络上某个网址的文件。
  • 读取数据库系统的数据到程序。
  • 读取某些硬件系统数据到程序。例如:车载电脑读取雷达扫描信息到程序;温控系统等。

输出(Output)指的是:程序输出数据给外部系统从而可以操作外部系统(核心含义是“写”,将数据写出到外部系统)。常见的应用有:

  • 将数据写到硬盘中。例如:我们编辑完一个word文档后,将内容写到硬盘上进行保存。
  • 将数据写到数据库系统中。例如:我们注册一个网站会员,实际就是后台程序向数据库中写入一条记录。
  • 将数据写到某些硬件系统中。例如:导弹系统导航程序将新的路径输出到飞控子系统,飞控子系统根据数据修正飞行路径。

File类介绍

Java文件类以抽象的方式代表文件名和目录路径名。该类主要用于文件和目录的创建、文件的查找和文件的删除等。

File对象代表磁盘中实际存在的文件和目录。通过以下构造方法创建一个File对象。

通过给定的父抽象路径名和子路径名字符串创建一个新的File实例。

File(File parent, String child);

通过将给定路径名字符串转换成抽象路径名来创建一个新 File 实例。

File(String pathname) 

根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例。

File(String parent, String child) 

通过将给定的 file: URI 转换成一个抽象路径名来创建一个新的 File 实例。

File(URI uri) 

创建File对象成功后,可以使用以下列表中的方法操作文件。

序号 方法描述
1 public String getName() 返回由此抽象路径名表示的文件或目录的名称。
2 **public String getParent()** 返回此抽象路径名的父路径名的路径名字符串,如果此路径名没有指定父目录,则返回 null
3 public File getParentFile() 返回此抽象路径名的父路径名的抽象路径名,如果此路径名没有指定父目录,则返回 null
4 public String getPath() 将此抽象路径名转换为一个路径名字符串。
5 public boolean isAbsolute() 测试此抽象路径名是否为绝对路径名。
6 public String getAbsolutePath() 返回抽象路径名的绝对路径名字符串。
7 public boolean canRead() 测试应用程序是否可以读取此抽象路径名表示的文件。
8 public boolean canWrite() 测试应用程序是否可以修改此抽象路径名表示的文件。
9 public boolean exists() 测试此抽象路径名表示的文件或目录是否存在。
10 public boolean isDirectory() 测试此抽象路径名表示的文件是否是一个目录。
11 public boolean isFile() 测试此抽象路径名表示的文件是否是一个标准文件。
12 public long lastModified() 返回此抽象路径名表示的文件最后一次被修改的时间。
13 public long length() 返回由此抽象路径名表示的文件的长度。
14 public boolean createNewFile() throws IOException 当且仅当不存在具有此抽象路径名指定的名称的文件时,原子地创建由此抽象路径名指定的一个新的空文件。
15 public boolean delete() 删除此抽象路径名表示的文件或目录。
16 public void deleteOnExit() 在虚拟机终止时,请求删除此抽象路径名表示的文件或目录。
17 public String[] list() 返回由此抽象路径名所表示的目录中的文件和目录的名称所组成字符串数组。
18 public String[] list(FilenameFilter filter) 返回由包含在目录中的文件和目录的名称所组成的字符串数组,这一目录是通过满足指定过滤器的抽象路径名来表示的。
19 public File[] listFiles() 返回一个抽象路径名数组,这些路径名表示此抽象路径名所表示目录中的文件。
20 public File[] listFiles(FileFilter filter) 返回表示此抽象路径名所表示目录中的文件和目录的抽象路径名数组,这些路径名满足特定过滤器。
21 public boolean mkdir() 创建此抽象路径名指定的目录。
22 public boolean mkdirs() 创建此抽象路径名指定的目录,包括创建必需但不存在的父目录。
23 public boolean renameTo(File dest) 重新命名此抽象路径名表示的文件。
24 public boolean setLastModified(long time) 设置由此抽象路径名所指定的文件或目录的最后一次修改时间。
25 public boolean setReadOnly() 标记此抽象路径名指定的文件或目录,以便只可对其进行读操作。
26 public static File createTempFile(String prefix, String suffix, File directory) throws IOException 在指定目录中创建一个新的空文件,使用给定的前缀和后缀字符串生成其名称。
27 public static File createTempFile(String prefix, String suffix) throws IOException 在默认临时文件目录中创建一个空文件,使用给定前缀和后缀生成其名称。
28 public int compareTo(File pathname) 按字母顺序比较两个抽象路径名。
29 public int compareTo(Object o) 按字母顺序比较抽象路径名与给定对象。
30 public boolean equals(Object obj) 测试此抽象路径名与给定对象是否相等。
31 public String toString() 返回此抽象路径名的路径名字符串。

使用递归及面向对象思想统计文件夹大小、文件个数、文件夹个数

代码如下:

package com.msl.io;

import java.io.File;

/**
 * 使用递归及面向对象: 统计文件夹大小、文件个数、文件夹个数
 * @author Senley
 * 
 */
public class DirSize {
    //大小
    private long len;
    //文件夹路径
    private String path;
    //文件个数
    private int fileCount;
    //文件夹个数
    private int dirCount;
    //源
    private File src;
    
    public long getLen() {
        return len;
    }
    
    public int getFileCount() {
        return fileCount;
    }

    public int getDirCount() {
        return dirCount;
    }
    
    public DirSize(String path) {
        this.path = path;
        this.src = new File(path);
        count(src);
    }

    public static void main(String[] args) {
        DirSize dir = new DirSize("E:/Workspace/IO");
        System.out.println("文件夹大小:"+dir.getLen()+"字节"+ "--"+"文件个数:"+dir.getFileCount()+" --"+"文件夹个数:"+dir.getDirCount());
    }
    
    //统计大小
    private void count(File src){
        //获取大小
        if(null!=src && src.exists()) {
            if(src.isFile()) {//大小
                fileCount++;
                len+=src.length();
            }else{//子孙级
                dirCount++;
                for(File s:src.listFiles()) {
                    count(s);
                }
            }
        }
    }

}

结果如下:

文件夹大小:60850字节 --文件个数:29 --文件夹个数:13

需注意的是,文件夹个数为子孙级文件夹个数+本身,如需仅显示子孙级文件夹,可定义dirCount = -1。

使用容器存储表格数据

使用容器存储表格数据

使用容器存储以下表格数据。

ID 姓名 薪水 入职日期
1001 张三 20000 2018.5.5
1002 李四 30000 2005.5.4
1003 王五 3000 2020.5.4

Map和List结合存储

Map表示一行数据,多行数据是多个Map,将多个Map放到List中。代码如下:

package com.msl.collection;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 测试表格数据的存储
 * ORM思想的简单实验:map表示一行数据,多行数据是多个map;将多个map放到list中
 * @author Senley
 *
 */
public class TestStoreData {
    public static void main(String[] args) {
        Map<String,Object> row1 = new HashMap<>();
        row1.put("id", 1001);
        row1.put("姓名", "张三");
        row1.put("薪水", 20000);
        row1.put("入职日期", "2018.5.5");
        
        Map<String,Object> row2 = new HashMap<>();
        row2.put("id", 1002);
        row2.put("姓名", "李四");
        row2.put("薪水", 30000);
        row2.put("入职日期", "2005.4.4");
        
        Map<String,Object> row3 = new HashMap<>();
        row3.put("id", 1003);
        row3.put("姓名", "王五");
        row3.put("薪水", 3000);
        row3.put("入职日期", "2020.5.4");
        
        List<Map<String,Object>> table1 = new ArrayList<>();
        table1.add(row1);
        table1.add(row2);
        table1.add(row3);
        
        for(Map<String,Object> row:table1) {
            Set<String> keyset = row.keySet();
            for(String key:keyset) {
                System.out.print(key+":"+row.get(key)+"\t");
            }
            System.out.println();
        }
    }
}

结果如下:

姓名:张三    薪水:20000    id:1001    入职日期:2018.5.5    
姓名:李四    薪水:30000    id:1002    入职日期:2005.4.4    
姓名:王五    薪水:3000    id:1003    入职日期:2020.5.4    

JavaBean和List结合存储

每一行数据使用JavaBean对象存储,多行使用放到Map或List中。代码如下:

package com.msl.collection;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 测试表格数据的存储
 * 体会ORM思想
 * 每一行数据使用javabean对象存储,多行使用放到map或list中
 * @author Senley
 *
 */
public class TestStoreData2 {
    public static void main(String[] args) {
        User user1 = new User(1001,"张三",20000,"2018.5.5");
        User user2 = new User(1002,"李四",30000,"2005.5.4");
        User user3 = new User(1003,"王五",3000,"2020.5.4");
        
        List<User> list = new ArrayList<>();
        list.add(user1);
        list.add(user2);
        list.add(user3);
        for(User u:list) {
            System.out.println(u);
        }
        
        Map<Integer,User> map = new HashMap<>();
        map.put(1001, user1);
        map.put(1002, user2);
        map.put(1003, user3);
        Set<Integer> keyset = map.keySet();
        for(Integer key:keyset) {
            System.out.println(key+"===="+map.get(key));
        }
    }
}

class User{
    private int id;
    private String name;
    private double salary;
    private String hiredate;
    
    //一个完整的javabean要有set和get方法以及一个无参构造器
    public User() {
    }
    
    public User(int id, String name, double salary, String hiredate) {
        super();
        this.id = id;
        this.name = name;
        this.salary = salary;
        this.hiredate = hiredate;
    }
    
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public double getSalary() {
        return salary;
    }
    public void setSalary(double salary) {
        this.salary = salary;
    }
    public String getHiredate() {
        return hiredate;
    }
    public void setHiredate(String hiredate) {
        this.hiredate = hiredate;
    }
    
    @Override
    public String toString() {
        return "id:"+id+",name:"+name+",salary:"+salary+",hiredate:"+hiredate;
    }
}

结果如下:

id:1001,name:张三,salary:20000.0,hiredate:2018.5.5
id:1002,name:李四,salary:30000.0,hiredate:2005.5.4
id:1003,name:王五,salary:3000.0,hiredate:2020.5.4
1001====id:1001,name:张三,salary:20000.0,hiredate:2018.5.5
1002====id:1002,name:李四,salary:30000.0,hiredate:2005.5.4
1003====id:1003,name:王五,salary:3000.0,hiredate:2020.5.4
  • Copyrights © 2021-2022 Senley
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信