Java的面向对象

19-抽象类

2021-07-06 506 1

简介 抽象类、抽象方法、接口、代理模式等相关介绍


1.  抽象类和抽象方法

    随着继承层次中一个个新子类的定义,子类变得越来越具体,而父类则更一般更通用。类的设计应该保证父类和子类能够共享特征。有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类。


    abstract关键字的使用

    abstract英文翻译抽象的

    abstract可以用来修饰的结构有类和方法


    抽象类

    abstract修饰类

    此类不能实例化

    抽象类中一定有构造器,子类实例化时调用(涉及:子类对象实例化的全过程)

    开发中,都会提供抽象类的子类,让子类实例化,完成相关的操作


    抽象方法

    abstract修饰方法

    抽象方法只有方法的声明,没有方法体(例如 public abstract void eat();)

    包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法。

    若子类重写了父类中的所有的抽象方法后,此子类方可实例化

    若子类没有重写父类(包含间接父类)中的所有的抽象方法,则此子类需要声明为抽象类,需要使用abstract修饰。(类中的方法没有方法体,就不能实例化)


    abstract使用上的注意点

    abstract不能用来修饰:属性、构造器等结构

    abstract不能用来修饰私有方法、静态方法、final的方法、final的类

    私有方法不能再被重写(子类对父类的private方法不可见,不能重写,如果声明为抽象的,就无法重写了),与abstract冲突

    abstract修饰的方法是希望子类被重写

    静态方法:静态方法是属于类的,随着类加载而加载,类可以直接调用静态方法(如果声明为抽象的,就不能调用了,那就与静态方法矛盾了)

    final的方法、final的类,

    final与abstract功能完全是冲突的。final类不可再继承、final方法不可再重写,而abstract类希望被继承、abstract方法希望被重写。


upfile


//AbstractTest.java
package com.ylaihui.oop11;

abstract class Creature{
    	public abstract void breath();
}

abstract class Person extends Creature{
    	String name;
	int age;
	
	Person(){}
	Person(String name, int age){
		this.name = name;
		this.age = age;
	}
	public abstract void eat();
}

class Student extends Person{
	String major;

	Student(){}
	Student(String name, int age, String major){
		super(name,age);
		this.major = major;
	}
	
	public void eat() {
		System.out.println("Student eat()...");
	}

	public void breath() {
		System.out.println("Student breath()...");
	}
	
}

public class AbstractTest {
	// Cannot instantiate the type Person
//	Person p = new Person();
	
	Student s = new Student();
	
}

2. 抽象类的匿名子类

    抽象类的匿名子类(有对象名的形式)

    抽象类匿名子类的匿名对象(类名没有,对象名也没有的情况)

//OrderTest.java
package com.ylaihui.oop11;

abstract class Order{
    	public abstract void show();
}

public class OrderTest {
	
	public static void method(Order o){
		o.show();
	}
	public static void main(String[] args) {
		
		// Cannot instantiate the type Order
//		Order o = new Order();
		
		// Order o = new Order(){};  -- {} 中 提供 抽象类 Order 需要重写的方法
		//创建了一匿名子类的对象:o
		Order o = new Order(){
			@Override
			public void show() {
				System.out.println("this is book order.");
			}
		};
        System.out.println(o.getClass());
        System.out.println(o.getClass().getSuperclass());
		
		// 多态, 形参是 Order类型,实参是 Order类的子类对象, Order类的子类是匿名的,对象名是 o
		method(o);
		//-----------------------------------------
		//创建匿名子类的匿名对象
		//method(new Order());  // Cannot instantiate the type Order
		//method(new Order(){}); // {} 中提供需要重写的方法  
		method(new Order(){
			@Override
			public void show() {
				System.out.println("this is computer order.");
			}
		});
	}
}

class com.ylaihui.oop11.OrderTest$1

class com.ylaihui.oop11.Order

this is book order.

this is computer order.


