您的位置:首页 > 职场人生

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

2013-05-23 21:06 92 查看
------- 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;
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;	//返回将一个元素插入该数组中的位置。
}
}

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

[b]二、设计模式


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 中存入的数据没有被取出,就要在循环体外再判断一次。

总结:设计模式是根据实际经验总结出来的,是一种纯思想的思考问题的方法,深入学习必须了解设计模式,有利于自己在各开发语言学习中的发展(不论java、c++、c#都通吃),虽然它是纯思想的,但是得根据具体的项目来实施,如果没有实际的开发经验,只通理论同样不切实际。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: