黑马程序员---工具类和设计模式

来源:互联网 发布:矩阵论 清华 编辑:程序博客网 时间:2024/04/29 10:29

------- Java、.Net、Android培训期待与您交流!------- 

一、工具类

        需求:定义一个工具类,实现int型数组的打印、获取最值、选择排序、冒泡排序、折 半查找(二分查找)等功能。
       思路:
       工具类:工具类中不需要有类的特有数据(属性),仅仅用到了类中的方法,那么将方法都static化,直接通过类名调用,而且不需要建立对象,所以也把构造函数私有化,进制其他程序创建该类对象,使得代码更严谨。我们只需调用相应方法就可以达到目的。把需要对外的方法都public,扩大其权限,同时把不需要对外的成员都private,不对外公开。
       步骤:
              1.定义类ArrayTool工具类,且将构造函数私有化;
              2.分别定义各功能函数,需要对外的方法全部public,不需要对外的成员都private

public class ArrayTool{private ArrayTool(){}///构造函数私有化//获取最小值角标public static int getMin(int[] arr){/*int min = arr[0];//初始化min使其等于数组arr的第一个值。for (int i=1; i<arr.length; i++){if (min>arr[i])min = arr[i];}return min;*/int min = 0;for (int i=1; i<arr.length; i++){if (arr[min]>arr[i])//将min作为数组的角标使用。min = i;}return min;}//获取最大值的角标public static int getMax(int[] arr){int max = 0;for (int i=1; i<arr.length; i++){if (arr[max]<arr[i])//将max作为数组的角标使用。max = i;}return max;}//选择排序(升序):首先确定最小值public static void selectSort(int[] arr){for (int i=0; i<arr.length-1; i++){for (int j=i+1; j<arr.length; j++){if (arr[i]>arr[j])swap(arr,i,j);}}}//冒泡排序(降序):首先确定最小值public static void bubbleSort(int[] arr){for (int i=0; i<arr.length-1; i++){for (int j=0; j<arr.length-1-i; j++)//-i:让每一次比较的元素减少,-1:避免角标越界。{if (arr[j]<arr[j+1])swap(arr,j,j+1);}}}//打印数组public static void printArray(int[] arr){System.out.print("[");for (int i=0; i<arr.length; i++){if (i<arr.length-1)System.out.print(arr[i]+",");elseSystem.out.println(arr[i]+"]");}}//元素位置置换private static void swap(int[] arr,int a,int b){arr[a] = arr[a] + arr[b];arr[b] = arr[a] - arr[b];arr[a] = arr[a] - arr[b];}//折半查找(二分查找):必须保证该数组是有序,升序降序均可,并返回将一个数插入该数组中的位置。public static int halfSearch(int[] arr,int key){int start = 0, end = arr.length-1, mid;int max = getMax(arr);int min = getMin(arr);//若key值大于最大值或小于最小值,返回插入元素的位置if (key>arr[max])return max;else if(key<arr[min])return min;//while循环对在数组区间内的元素进行判断while (start<=end){mid = (start+end)>>1;if (key>arr[mid]){//判断数组是升序还是降序if (arr[0]<=arr[arr.length-1])start = mid + 1;elseend = mid - 1;}else if (key<arr[mid]){if (arr[0]>=arr[arr.length-1])start = mid + 1;elseend = mid - 1;}elsereturn mid;}return start;//返回将一个元素插入该数组中的位置。}}

        总结:工具类的定义,方便了程序员对资源的合理利用,我们只需要知道该工具类(或工具包)有哪些功能,选择适合自己需要的功能就行,而不需要知道该功能具体是怎么实现的,因为方法都是静态的,不需要创建对象,方便又快捷。在正式的开发中就会经常用到工具类来提高程序员的工作进度。

二、设计模式

        java中一共有23种设计模式。

  1、单例设计模式

    作用:使对象唯一,用于数据共享。必定提供一个静态的对外方法来获取该单利的实例。

        饿汉式:先初始化对象。类一进内存,就已经建立好了对象(单例设计常用:因为安全简单)
        懒汉式:对象是方法被调用时,才开始初始化,也叫做延时加载。类进内存,对象还没有存在,只有调用了与之对应的方法时才创建对象(面试考点:加载存在缺陷,可能会导致对象重复建立,与单例设计对象唯一相悖,开发基本不用,改进型代码体相对饿汉式麻烦)