3. 模板方法设计模式

    抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩展、改造,但子类总体上会保留抽象类的行为方式。

    

    解决的问题:

    当功能内部一部分实现是确定的, 一部分实现是不确定的。这时可以把不确定的部分暴露出去,让子类去实现。

    例如在软件开发中实现一个算法时,整体步骤很固定、通用,这些步骤已经在父类中写好了。但是某些部分易变,易变部分可以抽象出来,供不同子类实现。这就是一种模板模式。提供给你一个模板,你按模板“干活”


    各个抽象类以及抽象方法就是模板, 子类需要提供具体的实现

    模板方法设计模式是编程中经常用得到的模式。 各个框架、 类库中都有他的影子, 比如常见的有:

    数据库访问的封装

    Junit单元测试

    JavaWeb的Servlet中关于doGet/doPost方法调用

    Hibernate中模板程序

    Spring中JDBCTemlate、 HibernateTemplate等

    

upfile  


代码示例:

//TemplateTest.java
package com.ylaihui.oop11;

//抽象类的应用:模板方法的设计模式
public class TemplateMethodTest {

    	public static void main(String[] args) {
    		System.out.println("----customer A----");
    		BankTemplateMethod btm = new DrawMoney();
		btm.process();

		System.out.println("----customer B----");
		BankTemplateMethod btm2 = new ManageMoney();
		btm2.process();
	}
}
abstract class BankTemplateMethod {
	// 具体方法
	public void takeNumber() {
		System.out.println("取号排队");
	}

	public abstract void transact(); // 办理具体的业务 //钩子方法

	public void evaluate() {
		System.out.println("反馈评分");
	}

	// 模板方法,把基本操作组合到一起,子类一般不能重写
	public final void process() {
		this.takeNumber();

		this.transact();// 像个钩子,具体执行时,挂哪个子类,就执行哪个子类的实现代码(回调方法)

		this.evaluate();
	}
}

class DrawMoney extends BankTemplateMethod {
	public void transact() {
		System.out.println("我要取款!!!");
	}
}

class ManageMoney extends BankTemplateMethod {
	public void transact() {
		System.out.println("我要理财!我这里有2000万美元!!");
	}
}

  

4. 接口(interface)

    接口出现的背景

    一方面, 有时必须从几个类中派生出一个子类, 继承它们所有的属性和方法。 但是, Java不支持多重继承。 有了接口, 就可以得到多重继承的效果。

    另一方面, 有时必须从几个类中抽取出一些共同的行为特征,而它们之间又没有is-a的关系,仅仅是具有相同的行为特征而已。例如:鼠标、键盘、打印机、扫描仪、摄像头、充电器、 MP3机、手机、数码相机、移动硬盘等都支持USB连接。

    

    关联知识解释:

    is - a 衡量的是子父类的关系,比如学生继承人, 学生是一个人

    继承是一个"是不是"的关系,而接口实现则是 "能不能"的关系。

    has-a  表示组合,包含关系。比如兔子有腿、头等,不能说兔子腿是属于一种兔子(不能说是继承关系)

    接口就是规范,定义的是一组规则,继承是一个"是不是"的关系,而接口实现则是 "能不能"的关系。

    接口的本质是契约,标准,规范,就像我们的法律一样,制定好后大家都要遵守。

    接口和类是并列关系

upfile


    接口的使用

    1.接口使用interface来定义

    2.Java中,接口和类是并列的两个结构

    3.接口中只能定义全局常量和抽象方法(JDK7及以前)

    全局常量:public static final修饰的的,但是书写时,可以省略不写

    抽象方法:public abstract修饰的

    JDK8开始,接口中可以定义静态方法和默认方法

    静态方法: 使用 static 关键字修饰。 可以通过接口直接调用静态方法,并执行其方法体。我们经常在相互一起使用的类中使用静态方法。可以在标准库中找到像Collection/Collections或者Path/Paths这样成对的接口和类。

    默认方法: 默认方法使用 default 关键字修饰。可以通过实现类对象来调用。在已有的接口中提供新方法的同时,还保持了与旧版本代码的兼容性。

    4. 接口中不能定义构造器的,意味着接口不可以实例化

    5. Java开发中,接口通过让类去实现(implements)的方式来使用.

    如果实现类覆盖了接口中的所有抽象方法,则此实现类就可以实例化

    如果实现类没有覆盖接口中所有的抽象方法,则此实现类仍为一个抽象类

