Java面向对象05
2017-05-21 08:24
141 查看
3.1、继承性(重点)
继承性是面向对象的第二大主要特征。3.1.1、继承问题的引出
任何的概念出现都有其自己的目的以及可以结局的问题范畴,那么下面首先编写两个程序:Person类、Student类。Person.java: | Student.java: |
class Person { private String name ; private int age ; public void setName(String name) { this.name = name ; } public void setAge(int age) { this.age = age ; } public String getName() { return this.name ; } public int getAge(){ return this.age ; } } | class Student { private String name ; private int age ; private String school ; public void setName(String name) { this.name = name ; } public void setAge(int age) { this.age = age ; } public void setSchool(String school) { this.school = school ; } public String getName() { return this.name ; } public int getAge(){ return this.age ; } public String getSchool() { return this.school ; } } |
3.1.2、继承的概念
继承性严格来讲就是指扩充一个类已有的功能。在Java之中,如果要实现继承的关系,可以使用如下的语法:class 子类 extends 父类 {} |
· 子类又被称为派生类;
· 父类又被称为超类(Super Class)。
范例:观察继承的基本实现
class Person { private String name ; private int age ; public void setName(String name) { this.name = name ; } public void setAge(int age) { this.age = age ; } public String getName() { return this.name ; } public int getAge(){ return this.age ; } } class Student extends Person { // Student类继承了Person类 } public class TestDemo { public static void main(String args[]) { Student stu = new Student() ; // 实例化的是子类 stu.setName("张三") ; // Person类定义 stu.setAge(20) ; // Person类定义 System.out.println("姓名:" + stu.getName() + ",年龄:" + stu.getAge()) ; } } |
范例:在子类之中扩充父类的功能
class Person { private String name ; private int age ; public void setName(String name) { this.name = name ; } public void setAge(int age) { this.age = age ; } public String getName() { return this.name ; } public int getAge(){ return this.age ; } } class Student extends Person { // Student类继承了Person类 private String school ; // 子类的属性 public void setSchool(String school) { this.school = school ; } public String getSchool() { return this.school ; } } public class TestDemo { public static void main(String args[]) { Student stu = new Student() ; // 实例化的是子类 stu.setName("张三") ; // Person类定义 stu.setAge(20) ; // Person类定义 stu.setSchool("清华大学") ; // Student类扩充方法 System.out.println("姓名:" + stu.getName() + ",年龄:" + stu.getAge() + ",学校:" + stu.getSchool()) ; } } |
3.1.3、继承的限制
虽然继承可以进行类功能的扩充,但是其在定义的时候也是会存在若干种限制的。限制一:一个子类只能够继承一个父类,存在单继承局限
这个概念实际上是相对于其他语言而言,在其他语言之中,一个子类可以同时继承多个父类,就好比如下代码:
范例:错误的程序
class A {} class B {} class C extends A,B {} // 一个子类继承了两个父类 |
范例:正确的程序
class A {} class B extends A {} class C extends B {} |
严格来讲,按照以上的代码格式,可以一直无限制的继承下去,但是从实际来讲,应该不超过三层。
限制二:在一个子类继承的时候,实际上会继承父类之中的所有操作(属性、方法),但是需要注意的是,对于所有的非私有(no private)操作属于显式继承(可以直接利用对象操作),而所有的私有操作属于隐式继承(间接完成)。
class A { private String msg ; public void setMsg(String msg) { this.msg = msg ; } public String getMsg() { return this.msg ; } } class B extends A { public void print() { System.out.println(msg) ; // 错误: msg可以在A中访问private } } public class Test { public static void main(String args[]) { B b = new B() ; b.setMsg("张三") ; System.out.println(b.getMsg()) ; } } |
限制三:在继承关系之中,如果要实例化子类对象,会默认先调用父类构造,为父类之中的属性初始化,之后再调用子类构造,为子类之中的属性初始化,即:默认情况下,子类会找到父类之中的无参构造方法。
class A { public A() { // 父类无参构造 System.out.println("*************************") ; } } class B extends A { public B() { // 子类构造 System.out.println("#########################"); } } public class Test { public static void main(String args[]) { B b = new B() ; // 实例化子类对象 } } |
class B extends A { public B() { // 子类构造 super() ; // 调用父类构造 System.out.println("#########################"); } } |
class A { public A(String msg) { // 父类构造 System.out.println("*************************") ; } } class B extends A { public B() { // 子类构造 super("Hello") ; // 调用父类构造 System.out.println("#########################"); } } public class Test { public static void main(String args[]) { B b = new B() ; // 实例化子类对象 } } |
疑问?有没有可能性,不让子类去调用父类构造?
既然super()和this()都是调用构造方法,而且都要放在构造方法的首行上,那么如果说this()出现了,那么super()应该就不会出现了,所以他编写了如下的程序:
class A { public A(String msg) { // 父类构造 System.out.println("*************************") ; } } class B extends A { public B(String msg) { this(msg,10) ; // 调用本类构造 } public B(String msg,int age) { // 子类构造 this(msg) ; // 调用本类构造 System.out.println("#########################"); } } public class Test { public static void main(String args[]) { B b = new B("",20) ; // 实例化子类对象 } } |
此时在某种程度上来讲,有一个问题解释了一半:一个简单Java类一定要保留有一个无参构造方法。
3.2、覆写(重点)
既然现在出现了继承的关系,那么就存在了子类和父类的联系,而在子类之中有可能定义和父类完全相同的方法或属性的名称,这个时候就称为覆写了。3.2.1、方法的覆写
当子类定义了和父类在方法名称、返回值类型、参数类型及个数完全相同的方法的时候,称为方法的覆写。范例:没有覆写的操作
class A { public void print() { System.out.println("Hello World .") ; } } class B extends A { } public class Test { public static void main(String args[]) { B b = new B() ; b.print() ; // 方法从父类继承而来 } } |
范例:实现覆写
class A { public void print() { System.out.println("Hello World .") ; } } class B extends A { public void print() { // 方法名称、参数类型及个数、返回值全相同 System.out.println("世界,你好!") ; } } public class Test { public static void main(String args[]) { B b = new B() ; b.print() ; // 方法从父类继承而来 } } |
但是在进行方法覆写的时候有一个点需要注意:被子类所覆写的方法不能拥有比父类更严格的访问控制权限,对于访问控制权限现在已经接触过三种:private < default(不写) < public;
如果此时父类之中的方法是default权限,那么子类覆写的时候只能是default或public权限,而如果父类的方法是public,那么子类之中方法的访问权限只能是public。
范例:错误的操作
class A { public void print() { System.out.println("Hello World .") ; } } class B extends A { void print() { System.out.println("世界,你好!") ; } } |
当一个子类覆写了一个父类方法的时候,那么在这种情况下,子类要想调用父类的被覆写过的方法,则在方法前要加上“super”。
class A { public void print() { System.out.println("Hello World .") ; } } class B extends A { public void print() { super.print() ; System.out.println("世界,你好!") ; } } public class Test { public static void main(String args[]) { B b = new B() ; b.print() ; // 方法从父类继承而来 } } |
· this.方法():先从本类查找是否存在指定的方法,如果没有找到,则调用父类操作;
· super.方法():直接由子类调用父类之中的指定方法,不再找子类。
提问:请问以下的操作是覆写吗?
class A { private void print() { System.out.println("Hello World .") ; } public void fun() { this.print() ; } } class B extends A { public void print() { // 不叫覆写 System.out.println("世界,你好!") ; } } public class Test { public static void main(String args[]) { B b = new B() ; b.fun() ; // 方法从父类继承而来 } } |
3.2.2、属性的覆盖(别了解了)
当一个子类定义了和父类重名的属性名称的时候,就表示属性的覆盖了。class A { public String msg = "Hello World ." ; } class B extends A { public int msg = 100 ; // 属性同名 public void print() { System.out.println("msg = " + this.msg) ; System.out.println("msg = " + super.msg) ; } } public class Test { public static void main(String args[]) { B b = new B() ; b.print() ; // 方法从父类继承而来 } } |
面试题:请解释一下this和super的区别?
No. | 区别 | this | super |
1 | 定义 | 表示本类对象 | 表示父类对象 |
2 | 使用 | 本类操作:this.属性、this.方法()、this() | 父类操作:super.属性、super.方法()、super() |
3 | 调用构造 | 调用本类构造,要放在首行 | 子类调用父类构造,放在首行 |
4 | 查找范围 | 先从本类查找,找不到查找父类 | 直接由子类查找父类 |
5 | 特殊 | 表示当前对象 | - |
No. | 区别 | 重载 | 覆写 |
1 | 英文单词 | Overloading | Override |
2 | 定义 | 方法名称相同、参数的类型及个数不同 | 方法名称、参数类型及个数、返回值类型完全相同 |
3 | 权限 | 没有权限要求 | 被子类所覆写的方法不能拥有比父类更严格的访问控制权限 |
4 | 范围 | 发生在一个类之中 | 发生在继承关系类之中 |
3.3、思考题(总结,核心)
现在要求定义一个整型数组的操作类,数组的大小由外部决定,用户可以向数组之中增加数据,以及取得数组中的全部数据,也可以根据外部提供的数组的增长大小,在原本的数组之上扩充指定的容量,另外,在此类上派生两个子类:·排序类:取得的数组内容是经过排序出来的结果;
·反转类:取得的数组内容是反转出来的结果;
首先要完成的是定义父类,根本就不需要考虑子类。
范例:定义了父类 —— Array
class Array { // 数组操作类 private int [] data ; private int foot = 0 ; // 控制脚标 public Array(int len) { // 由外部传递大小 if (len > 0) { this.data = new int [len] ; } else { this.data = new int [1] ; // 维持一个大小 } } public boolean add(int num) { if (this.foot < this.data.length) { // 有位置 this.data[this.foot ++] = num ; // 保存数据 return true ; } return false ; } public int [] getData() { return this.data ; } public void increment(int num) { int [] newArr = new int [this.data.length + num] ; System.arraycopy(this.data,0,newArr,0,this.data.length) ; this.data = newArr ; // 改变引用 } } public class Test { public static void main(String args[]) { Array arr = new Array(5) ; System.out.println(arr.add(8)) ; System.out.println(arr.add(10)) ; System.out.println(arr.add(2)) ; System.out.println(arr.add(5)) ; System.out.println(arr.add(3)) ; System.out.println(arr.add(9)) ; arr.increment(3) ; System.out.println(arr.add(20)) ; System.out.println(arr.add(10)) ; System.out.println(arr.add(30)) ; int result [] = arr.getData() ; for (int x = 0 ; x < result.length ; x ++) { System.out.println(result[x]) ; } } } |
class SortArray extends Array { public SortArray (int len) { super(len) ; // 调用父类的有参构造 } public int [] getData() { java.util.Arrays.sort(super.getData()) ; return super.getData() ; } } public class Test { public static void main(String args[]) { SortArray arr = new SortArray(5) ; System.out.println(arr.add(8)) ; System.out.println(arr.add(10)) ; System.out.println(arr.add(2)) ; System.out.println(arr.add(5)) ; System.out.println(arr.add(3)) ; System.out.println(arr.add(9)) ; arr.increment(3) ; System.out.println(arr.add(20)) ; System.out.println(arr.add(10)) ; System.out.println(arr.add(30)) ; int result [] = arr.getData() ; for (int x = 0 ; x < result.length ; x ++) { System.out.println(result[x]) ; } } } |
范例:定义反转子类
class ReverseArray extends Array { public ReverseArray(int len) { super(len) ; } public int [] getData() { // 覆写 int head = 0 ; int tail = super.getData().length - 1 ; int center = super.getData().length / 2 ; for (int x = 0 ; x < center ; x ++) { int temp = super.getData()[head] ; super.getData()[head] = super.getData()[tail] ; super.getData()[tail] = temp ; head ++ ; tail -- ; } return super.getData() ; } } public class Test { public static void main(String args[]) { ReverseArray arr = new ReverseArray(5) ; System.out.println(arr.add(8)) ; System.out.println(arr.add(10)) ; System.out.println(arr.add(2)) ; System.out.println(arr.add(5)) ; System.out.println(arr.add(3)) ; System.out.println(arr.add(9)) ; arr.increment(3) ; System.out.println(arr.add(20)) ; System.out.println(arr.add(10)) ; System.out.println(arr.add(30)) ; int result [] = arr.getData() ; for (int x = 0 ; x < result.length ; x ++) { System.out.println(result[x]) ; } } } |
3.4、final关键字(重点)
在Java中,final关键字表示的是一个终结器的概念,使用final可以定义类、方法、变量。
1、 使用final定义的类不能有子类,太监类
final class A { } class B extends A { } |
class A { public final void print() {} } class B extends A { public void print() {} } |
class A { final String INFO = "hello world" ; // 常量 public final void print() { INFO = "world" ; // 无法修改 } } |
public static final String INFO = "hello world" ; // 全局常量 |
3.5、构造方法私有化(重点)
在讲解本操作之前,首先来观察如下的程序。class Singleton { // 定义一个类 public void print() { System.out.println("Hello World .") ; } } public class Test { public static void main(String args[]) { Singleton inst = null ; // 声明对象 inst = new Singleton() ; // 实例化对象 inst.print() ; // 调用方法 } } |
class Singleton { // 定义一个类 private Singleton() {} // 构造方法私有化 public void print() { System.out.println("Hello World .") ; } } |
那么现在就需要思考:在保证Singleton类之中的构造方法不修改不增加,以及print()方法不修改的情况下,如何操作,才可以让类的外部通过实例化对象再去调用print()方法?
思考过程一:使用private访问权限定义的操作只能被本类所访问,外部无法调用,那么现在既然构造方法被私有化了,就证明,这个类的构造方法只能被本类所调用,即:现在在本类之中产生本类实例化对象。
class Singleton { // 定义一个类 Singleton instance = new Singleton() ; private Singleton() {} // 构造方法私有化 public void print() { System.out.println("Hello World .") ; } } |
class Singleton { // 定义一个类 static Singleton instance = new Singleton() ; private Singleton() {} // 构造方法私有化 public void print() { System.out.println("Hello World .") ; } } public class Test { public static void main(String args[]) { Singleton inst = null ; // 声明对象 inst = Singleton.instance ; // 实例化对象 inst.print() ; // 调用方法 } } |
class Singleton { // 定义一个类 private static Singleton instance = new Singleton() ; private Singleton() {} // 构造方法私有化 public void print() { System.out.println("Hello World .") ; } public static Singleton getInstance() { return instance ; } } public class Test { public static void main(String args[]) { Singleton inst = null ; // 声明对象 inst = Singleton.getInstance() ; // 实例化对象 inst.print() ; // 调用方法 } } |
现在做一个简单的思考:如果说现在一个类只希望有唯一的一个实例化对象出现,应该控制构造方法,如果构造方法对外部不可见了,那么现在肯定无法执行对象的实例化操作,必须将构造方法隐藏,使用private隐藏。
既然清楚了这个目的,不过本程序依然有一个问题。
public static Singleton getInstance() { instance = new Singleton() ; return instance ; } |
class Singleton { // 定义一个类 private static final Singleton INSTANCE = new Singleton() ; private Singleton() {} // 构造方法私有化 public void print() { System.out.println("Hello World .") ; } public static Singleton getInstance() { return INSTANCE ; } } public class Test { public static void main(String args[]) { Singleton inst = null ; // 声明对象 inst = Singleton.getInstance() ; // 实例化对象 inst.print() ; // 调用方法 } } |
面试题:请编写一个Singleton程序,并说明其主要特点?
class Singleton { // 定义一个类 private static final Singleton INSTANCE = new Singleton() ; private Singleton() {} // 构造方法私有化 public void print() { System.out.println("Hello World .") ; } public static Singleton getInstance() { return INSTANCE ; } } public class Test { public static void main(String args[]) { Singleton inst = null ; // 声明对象 inst = Singleton.getInstance() ; // 实例化对象 inst.print() ; // 调用方法 } } |
对于单例设计模式,在日后的开发之中,只会用到此概念,但是具体的代码很少去编写。
扩展(可以不会):
对于单例设计模式按照设计模式的角度而言,分为两种:
·饿汉式:之前写的程序就属于饿汉式,因为在类之中的INSNTACE属性是在定义属性的时候直接实例化;
· 懒汉式:在第一次使用一个类实例化对象的时候才去实例化。
范例:观察懒汉式
class Singleton { // 定义一个类 private static Singleton instance ; private Singleton() {} // 构造方法私有化 public void print() { System.out.println("Hello World .") ; } public static Singleton getInstance() { if (instance == null) { // 没有实例化 instance = new Singleton() ; // 实例化 } return instance ; } } |
3.6、多例设计模式(理解)
单例设计模式只留有一个类的一个实例化对象,而多例设计模式,会定义出多个对象,例如:定义一个表示星期X的类,这个类的对象只有7个取值,定义一个表示性别的类,只有2个取值,定义一个表示颜色基色的操作类,颜色只有三个:红、绿、蓝,这种情况下,这样的类就不应该由用户无限制的去创造实例化对象,应该只使用有限的几个,这个就属于多例设计,但不管是单例设计还是多例设计,有一个核心不可动摇 —— 构造方法私有化。class Sex { private static final Sex MALE = new Sex("男") ; private static final Sex FEMALE = new Sex("女") ; private String title ; private Sex(String title) { // 构造方法私有化 this.title = title ; } public static Sex getInstance(String msg) { switch(msg) { case "male" : return MALE ; case "female" : return FEMALE ; default : return null ; } } public String getTitle() { return this.title ; } } public class Test { public static void main(String args[]) { Sex male = Sex.getInstance("male") ; System.out.println(male.getTitle()) ; } } |
3.7、多态性(重点)
多态是面向对象的最后一个主要特征,它本身主要分为两个方面:·方法的多态性:重载与覆写
|- 重载:同一个方法名称,根据不同的参数类型及个数可以完成不同的功能;
|- 覆写:同一个方法,根据操作的子类不同,所完成的功能也不同。
·对象的多态性:父子类对象的转换。
|-向上转型:子类对象变为父类对象,格式:父类 父类对象 = 子类实例,自动;
|- 向下转型:父类对象变为子类对象,格式:子类 子类对象 = (子类) 父类实例,强制;
范例:编写一个简单的程序,观察程序输出
class A { public void print() { System.out.println("A、public void print(){}") ; } } class B extends A { public void print() { // 方法覆写 System.out.println("B、public void print(){}") ; } } public class Test { public static void main(String args[]) { B b = new B() ; // 实例化子类对象 b.print() ; } } |
·看new的是那一个类;
·看new的这个类之中是否被覆写了父类要调用的方法。
范例:向上转型
public class Test { public static void main(String args[]) { A a = new B() ; // 向上转型 a.print() ; } } |
public class Test { public static void main(String args[]) { A a = new B() ; // 向上转型 B b = (B) a ; // 向下转型 b.print() ; } } |
public class Test { public static void main(String args[]) { A a = new A() ; // 没有转型 B b = (B) a ; // 向下转型,java.lang.ClassCastException: A cannot be cast to B b.print() ; } } |
在整个故事之中可以发现:
·如果两个人真的有关系,可以分配遗产;
·如果两个人真的没有关系,不能分配遗产。
转型因素:
·在实际的工作之中,对象的向上转型为主要使用,80%,向上转型之后,所有的方法以父类的方法为主,但是具体的实现,还是要看子类是否覆写了此方法;
·向下转型,10%,因为在进行向下转型操作之前,一定要首先发生向上转型,以建立两个对象之间的联系,如果没有这种联系,是不可能发生向下转型的,一旦发生了运行中就会出现“ClassCastException”,当需要调用子类自己特殊定义方法的时候,才需要向下转型;
·不转型,10%,在一些资源较少的时候,例如:移动开发。
class A { public void print() { System.out.println("A、public void print(){}") ; } } class B extends A { public void print() { // 方法覆写 System.out.println("B、public void print(){}") ; } public void getB() { System.out.println("B、getB()") ; } } public class Test { public static void main(String args[]) { A a = new B() ; // 向上转型 B b = (B) a ; // 向下转型 b.getB() ; // 父类没有的操作,只能通过子类对象调用 } } |
对象 instanceof 类 è 返回boolean型 |
public class Test { public static void main(String args[]) { A a = new A() ; System.out.println(a instanceof A) ; System.out.println(a instanceof B) ; if (a instanceof B) { B b = (B) a ; b.getB() ; } } } |
范例:观察完善的操作
class Person { private String name ; private int age ; public Person(String name,int age) { this.name = name ; this.age = age ; } public String getInfo() { return "姓名:" + this.name + ",年龄:" + this.age ; } } class Student extends Person { // Student类继承了Person类 private String school ; // 子类的属性 public Student(String name,int age,String school) { super(name,age) ; this.school = school ; } public String getInfo() { return super.getInfo() + ",学校:" + this.school ; } } public class TestDemo { public static void main(String args[]) { Person per = new Student("张三",20,"清华大学") ; System.out.println(per.getInfo()) ; } } |
class A { public void print() { System.out.println("A、public void print(){}") ; } } |
实现方式一:不使用对象转型
class A { public void print() { System.out.println("A、public void print(){}") ; } } class B extends A { public void print() { // 方法覆写 System.out.println("B、public void print(){}") ; } } class C extends A { public void print() { // 方法覆写 System.out.println("C、public void print(){}") ; } } public class Test { public static void main(String args[]) { fun(new B()) ; fun(new C()) ; } public static void fun(B b) { b.print() ; } public static void fun(C c) { c.print() ; } } |
实现方式二:利用对象向上转型完成
public class Test { public static void main(String args[]) { fun(new B()) ; fun(new C()) ; } public static void fun(A a) { a.print() ; } } |
class A { public void print() { System.out.println("A、public void print(){}") ; } } class B extends A { public void print() { // 方法覆写 this.getB() ; System.out.println("B、public void print(){}") ; } public void getB() { System.out.println("B、getB()") ; } } class C extends A { public void print() { // 方法覆写 this.getC() ; System.out.println("C、public void print(){}") ; } public void getC() { System.out.println("C、getC()") ; } } public class Test { public static void main(String args[]) { fun(new B()) ; fun(new C()) ; } public static void fun(A a) { a.print() ; } } |
在日后的所有开发之中,像之前程序那样,一个类去继承另外一个已经实现好的类的情况,是不可能出现的。即:一个类不能去继承一个已经实现好的类,只能继承抽象类或实现接口。对于抽象类和接口如果要想真正的清楚其概念,需要一段很长的时间,今天讲解它们的基本语法、定义形式、使用方式,给出一些代码的结构。
3.8、抽象类(核心)
3.8.1、抽象类的基本概念
普通类就是一个完善的功能类,可以直接产生对象并且可以使用,里面的方法都是带有方法体的,而抽象类之中最大的特点是包含了抽象方法,而抽象方法是只声明而未实现(没有方法体)的方法,而抽象方法定义的时候要使用abstract关键字完成,而抽象方法一定要在抽象类之中,抽象类要使用abstract关键字声明。范例:定义一个抽象类
abstract class A { private String info = "Hello World ." ; public void print() { System.out.println(info) ; } public abstract void get() ; // 只声明没有方法体 } |
public class Test { public static void main(String args[]) { A a = new A () ; // Test.java:10: 错误: A是抽象的; 无法实例化 } } |
一个类的对象实例化之后,可以调用类中的属性和方法,但是抽象类之中的抽象方法没有方法体,如果这样直接调用,那么不就乱了吗。
抽象类的使用原则:
· 抽象类必须有子类,使用extends继承,一个子类只能继承一个抽象类;
· 子类(如果不是抽象类)则必须覆写抽象类之中的全部抽象方法;
· 抽象类对象可以使用对象的向上转型方式,通过子类来进行实例化操作。
范例:使用抽象类
abstract class A { private String info = "Hello World ." ; public void print() { System.out.println(info) ; } public abstract void get() ; // 只声明没有方法体 } class Impl extends A { public void get() { System.out.println("Hello MLDN .") ; } } public class Test { public static void main(String args[]) { A a = new Impl() ; // 向上转型 a.print() ; // 自己类定义 a.get() ; // 子类负责实现 } } |
关于抽象类的若干种疑问?
· 抽象类能否使用final定义?
不能,因为抽象类必须有子类,final定义的类太监类,不能有子类;
· 抽象类之中能否包含构造方法?
可以,因为抽象类之中除了包含抽象方法之外,还包含了普通方法和属性,而属性一定要在构造方法执行完毕之后才可以进行初始化操作;
· 抽象类之中能否不包含抽象方法?
可以,抽象类之中可以没有抽象方法,但是反过来讲,如果有抽象方法,则一定是抽象类,即使抽象类之中没有抽象方法,也不能够被直接实例化;
· 抽象类能否使用static声明?
abstract class A { private String info = "Hello World ." ; static abstract class B {// 外部类 public abstract void print() ; } } class Impl extends A.B { public void print() { System.out.println("Hello MLDN .") ; } } public class Test { public static void main(String args[]) { A.B b = new Impl() ; b.print() ; } } |
3.8.2、抽象类的应用 ——模板设计模式(体会)
下面首先通过一个简单的程序来分析一下,例如:现在有三种类型:狗、机器人、人;·狗具备三种功能:吃、睡、跑;
·机器人具备两个功能:吃、工作;
·人具备四个功能:吃、睡、跑、工作。
现在就要求设计一个程序,可以让这三类不同的类型,进行工作。现在给出的三个类实际上并没有任何的联系,唯一的联系就是在于一些行为上。
abstract class Action { public static final int EAT = 1 ; public static final int SLEEP = 3 ; public static final int WORK = 5 ; public static final int RUN = 7 ; public void order(int flag) { switch (flag) { case EAT : this.eat() ; break ; case SLEEP: this.sleep() ; break ; case WORK : this.work() ; break ; case RUN : this.run() ; break ; case EAT + SLEEP + RUN : this.eat() ; this.sleep() ; this.run() ; break ; case EAT + WORK : this.eat() ; this.work() ; break ; case EAT + SLEEP + RUN + WORK : this.eat() ; this.sleep() ; this.run() ; this.work() ; break ; } } public abstract void eat() ; public abstract void sleep() ; public abstract void run() ; public abstract void work() ; } class Dog extends Action { public void eat() { System.out.println("小狗在吃。") ; } public void sleep() { System.out.println("小狗在睡。") ; } public void run() { System.out.println("小狗在跑步。") ; } public void work() {} } class Robot extends Action { public void eat() { System.out.println("机器人喝油。") ; } public void sleep() {} public void run() {} public void work() { System.out.println("机器人在工作。") ; } } class Person extends Action { public void eat() { System.out.println("人在吃饭。") ; } public void sleep() { System.out.println("人在睡觉。") ; } public void run() { System.out.println("人在跑步。") ; } public void work() { System.out.println("人在工作。") ; } } public class Test { public static void main(String args[]) { Action act1 = new Dog() ; act1.order(Action.EAT + Action.SLEEP + Action.RUN) ; Action act2 = new Robot() ; act2.order(Action.EAT + Action.WORK) ; } } |
3.9、接口(重点)
3.9.1、接口的基本概念
接口属于一种特殊的类,如果一个类定义的时候全部由抽象方法和全局常量所组成的话,那么这种类就称为接口,但是接口是使用interface关键字进行定义的。interface A { // 定义接口 public static final String INFO = "Hello World ." ; public abstract void print() ; } interface B { public abstract void get() ; } |
· 每一个接口必须定义子类,子类使用implements关键字实现接口;
· 接口的子类(如果不是抽象类)则必须覆写接口之中所定义的全部抽象方法;
· 利用接口的子类,采用对象的向上转型方式,进行接口对象的实例化操作。
下面给出子类实现接口的语法格式:
class 子类 [extends 父类] [implemetns 接口1,接口2,...] {} |
范例:让子类实现接口
interface A { // 定义接口 public static final String INFO = "Hello World ." ; public abstract void print() ; } interface B { public abstract void get() ; } class X implements A,B { // 同时实现了两个接口 public void print() { // 方法覆写 System.out.println("Hello World .") ; } public void get() { System.out.println(INFO) ; } } public class Test { public static void main(String args[]) { A a = new X() ; B b = new X() ; a.print() ; b.get() ; } } |
interface A { // 定义接口 public static final String INFO = "Hello World ." ; public abstract void print() ; } interface B { public abstract void get() ; } abstract class C { public abstract void fun() ; } class X extends C implements A,B { // 同时实现了两个接口 public void print() { // 方法覆写 System.out.println("Hello World .") ; } public void get() { System.out.println(INFO) ; } public void fun() { System.out.println("世界,你好!") ; } } public class Test { public static void main(String args[]) { A a = new X() ; B b = new X() ; C c = new X() ; a.print() ; b.get() ; c.fun() ; } } |
完整定义: | 简化定义: |
interface A { // 定义接口 public static final String INFO = "Hello World ." ; public abstract void print() ; } | interface A { // 定义接口 public String INFO = "Hello World ." ; public void print() ; } |
在Java之中每一个抽象类都可以实现多个接口,但是反过来讲,一个接口却不能继承抽象类,可是Java之中,一个接口却可以同时继承多个接口,以实现接口的多继承操作。
interface A { public void printA() ; } interface B { public void printB() ; } interface C extends A,B { // 一个接口继承了多个接口 public void printC() ; } class X implements C { public void printA() {} public void printB() {} public void printC() {} } |
interface A { public void printA() ; static interface B { // 外部接口 public void printB() ; } } class X implements A.B { public void printB() { System.out.println("Hello World .") ; } } public class Test { public static void main(String args[]) { A.B temp = new X() ; temp.printB() ; } } |
·制订操作标准;
·表示一种能力;
·将服务器端的远程方法视图暴露给客户端。
3.9.2、使用接口定义标准
在日常的生活之中,接口这一名词经常听到的,例如:USB接口、打印接口、充电接口等等。现在假设每一个USB设备只有两个功能:安装驱动程序、工作。
范例:定义出一个USB的标准
interface USB { // 操作标准 public void install() ; public void work() ; } |
class Computer { public void plugin(USB usb) { usb.install() ; usb.work() ; } } |
class Phone implements USB { public void install() { System.out.println("安装手机驱动程序。") ; } public void work() { System.out.println("手机与电脑进行工作。") ; } } |
class Print implements USB { public void install() { System.out.println("安装打印机驱动程序。") ; } public void work() { System.out.println("进行文件打印。") ; } } |
interface USB { // 操作标准 public void install() ; public void work() ; } class Computer { public void plugin(USB usb) { usb.install() ; usb.work() ; } } class Phone implements USB { public void install() { System.out.println("安装手机驱动程序。") ; } public void work() { System.out.println("手机与电脑进行工作。") ; } } class Print implements USB { public void install() { System.out.println("安装打印机驱动程序。") ; } public void work() { System.out.println("进行文件打印。") ; } } public class Test { public static void main(String args[]) { Computer c = new Computer() ; c.plugin(new Phone()) ; // USB usb = new Phone() ; c.plugin(new Print()) ; } } |
3.9.3、接口的实际作用 ——工厂设计模式(Factory)
下面首先来观察如下的程序代码。interface Fruit { public void eat() ; } class Apple implements Fruit { public void eat() { System.out.println("吃苹果。") ; } } class Orange implements Fruit { public void eat() { System.out.println("吃橘子。") ; } } public class Test { public static void main(String args[]) { Fruit f = new Apple() ; f.eat() ; } } |
之前一直在强调,主方法或者是主类是一个客户端,客户端的操作应该越简单越好。但是现在的程序之中,有一个最大的问题:客户端之中,一个接口和一个固定的子类绑在一起了。
在本程序之中,最大的问题在于耦合上,发现在主方法之中,一个接口和一个子类紧密耦合在一起,这种方式比较直接,可以简单的理解为由:A è B,但是这种紧密的方式不方便于维护,所以后来使用了A
è C è B,中间经历了一个过渡,这样一来B去改变,C去改变,但是A不需要改变,就好比JAVA的JVM一样:程序 è JVM è 操作系统。
范例:修改代码
interface Fruit { public void eat() ; } class Apple implements Fruit { public void eat() { System.out.println("吃苹果。") ; } } class Orange implements Fruit { public void eat() { System.out.println("吃橘子。") ; } } class Factory { public static Fruit getInstance(String className) { if ("apple".equals(className)) { return new Apple() ; } if ("orange".equals(className)) { return new Orange () ; } return null ; } } public class Test { public static void main(String args[]) { Fruit f = Factory.getInstance(args[0]) ; f.eat() ; } } |
3.9.4、接口的实际作用 ——代理设计模式(Proxy)
张金宇的不幸人生。。。555555。interface Subject { // 操作主题 public void get() ; // 要银子 } class RealSubject implements Subject { // 真正的要银子 public void get() { System.out.println("真实业务主题") ; } } class ProxySubject implements Subject { private Subject sub = null ; public ProxySubject(Subject sub) { this.sub = sub ; } public void prepare() { System.out.println("准备操作。") ; } public void destroy() { System.out.println("收尾操作。") ; } public void get() { this.prepare() ; this.sub.get() ; this.destroy() ; } } public class Test { public static void main(String args[]) { Subject sub = new ProxySubject(new RealSubject()) ; sub.get() ; } } |
3.9.5、抽象类和接口的区别(面试题)
通过如上的分析,感觉抽象类和接口在使用上似乎区别不大,那么下面就通过一个表格给出这两者的区别。(面试题:请解释抽象类和接口的区别?)
No. | 区别 | 抽象类 | 接口 |
1 | 定义关键字 | abstract class | interface |
2 | 组成 | 常量、变量、抽象方法、普通方法、构造方法 | 全局常量、抽象方法 |
3 | 权限 | 可以使用各种权限 | 只能是public |
4 | 关系 | 一个抽象类可以实现多个接口 | 接口不能够继承抽象类,却可以继承多接口 |
5 | 使用 | 子类使用extends继承抽象类 | 子类使用implements实现接口 |
抽象类和接口的对象都是利用对象多态性的向上转型,进行接口或抽象类的实例化操作 | |||
6 | 设计模式 | 模板设计模式 | 工厂设计模式、代理设计模式 |
7 | 局限 | 一个子类只能够继承一个抽象类 | 一个子类可以实现多个接口 |
到此时已经学习过了:对象、类、抽象类、接口、继承、实现等等,这些都属于什么样的关系呢?
接口就是在类的基础之上的进一步具体的抽象。
4、总结
1、 继承性用于扩充类的功能;2、 方法的覆写与对象多态性的联系;
3、 final关键字的使用;
4、 单例设计模式;
5、 对象的多态性,转型问题;
6、 抽象类和接口的概念;
7、 今天的三个设计模式(背下结构):单例、工厂、代理。
5、预习任务
匿名内部类、Object类、异常的捕获及处理、包及访问控制权限。6、作业
No. | 表达式 | 描述 |
1 | ||
2 | ||
3 | ||
4 | ||
5 | ||
6 | ||
7 | ||
8 | ||
9 |
No. | 方法名称 | 类型 | 描述 |
1 | |||
2 | |||
3 | |||
4 | |||
5 | |||
6 | |||
7 | |||
8 | |||
9 |
相关文章推荐
- JAVA学习笔记——面向对象05
- 黑马程序员——05Java面向对象1
- Java语言基础{Java_se(05)}-面向对象-封装-构造方法-成员变量,局部变量
- Java千百问_05面向对象(006)_is-a,has-a,like-a是什么
- 黑马程序员_java编程基础05面向对象
- (11)Java学习笔记——面向对象05——final关键字 / 抽象类
- Java千百问_05面向对象(014)_如何获取范型的类Class
- 黑马程序员_JAVA笔记05 ——面向对象1(类与对象关系、封装、构造函数、this)
- Java千百问_05面向对象(007)_java类的继承有什么意义
- java笔记——初识面向对象05接口
- java基础总结05-面向对象1
- Java千百问_05面向对象(011)_引用传递和值传递有什么区别
- 黑马程序员 05 Java基础教学 - 05 - 面向对象(1) 之 类、对象、封装
- java学习之旅32--面向对象_05_程序执行过程的内存分析_02
- Java_面向对象05 static关键字
- Java千百问_05面向对象(005)_接口和抽象类有什么差别
- Java千百问_05面向对象(012)_泛型是什么
- Java千百问_05面向对象(008)_java中覆盖是什么
- java学习05-面向对象(类)
- 黑马程序员---java基础---05面向对象(下)