接口:当抽象类的方法都是抽象的时候,这时可以把该类定义成接口的形式。你可以先期将接口理解为特殊的抽象类。 Eg.接口特点: 1,接口的方法都是抽象的。 2,接口不可以被实例化。 3,只有子类实现了接口中的所有抽象方法后,该子类才可以实例化,否则该类还是一个抽象类。 格式特点: 当定义接口时,接口中的常见的成员有两种: 1,全局常量。 2,抽象方法。 而且接口中的成员都有固定的修饰符: 全局常量:public static final 成员方法:public abstract 这修饰即使不写,系统也会自动加上。 建立一定书写,因为可以提高程序的阅读性。你会发现,接口中定义的成员都有一个共同的特点:都是public权限的。 继承和实现的一点小区别: 类与类之间称为继承:因为该类无论是抽象的还是非抽象的,它内部可以定义非抽象方法,这个方法可以直接被子类使用,所以子类继承就可以应用了。类与接口是实现关系:因为接口中的方法全都是抽象的,必须由子类实现完才可以实例化。所以就用了更确切的关键字:implements来表示
例子1.接口的引例实例演示。
package cn.itheima.day16; /** * 先前可以把接口理解为特殊的抽象类。 * 例如下面的代码: * interface Inter{ * public static final int num = 4; * public abstract void show(); * } * 接口-------->抽象类 * abstract class AbsDemo{ * abstract void show(); * abstract void method(); * } */ //定义一个接口 interface Inter{ public static final int num = 6; public abstract void show(); public abstract void method(); } /** * 如果要使用接口中的方法,必须定义一个类实现接口中的所有的抽象 * 方法后,该子类才可以建立对象,并调用这些方法。 * @author wl-pc */ //实现接口 class InterImpl implements Inter{ @Override public void show() { System.out.println("show run"); } @Override public void method() { System.out.println("method run"); } } public class InterfaceDemo { public static void main(String[] args) { InterImpl i = new InterImpl(); i.show(); i.method(); System.out.println(Inter.num); System.out.println(InterImpl.num); System.out.println(i.num); } }
类只能单继承,而接口可以被多实现。 java将多继承机制进行改良,通过多实现接口的形式来体现。 为什么不支持多继承呢? 因为当多个父类中定义了相同方法,而方法内容不同是,子类对象在调用该方法时,不明确要运行哪一个,有安全隐患,也就是不确定性。而接口进行多实现就没有这个问题了,因为接口中的方法都没有主体。接口的另一个好处:一个类在继承另一个类的同时可以实现多个接口.接口的出现就避免了单继承的局限性。父类中定义的该对象的基本功能,而接口中定义的是该对象的扩展功能。
例子2.接口的多实现的实例演示。
package cn.itheima.day16; class Demo{ public void function(){ System.out.println("function run"); } } interface Inter1{ void show(); } interface Inter2{ void show(); } //多实现接口的演示 class DemoA implements Inter1,Inter2{ //接口可以多实现 public void show(){ System.out.println("多实现接口的DemoA run"); } public void method(){ System.out.println("method run"); } } //继承类并实现接口的演示 class Test extends Demo implements Inter1,Inter2{ @Override public void show() { System.out.println("单继承和多实现接口的show run"); } } public class InterfaceDemo2 { public static void main(String[] args) { DemoA d = new DemoA(); d.show(); d.method(); Test test = new Test(); test.show(); t.function(); } }
抽象类和接口的区别: 1.抽象类只能被单继承。 接口可以被多实现。 2.抽象类中可以定义非抽象方法,直接被子类使用。 接口中只有抽象方法,必须被子类实现后才可以被使用。 3.抽象类中定义体系中的基本共性功能。 接口中通常定义体系中对象的扩展功能。 4.接口的出现避免了单继承的局限性。 5.抽象类被继承,是 is a 关系: xx1是xx2中的一种。 接口可以被实现,是 like a关系:xx1像xx2 中的一种。
例子3:抽象类和接口的整合实例演示。
package cn.itheima.day16; /** * 学员的例子 * 抽烟的学员 * * 老师 * 抽烟的老师 * @author wl-pc */ /*发现抽烟的功能并不是体系中应该具备的基本功能。而是具体子类所具备的特有扩展 * 功能。将这个功能抽取到一个接口中,这个是对对象的功能进行扩展*/ interface Smoking{ public abstract void smoke(); } //学员的抽象类 abstract class XueYuan{ abstract void study(); } class SmokeXueYuan extends XueYuan implements Smoking{ @Override void study() { System.out.println("study"); } @Override public void smoke() { System.out.println("Xueyua Smoke"); } } //老师的抽象类 abstract class Teacher{ abstract void teacher(); } class SmokeTeacher extends Teacher implements Smoking{ @Override void teacher() { System.out.println("teacher"); } @Override public void smoke() { System.out.println("teacher smoke"); } } public class InterfaceDemo3 { public static void main(String[] args) { SmokeXueYuan xueYuan = new SmokeXueYuan(); xueYuan.smoke(); xueYuan.study(); SmokeTeacher teacher = new SmokeTeacher(); teacher.teacher(); teacher.smoke(); } } /* 改变问题领域(如下): * 分析烟民这个领域。 * abstract class YanMin { * abstract void smoke(); * } * 对于烟民而言,吸烟是主要功能,而学习是添加的功能。 * interface Study { * void study(); * } * 烟民不一定都具有学习的功能,所以将学习功能定义为借口 * class StudentYanMin extends YanMin implements Study { * void smoke() { * System.out.println("Student smoke"); * } * public void study(){} * } */
类与类之间是继承关系。类与接口之间是实现关系。 接口与接口之间继承关系,通过接口可以多继承。 Java支持多继承,在类上,Java的多继承是不允许的,因为存在着不确定性。但是,在接口上可以存在多继承的。例子4:接口的多实现的实例演示。
package cn.itheima.day16; interface A{ void a(); } interface C{ void c(); } interface B extends A,C{ void b(); } class D implements B{ @Override public void a() { System.out.println("a"); } @Override public void c() { System.out.println("c"); } @Override public void b() { System.out.println("b"); } } /*class AA{} class BB{} //这种写法是错误的,因为类只支持单继承,不支持多继承 class CC extends AA,BB{ }*/ public class InterfaceDemo4 { public static void main(String[] args) { D d = new D(); d.a(); d.c(); d.b(); } }
猫,狗:这些事物都称为动物。 动物这种类型:有多种体现的形态。 一个具体的动物,具备多种体现类型。(猫:称为猫或动物) 猫这个具体对象,它既可以称之为猫,也可以称之为动物。 class 动物{ } class 猫 extends 动物{ } 猫 x = new 猫(); 动物 y = new 猫(); class 狗 extends 动物{ } 狗 x = new 狗(); 动物 y = new 狗(); 多态: 1,在程序中的体现:父类引用或者接口引用指向了自己的子类对象。 2,好处:提高了代码的扩展性,后期的可维护性 3,前提: 1)类与类之间必须有继承关系或者实现关系。 2)通常都需要进行覆盖。 4,弊端: 进行多态应用时,前期并不知道后期会有多少个子类出现,但是可以使用父类的引用来调用父类的方法。而后期哪个子类对象出现就执行哪个子类的具体方法实现。这就是程序的扩展性。 但是,前期调用的时候,只能调用父类中的方法,而不能调用具体子类的特有方法。因为前期还不知道具体的子类是谁。 多态的思想: 以前:定义对象,并指挥对象做事情(调用对象方法)当对象多了以后,指挥动作就会变的很麻烦。这时重新思考这个问题。找到这些对象的共性类型。直接指挥这个共性类型做事情即可。这样凡是属于这个类型的个体都会执行。 比如:建立猫对象,调用猫的eat方法。建立狗对象调用eat方法。很麻烦。 找到猫和狗的共性类型:动物。只要指挥动物的eat方法。即可。这样猫和狗都会进行eat动作。
例子5:多态的演示程序。
package cn.itheima.day16; abstract class Animal{ abstract void eat(); } class EatSomething{ void startEat(Animal a){ a.eat(); } } class Cat extends Animal{ void eat(){ System.out.println("鱼"); } void catchMouse(){ System.out.println("做老鼠"); } } class Dog extends Animal{ @Override void eat() { System.out.println("骨头"); } void looHome(){ System.out.println("看家"); } } public class DuoTaiDemo1 { public static void main(String[] args) { EatSomething es = new EatSomething(); es.startEat(new Cat()); es.startEat(new Dog()); } }
在转型过程中,其实都是子类对象在做着变化。当你想使用子类的特有功能时,需要将父类型转成子类型才可以使用。注意:必须保证,这个父类型指向的是该子类型的对象. 例子6:多态中的对象的向上转型和向下转型的实例演示。
package cn.itheima.day16; abstract class Animal1{ abstract void eat(); } class EatSomething1{ void startEat(Animal1 a){ a.eat(); } } class Cat1 extends Animal1{ void eat(){ System.out.println("鱼"); } void catchMouse(){ System.out.println("做老鼠"); } } class Dog1 extends Animal1{ @Override void eat() { System.out.println("骨头"); } void looHome(){ System.out.println("看家"); } } public class DuoTaiDemo2 { public static void main(String[] args) { Animal1 a = new Cat1(); //父类引用指向子类对象,子类对象提升为了父类型,向上转型。自动类型提升。 a.eat(); //c.catchMouse(); //编译错误 //注意:在转型过程中,至始至终都是子类对象在做着改变。 //当你想使用子类的特有功能时,需要将父类型转成子类型才可以使用。 //注意:必须保证,这个父类型指向的该子类型的对象。 //想要使用猫的做老鼠的功能,强制类型转换,向下转型。 Cat1 c = (Cat1)a; c.eat(); c.catchMouse(); } }
可以对传入的对象进行类型的判断。通过一个关键字来完成 instanceof : 对象instanceof 类型用于判断该对象是否所属于该类型。
例子7:判断对象类型是否属于指定的类型。
package cn.itheima.day16; import java.lang.reflect.Method; abstract class Animal2{ abstract void eat(); } class EatSomething2{ void startEat(Animal2 a){ a.eat(); } } class Cat2 extends Animal2{ void eat(){ System.out.println("鱼"); } void catchMouse(){ System.out.println("做老鼠"); } } class Dog2 extends Animal2{ @Override void eat() { System.out.println("骨头"); } void looHome(){ System.out.println("看家"); } } public class DuoTaiDemo3 { public static void main(String[] args) { Method(new Dog2()); } private static void Method(Animal2 a) { //可以对传入的对象进行类型的判断。 //通过一个关键字来完成 instanceof : 对象 instanceof 类型 用于判断该对象是否所属于该类型。 if(a instanceof Cat2){ Cat2 c= (Cat2)a; c.eat(); c.catchMouse(); }else if(a instanceof Dog2){ Dog2 d = new Dog2(); d.eat(); d.looHome(); }else { a.eat(); } } }
接口的基本思想: 举例说明:主板的例子。 接口的三个特性: 1.接口就是对外暴露的规则。 2.接口是功能的扩展。 3.接口的出现降低了耦合性(紧密联系程度降低),耦合性的降低就实现了模块化的开发,叫做开发前定义好规则,你在实现规则,我在使用规则,至于怎么实现的和我没关系。我只要知道这个规则即可。 面试回答:电脑的机箱 电源插孔的例子 笔记本的例子 主板的例子 例子8:主板运行和PCI结合的实例演示。package cn.itheima.day16; /** * 需求:主板运行。为了提高主板的功能扩展。因为日后有可能会出现一些新的功能,比如音频,网络等等。 * 以后会出现什么样的功能,是不确定的。但是前期在设计的时候,可以对外提供一个规则。只要后期的 * 功能设备符合这个主板对外提供的规则即可被主板使用。就在主板上定义了PCI插槽。 这个插槽就是一个接口。 * 这个示例作为一个重点看一下 * @author wl-pc */ /** * 接口的基本思想: * 举例说明:主板的例子。 * 接口的三个特性: * 1.接口就是对外暴露的规则。 * 2.接口是功能的扩展。 * 3.接口的出现降低了耦合性(紧密联系程度降低),耦合性的降低就实现了模块化的开发, 叫做开发前定义好规则,你在实现规则,我在使用规则,至于怎么实现的和我没关系。我只要知道这个规则即可。 * 面试回答:电脑的机箱 * 电源插孔的例子 * 笔记本的例子 * 主板的例子 */ interface PCI{ public void open(); public void close(); } class MainBoard{ public void run(){ System.out.println("mainboard run"); } /** * 主板还有一个功能,就是使用PCI插槽上的具体设备。 * 只要定义PCI 接口的引用,用于接收符合该接口的设备即可。 */ public void usePCI(PCI p){ //p就是一个接口类型的应用,接口类型的引用需要指向其子类的对象。 if(p!=null){ p.open(); p.close(); } } } //定义网卡实现PCI接口 class NetCard implements PCI{ @Override public void open() { System.out.println("netcard open"); } @Override public void close() { System.out.println("netcard close"); } } //定义声卡实现PCI接口 class SoundCard implements PCI{ //like a关系 :网卡很像一个PCI @Override public void open() { System.out.println("SoundCard open"); } @Override public void close() { System.out.println("SoundCard close"); } } public class PCIDemo { public static void main(String[] args) { MainBoard mb = new MainBoard(); mb.run(); mb.usePCI(new NetCard()); mb.usePCI(new SoundCard()); } }
例子9:数据库使用接口的实例演示。
interface SQLTool{ add(); delete(); } class MySqlTool implements SQLTool{ add(){ 1,连接mysql数据库。 2,添加数据。 3,关闭数据库。 } delete(){ 1,连接mysql数据库。 2,删除数据。 3,关闭数据库。 } } class OracleTool implements SQLTool{ add(){ 1,连接Oracle数据库。 2,添加数据。 3,关闭数据库。 } delete(){ 1,连接Oracle数据库。 2,删除数据。 3,关闭数据库。 } } //定义一个工具类,返回指定的对象。 class Tool{ //静态工厂设计模式。 public static SQLTool getInstance(String name){ if(name.equals("mysql")) return new MySqlTool(); if(name.equals("oracle)) return new OracleTool(); } } class SQLDemo{ public static void main(String[] args){ SQLTool t = Tool. getInstance(“mysql”); t.add(); t.delete(); SQLTool t2 = Tool. getInstance(“oracle”); t2.add(); t2.delete(); } }
多态中的细节。 在多态中: 成员变量: 编译时期:看的引用型变量所属的类中是否有所调用的变量. 运行时期:也是看引用型变量所属的类是否有调用的变量。 简单一句话:成员变量,无论编译还是运行都看引用型变量所属的类。 更简单的话:成员变量,编译和运行都看等号 的左边。 成员函数: 编译时期:看的引用型变量所属的类中是否有调用的方法。 运行时期:看的对象所属的类中是否有调用的方法, 如果父子出现同名方法,会运行子类中的方法。因为方法有覆盖特性。 简单说:对于成员函数,编译看左边,运行看右边。 其实非静态函数,在内存有一个动态绑定机制。其实就是f指向了具体的子类对象。编译看到的f所属的Fu类中是否有这个方法。运行的时候f就绑定到了一个具体的子类对象上,子类对象运行时会覆盖父类中的相同方法,而运行子类的内容。 静态函数: 编译时期:看的是引用型变量所属的类中是否有调用的方法。 运行时期:也是看的是引用型变量所属的类中是否有调用的方法。 简单说: 对于静态函数,编译看左边,运行也看左边. 静态函数存放于每一个所属类的静态区中,它是被静态绑定,就是类一加载,就已经有固定所属的调用就是那个类名。 整体总结: 成员变量和静态成员:编译和运行都看左边。 只有非静态的成员函数:编译看左边,运行看右边。 当定义功能时,该功能中有一部分功能是确定,一部分功能是不确定的。 这时将不确定的部分通过一个函数暴露出去,延迟到子类去完成。这时我们发现解决这类功能部分明确部分不明确需求,用这个思想就可以很高效。这个思想就成了一个解决该类问题就有效的方案。起个名字就叫做:模版方法设计模式。例子10:模版方法设计模式的实例演示。
package cn.itheima.day16; import org.omg.IOP.Codec; /* 计算一个程序运行的时间。 思路: 在该程序运行之前记录一个时间。 运行完在记录一个时间,相减即可. 如何获取时间呢? */ /* 为了提高该功能的复用性,将该功能封装到对象中。只要以后使用时找到该对象即可。 当定义功能时,该功能中有一部分功能是确定,一部分功能是不确定的。 这是将不确定的部分通过一个函数暴露出去,延迟到子类去完成。 这是我们发现解决这类功能部分明确部分不明确需求,用这个思想就可以很高效。 这个思想就成了一个解决该类问题就有效的方案。起个名字就叫做:模版方法设计模式。 */ abstract class GetTime{ public final void getTime(){ long start = System.currentTimeMillis(); code(); long end = System.currentTimeMillis(); System.out.println("毫秒="+(end-start)); } public abstract void code(); } class Sub extends GetTime{ public void code(){ for(int x=0; x<1000; x++){ System.out.println("y"); } } } public class TemplateDemo { public static void main(String[] args) { Sub sub = new Sub(); sub.getTime(); } }