//InterfaceTest.java
package com.ylaihui.oop12;

interface Moveable{
    	public static final int MIN_SPEED = 0;
    	int MAX_SPEED = 150;  // 省略了 public static final
    	
    	public abstract void move();
    	void stop(); // 省略 public abstract 
	
	// Interfaces cannot have constructors
//	Moveable(){
//		
//	}
}

class Person implements Moveable{
	@Override
	public void move() {
		System.out.println("Person walk...");
	}

	@Override
	public void stop() {
		System.out.println("Person stop...");		
	}
}

// 只实现了接口中的部分抽象方法,那么本类要定义为 abstract
abstract class Animal implements Moveable{
	@Override
	public void move() {
		
	}	
}

public class InterfaceTest {
	public static void main(String[] args) {
		System.out.println(Moveable.MAX_SPEED);
		System.out.println(Moveable.MIN_SPEED);
		
//		Moveable.MIN_SPEED = 100; // The final field Moveable.MIN_SPEED cannot be assigned
		Person person = new Person();
		
	}
}


5. 接口的多继承

    1. 接口与接口之间可以继承,而且可以多继承  

    1. 接口的具体使用,体现多态性

    3. 接口实际上可以看做是一种规范

    * 面试题:抽象类与接口有哪些异同?

    定义Java类的语法格式: 先写extends,后写implements

    例如: class SubClass extends SuperClass implements InterfaceA, InterfaceB, InterfaceC{ }

    public class LinkedList<E>

        extends AbstractSequentialList<E>

        implements List<E>, Deque<E>, Cloneable, java.io.Serializable

    一个类可以实现多个接口, 接口也可以继承其它接口。

    实现接口的类中必须提供接口中所有方法的具体实现内容,方可实例化。否则,仍为抽象类。

    接口的主要用途就是被实现类实现。(面向接口编程)

    与继承关系类似,接口与实现类之间存在多态性

    接口和类是并列关系, 或者可以理解为一种特殊的类。 从本质上讲,接口是一种特殊的抽象类,这种抽象类中只包含常量和方法的定义(JDK7.0及之前), 没有变量和方法的实现。JDK8之后,接口中支持静态方法和默认方法,随着JDK版本的迭代,接口设计的越来越像类了。

    

    代码示例:

类图

upfile


//USBTest.java
package com.ylaihui.oop12;

interface USB{
    	void start();
    	void stop();
}

class FlashMemory implements USB{

	@Override
	public void start() {
		System.out.println("flash memory start!");
	}

	@Override
	public void stop() {
		System.out.println("flash memory stop!");		
	}
}

class Printer implements USB{
	@Override
	public void start() {		
		System.out.println("Printer stop!");		
	}

	@Override
	public void stop() {		
		System.out.println("Printer stop!");		
	}
}

class Computer{
	public void transdata(USB usb){
		usb.start();
		System.out.println("computer trans data...");
		usb.stop();
	}
}

public class USBTest {
	public static void main(String[] args) {
		Computer c = new Computer();
		c.transdata(new Printer());
		
		c.transdata(new FlashMemory());
	}

}

Printer stop!

computer trans data...

Printer stop!

flash memory start!

computer trans data...

flash memory stop!


