黑马程序员——JAVA7K面试题——交通灯管理系统

来源:互联网 发布:java二叉树判断 编辑:程序博客网 时间:2024/05/19 00:10

-----------android培训java培训、java学习型技术博客、期待与您交流!------------

第一讲.概述

  1. 通过对张孝祥老师7k面试题视频的学习,边看边敲代码
  2. 其中涉及到枚举、线程池(Java5.0新增的线程运行方法)
  3. 交通灯管理系统的需求如下:

/*模拟实现十字路口的交通灯管理系统逻辑,具体需求如下:  1、异步随机生成按照各个路线行驶的车辆。     例如:     由南向而来去往北向的车辆 ---- 直行车辆     由西向而来去往南向的车辆 ---- 右转车辆     由东向而来去往南向的车辆 ---- 左转车辆     。。。  2、信号灯忽略黄灯,只考虑红灯和绿灯。  3、应考虑左转车辆控制信号灯,右转车辆不受信号灯控制。  4、具体信号灯控制逻辑与现实生活中普通交通灯控制逻辑相同,不考虑特殊情况下的控制逻辑。     注:南北向车辆与东西向车辆交替放行,同方向等待车辆应先放行直行车辆而后放行左转车辆。  5、每辆车通过路口时间为1秒(提示:可通过线程Sleep的方式模拟)。  6、随机生成车辆时间间隔以及红绿灯交换时间间隔自定,可以设置。  7、不要求实现GUI,只考虑系统逻辑实现,可通过Log方式展现程序运行结果*/
第二讲.模型设计

  1. 首先通过画图更好地理解题意                            
  2. 总共有12条线路,分别为从四个方向驶来的左转、右转、直行三种行驶方向的车辆
  3. 交通灯的控制逻辑为:放行南北方向直行--->放行南北方向左转--->放行东西方向直行--->放行东西方向左转,右转车辆不受交通灯限制,可以设计成它的灯常绿(这里隐含了为每个路线类设计灯这个属性的思路;右转也是路线的一种,所以统一设计有助于简化思路)。这里12条路线只有四条控制逻辑,因为右行不被灯所限制,而南向北、北向南等等对称路线它们的控制逻辑一致。
  4. 上述分析都可以在代码中有实际的体现,这是我认为比较牛的。。。