        需求:写出单例设计模式的两种体现:分别为饿汉式和懒汉式。
        思路:
                单例设计模式是解决某一类问题最行之有效的方法,它解决的是某一个类中只存在一个对象,所以只需做到下面的三点即可完成设计,而其他的对于事物的描述,一切照旧。
                1.将构造函数私有化,保证其不被创建对象;
                2.在类中创建一个本类对象,保证其他程序可以访问到该类对象;
                3.提供一个方法可以获取到该对象,方便其他程序对自定义对象的访问。
        步骤:懒汉式
                1.创建一个空对象;
                2.创建私有化的构造函数;
                3.定义一个方法体来获取该对象,且在方法体中进行判断对象是否为空,不为空则不创建对象,直接返回对象在堆内存中的地址。
                4.饿汉式外加方法体:通过覆盖父类Object.equals来实现本类对象特有的比较内容。

//饿汉式:class SingleE{private int num;//私有化的成员变量,描述照旧//饿汉式代码体现块private static SingleE s = new SingleE();private SingleE(){}public static SingleE getInstance(){return s;}//方法体,描述照旧public void setNum(int num){this.num = num;}public int getNum(){return num;}//java中任何类都是Object的直接或间接子类,所以可以直接复写(重写、覆盖)Object父类中的equals方法,//来定义属于自己的比较内容。其中参数类型是Object表示可以传入任何类类型值。public boolean equals(Object obj){//判断传入值类型是否是SingleE类类型,若不是返回false。不同类型对象不能参与比较。作用:增强程序健壮性。if (!(obj instanceof SingleE))return false;SingleE e = (SingleE)obj;//多态:向下转型(类型符合)return this.num == e.num;}}class Person{int num;Person(int num){this.num = num;}}class SingleDemo{public static void main(String[] args) {SingleL l = SingleL.getInstance();SingleE s1 = SingleE.getInstance();//创建对像s1SingleE s2 = SingleE.getInstance();//创建对象s2,和s1指向的堆内存地址相同,即指向对象唯一,单例设计成功。Person p = new Person(8);//创建Person类型变量p指向new Person()在堆内存中的首地址。//System.out.println(p);//输出的是Person类类型变量所指向的地址:Person@14a55f2s1.setNum(8);//给s1所指对象赋值,结果为:num=8. //System.out.println("num="+s1.getNum());s2.setNum(11);//给s2所指对象即s1所指对象传赋值,结果为:num=11,覆盖了原来的num=8.所以下面两条的输出语句都是8//System.out.println("num="+s1.getNum());//System.out.println("num="+s2.getNum());System.out.println("compare end:"+s1.equals(p));}}//懒汉式:代码体现块class SingleL{private static SingleL s = null;//创建空对象private SingleL(){}//私有化构造函数public static SingleL getInstance()//定义方法体获取对象{if(s == null)//这个if语句执行可能会出现进程挂载的情况{//s = new Single(); //为了防止多个进程挂载后出现对象重复建立的情况,采用synchronized同步代码块保证一次只有一个程序//进入判断体。若该if语句中有一个程序挂载,那么就锁住该同步代码块语句防止其他调用程序进入该代码块。synchronized(SingleL.class)//同步代码块{if(s==null)s = new SingleL();}}return s;}}

  2、模版设计模式

        模版方法设计模式:在定义功能时,功能的一部分是确定的,但是有一部分是不确定的,而确定的部分在使用不确定的部分,
那么这时候就将不确定的部分暴露出去,有该类的子类去完成。