6. JDK8中的接口-静态方法和默认方法


    Java8中,可以为接口添加静态方法和默认方法。从技术角度来说,这是完全合法的,只是它看起来违反了接口作为一个抽象定义的理念。

    接口中的静态方法: 使用 static 关键字修饰。 可以通过接口直接调用静态方法,并执行其方法体。我们经常在相互一起使用的类中使用静态方法。可以在标准库中找到像Collection/Collections或者Path/Paths这样成对的接口和类。

    接口中的默认方法: 默认方法使用 default 关键字修饰。可以通过实现类对象来调用。在已有的接口中提供新方法的同时,还保持了与旧版本代码的兼容性。

    比如: java 8 API中对Collection、 List、 Comparator等接口提供了丰富的默认方法。

    接口中定义的静态方法,只能通过接口来调用,不能通过实现类或其对象来调用,说白了,接口中的静态方法不是给实现类用的

    通过实现类的对象,可以调用接口中的默认方法

    如果实现类重写了接口中的默认方法,调用时,仍然调用的是重写以后的方法


    类优先原则:

    如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的默认方法,那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法,而非调用接口中的方法。

    接口冲突:

    如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,那么在实现类没有重写此方法的情况下报错。这就需要我们必须在实现类中重写此方法

    ---- Duplicate default methods named defaultMethod3 with the parameters () and () are inherited from the types InterfaceB and InterfaceA

    如何在子类(或实现类)的方法中调用父类、接口中被重写的方法  --- 例如 CompareA.super.method3();


代码示例

upfile

//InterfaceA.java
package com.ylaihui.oop13;

public interface InterfaceA {
   // 静态方法
   public static void staticMethod(){
      System.out.println("interface staticMethod...");
   }
   // 默认方法
   public default void defaultMethod(){
      System.out.println("interface defaultMethod...");
   }
   public default void defaultMethod1(){
      System.out.println("interface defaultMethod1...");
   }
   public default void defaultMethod2(){
      System.out.println("interface defaultMethod2...");
   }
   public default void defaultMethod3(){
      System.out.println("interface defaultMethod3...");
   }
}
//InterfaceB.java
package com.ylaihui.oop13;

public interface InterfaceB {
   public default void defaultMethod3(){
      System.out.println("interface defaultMethod3...");
   }
}
//SuperClass.java
package com.ylaihui.oop13;

public class SuperClass {
   public void defaultMethod2(){
      System.out.println("SuperClass defaultMethod...");
   }
}
//SubClassTest.java
package com.ylaihui.oop13;

class subClass extends SuperClass implements InterfaceA,InterfaceB{
   public void defaultMethod1(){
      System.out.println("subClass defaultMethod1...");
   }

   // 如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,那么在实现类没有重写此方法的情况下,报错。-->接口冲突。
   // 这就需要我们必须在实现类中重写此方法
   public void defaultMethod3() {
      System.out.println("subClass defaultMethod3...");
   }
   public void Method(){
      // 在子类(或实现类)的方法中调用父类、接口中被重写的方法
      InterfaceA.super.defaultMethod3();
   }
}

public class SubClassTest {
   public static void main(String[] args) {
      subClass sc = new subClass();
      // 接口中定义的静态方法,只能通过接口来调用,不能通过实现类或其对象来调用
//    sc.staticMethod(); // The method staticMethod() is undefined for the type subClass
      InterfaceA.staticMethod();
      
      // 通过实现类的对象,可以调用接口中的默认方法
      sc.defaultMethod();
      
      // 如果实现类重写了接口中的默认方法,调用时,仍然调用的是重写以后的方法
      sc.defaultMethod1(); // subClass defaultMethod1...

      // 如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的默认方法,那么子类在没有重写此方法的情况下,
      // 默认调用的是父类中的同名同参数的方法,而非调用的接口中的默认方法
      sc.defaultMethod2(); // SuperClass defaultMethod...

   }
}

代码输出

interface staticMethod...

interface defaultMethod...

subClass defaultMethod1...

SuperClass defaultMethod...


点赞 1

文章评论

欢迎您:

纸上得来终觉浅,绝知此事要躬行!

112 文章 38820 浏览 3 评论

联系我

  •   QQ:    361352119
  •  Email:  lisimmy@sina.com
  • 微信: