Java——类的应用(二)
3、接口的应用
接口是一种特殊的抽象类。抽象类中的抽象方法仅提供函数原型,而没有具体的实现。子类必须提供父类中的抽象方法的实现方式。和抽象类一样,接口也可以定义抽象的方法。在就扣中只能包含抽象方法和常量,不能有变量、初始化块和构造函数。另外在Java中类的继承是单继承,而接口是可以用于多继承的。
1)、如何定义接口
当创建一个接口时,这个接口中的方法是抽象的。也就是说接口不提供具体的实现,他仅指定要做什么,而不说如何去做。一旦定义了接口,任何类都可以实现这个接口,而且一个类可以实现多个接口。接口只能定义常量和抽象方法。在Java中使用关键字interface创建一个接口。创建接口的如法如下:
[public] interface <接口名>{
[常量]
[<抽象方法>]
}
由public修饰的接口能被任何包中的接口或类访问,而具有默认访问的接口只能被所在的包内的接口或类访问。
接口中定义的所有变量必须是共有的、静态的和最终的。也就是说接口只能声明常量,而不能声明变量。接口中定义的所有方法都是公有的和抽象的,这些修饰符可以写也可以不写。下面的实例讲解了接口中的方法:
interface test02 {//定义了一个接口 void print(int a); void print1(int b);} class test03 implements test02{ @Override public void print(int a) {//提供了方法print()的具体实现 System.out.println("a=" + a); } @Override public void print1(int b) {//提供了方法print1()的具体实现 System.out.println("b=" + b); }} public class test { public static void main(String[] args) { test03 t3 = new test03(); t3.print(1); t3.print1(2); }}
2)、接口常量
在Java中规定定义在接口中的变量必须具有public、static和final属性,所以在接口中只能声明常量,而不能声明变量。这样做能够保证实现该接口的所有类可以访问相同的量。下面的程序证明了接口中只能声明常量:
interface test02 {//定义了一个接口 int a = 1; int b = 2; int c = 3; int d = 4; int e = 5; void print(); void print1();} class test03 implements test02{ @Override public void print() {//提供了方法print()的具体实现 System.out.println("a+b=" + (a+b)); } @Override public void print1() {//提供了方法print1()的具体实现 System.out.println("c+d+f=" + (c+d+e)); }} public class test { public static void main(String[] args) { test03 t3 = new test03(); t3.print(); t3.print1(); }}
在一个程序中,如果一个常量被赋初值,那么这个值就 被更改。
接口内的方法都是公有的和抽象的。在方法声明中也可以不写public和abstract修饰符,但是该方法还是公有的和抽象的。下面的实例说明了就扣中的方法只能是public和abstract类型。
interface test02 {//定义了一个接口 void print(); public void print1(); abstract void print2(); public abstract void print3(); abstract public void print4();} class test03 implements test02{ @Override public void print() { System.out.println("这个方法没有修饰符"); } @Override public void print1() { System.out.println("这个方法有public修饰符"); } @Override public void print2() { System.out.println("这个方法有abstract修饰符"); } @Override public void print3() { System.out.println("这个方法有public和abstract修饰符"); } @Override public void print4() { System.out.println("这个方法有abstract和public修饰符"); }} public class test { public static void main(String[] args) { test03 t3 = new test03(); t3.print(); t3.print1(); t3.print2(); t3.print3(); t3.print4(); }}
3)、接口的实现
当一个接口被定义后,一个或多个类都可以实现该接口,实现该接口的类必须符合以下几点:
>为接口中的所有方法提供具体的实现
>必须遵守重写所有规则
>保持相同的返回类型
如果一个类实现了多个接口,那么这些接口之间需要使用逗号隔开。如果几个接口有相同的常量和方法,那么相同的常量可以通过<接口名>.<差量名>的形式来访问相应的常量,相同的方法将被其中一个接口使用。下面的程序中实现了多个接口:
interface itf01{ int add(int a , int b);}interface itf02{ int sub(int a , int b);}interface itf03{ int mul(int a , int b);}interface itf04{ int umul(int a , int b);} class test03 implements itf01 , itf02 , itf03 , itf04{ @Override//提供了方法add()的具体实现 public int add(int a, int b) { return a + b; } @Override//提供了方法sub()的具体实现 public int sub(int a, int b) { return a - b; } @Override//提供了方法mul()的具体实现 public int mul(int a, int b) { return a * b; } @Override//提供了方法umul的具体实现 public int umul(int a, int b) { return a / b; }} public class test { public static void main(String[] args) { test03 t3 = new test03(); System.err.println("a + b = " + t3.add(3, 2)); System.err.println("a - b = " + t3.sub(3, 2)); System.err.println("a * b = " + t3.mul(3, 2)); System.err.println("a / b = " + t3.umul(3, 2)); }}
如果实现接口的类时抽象类,那该怎么办呢?很简单,它可以将其推给第一个具体子类。其子类可以完成父类的任务,重写接口和父类中的抽象方法,提供具体实现。下面是一个抽象类实现接口的实例:
interface ladd { void add(int a , int b);}//抽象类实现了接口abstract class test02 implements ladd{ //提供了add()方法的具体实现,也可以不提供 public void add(int a , int b){}; //定义了一个抽象的方法 abstract void sub(int a , int b);} class test03 extends test02{ @Override public void add(int a, int b) { System.out.println("a + b" + (a + b)); } @Override void sub(int a, int b) { System.err.println("a - b" + (a - b)); }} public class test { public static void main(String[] args) { test03 t3 = new test03(); t3.add(2, 3); t3.sub(2, 3); }}
一个类实现多个接口,这就需要它为接口中的抽象方法提供具体的实现,如果这个接口继承了另一个接口,那么这个类也需要实现它所实现的子接口的付接口的抽象方法。
interface ladd { void add(int a , int b);}//接口继承实现了另一接口interface test02 extends ladd{ void sub(int a , int b);} class test03 implements test02{ @Override public void sub(int a, int b) { } @Override public void add(int a, int b) { }}
4)、如何引用接口
在Java中可以建立接口类型的引用变量,接口的引用变量能够存储一个指向对象的引用值,这个对象可以是任何实现该接口的类的实例。通过接口引用可以调用对象的方法,这些方法必须是类中的抽象方法。下面是通过程序说明如何引用接口:
interface ladd { int add(int a , int b);}interface lsub { int sub(int a , int b);}interface lmul { int mul(int a , int b);}interface lumul { int umul(int a , int b);} class test03 implements ladd , lmul , lsub , lumul { @Override public int add(int a, int b) { return a + b; } @Override public int mul(int a, int b) { return a - b; } @Override public int sub(int a, int b) { return a * b; } @Override public int umul(int a, int b) { return a / b; }} public class test { public static void main(String[] args) { test03 t3 = new test03(); //接口引用指向对象的引用 ladd bb = t3; lsub cc = t3; lmul dd = t3; lumul ee = t3; //对象引用调用add()方法 System.err.println("a + b= " + t3.add(2, 3)); System.err.println("a - b = " + t3.sub(2, 3)); System.err.println("a * b = " + t3.mul(2, 3)); System.err.println("a / b = " + t3.umul(2, 3)); //接口引用调用接口的方法 System.err.println("a + b = " + bb.add(4, 5)); System.err.println("a - b = " + cc.sub(4, 5)); System.err.println("a * b = " + dd.mul(4, 5)); System.err.println("a / b = " + ee.umul(4, 5)); }}
4、内部类的应用
内部类即在一个雷黄总可以再嵌套定义另一个类,同时内部类是外部类的一个成员。
1)、内部类
在Java中,一个类被嵌套定义在另一个类内部,那么这个类称为内部类,而包含内部类的类被称为外部类。对于外部类而言,内部类相当于成员变量或方法,是外部类的一个成员。同时内部类也是一个类,它也具有自己的成员变量和方法。通过创建内部类的一个对象,可以调用自己的成员方法,如下所示:
public class mytest01 { int a ; void print() { System.err.println("a= " + a); } class test01 { int b; void print() { System.err.println("b=" + b); } }}
2)、内部类的使用
内部类分为以下几种:
>常规内部类:在一个类中使用内部类可以在内部类中直接存取其所在的类的私有成员变量;
>方法内部类:它对外面的所有类来说都是隐藏的,仅有它所在的方法指导它;它不仅可以访问所属外部类中的数据,还可以访问局部变量,不过局部变量必须声明为final数据;
>匿名内部类:就是没有名字的内部类,这是Java为了方便我们编写程序而设计的一个机制。因为有时有的内部类只需要创建一个它的对象就可以了,在下面的编程中不会用到这个类,这时使用匿名内部类就比较合适,而且也免去了给它取名的烦恼;
>静态内部类:即在内部类的前面增加了static修饰符(modifier)。
常规内部类是指内部类直接嵌套在外部类的类体中,这时内部类就相当于外部类的一个成员变量或成员方法,是外部类的一个成员。下面的实例讲解了常规内部类是如何定义的:
Class test01{
Private int a = 3;
Class test{ //定义了一个常规内部类
Void print() {
System.out.println(“a = ” + a);
}}}
如果希望运行上面内部类中的方法,唯一的方法是通过外部类的一个实例,也就是说只有先创建一个外部类的实例,通过外部类的实例才能访问内部类。下面是对上面类的使用:
class test01 { private int a = 4; public void print() { test02 t2 = new test02(); t2.print(); } class test02 { int b; void print() { System.err.println("a + b=" + (a + b)); } }} public class mytest01 { public static void main(String[] args) { test01 t1 = new test01(); t1.print(); }}
使用外部类来创建内部类的对象,就如同在一个外部类实例中调用一个方法,同时这个方法正好是内部类的方法。下面是一个通过外部类来创建内部类的对象的实例
class test01 { private int a = 4; class test02 { void print() { System.err.println("a = " + a); } }}public class mytest01 { public static void main(String[] args) { test01 t1 = new test01(); test01.test02 t2 = t1.new test02(); t2.print(); }}
方法内部类对外面的所有类来说都是隐藏的,仅有它所在的方法知道它;它不仅可以访问它所属外部类中的数据,还可以访问局部变量,不过局部变量必须声明为final类型。下面是实现方法内部类的定义实例:
class test01 { private String a = "这是一个方法内部类"; void print() { class test02 {//定义一个方法内部类 public void print() { System.err.println(a); } } }}
下面的实例是在外部类的方法内创建内部类的对象:
class test01 { private String a = "这是一个方法内部类"; void print() { String b = "这是一个局部变量"; class test02 {//定义一个方法内部类 public void print() { System.err.println(a); System.err.println(b); } } test02 t2 = new test02(); t2.print(); }}
上面的代码会在System.err.println(b);这一行报错,但如果在String b = “”前面加上final,就会正确。
3)、匿名内部类
匿名内部类比较怪异,内明内部类也就是没有名字的内部类。下面是一个定义内部类的方法:
class test01 { public void print(){ System.err.println("这是一个类类型的匿名内部类"); }}class test02 { test01 t1 = new test01() {//定义了一个匿名内部类 public void print() { System.err.println("这个子类重写了父类的方法"); } }; void print1() { t1.print(); }}public class mytest01 { public static void main(String[] args) { test02 t2 = new test02(); t2.print1(); }}
在匿名内部类中必须重写父类的方法,同时也可以定义自己的方法。但是匿名内部类怎么调用自己的方法呢?父类不知道关于子类中新的方法的任何内容。因此,当在匿名内部类引用上调用父类中没有的方法,编译器将会报错。下面是一个关于在匿名内部类中调用不到父类原方法而产生的错误:
class test02 { test01 t1 = new test01() {//定义了一个匿名内部类 public void print() { System.err.println("这个子类重写了父类的方法"); } Public void prt(){ System.out.println(“这是匿名内部类自己的新方法”)} }; void print1() { t1.print(); }}
上面的代码将会在prt()方法中报错。
匿名内部类是指创建父类类型的匿名子类,而接口类型的匿名内部类是指创建接口类型的匿名类,这个类必须实现这个接口。
interface Useing {//定义了一个接口 public void print();} class test01 { Useing useing = new Useing() {//接口类型的匿名内部类 @Override public void print() { System.err.println("这是一个接口类型的匿名内部类"); } }; void print() { useing.print(); }}public class mytest01 { public static void main(String[] args) { test01 t = new test01(); t.print(); }}
下面的程序中,利用接口的形式进行对象的传递。先定义一个类test01,该类中定义了一个方法print()。在方法print()中又定义了一个内部类,接着对这个内部类进行实例化,通过类的实例调用自己的带参数的方法。这个参数是 一个能实现接口book中的抽象方法的一个实例,然后在这个类重写这个方法。下面是一个实例:
interface Book {//定义了一个接口 public void print();} class test01 { void print1(){ class good { void print2(Book b) {} } good g = new good(); g.print2(new Book() { @Override public void print() { System.err.println("这是一个参数"); } }); { System.err.println("这是一个带参数的匿名内部类"); } }}public class mytest01 { public static void main(String[] args) { test01 t = new test01(); t.print1(); }}