        需求:获取一段程序运行的时间。
        思路:获取程序开始和结束时间并相减即该程序的运行时间。获取时间的方法:System.currentTimeMillis()

abstract class GetTime//抽象类{/*final:最终1、可以修饰类、函数和变量;2、被final修饰的类不可以被继承(意为最终类);3、被final修饰的方法不可以被子类复写;4、被final修饰的变量(成员变量和局部变量)是一个常量只能赋值一次,且常量名所有字母均必须大写,如:PI、MY_BIRTHDAY;5、内部类定义在类中的局部位置上,只能访问该局部被final修饰的局部变量(后期涉及)。*///固定方法调用未知方法(模版设计模式)public final void getTime(){long start = System.currentTimeMillis();//获取程序开始执行的时间runcode();//未知方法long end = System.currentTimeMillis();//获取程序执行完后的时间System.out.println("毫秒:"+(end-start));//打印程序运行的时间}//抽象型函数,没有方法体(不可以实例化),抽象的出现就是为了让子类复写。因为抽象类函数定义的是一个总体的方法,//但是不确定具体的执行内容,会有多种方式来实现,所以不具备方法体和参数列表。//abstract不能和final、static、private关键字共存,共存后都不能被子类复写,抽象就没有意义了。public abstract void runcode();}//子类继承父类,那么子类就具有父类中所有的非私有化成员。class SubTime extends GetTime{public void runcode(){for (int i=0; i<2222; i++){System.out.print("*");}}}class Template{public static void main(String[] args){//GetTime gt = new SubTime();//多态:向上转型:父类型引用指向子类对象。SubTime gt = new SubTime();gt.getTime();}}

  3、装饰设计模式

        当想要对已有的对象进行功能增强时,可以定义类,将已有对象传入,基于已有的功能,并提供加强功能。那么自定义的该类成为装饰类。装饰类通常会通过构造方法接收被装饰的对象。

        BufferedReader 类中特有方法 readLine 底层调用的仍然是 read 方法的,是一个字符一个字符的读取,并将暂时读取的字符存入到一个临时容器中,当读取到某一行的终止(换行 ('\n')、回车 ('\r') 或回车后直接跟着换行)时,再返回容器中的数据。也属于装饰设计模式。

        装饰和继承:若多个类需要另一类的缓冲功能,以前是通过继承将每一个子类都具备缓冲功能。那么继承体系会复杂,并不利于扩展。现在优化思想,单独描述一下缓冲内容。将需要被缓冲的对象,传递进来,也就是谁需要被缓冲,谁就作为参数传递给缓冲区。这样继承体系就变得很简单。优化了体系结构。

        特点:装饰模式比继承要灵活,避免了继承体系臃肿,而且降低了类于类之间的关系。

        注:装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强功能。所以装饰类和被装饰类通常是都属于一个体系中的。

        示例:

        class  MyLineNumberReader{                private BufferedReader bufr;                private int lineNumber;  //定义行变量,可以通过设置行显示的起始位置。                MyLineNumberReader(BufferedReader bufr) {                        this.bufr = bufr;                }                //装饰设计模式                public String myReadLine() throws IOException                {                        lineNumber++;                        return bufr.ReadLine(); //指向BufferedReader ,调用其方法                        /*                        // 自定义读取一行的功能                        StringBuilder sb = new StringBuilder();                        int ch = 0;                        while ((ch=r.read())!=-1){                                if (ch=='\r') //满足条件,跳出当前循环,进入下一次循环                                        continue;                                if (ch=='\n') //独到换行符,就返回字符串容器的中的内容                                        return sb.toString();                                sb.append((char)ch); //向容器中添加读取到的字符                        }                        if (sb.length()!=0)                                 return sb.toString();                        return null;                        */                }                public void setLineNumber(int lineNumber){                        this.lineNumber = lineNumber;                }                public int getLineNumber(){                        return lineNumber;                }                @Test                public static void main(String[] args) throws IOException{                        FileReader fr = new FileReader("Demo.java");                        MyLineNumberReader mylnr = new MyLineNumberReader(fr);                          String line = null;                        mylnr.setLineNumber(50);                        while ((line=mylnr.myReadLine())!=null)                                System.out.println(mylnr.getLineNumber()+":"+line);                }        }

        解析:自定义读取行功能中,if(sb.length()!=0) 存在的理由:如果一行文本的结尾没有回车符,那么while 循环体中的 if(ch=='\n') 将读不到,所以 StringBuilder 中存入的数据没有被取出,就要在循环体外再判断一次。

        总结:设计模式是根据实际经验总结出来的,是一种纯思想的思考问题的方法,深入学习必须了解设计模式,有利于自己在各开发语言学习中的发展(不论javac++c#都通吃),虽然它是纯思想的,但是得根据具体的项目来实施,如果没有实际的开发经验,只通理论同样不切实际。

原创粉丝点击