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

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

一、工具类

        需求:定义一个工具类,实现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]+",");
			else
				System.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;
				else
					end = mid - 1;
			}
			else if (key<arr[mid])
			{
				if (arr[0]>=arr[arr.length-1])
					start = mid + 1;
				else
					end = mid - 1;
			}
			else
				return 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();	//创建对像s1
		SingleE s2 = SingleE.getInstance();	//创建对象s2,和s1指向的堆内存地址相同,即指向对象唯一,单例设计成功。
		Person p = new Person(8);	//创建Person类型变量p指向new Person()在堆内存中的首地址。
		//System.out.println(p);	//输出的是Person类类型变量所指向的地址:Person@14a55f2
		s1.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#都通吃),虽然它是纯思想的,但是得根据具体的项目来实施,如果没有实际的开发经验,只通理论同样不切实际。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值