Day25 --多线程(下) 设计模式 GUI

来源:互联网 发布:qq输入法 linux 编辑:程序博客网 时间:2024/06/06 00:43
 a.
    单例设计模式 --单个实例单个对象
        * 保证类在内存中只有一个对象
        
    如何保证类在内存中只有一个对象?        
        1. 控制类的创建(私有构造),不让其他类去创建该类对象,即私有该类private
        2. 在本类中定义一个该类对象,如:private static Singleton s = new Singleton(); --成员变量私有,不让其通过 类名.调用
        3. 对外提供公共的访问方式:public static Singleton getInstance(){ return s}  --获取实例
        
    单例设计模式的两种写法:
        * 饿汉式 --一上来就创建对象 
            1. 私有本类构造,不让其他类去创建访问本类对象
            2. 创建该类对象,并且将该类对象(成员变量)私有
            3. 对外提供公共的访问方式,让其他类去调用(而不是让其他类创建该类对象) 
            p.s.
            饿汉式不存在安全问题,因为不存在多个线程同时操作数据的情况        

            class Singleton{
                private Singleton(){} // 1.私有本类构造,不让其他类去创建访问本类对象
                private static Singleton s = new Singleton(); // 2.创建该类对象,并且将该类对象(成员变量)私有
                public static Singleton getInstance(){ // 3.对外提供公共的访问方式,让其他类去调用(而不是让其他类创建该类对象) 
                    return s;
                }
            }            
        
            main{
                Singleton s1 = Singleton.getInstance();
                Singleton s2 = Singleton.getInstance();
                sop(s1 == s2); //true,表示是同一个对象
            }

                     


        * 懒汉式 --单例的延迟加载模式,存在安全隐患。什么时候要,什么时候创建对象,工作中不用,面试上用。
            1. 私有本类构造,不让其他类去创建访问本类对象
            2. 创建该类对象,并且将该类对象(成员变量)私有
            3. 对外提供公共的访问方式,先判断是否存在,如果为空,就创建,但是这种模式在多线程访问的情况下,会有安全隐患,会创建多个对象     
            
            class Singleton{
                private Singleton(){} // 1.私有本类构造,不让其他类去创建访问本类对象

                private static Singleton s; // 2.创建该类对象,并且将该类对象(成员变量)私有
                
                public static Singleton getInstance(){ // 3.对外提供公共的访问方式,先判断是否存在,如果为空,就创建,但是这种模式在多线程访问的情况下,会有安全隐患,会创建多个对象     
                    if(s == null){
                        s = new Singleton();
                    }
                    return s;
                }
            }            
        
            main{
                Singleton s1 = Singleton.getInstance();
                Singleton s2 = Singleton.getInstance();
                sop(s1 == s2); //true,表示是同一个对象
            }

            



        * 第三种格式 --没有名字,也没那么出名
        class Singleton{
            private Singleton(){}  // 1.私有本类构造
            public static final Singleton s = new Singleton(); //2. 创建成员方法,将其用final修饰且静态
         }
        main{
                Singleton s1 = Singleton.s;
                Singleton s2 = Singleton.s; //即使创建了两个对象,但是指向的是同一个对象,因为Singleton类中 使用了final来修饰成员变量的
                System.out.println(s1 == s2); //true, 因为指向的还是同一个对象的引用:s
        }
        


饿汉式 和 懒汉式 区别,建议使用哪种?
        区别:
            * 懒汉式要比饿汉式节省内存空间,什么时候用什么时候创建。
            * 但是建议使用饿汉式,因为如下:
                * 工作中是不使用懒汉式的,因为如果现在是多线程访问,当程序执行到if的时候,判断完s==null,如果此时的线程A执行权被B线程抢去,那么线程A就等待了,线程B开始进来执行,但此时线程A又抢回了执行权就创建一个对象,将s返回,而随后s2又抢到了执行权,就又创建
                象,就不是单例设计模式了。 所以开发中是不用懒汉式的 因为在多线程访问的时候,有安全隐患,可能会创建多个对象。


饿汉式 和 懒汉式 区别  --时间越来越少,也越来越重要,空间会随着硬盘的升级而增大不用我们操心。
空间指的是 内存空间
时间指的是 程序运行时间

  1. 饿汉式     是 拿空间换时间
          * 因为饿汉式一上来就创建对象将空间浪费了,但是执行时间效率快[节省时间],所以说是空间换时间
          
  2. 懒汉式     是 拿时间换空间
          * 在创建懒汉式的时候,会去判断,如果没有就创建,所以说浪费了时间,但是这样做节省了空间。
          * 一个程序加载开启的时间过长,而导致程序缓慢,所以不建议使用
 
注意:在多线程访问时,饿汉式不会创建多个对象,而懒汉式会有可能创建多个对象


b.
     Runtime类
        概述
            * Runtime是一个单例类
            * 每个 Java 应用程序都有一个 Runtime 类实例,使应用程序能够与其运行的环境相连接。可以通过 getRuntime 方法获取当前运行时。 
            
        Runtime r = Runtime.getRuntime();
        System.out.println(r); // java.lang.Runtime@1db9742 十六进制的地址值
//        r.exec(" shutdown -s -t 300"); //关机命令,-s表示关机 -t表示设置时间
//        r.exec(" shutdown -a"); // 取消关机命令
        

c.
    Timer类
        概述
            * java.util.timer 计时器类
            * schedule(TimerTask task, Date time); 安排在指定的时间做指定的任务
            * schedule(TimerTask task, Date firstTime, long period); 安排指定的任务在指定的时间开始进行重复的固定延迟执行

代码如下:
    public static void main(String[] args) throws InterruptedException {
//        new Date(year, month, date, hrs, min, sec) 年月日时分秒
        Timer t = new Timer(); // 创建计时器类
//        安排指定的任务在指定的时间开始进行重复的固定延迟执行 
        t.schedule(new MyTimerTask(), new Date(2017-1900, 10,  6,  15,  38,  20), 3000);
                                            // 在2017年11月6日15点38分20秒的时候打印第一次,然后每隔3秒,就打印一次
        while (true) {
            Thread.sleep(1000); //线程每隔1秒种,执行一次。
            System.out.println(new Date()); //不断的打印当前具体时间(年月日时分秒)
        }
    }

}

// 定义一个任务
class MyTimerTask extends TimerTask{
    @Override
    public void run() {
        System.out.println("闹钟响了");
    }
}


d.
    两个线程之间的通信
        概述
            * 什么时候需要通信
                * 多个线程并发的执行时,在默认情况下cpu是随机切换线程的
                * 如果我们希望有规律的执行,那就要使用到 通信技术
                    * 例如两个线程间隔打印,每次只交替打印一个
            * 怎么通信
                * 如果是希望线程等待,就调用wait();
                * 如果希望唤醒等待的线程,就用notify();
                * 这两个方法必须在同步代码块中执行,并且使用同步锁来调用
        
    三个或三个以上的线程通信
        * notify()方法是随机唤醒一个线程
        * notifyAll()方法是唤醒所有线程
        * JDK5之前无法唤醒一个指定的线程
        * 如果多个线程之间需要通信,需要使用notifyAll()同时所有线程,再使用while来返回判断条件


    线程间的通信注意的问题
        [1] 
            在同步代码块中,用哪个对象锁,就用哪个对象调用wait()方法    。
                * 非静态的同步方法是this, 就用this来调用wait
                    * 如:this.wait();
                * 静态的同步方法是 该类字节码对象,那么就用 该类字节码对象调用wait
                    * 如:Demo05_三个或三个以上的线程通信.class.wait();
        [2]
            为什么wait和notify方法定义在Object类中?
                * 因为锁对象可以是任意的,而Object类是所有类的超类,所以wait和notify方法需要定义在Object中
        [3]
            sleep方法和wait方法区别
                * sleep中必须要传入参数,参数就是时间,当时间一过就自动唤醒
                * wait中可传参数,也可不传参数,传入参数就是在指定时间结束后让其等待,不传入参数就是让其一直处于等待状态
        [4]
            sleep 在同步方法或同步代码块中 不释放锁(让出CPU执行权)
                * 可以不释放锁,因为sleep指定时间一过,可以自己醒来
            wait  在同步方法或同步代码块中 必须释放锁(不让出CPU执行权,一直耗着,其他线程都不能执行)
                * 必须释放锁,如果不释放锁,就一直处于等待状态(睡)
    
e.
    jdk1.5的新特性互斥锁
        概述
            * Reentrant(瑞安纯可)Lock类 要比 synchronized类 更加强大
            * Condition类 
                * Condition 将 Object 监视器方法(wait、notify 和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set(wait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。
                * await(); 造成当前线程在接到信号或被中断之前一直处于等待状态 
                * signal(); 唤醒一个等待线程。
                
        同步
            * ReentrantLock类 一个可重入的互斥锁 Lock,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。 
            * 使用ReentrantLock类的lock()[获取锁]和unlock()[释放锁]方法进行同步
        通信
            * 使用ReentrantLock类的newCondition()方法可以获取Condition[环境]对象
            * 需要等待的时间使用Condition[环境]的await()方法,唤醒的时候用signal()方法
            * 不同的线程使用不同的Condition[环境],这样就能区分唤醒的时候找哪个线程了

        Reentrant(瑞安纯可)Lock类 和 synchronized(森困奈斯)类 区别
            * ReentrantLock类是jdk1.5的新特性,叫互斥锁
            * synchronized类是jdk1.0的,也是锁的描述
            * ReentrantLock类 有替代 synchronized类的功能
            * syn中的 synchronized(this){} 可用  Reent中的 lock()和unlock()来代替
            * this.wait 可用  Condition类中的 await()来替代 --指定线程等待
            * this.notify 可用 Condition类中的 signal()来替代 --指定线程唤醒
            
代码如下:
public class Demo06_jdk5的新特性互斥锁_ReentrantLock类 {

    public static void main(String[] args) {
        Print3 p3 = new Print3();
        new Thread(){
            public void run() {
                while (true) {
                    try {
                        p3.print1();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
        }.start();
        
        new Thread(){
            public void run() {
                while (true) {
                    try {
                        p3.print2();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            };
        }.start();
        
        new Thread(){
            public void run() {
                while (true) {
                    try {
                        p3.print3();
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            };
        }.start();
    }

}

class Print3{
    ReentrantLock r = new ReentrantLock(); //代替synchronized类
    Condition c1 = r.newCondition(); //返回用来与此 Lock(ReentrantLock) 实例一起使用的 Condition 实例。
    Condition c2 = r.newCondition();
    Condition c3 = r.newCondition();
    
    private int flag =1;
    public void print1() throws InterruptedException{ //该方法声明也不需要加 snychronized来修饰为同步方法了
        r.lock(); // 使用ReentrantLock中的lock()方法 代替 synchronized(this){
            if( flag !=1){
                c1.await(); //使用Condition中的代替Object中的wait()方法
            }else{
                 System.out.print("夏");
                 System.out.print("侯");
                 System.out.print("惇");
                 System.out.println();
                 flag =2;
                 c2.signal(); //使用Condition中的代替Object中的notify()方法
            }
        r.unlock(); //使用ReentrantLock中的unlock()方法 代替 synchronized(this)的}
    }
    
    public void print2() throws InterruptedException{
        r.lock();
            if( flag !=2){
                c2.await();
            }else{
                 System.out.print("关");
                 System.out.print("云");
                 System.out.print("长");
                 System.out.println();
                 flag =3;
                 c3.signal();
            }
        r.unlock();
    }
    
    public void print3() throws InterruptedException{
        r.lock();
            if( flag !=3){
                c3.await();
            }else{
                 System.out.print("周");
                 System.out.print("瑜");
                 System.out.println();
                 flag =1;
                 c1.signal();
            }
        r.unlock();
    }
}



f.
    线程[组]的概述和使用
        概述
            * Java中使用ThreadGroup来表示线程组,它可以对一批线程进行分类管理,Java运行程序对线程组进行控制
            
        使用
            * 默认情况下,所有线程都属于主线程组的
                * public final ThreadGroup getThreadGroup(); 通过线程对象获取它所属的线程组对象
                * public final String getName(); 通过线程组对象获取它所在组的名称是什么
                
            * 我们也可以对线程进行设置分组
                * 1.ThreadGroup(String name); 创建线程组对象并且赋值名字 
                        * ThreadGroup tg = new ThreadGroup("设置新的线程组");
                        
                * 2.创建线程对象
                        * MyRunnable mr = new MyRunnable();
                        
                * 3.Thread(ThreadGroup group, Runnable target, String name);
                    * Thread(ThreadGroup group, Runnable target, String name);
                    * 参数:
                        * ThreadGroup group;表示该线程属于哪个线程组的
                        *  Runnable target; 将一个线程放到线程组中
                        *  String name; 设置该线程的名称
                

代码如下:
private static void demo02_设置新的线程主() {
        //            给线程设置分组
        //            ThreadGroup(String name); 创建线程组对象并且赋值名字
                    ThreadGroup tg = new ThreadGroup("设置新的线程组");
                    MyRunnable mr = new MyRunnable();
                    
                /*    Thread(ThreadGroup group, Runnable target, String name);
                    * 参数:
                        * ThreadGroup group;表示该线程属于哪个线程组的
                        *  Runnable target; 将一个线程放到线程组中
                        *  String name; 设置该线程的名称
                */
                    Thread td1 = new Thread(tg, mr, "线程1");
                    Thread td2 = new Thread(tg, mr, "线程2");
                    
                    System.out.println(td1.getName()+" 属于:"+td1.getThreadGroup().getName()+" 的");
                    System.out.println(td2.getName()+" 属于:"+td2.getThreadGroup().getName()+" 的");
                    
        //            tg.setDaemon(true); //设置 新的线程组为守护线程。
    }

    private static void demo01_默认情况下所有线程都属于main主线程的() {
        //        默认情况下,所有线程都属于主线程组的
                MyRunnable mr = new MyRunnable();
                Thread t1 = new Thread(mr, "线程一");
                Thread t2 = new Thread(mr, "线程二");
                
//                获取线程组对象
//                public final ThreadGroup getThreadGroup(); 通过线程对象获取它所属的线程组对象
                ThreadGroup tg1 = t1.getThreadGroup(); 
                ThreadGroup tg2 = t2.getThreadGroup();
                
                System.out.println(t1.getName()+" 属于:"+tg1.getName()+" 线程组的");
                System.out.println(t2.getName()+" 属于:"+tg1.getName()+" 线程组的");
    }
}

class MyRunnable implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 1000; i++) {
            System.out.println(Thread.currentThread().getName()+"  "+i);
        }
    }
}            



g.
【线程的五种状态】
    概述
        * 线程的运行一共经历五种状态,分别是:
        * 新建,就绪,运行,阻塞,死亡
    
    这五种状态的执行过程:
        新建:创建线程对象
            * 当start()方法执行,就到了线程 “就绪”状态
            
        就绪:线程对象已经启动,但暂时还没有获取到CPU执行权
            * 就绪状态 有执行资格 和 但暂时没有CPU执行权
            
        运行:获取到CPU执行权,执行当前线程代码
            * 运行状态,既有执行资格,又有CPU执行权
            
        阻塞:没有获取到CPU执行权,又回到 “就绪” 状态
            * 通过sleep 或 wait 方法可以让一个正在运行的线程变成 “阻塞” 状态
            * 变成阻塞状态后,该线程又回到 “就绪” 状态
        
        死亡:所有线程代码运行完毕,线程对象消亡变成垃圾
            * 当程序中所有线程的代码执行完毕后,线程对象就消亡了变成垃圾
        


h.
    线程[池]的概述和使用
        概述
            * 程序启动一个新的线程,成本是比较高的。因为它涉及到与操作系统之间的交互。
            * 而使用线程池可以很好的提高程序性能,减少与操作系统的交互。尤其是当程序中要创建大量生命周期很短的线程时,更应该考虑使用到 线程池。
            * 线程池里的每一个线程代码结束后,并不会死亡,而是会回到线程池中,成为空闲状态,等待下一个对象来继续使用。
            * 在JDK1.5之前,是需要我们手动的实现自己的线程池,而从JDK1.5以后JDK就为我们提供了 内置支持线程池的技术了。

        内置线程池的使用概述
            Executors; n. [法] 执行者;[法] 实施者;[法] 遗嘱执行者(executor的复数)
            ExecutorService; 线程池 任务调度 执行器服务
            fixed; adj. 固执的;<美口>处境...的;准备好的;确定的
            Future; n. 未来;前途;期货;将来时  adj. 将来的,未来的
            submit vi. 提交;服从 vt. 使服从;主张;呈递

            * JDK1.5以后新增了一个 Executors 工厂类来产生 线程池,有如下几个方法
                * public static ExecutorService newFixedThreadPool(int nThreads);
                * public static ExecutorService newSingleThreadExecutor();
                这些方法的返回值都是 ExecutorService对象,该对象表示一个线程池,可以执行 Runnable对象 或者 Callable对象代表的线程,它提供了如下方法:
                    * Future<?> submit(Runnable task);
                    * <T> Future<T> submit(Callable<T> task)
            
            * 使用步骤:
                * 创建线程池对象
                * 创建Runnable实例(创建Runnable的子类对象)
                * 提交Runnable实例
                * 关闭线程池

代码如下
    public static void main(String[] args) {
//        创建线程池对象,并放入两条线程
        ExecutorService pool = Executors.newFixedThreadPool(2); 
//        创建Runnable的子类对象
        MyRunnable rb = new MyRunnable();
        
//        提交Runnable实例
        pool.submit(rb);  // 提交一个返回值的任务用于执行 ( 将线程放入线程池中并执行)
        pool.submit(rb); // 提交一个返回值的任务用于执行 ( 将线程放入线程池中并执行)
        
//        关闭线程池
        pool.shutdown();
    } 
                         
                    
 
i.                
    多线程程序实现的方式3 --不用掌握
        * 其他两种方式
            * 继承 Thread 类
            * 实现 Runnable 接口
            
        * 线程的第三种创建方式
            * 提交 Callable<T> 接口
            
    多线程实现的第三种方式的好处和弊端
        好处
            * 可以有返回值
            * 可以抛出异常
        弊端
            * 代码繁琐,所以开发中不使用该方法
            
                
代码如下
public class Demo09_线程的第三种创建方式_Callable {

    public static void main(String[] args) throws InterruptedException, ExecutionException {
//        创建线程池对象,并声明该线程池中有2条线程
        ExecutorService es = Executors.newFixedThreadPool(2);
//    创建 Callable 子类线程对象 
        MyCallable my1 = new MyCallable(100); 
//        提交线程
        Future<Integer> f1 = es.submit(my1); //100之间的和
        Future<Integer> f2 = es.submit(new MyCallable(50));  //50之间的和
        
//        关闭线程池对象
        es.shutdown();
        
        System.out.println(f1.get()); //  如有必要,等待计算完成,然后获取其结果
        System.out.println(f2.get());
        
//        如果指明 第三条线程
//        Integer i = es.submit(new MyCallable(200)).get(); 报错,不是链式编程错误,而是 事先在线程池中声明了2条线程,而现在又创建了第三条线程给已经声明是2条的线程池,所以报错。
//        System.out.println(i);
    }

}

// 使用第三种线程方式 来计算某个数字范围内的和
class MyCallable implements Callable<Integer>{
    private int num;
    public MyCallable(int number) {
        this.num = number;
    }
    @Override
    public Integer call() throws Exception {
        int sum = 0;
        for (int i = 0; i < num; i++) {
            sum +=i;
        }
        return sum;
    }
}



j.
    简单工厂方法模式
        概述
            * 简单工厂模式 又叫 静态工厂方法模式。
            * 它定义一个具体的工厂类来负责创建一些类的实例
        优点
            * 客户端不需要在负责对象的创建,从而明确了各个类的职责
        缺点
            * 这个静态工厂类负责所有对象的创建,如果有新的对象的添加,或者某些对象的创建方式不同,就要需要不断的修改工厂类,这不利于后期的维护
        使用
            * 开始,在测试类中每个具体的内容自己创建对象,但是,创建对象的工作如果比较麻烦,就需要有人专门做这个事情,所以就知道了一个专门的类来创建对象。
            
        代码
            动物抽象类:public abstract class Animal{public abstract void eat();}
            具体狗类:public class Dog extends Animal{public void eat{System.out.println("狗吃肉");}}
            具体猫类:public class Cat extends Animal{public void eat() {System.out.println("猫吃鱼");}}
            动物工厂类:
                    public static Animal getName(String name){
                    //        发现如果方法定义很多,复用性就太差
                    //        改进
                            if("dog".equals(name)){
                                return new Dog();
                            }else if("cat".equals(name)){
                                return new Cat();
                            }else {
                                return null; //不是以上两种,就返回null
                            }
                        }
                    }
            测试类:
                public class Demo01_简单工厂方法模式 {
                    public static void main(String[] args) {
                        Dog d = (Dog) AnimalFactory.getName("dog");
                        d.eat();
                        
                        Cat c = (Cat)AnimalFactory.getName("cat");
                        c.eat();
                    }
                }


k.
    工厂方法模式
        概述
            * 工厂方法模式中抽取工厂类负责定义创建对象接口,具体对象的创建工厂由抽象工厂的具体类 来实现。
        优点
            * 客户端不需要再负责对象的创建,从而明确各个类的职责,如果有新的对象添加,只需要增加一个具体的类(Pig)和具体的工厂类接口(PigFactory),不影响已有的代码,后期维护容易,增强了系统的扩展性。
        缺点
            * 需要额外编写代码,增加了工作量。 
            
        代码
            动物抽象类:public abstract class Animal{public abstract void sleep();}
            具体狗类:public class Dog extends Animal{public void sleep(){System.out.println("狗站着睡觉");}}
            具体猫类:public class Cat extends Animal{public void sleep(){System.out.println("猫趴着睡觉");}}
            具体猪类:public class Pig extends Animal{public void sleep(){System.out.println("猪卧着睡觉");}}
            工厂接口类:public interface Factory{public abstract Animal createAnimal();}
            具体狗的工厂接口类:public class DogFactory implements Factory{public Animal createAnimal() {return new Dog();}}
            具体猫的工厂接口类:public class CatFactory implements Factory{public Animal createAnimal() {return new Cat();}}
            具体猪的工厂接口类:public class PigFactory implements Factory{public Animal createAnimal() {return new Pig();}}
        
            测试类: public class Demo01_工厂方法模式 {
                    public static void main(String[] args) {
                        Dog d =  (Dog) new DogFactory().createAnimal();
                        d.sleep();
                        
                        Cat c = (Cat) new CatFactory().createAnimal();
                        c.sleep();
                        
                        Pig p = (Pig) new PigFactory().createAnimal();
                        p.sleep();
                        }
                    }


             

l.
    GUI
        * 开发不用,只是了解。
        * 它的BUG很可恶,一处编译,到处调试。

如何创建一个窗口并显示
 Graphical User Interface(图形用户接口)。
        Frame  f = new Frame(“my window”);
        f.setLayout(new FlowLayout());//设置布局管理器
        f.setSize(500,400);//设置窗体大小
        f.setLocation(300,200);//设置窗体出现在屏幕的位置
        f.setIconImage(Toolkit.getDefaultToolkit().createImage("qq.png"));
        f.setVisible(true);

    布局管理器
    * FlowLayout(流式布局管理器)
        * 从左到右的顺序排列。
        * Panel默认的布局管理器。
    * BorderLayout(边界布局管理器)
        * 东,南,西,北,中
        * Frame默认的布局管理器。
    * GridLayout(网格布局管理器)
        * 规则的矩阵
    * CardLayout(卡片布局管理器)
        * 选项卡
    * GridBagLayout(网格包布局管理器)
        * 非规则的矩阵
    

    窗体监听
        Frame f = new Frame("我的窗体");
        //事件源是窗体,把监听器注册到事件源上
        //事件对象传递给监听器
        f.addWindowListener(new WindowAdapter() {
                  public void windowClosing(WindowEvent e) {
                             //退出虚拟机,关闭窗口
                System.exit(0);
            }
        });


    鼠标监听
    键盘监听和键盘事件
    动作监听


m.
    适配器设计模式 --掌握
        概述
        * 什么是适配器?
            * 在使用监听器的时候,需要定义一个类事件监听器接口
            * 通常接口中有多个方法,但程序不一定使用到全部的方法,但又必须重写某个方法,这样很繁琐
            * 适配器的出现简化了这些操作,定义监听器时只要继承适配器类,然后只重写我们需要的方法即可
            
        * 适配器原理?
            * 适配器就是一个类,实现了监听器接口,所有抽象方法都被重写了,但是被重写的方法里全是空的。
            * 适配器类还需要定义成抽象的。--因为创建该类对象,调用该类中的空方法是没有意义的。
            * 目的是为了简化程序员的操作,调用监听器时继承适配器。只重写需要的方法即可

    

    需要知道的事情
        * 事件处理
            * 事件: 用户的一个操作动作
            * 事件源: 被操作的组件(如:被操作的按钮,被操作的窗口)
            * 监听器: 一个自定义类的对象, 实现了监听器接口, 包含事件处理方法,把监听器添加在事件源上, 当事件发生的时候虚拟机就会自动调用监听器中的事件去处理方法功能代码。


目前学过的设计模式有哪些?
    1.装饰者设计模式 --用于对功能代码的扩张
        * 对原功能不强的代码进行功能的扩张


    2.单例设计模式 --限制类对象的创建
        * 饿汉式
            * 一上来就创建对象
        * 懒汉式
            * 单例的延迟加载模式,存在安全隐患。什么时候要,什么时候创建对象
        * final式 --不用
            * public static final Singleton s = new Singleton(); //2. 创建成员方法,将其用final修饰且静态


    3.简单工厂设计模式
        * 优点
            * 客户端不用负责对象的创建,从而明确了各个类的职责
        * 缺点
            * 这个静态工厂类(简单工厂类)负责所有对象的创建,如果有新对象的添加,或某些对象的创建方式不同,就要对该 简单工厂类 需要修改,不利于后期的维护


    4.工厂设计模式 --类似模块似开发
        * 优点
             * 客户端不需要再负责对象的创建,从而明确各个类的职责,如果有新的对象添加,只需要增加一个具体的类(Pig)和具体的工厂类接口(PigFactory),不影响已有的代码,后期维护容易,增强了系统的扩展性。
        * 缺点
             * 需要额外编写代码,增加了工作量。 


    5.适配器设计模式 --使用适配器类重写指定方法,提高程序员工作效率
        什么是适配器?
            * 在使用监听器的时候,需要定义一个类事件监听器接口
            * 通常接口中有多个方法,但程序不一定使用到全部的方法,但又必须重写某个方法,这样很繁琐
            * 适配器的出现简化了这些操作,定义监听器时只要继承适配器类,然后只重写我们需要的方法即可
            
        * 适配器原理?
            * 适配器就是一个类,实现了监听器接口,所有抽象方法都被重写了,但是被重写的方法里全是空的。
            * 适配器类还需要定义成抽象的。--因为创建该类对象,调用该类中的空方法是没有意义的。
            * 目的是为了简化程序员的操作,调用监听器时继承适配器。只重写需要的方法即可