08_面向对象_下_02
一/多态的应用:模板方法设计模式
- 抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象类的行为方式。
- 解决问题:
- 当功能内部一部分实现是确定的,一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。
- 换句话说,在软件开发中实现一个算法时,整体步骤很固定、通用,这些步骤已经在父类中写好了。但是某些部分易变,易变部分可以抽象出来,供不同子类实现。这就是一种模板模式
- 示例:
1 package com.teacher.java; 2 public class TemplateTest { 3 public static void main(String[] args) { 4 Template p = new SubTemplate(); 5 p.runTime(); 6 } 7 } 8 //整体步骤是固定、通用,code是异变的 9 abstract class Template{ 10 public void runTime(){ 11 long start = System.currentTimeMillis(); 12 code(); 13 long end = System.currentTimeMillis(); 14 System.out.println("runTime =" + (end - start)); 15 } 16 public abstract void code(); 17 } 18 class SubTemplate extends Template{ 19 @Override 20 public void code() { 21 for(int i = 2;i <= 1000;i++){ 22 Boolean isFlag = true; 23 for(int j = 2;j <= Math.sqrt(i);j++){ 24 if (i % j == 0) { 25 isFlag = false; 26 break; 27 } 28 } 29 if(isFlag){ 30 System.out.println(i); 31 } 32 } 33 } 34 }
*二/接口(interface)
- 一方面,有时必须从几个类中派生出一个子类,继承它们所有的属性和方法。但是,Java 不支持多重继承。有了接口,就可以得到多重继承的效果。
- 另一方面,有时必须从几个类中抽取出一些共同的行为特征,而它们之间又没有 is-a 的关系,仅仅是具有相同的行为特征而已。例如:鼠标、键盘、打印机、扫描仪、摄像头、充电器、MP3 机、手机、数码相机、移动硬盘等都支持 USB 连接。
- 接口就是规范,定义的是一组规则,体现了现实世界中“如果你是/要…则必须能…”的思想。继承是一个"是不是"的关系,而接口实现则是"能不能"的关系。
- 接口的本质是契约,标准,规范,就像我们的法律一样。制定好后大家都要遵守。
1.接口特点:接口中省略的三个关键字 static final abstract public
- 接口(interface)是抽象方法和常量值定义的集合。
- 用 interface 来定义。
- 接口中的所有成员变量都默认是由 public static final 修饰的。通常省略
- 接口中的所有抽象方法都默认是由 public abstract 修饰的。通常省略
- 接口中没有构造器。
- 接口采用多继承机制
2.接口的使用
- 接口使用 interface 来定义。
- 在 Java 中:接口和类是并列的两个结构
- 如何去定义两个接口:定义接口中的成员
- JDK7 及以前:只能定义全局常量和抽象方法,全局常量意味着可以通过接口名调用,抽象方法意味着实现类必须重写
- 全局常量:public static final 的,但是书写中,可以省略不写。常量大写.全局常量通过
- 抽象方法:public abstract 的,但是书写中,可以省略不写。必须需要实现类重写
- JDK8:除了全局常量和抽象方法之外,还可以定义静态方法、默认方法。
- 静态方法:
- 使用 static 关键字修饰。可以通过接口直接调用静态方法,并执行其方法体。
- 接口只能调用接口中的静态方法,不能调用默认方法,静态方法不能通过实现类或类的对象来调用
- 接口只能调用静态方法,而不能调用默认方法,
- 默认方法:
- 默认方法使用 default 关键字修饰。默认方法可省略的是public,而不是代表缺省。
- 默认方法只能通过实现类的对象来调用。如果实现类重写了接口中的默认方法,调用时,调用的是重写的方法
- 实现类的对象只能调用接口中的默认方法,不能调用静态方法;
- 对比:接口只能调用静态方法,而不能调用默认方法,
- 静态方法:
- 接口中不能定义构造器!意味着接口不可以实例化.
- Java 开发中,接口通过让类去实现(implements)的方式来使用。
- 如果实现类覆盖了(也就是重写,一般说实现)接口中的所有方法,则此实现类就可以实例化
- 如果实现类没有覆盖接口中所有的抽象方法,则此实现类只能是一个抽象类
- Java 类可以实现多个接口 --->弥补了 Java 单继承性的局限性
- 格式:class AA extends BB implementd CC,DD,EE -->先写继承 后写实现
- 接口与接口之间是继承,而且可以多继承
示例:
1 public class InterfaceTest { 2 public static void main(String[] args){ 3 Plane2 p = new Plane2(); 4 p.fly(); 5 p.stop(); 6 p.attack(); 7 } 8 } 9 10 //接口1 11 interface Flyable{ 12 //全局变量 13 public static final int MAX_SPEED = 7900; 14 int MIN_SPEED = 0;//public static final 可以省略不写 15 16 //抽象方法 17 public abstract void fly(); 18 void stop();//public abstract 可以省略 19 //nterfaces cannot have constructors 接口不能有构造器,意味着不能实例化 20 // public Flyable(){ 21 // 22 // } 23 } 24 25 //接口二 26 interface AttackTest{ 27 void attack(); 28 } 29 30 31 //接口通过类区实现(implements)的方式使用接口 32 class Plane implements Flyable{ 33 34 35 //方法的实现 36 public void fly() { 37 System.out.println("能够飞"); 38 } 39 40 public void stop() { 41 System.out.println("能够停"); 42 } 43 } 44 45 class Plane2 extends Plane implements Flyable, AttackTest{ 46 47 @Override 48 public void attack() { 49 System.out.println("2代飞机能够攻击"); 50 51 } 52 53 @Override 54 public void fly() { 55 System.out.println("2代飞机飞的很快"); 56 57 } 58 59 @Override 60 public void stop() { 61 System.out.println("2代飞机停的很稳"); 62 63 } 64 65 } 66 //*************************************************************** 67 //接口与接口之间是继承,而且可以多继承 68 interface A{ 69 void eat(); 70 } 71 72 interface B{ 73 void sleep(); 74 } 75 //c继承B和A 76 interface C extends A,B{ 77 void walk(); 78 } 79 //D实现接口C,需要覆盖所以方法 80 class D implements C{ 81 82 @Override 83 public void eat() { 84 System.out.println("D吃"); 85 86 } 87 88 @Override 89 public void sleep() { 90 System.out.println("D睡"); 91 92 } 93 94 @Override 95 public void walk() { 96 System.out.println("D走"); 97 } 98 }
- JDK8新特性:静态方法和默认方法
1 public class InterfaceTest1 { 2 public static void main(String[] args) { 3 BB test = new BB(); 4 //接口中的静态方法只能通过接口去调用,不能通过实现类和实现类的对象调用,接口只能调用接口中的静态方法,不能调用默认方法 5 AA.method1(); 6 //接口中的默认方法,只能通过实现类的对象调用,实现类的对象只能调用接口中的默认方法,不能调用静态方法 7 test.method2(); 8 } 9 } 10 //接口 11 interface AA{ 12 //静态方法:static 13 public static void method1(){ 14 System.out.println("AA:静态:北京"); 15 } 16 //默认方法:defaul 17 public default void method2(){ 18 System.out.println("AA:默认:北京"); 19 } 20 //默认方法可省略的是public,而不是代表缺省 21 default void method3(){ 22 System.out.println("AA:默认:省略:北京"); 23 } 24 } 25 //实现类 26 class BB implements AA{ 27 public void eat(){ 28 29 } 30 }
- 调用父类和接口中相同名称的属性时候,如何执行
1 public class Test121 { 2 public static void main(String[] args) { 3 new C().getI();//输出结果 3 2 1 4 } 5 } 6 interface A{ 7 int I = 1; 8 } 9 class B{ 10 int I = 2; 11 } 12 class C extends B implements A{ 13 int i = 3; 14 15 public void getI(){ 16 // System.out.println(i);//报错:The field i is ambiguous i是不确定的 17 System.out.println(this.i);//自身的 18 System.out.println(super.i);//父类的 19 System.out.println(A.i);//调用接口A中的 20 } 21 }
- 如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的默认方法,那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法。-->类优先原则
1 public class Test12 { 2 public static void main(String[] args) { 3 Son son = new Son(); 4 son.help();//输出为父类中的默认的同名同参数方法 5 } 6 } 7 8 9 interface Spoony{ 10 default void help(){ 11 System.out.println("救媳妇"); 12 } 13 } 14 interface Filial{ 15 default void help(){ 16 System.out.println("救母亲"); 17 } 18 } 19 20 class Father{ 21 public void help(){ 22 System.out.println("救你妈妈"); 23 } 24 } 25 class Son extends Father implements Spoony,Filial{ 26 27 }
- 如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,那么在实现类没有重写此方法的情况下,报错。-->接口冲突。这就需要我们必须在实现类中重写此方法.此时实现类调用的是重写的方法
1 public class Test12 { 2 public static void main(String[] args) { 3 Son son = new Son(); 4 son.help();//输出为不知道 5 } 6 } 7 interface Spoony{ 8 default void help(){ 9 System.out.println("救媳妇"); 10 } 11 } 12 interface Filial{ 13 default void help(){ 14 System.out.println("救母亲"); 15 } 16 } 17 18 class Father{ 19 public void help(){ 20 System.out.println("救我媳妇"); 21 } 22 23 } 24 class Son extends Father implements Spoony,Filial{ 25 26 //重写了接口中的默认方法 27 public void help() { 28 System.out.println("不知道"); 29 } 30 }
- 如何在子类(或实现类)的方法中调用父类、接口中被重写的方法
1 class Son extends Father implements Spoony,Filial{ 2 public void help2() { 3 System.out.println("不知道"); 4 } 5 //方法的重写 6 public void help(){ 7 help2();//调用类内部的方法 8 super.help();//调用父类的方法 9 Filial.super.help();//调用接口Filial中的方法 10 Spoony.super.help();//调用Spoony中的方法 11 } 12 }
- 接口的具体使用,体现多态性
- 接口的主要用途就是被实现类实现-->面向接口编程
- 接口,实际可以看作是一种规范
三/内部类
1.内部类的使用
- 1.Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B就是外部类
- 2.内部类的分类:成员内部类 VS 局部内部类(方法内、代码块内、构造器内)
- 成员内部类
- 内部类作为外部类的成员,
- 内部类可以调用外部类的结构
- 内部类可以被static
- 内部类可以被4种不同的权限修饰
- 内部类作为一个类
- 类内可以定义属性、方法、构造器/内部类/代码块等
- 可以被final修饰,表示此类不能被继承。言外之意,不使用final,就可以被继承
- 可以abstract修饰,作为抽象类,必须被继承
- 内部类作为外部类的成员,
- 成员内部类
1 class AA{ 2 //成员内部类 3 //静态内部类 4 static class BB{ 5 6 } 7 //非静态内部类 8 class CC{ 9 10 } 11 //局部内部类 12 //方法中 13 public void method1(){ 14 class DD{ 15 16 } 17 } 18 //代码块中 19 { 20 21 } 22 23 //构造器中 24 public AA(){ 25 class EE{ 26 27 } 28 } 29 }
2.如何实例化成员内部类
1 public class InnerClassTest { 2 public static void main(String[] args) { 3 //静态内部类实例化 4 AA.BB p = new AA.BB(); 5 p.eatBB(); 6 //非静态内部类实例化 7 AA i = new AA(); 8 AA.CC j = i.new CC(); 9 j.eatCC(); 10 } 11 } 12 class AA{ 13 public void eatAA(){ 14 System.out.println("AA吃饭"); 15 } 16 17 //静态内部类 18 static class BB{ 19 public void eatBB(){ 20 System.out.println("BB吃饭"); 21 } 22 } 23 //非静态内部类 24 class CC{ 25 public void eatCC(){ 26 System.out.println("CC吃饭"); 27 } 28 } 29 }
3.如何在成员内部类中区分调用的外部类的结构
1 class AA{ 2 String name = "AA"; 3 public void eatAA(){ 4 System.out.println("AA吃饭"); 5 } 6 public void walk1(){ 7 System.out.println("走路1"); 8 } 9 public static void walk2(){ 10 System.out.println("走路2"); 11 } 12 //静态内部类 13 static class BB{ 14 String name = "BB"; 15 public void eatBB(){ 16 System.out.println("BB吃饭"); 17 //静态内部类方法中调用外部类方法//方法中只能调用静态方法 18 walk2();//原型 AA.walk2(); 省略了AA. 19 } 20 //非静态内部类方法中调用外部类属性//适用于重名 21 public void test(String name){ 22 System.out.println(name);//形参 23 System.out.println(this.name);//BB中的属性 24 // System.out.println(AA.name);//报错,此时name是实例变量,不能通过类去调用,需要将name变成类变量 25 } 26 } 27 //非静态内部类 28 class CC{ 29 String name = "CC"; 30 public void eatCC(){ 31 System.out.println("CC吃饭"); 32 //非静态内部类方法中调用外部类方法 33 //方法中调用外部非静态方法 34 walk1();//原型:AA.this.walk();省略了AA.this. 35 //方法中调用外部静态方法 36 walk2();//原型 AA.walk2(); 省略了AA. 37 } 38 //非静态内部类方法中调用外部类属性//适用于重名 39 public void test(String name){ 40 System.out.println(name);//形参 41 System.out.println(this.name);//CC中的属性 42 System.out.println(AA.this.name);//AA中的属性 43 } 44 } 45 }
4.开发中局部内部类的使用:匿名内部类使用较多
1 package com.teacher.java2; 2 3 import javax.print.attribute.standard.RequestingUserName; 4 5 public class InnerClassTest { 6 7 } 8 class AA{ 9 //开发中很少用 10 public void method(){ 11 class CC{ 12 13 } 14 } 15 16 //开发中常用的 17 //方法:返回一个实现了Comparable接口的类的匿名对象//方式一: 18 public Comparable getComparable(){ 19 //局部内部类:创建一个实现了Comparable接口的类 20 class MyComparable implements Comparable{ 21 22 @Override 23 public int compareTo(Object o) { 24 // TODO Auto-generated method stub 25 return 0; 26 } 27 } 28 return new MyComparable();//返回一个实现了Comparable接口的类的对象 29 } 30 } 31 32 class BB{ 33 //方法:返回一个实现了Comparable接口的匿名实现类的匿名对象 //方式二: 34 public Comparable getComparable(){ 35 return new Comparable(){ 36 @Override 37 public int compareTo(Object o) { 38 // TODO Auto-generated method stub 39 return 0; 40 } 41 };//原型 return new Comparable(){}; 42 } 43 }
- 内部类注意的一个点:
- 在局部内部类的方法中(比如:show)如果调用局部内部类所声明的方法(比如:method)中的局部变量(比如:num)的话, * 要求此局部变量声明为final的。
- jdk 7及之前版本:要求此局部变量显式的声明为final的
- jdk 8及之后的版本:可以省略final的声明
1 class AA{ 2 public void method(){ 3 int num = 10; //此时num是一个常量, final int num = 10; JDK8以上版本final省略了,8以前的需要加上final 4 class CC{ 5 public void show(){ 6 System.out.println(num); 7 } 8 } 9 } 10 }

浙公网安备 33010602011771号