模板方法模式、接口、多态、Object类


一、模板方法模式:

在定义功能时,功能的一部分是确定的,但是有一部分是不确定的,而确定的部分在使用不确定的部分,这时,就将不确定的部分暴露出去,由该类的子类去完成。这样可以提高扩展性、提高复用性。
比如,想设计一个获取某功能运行时间的类。这个时候,获取运行前后的时间并计算,这部分是确定的,只是中间运行的部分代码是不确定的。所以,可以将中间部分的功能抽取出来成为一个方法,其他部分确定的再成为一个方法。不确定的方法,由子类继承并实现。获取运行时间时,去调用确定的方法即可。比如说:

abstract class GetTime {//父类不一定是抽象的
	public final void getTime() //这是父类的功能,所以,用final修饰,不能被复写。
	{
		long start = System.currentTimeMillis();
		runCode();
		long end = System.currentTimeMillis();
		System.out.println(“毫秒:” + (start – end));
	}
	public abstract void runCode(); //runCode不一定是抽象的
}
class SubTime extends GetTime {
	public void runCode() //复写父类的runCode方法
	{
		for(int i = 0;i < 4000; i++)
		{
			System.out.print(i);
		}
	}
}

其实,父类的runCode方法不一定必须抽象,因为它完全可以有自己的实现,当子类有自己的需求时,再去复写。这样,创建一个子类的对象,获取它的runCode方法运行时间,就直接调用父类的getTime方法即可,父类的getTime自己会取调用子类复写后的runCode。

二、接口

接口其实以前接触过,所以还比较好理解,它可以看做是一个特殊的抽象类,它里面的方法都是抽象的。
定义格式:当定义为接口的时候,这些修饰符都可以省略
常量:public static final
方法:public abstract
(上面几个修饰符都可以省略),因为是接口,它的成员的修饰符都是固定的,系统会自动加上。
所以,接口是不能创建对象的,因为有抽象方法,必须被子类实现。而且,子类对接口中的所有方法全都覆盖后,子类才可以实例化,否则子类也是一个抽象类。接口可以多实现,接口可以继承接口,并可以多继承。可以用于功能扩展。就是,基本功能定义在类里(必须具备的功能),扩展功能定义在接口中(可以选择性的实现)。比如:学生类,它的基本功能是学习,所以不管java班的,还是.Net班的都要具备这个功能;但是,有的学生抽烟,有的学生不抽烟。抽烟这个功能不是必须具备的,可以被抽取出来作为一个接口,让不同的学生去有选择的去实现。

接口:降低了耦合性,提高了扩展性,提供了规则。

三、多态

可以理解为事物存在的多种体现形态。
比如有一只猫,它是一只猫,也是一个动物。所以:下面的定义都可以。
(前提:猫这个事物,必须是一种动物。class Cat extends Animal)
Cat cat = new Cat();
或者Animal cat = new Cat();

父类的引用指向了自己的子类对象;
父类的引用也可以接收自己的子类对象。
比如:

abstract class Animal {
	public abstract void eat();
}

class Cat extends Animal {
	public void eat() {
		System.out.println("吃鱼");
	}
	public void catchMouse() {
		System.out.println("抓老鼠");
	}
}
class Test {
	public static void main(String[] args) {
		Animal a = new Cat(); //父类的引用指向子类对象
		func(a); //父类的引用接收自己的子类对象
	}
	public static void func(Animal a)
	{
		a.eat();
	}
}

如果以后再新增一个Dog类继承Animal,则调用func时,同样可以把Dog类的实例对象传进去。所以,多态大大提高了程序的扩展性。
其中,Animal a = new Cat();这句的动作叫做向上转型(类型提升,类似byte b;int i = b;),是自动完成的。
那么这时,a只能执行eat方法(即Animal类的基本功能),如果想让a抓老鼠怎么办,可以将a的引用强制转换成子类类型:
Cat c = (Cat)a;这时调用c.catchMouse才可以(向下转型)。但是下面的操作不允许:
Animal a = new Animal();
Cat c = (Cat)a;

不能将父类对象转成子类类型。我们所能做的是,当父类的引用指向子类对象时,可以将它提升,或者强制转换。多态,自始至终都是子类对象在做着变化。(毕老师在这个地方用了一个毕老师化妆成老毕老师的例子,挺搞笑的,呵呵。)

instanceof:测试它左边的对象是否是它右边的类的实例,返回boolean类型的数据。
一般不常用,以下两种情况可以考虑:
1.子类的类型是有限的,并且基本不会再增加,比如人:只有男人和女人两类,传的是哪种值,可以使用相应的特有处理方式;
2.当传入的类型需要进行其他操作,比如比较,需要确定到底是哪一种子类型,也需要调用其特有处理方法。

多态中(非静态)成员方法调用时的特点(SuperClass sc = new SubClass();):
①在编译时期:参阅引用型变量所属的类中是否有调用的方法,如果有,编译通过,无则失败。
②在运行时期:参阅对象所属的类中是否有调用的方法。
简单总结就是:成员方法在多态调用时,编译看左边,运行看右边。

在多态中,成员变量非静态成员方法的特点:
无论编译和运行,都参考左边(参考引用型变量所属的类)
。【面试常遇到】
如:

class Fu {
	int num = 4;
	public static void method() 
	{
		print(“fu”);
	 }
	//…其他方法
}
class Zi {
	int num = 8;
	public static void method() 
	{
		print(“zi”);
	 }
	//…其他方法
}

后来多态使用中:
用父类的引用指向了子类的对象:
Fu f = new Zi();

println(f.num);
运行结果是:4
f.method();
运行结果是:fu
因为静态方法是先于类的实例对象而存在的,当某个类被加载到内存中时,它的静态成员也就随之被分配了一块空间。所以,调用静态方法时,都不需要创建对象,直接Fu.method()就可以,同样,f也不会去看指向的是哪个类型的对象,而是看自己所属是什么类型。
扩展:在内存中,成员方法区分两块区域存在:静态区和非静态区。非静态区有两个引用:this和super,而静态区只有类名引用。

四、Object类:

Object类是java所有类结构层次的根类,用毕老师的话说,就是java里边的上帝。我们只要创建一个类,它默认的父类都是Object。
Object有一个方法叫做boolean equals(Object obj),因为在java中,所有的对象都是可以比较的,Object里默认比较的是两个对象的地址。所以,即使定义了一个Class1和一个Class2,调用某个Class1实例对象的equals方法与Class2的某个实例对象比较,也是可以的,返回false。另一点,如果自定义的一个Class1类复写了Object的equals方法,把比较方式改成自己类型特有的(比如Person类,比较的是age而非地址),那么,传递的参数类型依然是(Object obj),所以,这时,在方法里面就要加上对obj类型的判断,即:要比较之前,先判断该obj是否是本类型的实例对象,如果不是,直接返回false;如果是,则将obj强转成本类型,并进行比较。

Object还有一个toString()方法,返回该对象的字符串表示。通常,toString 方法会返回一个“以文本方式表示”此对象的字符串。结果应是一个简明但易于读懂的信息表达式。建议所有子类都重写此方法。这个返回的字符串由完整的类名(参数对象是该类的一个实例)、at 标记符“@”和此对象哈希码的无符号十六进制表示组成。

换句话说,该方法返回一个字符串,它的值等于:
getClass().getName() + '@' + Integer.toHexString(hashCode())

Object还有一个getClass()方法,返回的类型是Class<?>(?代表未知类型),即当前调用者的运行时类型,因为程序运行时会把所有用到的.class文件封装成对象,并加载进内存,而Class<T>这个类型描述的是运行时该class文件的类型T。比如String.class的类型就是Class<String>,所以,既然能描述该对象的类型,说明Class<T>里肯定有一个name属性,所以同样就有能获得该类的类名的get方法,也就是Class<T>的getName()方法。可以这么理解:A.class是张三,B.class是李四,那么他们有一些共性的属性或者功能,比如:都有名称,都有构造函数,都有一般方法,所以,可以用一个类型来描述他们,就是Class类(类文件类型,对于张三李四,就是Person类)。那么如何知道某个改类型对象的名称呢?那当然就是getName()。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

哪知道

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值