第三讲.代码编写——Road类

  1. 设计Road——路线类,总共有12条路线:S2N,S2W,E2W,E2S,N2S,N2E,W2E,W2N,S2E,E2N,N2W,W2S,其中X2X代表从某方向来,驶向某方向的路线,2是to的意思。
  2. Road类有vihicles字段,是一个List类型,用于存储道路上的车辆。
  3. 还要为Road类添加生成车辆和消费车辆(车辆驶过路口就从vihicles列表中删除此项)这两个方法,显然要为它们单独创建线程,张老师就直接把它们写到构造方法中去了,可以在实例化的同时开启生产消费两个线程(这里按照我的理解,路线本质其实是一种资源,它存储着许多的车辆,那么生产消费车辆这两大线程属于路线本身,回想毕老师Java基础里线程的demo代码可以发现其中的相同本质)。
    public class Road {List<String> vehicles = new ArrayList<>();private String name;public Road(String name){this.name=name;ExecutorService pool = Executors.newSingleThreadExecutor();//new thread generating methods in JDK5.0 pool.execute(new Runnable(){public void run(){for(int i=0;i<1000;i++){try {Thread.sleep((new Random().nextInt(10)+1)*1000);//coming vehicles with an interval of 1~10 seconds} catch (InterruptedException e) {e.printStackTrace();}vehicles.add(Road.this.name+"_"+i);}}});ScheduledExecutorService timer = Executors.newScheduledThreadPool(1);timer.scheduleAtFixedRate(new Runnable(){public void run(){if(vehicles.size()>0)if(Lamp.valueOf(Road.this.name).isLighted())//内部类访问外部类name变量System.out.println(vehicles.remove(0)+" is travelling");}},1, 1,TimeUnit.SECONDS);}}
  4. 上面代码中用到了线程池,这是JDK5.0的新增的线程方法。我们知道,CPU在多个线程中来回的切换、还有频繁地创建销毁线程的时候都是会耗费资源的,所有就出现了线程池的概念解决这些问题。线程池中的线程在执行完任务后并不会销毁,而是转到空闲状态等待下一次调用;而且通过限制线程池的大小,也可以限制系统中并发线程的个数。以后推荐使用这种方法运行线程。下面代码展示了用法及说明:
    /**通过线程池来执行线程任务的步骤如下:(1)调用Executors类的静态方法创建一个ExecutorService对象,该对象代表一个线程池(2)创建Runnable或Callable实例,作为线程执行任务(3)调用ExecutorService对象的execute()方法运行Runnable、Callable实例(4)当不想提交任务时,调用ExecutorService的shutdown()方法关闭线程池*/import java.util.concurrent.*;class  ExecutorServiceTest{public static void main(String[] args) {//直接用匿名内部类的方式提交Runnable实例Executors.newFixedThreadPool(1).execute(new Runnable(){public void run(){for (int i=0;i<1000;i++){System.out.println(i);}}});//newCachedThreadPool():按需创建线程,不用指定个数(简单)//newFixedThreadPool(int nThreads):创建一个固定线程数的线程池//newSingleThreadExecutor:创建只有一个线程的线程池,相当于上面参数为1Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable(){public void run(){int i=0;while(i<1000)System.out.println(i++);}},1,1,TimeUnit.SECONDS);//newScheduledThreadPool(int corePoolSize):可以在指定延迟后执行线程任务,就是//调用scheduldAtFixedRate(Runnable command,long initDelay,long period,TimeUnit unit)方法//上面代码就是指1秒钟后每隔一秒运行一次Runnable里的run方法。//newSingleThreadScheduledExecutor()单个线程}}

第四讲.代码编写——Lamp枚举    

  1. 把交通灯设计为枚举类型。
  2. 这里可能有疑问:Road也有12条路线,为什么不设计成枚举呢?我觉得,这是因为Lamp会包含具体的交通工具灯控制逻辑,那些相关的具体逻辑及简并不用枚举是很难表达的,而Road只负责成产消费车辆,就无所谓了。
  3. Lamp枚举里有变红(blackOut),变绿(light),是否可以通行(isLighted)这几个方法,还提供了next、opposite这两个属性代表与之对称的灯及下一个变绿的灯,来实现上面提到的控制逻辑:在light中同时点亮对称的两个灯,在blackOut中返回下一个要点亮的灯。注意next、opposite要是设计为String类型而不是Lamp本身(可以用valueOf方法转换),否则会出问题。
  4. 还要注意这里的面向对象的编程思想:交通灯的控制逻辑理应是交通灯的方法。张老师视频里举了几个很生动的例子:人在黑板上画圆,那画圆这个方法是属于圆的;人关门,关门这个方法是属于门的;司机师傅刹车,那刹车这个方法是属于车的。我相信深入理解这些例子,是有助于提高面向对象程序设计功力的、颇为重要的事。
    public enum Lamp {S2N("N2S","S2W",false),S2W("N2E","E2W",false),E2W("W2E","E2S",false),E2S("W2N","S2N",false),N2S(null,null,false),N2E(null,null,false),W2E(null,null,false),W2N(null,null,false),S2E(null,null,true),E2N(null,null,true),N2W(null,null,true),W2S(null,null,true);private boolean lighted;private String opposite;private String next;private Lamp(){}private Lamp(String opposite,String next,boolean lighted){this.opposite=opposite;this.next=next;this.lighted=lighted;}//application of method valueOfpublic void light(){lighted =true;if(opposite!=null)Lamp.valueOf(opposite).light();System.out.println(this+" turns Green");}public Lamp blackOut(){lighted =false;if(opposite!=null)Lamp.valueOf(opposite).blackOut();Lamp nextLamp=null;if(next!=null){nextLamp=Lamp.valueOf(next);nextLamp.light();System.out.println("Green switch from "+this+" to "+next);}return nextLamp;}public boolean isLighted(){return lighted;}}
  5. 上面代码用到了枚举,这个知识在张老师Java高新技术里讲过,下面这段代码简单展示了枚举的用法:其实枚举就是特殊的Java类(类中能做的事情在枚举中也可以),类似于单例模式,它在内部声明有限个实例供调用,但不允许外部创建实例。
    public class EnumTest {public static void main(String[] args) {//枚举入门——星期WeekDayWeekDay weekday = WeekDay.MONDAY;System.out.println(weekday);System.out.println(weekday.name());//就是命名时的名字System.out.println(weekday.ordinal());//元素的排行System.out.println(WeekDay.valueOf("SUNDAY"));//字符串转化为元素System.out.println(WeekDay.values().length);//把枚举里所有元素转化为数组}public enum WeekDay{SUNDAY,MONDAY,TUSEDAY,WEDNESDAY,THURSDAY,FIRDAY,SATRUDAY;}//带构造方法及抽象方法的枚举——交通灯TrafficLamppublic enum TrafficLamp{RED(30) {@Overridepublic TrafficLamp nextLamp() {return GREEN;}},GREEN(60) {@Overridepublic TrafficLamp nextLamp() {return YELLOW;}},YELLOW(10) {@Overridepublic TrafficLamp nextLamp() {return RED;}};long delay;//交通灯持续时间public abstract TrafficLamp nextLamp();//下一个灯,在上面声明的实例中实现private TrafficLamp(long delay){//构造方法要是私有的,不让外部创建实例this.delay=delay;}}}

第五讲 .代码编写——LampController交通灯控制器

  1. 控制交通灯的切换,在实例化的同时开启负责交通灯切换的线程
    public class LampController {private Lamp currentLamp;LampController(){currentLamp = Lamp.S2N;currentLamp.light();ScheduledExecutorService timer = Executors.newScheduledThreadPool(1);timer.scheduleAtFixedRate(new Runnable(){public void run(){currentLamp = currentLamp.blackOut();}},10,10,TimeUnit.SECONDS);}}
第六讲. 代码编写——MainClass:实例化相应的类,通过调用相应方法(这里就是构造方法)启动这些线程,来运行系统。
public class MainClass {public static void main(String[] args) {String[] directions = new String[]{"S2N","S2W","E2W","E2S","N2S","N2E","W2E","W2N","S2E","E2N","N2W","W2S"};for(int i=0;i<directions.length;i++)new Road(directions[i]);new LampController();}}

0 0
原创粉丝点击