Java基础之面向对象的多态和接口(4)续
本篇是作为第四篇多态与接口出现的续篇,由于在第四篇篇幅感觉较大,因此以本篇作为扩展和延伸。
在java设计模式中,处处都蕴含着面向对象的基本思想和概念,如果没有基本的java程序设计经验,和很深的面向对象的功力是比较难读懂这些设计模式的,它们都是大牛们在多年开发经验和软件设计过程中逐渐提炼出来的。因此如果想向更高一层研修,这些东西都是必须掌握的。
一、面向接口编程的介绍。
因为接口非常适合于对对象的抽象化,因此很多软件架构设计理论都提倡“面向接口”的编程。
下面介绍一种简单的设计模式来展示面向接口编程的优势
1.1工厂方法模式
实例代码1
package cn.com.basicThree.one;
/**
* @author fcs
* 说明:手机接口,有四个方法,前三个是基本,方法后三个返回具体手机的基本信息
*/
public interface Iphone {
public void call();
public void talk();
public void overTalk();
public String getInfo();
}
/**
* @author fcs
* 说明:简单工厂模式的核心类
*/
public class IphoneFactory {
@SuppressWarnings("unchecked")
public static <T extends Iphone> T createIphone(Class<T> c) {
Iphone phone = null;
try{
phone = (T)Class.forName(c.getName()).newInstance();
}catch(Exception e){
System.out.println("人种产生错误!!");
}
return (T)phone;
}
}
package cn.com.basicThree.one;
/**
*
* @author fcs
* 2014年8月21日
* 说明:手机实现类
*/
public class Apple implements Iphone{
private String name = "苹果5";
private String band = "苹果手机";
private double price = 5600.0;
private String size = "4.5英寸";
private String talkMan = "某某某";
public void call() {
System.out.println("我在呼叫"+talkMan+"\t 铃声:班得瑞音乐。");
}
@Override
public void talk() {
System.out.println("交谈进行中。。。。。。。。");
}
@Override
public void overTalk() {
System.out.println("结束谈话,自动提示,goodbye....");
}
@Override
public String getInfo() {
String info = "";
info = info+name+"\t";
info = info + band+"\t";
info = info+size+"\t";
info = info +price;
return info;
}
}
package cn.com.basicThree.one;
/**
* @author fcs
* 说明:手机实现类
*/
public class ThreeStar implements Iphone{
private String name = "三星galaxy s5";
private String band = "三星系列";
private double price = 4800.0;
private String size = "5.4英寸";
private String xiangSu = "1920*1080像素";
private String num = "131938204543";
@Override
public void call() {
System.out.println("我在拨打: "+num+" \t请接听。。。。。");
}
@Override
public void talk() {
System.out.println("我们在交谈中。。。。。。");
}
@Override
public void overTalk() {
System.out.println("谈话结束了。。。");
}
@Override
public String getInfo() {
String info = name+"\t"+band+"\t"+size+"\t"+price+"\t"+xiangSu;
return info;
}
}
package cn.com.basicThree.one;
/**
* @author fcs
* 2014年8月21日
* 说明:场景类
*/
public class Client {
public static void main(String[] args) {
//产生三星手机
Iphone sanxing = (Iphone) IphoneFactory.createIphone(ThreeStar.class);
sanxing.call();
sanxing.talk();
sanxing.overTalk();
sanxing.getInfo();
Iphone apple = (Iphone)IphoneFactory.createIphone(Apple.class);
apple.call();
apple.talk();
apple.overTalk();
apple.getInfo();
}
}
实现就是这么简单,但是里面的原理应该更深入。
二、内部类
2.1定义:
1.把一个类放在另一个类的内部定义,这个定义在其他类内部的类称为内部类(或者叫嵌套类),包含内部类的外部类也称为宿主类。
内部类的作用,
1.内部类提供了更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中的其他类访问该类。
2.内部类成员可以直接访问外部类的数据,因为内部类被当成其外部类成员,同一个类的成员之间可以可以互相访问,但外部类不能访问内部类的实现细节,
例如内部类的成员变量。
3.匿名内部类适合用于创建那些仅需要一次使用的类。
实例代码2
2.2、非静态内部类
1.只要把一个类放在另一个类的内部定义即可。
2.位置:内部类的位置是所在外部类的任何位置包括方法(在方法内称为局部内部类)
说明:
1.内部类都被作为成员内部类定义,而不是作为局部内部类,成员内部类是一种与Field,方法,构造函数和初始化块相似的类成员,
局部内部类:局部内部类则不是类的成员。
2. 成员内部类:静态内部类(有static修饰)和非静态内部类。
3.内部类可以作为外部类的成员,所以可以使用任意访问修饰符:public protecteddefault,private。
实例代码2
package cn.com.innerClassInfo;
/**
*
* @author fcs
* 说明:非静态内部类演示
*/
public class Cow {
private double weight;
public Cow(){}
public Cow(double weight){this.weight = weight;};
//非静态内部类
private class CowLeg{
private double length;
private String color;
//重载构造方法
public CowLeg(){}
public CowLeg(double length,String color){
this.length = length;
this.color = color;
}
public double getLength() {
return length;
}
public void setLength(double length) {
this.length = length;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public void info(){
System.out.println("当前牛腿的颜色是: "+color+",高: "+length);
//直接访问外部类的private修饰的Field
System.out.println("本牛腿所在奶牛重:"+weight);
}
}
public void test(){
CowLeg cl = new CowLeg(1.23,"黑白相间");
cl.info();
}
public static void main(String[] args) {
Cow cow = new Cow(34534.0);
cow.test();
}
}
非静态内部类与外部类的访问规范
1.内部类可以访问外部类的成员,如果外部类成员变量,内部咧成员变量与内部类方法的局部变量同名,则可以通过使用this,外部类类名.this作为限定来区分。
2.非静态内部类的成员可以访问外部类的private成员,但是外部类要访问非静态内部类的成员,则必须显示创建非静态内部类的对象来调用访问其实例成员。
3.非静态内部类对象必须寄存在外部类的对象里,而外部类的对象则不必一定有非静态内部类的对象寄存其中。外部类对象访问非静态内部类的成员时,非静态内部类的对象根本不存在,而非静态内部类对象访问外部类成员时,外部类对象一定存在。
4.java不允许在非静态内部类定义静态变量和静态方法,静态初始化块。可以包含静态常量(static final修饰)和final方法,普通初始化块。
5.非静态内部类普通初始化块的作用与外部类初始化块的作用完全相同。
实例代码3:
package cn.com.innerClassInfo;
public class Outer {
private long outprop = 10;
private static String str ="staticString";
public void outMethod(){
System.out.println("外部方法的实例方法,,,,,,");
}
public static void outMethod1(){
System.out.println("外部类的静态方法,,,,,");
}
class Inner{
private int inprop = 23424;
//静态常量
private static final int inner1 = 1;
//private static int info = 2324; 静态变量编译出错,
public void getOutInfo(){
System.out.println("外部类的实例prop: "+outprop);
System.out.println("外部类的静态str: "+str);
//外部类的方法
outMethod();
outMethod1();
}
/*非静态内部类不能有静态方法和静态变量
public static void ingo(){
System.out.println();
}*/
/*非静态内部类不能有静态初始化块
static{
System.out.println("静态初始化块。");
}*/
}
public void accessInnerProp(){
//外部类不能直接访问非静态内部类的实例变量
//System.out.println("内部类的inProp值: "+inprop);
System.out.println("显示创建内部类的对象进行直接访问: "+new Inner().inprop);
//getOutInfo();不能直接调用内部类的方法
new Inner().getOutInfo();
}
public static void main(String[] args) {
//只创建了外部对象
Outer outer = new Outer();
outer.accessInnerProp();
}
}
2.3、 静态内部类
1.static修饰的内部类称为类内部类,或者静态内部类,属于外部类本身,而不属于外部类的某个对象。
2.static关键字的作用是把类的成员变量变成类相关,而不是实例相关,。
3.static关键字不能修饰外部类,单可以修饰内部类。
静态内部类与外部类的访问规范
1.内部类访问外部类
静态内部类可以直接外部类的类变量,类方法。
静态内部类需要使用外部类的实例访问外部类的实例方法,实例变量。
2.外部类访问内部类
外部类需要使用内部类的类名访问静态内部类的类变量,类方法。
外部类需要使用内部类的实例访问内部类的实例方法,实例变量。
实例代码4:
package cn.com.innerClassInfo;
/**
*
* @author fcs
* @date 2014-8-22
* 描述:静态内部类演示
* 说明:
*/
public class StaticInnerClassTest {
private int p1= 2342;
private static int p2= 2424;
static class StaticInnerClass{
private String name = "InnerMan";
private static int agel = 242304;;
public static void ageinfo(){
System.out.println("age = "+agel);
}
//静态内部类访问外部类的方法变量。
public void accessOuterProp(){
//System.out.println(p1); 静态内部类不能访问外部实例变量
//静态内部类可以访问外部类的静态变量。
System.out.println(p2);
//outMethod();静态内部类不能访问外部实例方法,
//静态内部类可以访问外部类的静态方法
outStaticMethod();
//如果需要访问外部类的实例方法和实例变量,则需使用类名调用
System.out.println(new StaticInnerClassTest().p1);
new StaticInnerClassTest().outMethod();
}
}
public void outMethod(){
System.out.println("外部类的实例方法,");
}
public static void outStaticMethod(){
System.out.println("外部类的静态方法,,,,");
}
//外部类访问内部类方法变量
public void accessInnerProp(){
//1.使用静态内部类类名访问静态内部类的类变量,类方法
System.out.println(StaticInnerClass.agel);
StaticInnerClass.ageinfo();
//2.使用静态内部类的实例访问静态内部类的实例变量
System.out.println(new StaticInnerClass().name);
new StaticInnerClass().accessOuterProp();
}
public static void main(String[] args) {
StaticInnerClassTest stInTest = new StaticInnerClassTest();
stInTest.accessInnerProp();
}
}
内部类的使用规范
1.在外部类以外使用非静态内部类。
注意:不要在外部类的 静态成员中(包括浸提方法和静态初始化块)中使用非静态内部类。因为静态成员不允许访问非静态成员。
2.在内部类外使用内部类(包括静态和非静态)
注意:内部类不能使用private访问控制权限,private修饰的内部类只能在外部类的内部使用,
语法格式:OuterInstance.InnerClass varName; 如果有包名则还应该加上包名
在非静态内部类的对象必须寄存字外部类的对象里,因此创建非静态内部类对象之前,必须先创建其外部类对象。在外部类以外的地方创建非静态内部类实例的语法:
OuterIntace.new InnerConstructor();
说明:在外部类以外的地方创建非静态内部类实例必须使用外部类实例和new来调用非静态内部类的构造器。
说明:非静态内部类的子类不一定是内部类,可以是一个外部类,如果有一个内部类子类的对象存在,则一定存在与之对应的外部类对象。
实例代码5:
package cn.com.innerClassInfo;
/**
*
* @author fcs
* @date 2014-8-22
* 描述:继承内部类
* 说明:
*/
public class SubClass extends OutInfo.Ininfo {
//显示定义SubClass的构造函数
public SubClass(OutInfo out){
//通过传入的OutInfo对象显示调用Ininfo的构造函数
out.super("hello ....");
}
}
3.在外部类以外使用静态内部类。
1. 创建内部类对象时无须创建外部类对象。
2. 静态内部类只需使用外部类即可调用构造器,而非静态内部类必须使用外部类对象来调用构造器。
说明:使用静态内部类比使用非静态内部类要简单很多,只要把外部类当成静态内部类的包空间即可。因此当程序需要使用内部类时,应该 考虑使用静态内部类。
2.4、局部内部类
1、定义:在方法中定义的类称为局部内部类。 仅在该方法内有效。
说明:对于所有的局部成员,它们的上一级程序单元都是方法,而不是类,因此在方法中声明的变量和内部类不能使用是Static修饰,局部
变量的作用域是该方法,所有的局部成员都不能使用访问控制符修饰。
2.如果需要用局部内部类定义变量,创建实例或者派生子类,那么只能在局部内部类所在的方法内进行。
实例代码6:package cn.com.innerClassInfo;
/**
* @author fcs
* 2014年8月24日
* 说明:局部内部类演示
*/
public class LocalInnerClass {
public static void main(String[] args) {
//定义局部内部类
class InnerBase{
int a;
}
//定义局部内部类的子类
class InnerSub extends InnerBase{
int b ;
}
//创建局部内部类的对象
InnerSub is = new InnerSub();
is.a = 5;
is.b = 23;
System.out.println("InnerSub的对象a和b Field是:"+is.a+","+is.b);
}
}
2.5、匿名内部类
1.说明:匿名内部类适合创建那种只需要一次使用的类。定义
2.语法:创建匿名内部类时会立即创建一个该类的实例,这个类定义立即消失,匿名内部类的不能重复使用。
3.格 式:new 父类构造函数(实参列表) | 实现接口(){
//匿名内部类的类体部分
}
4.定义匿名内部类无须使用class关键字,而是在定义匿名内部类时直接生成该匿名内部类的对象。
实例代码7:
package cn.com.innerClassInfo;
//匿名内部类实现的接口
public interface Product {
public double getPrice();
public String getName();
}
package cn.com.innerClassInfo;
/**
*
* @author fcs
* @date 2014-8-22
* 描述:使用接口创建匿名内部类,
* 说明:这种方式的接口实现类只能使用一次。如果想重复使用则需要单独实现这个接口,
* 然后将实现类的对象作为参数。
*/
public class AnonymousTest {
public void test(Product p){
System.out.println("购买了一个: "+p.getName()+" ,花掉了 "+p.getPrice());
}
public static void main(String[] args) {
AnonymousTest ta = new AnonymousTest();
//调用Test方法时,需要传入一个Product参数
ta.test(new Product(){ // 该括号包围的范围内是匿名内部类
public double getPrice() {
return 35435.0;
}
public String getName() {
return "AGP显卡";
}
});
}
}
5.格式说明:
1.匿名内部类必须继承一个父类,或者实现一个接口,但是最多只能继承一个父类,或实现一个接口。
6.使用规则
1.匿名内部类不能是抽象类,因为系统在创建匿名内部类时,会立即创建匿名内部类的对象。
2;匿名内部类不能定义构造函数,因为匿名内部类没有类名,所以无法定义构造函数,但匿名内部类可以定义实例初始化块, 通过实例初始化块来完成构造方法需要完成的事情,
3.匿名内部类不能是抽象类,所以匿名内部类必须实现它的抽象父类或者接口里包含的所有抽象方法。
4.通过继承父类来创建匿名内部类时,匿名内部类将拥有和父类相似的构造函数,此处相似值得是拥有相同的形参列名。
5.如果匿名内部类需要访问外部类的局部变量,则必须使用final修饰外部类的局部变量。
6.匿名内部类在需要的时候可以重写父类的方法,
实例代码8:
package cn.com.innerClassInfo;
/**
* @author fcs
* 描述:通过抽象类继承实现匿名内部类
* 说明:匿名内部类可以重写父类的方法
*/
abstract class Device {
private String name;
public abstract double getPrice();
public Device() {
};
public Device(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class AnonymousInner {
public void test(Device d){
System.out.println("购买了一个: "+d.getName()+" ,花掉了: "+d.getPrice());
}
public static void main(String[] args) {
AnonymousInner ai = new AnonymousInner();
//调用有参构造函数创建Device匿名实现类的对象
ai.test(new Device("电子示波器"){ //该括号内是匿名内部类
public double getPrice() {
return 3536.0;
}
});
Device d = new Device(){
{
System.out.println("匿名内部类的初始化块。。。。。");
}
@Override
public double getPrice() { //实现抽象方法
return 2424;
}
//重写父类的实例方法
public String getName(){
return "键盘";
}
};
ai.test(d);
}
}
2.6、闭包(Closure)和回调
1.定义:闭包是一种能被调用的对象,它保存了创建它的作用域信息
2.说明:对于非静态内部类而言,它不仅记录了其外部类的详细信息,也可以直接调用外部类的private成员。因此可以把非静态内部类
当成面向对象领域的闭包。
3.扩展:通过这种仿闭包的非静态内部类,可以很方便的实现回调功能,回调就是某个方法一旦获得了内部类对象的引用后,
就可以在合适的时候反过来去调用外部类的实例的方法。所谓回调,就是允许客户类通过内部类引用来调用外部类的方法。
实例代码9
package cn.com.innerClassInfo;
/**
* @author fcs
* 描述:闭包功能回调演示
*/
interface Teacher{
void work();
}
public class Programmer {
private String name;
public Programmer(){}
public Programmer(String name){
this.name = name;
}
public void work(){
System.out.println(name+"在灯下敲键盘");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package cn.com.innerClassInfo;
public class TeachableProgrammer extends Programmer {
public TeachableProgrammer(String name){
super(name);
}
public TeachableProgrammer(){}
//教学工作由TeachableProgrammer定义
private void teach(){
System.out.println(getName()+"教师在讲台上讲解。。。");
}
private class Closure implements Teacher{
/**
* 非静态内部类回调外部类实现work方法,非静态内部类引用的作用仅仅是
* 向客户类提供一个回调外部类的途径。
*/
public void work() {
teach();
}
}
//返回一个非静态内部类引用,允许外部类通过该非静态内部类引用来回调外部类的方法
public Teacher getCallBackReference(){
return new Closure();
}
}
package cn.com.innerClassInfo;
/**
*
* @author fcs
* @date 2014-8-22
* 描述:演示了让TeachableProgrammer对象执行两种work方法,
* 说明:这种方式可以实现两种work方法
*/
public class TeachableProgrammerTest {
public static void main(String[] args) {
TeachableProgrammer tp = new TeachableProgrammer("李刚");
//直接调用TeachableProgrammer类才Programmer类继承到的work()方法。
tp.work();
//表面上调用的是closure的work方法
//实际上是回调TeachableProgrammer的teach方法
tp.getCallBackReference().work();
}
}
注:本博客部分代码参考李刚老师的《疯狂java讲义》,算是阅读笔记了。
关于闭包: http://www.cnblogs.com/chenjunbiao/archive/2011/01/26/1944417.html