Java面向对象(八)

目录

二十四、abstract 关键字

  1. abstract 可以用来修饰的结构:类、方法。
  2. abstract 修饰类:抽象类
    • 此类不能实例化
    • 抽象类中一定有构造器,便于子类实例化时调用。(涉及:子类对象实例化的全过程)
    • 开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作。
  3. abstract 修饰方法:抽象方法
    • 抽象方法只有方法的声明,没有方法体
    • 包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法的
    • 若子类重写了父类中的所有的抽象方法后,此子类方可实例化。若子类没有重写父类中的所有的抽象方法,则此子类也是一个抽象类,需要使用abstract修饰
  4. 注意:
    • abstract 不能用来修饰:属性、构造器等结构。
    • abstract 不能用来修饰私有方法、静态方法、final 的方法、final 的类。

创建抽象类的匿名子类对象

public class PersonTest {	
	public static void main(String[] args) {	
	
		//非匿名的类非匿名的对象
		Worker worker = new Worker();
		method1(worker);
		//非匿名的类匿名的对象
		method1(new Worker());
		//创建了一匿名子类的非匿名对象:p
		Person p = new Person(){
			@Override
			public void eat() {
				System.out.println("吃东西");
			}
			@Override
			public void breath() {
				System.out.println("好好呼吸");
			}			
		};		
		method1(p);
		//创建匿名子类的匿名对象
		method1(new Person(){
			@Override
			public void eat() {
				System.out.println("吃好吃东西");
			}
			@Override
			public void breath() {
				System.out.println("好好呼吸新鲜空气");
			}
		});
	}		
	public static void method1(Person p){
		p.eat();
		p.breath();
	}	
}
class Worker extends Person{
	@Override
	public void eat() {
	}
	@Override
	public void breath() {
	}	
}

二十五、模板方法设计模式(TemplateMethod)

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

  2. 解决的问题:

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

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

  3. 例子:

public class TemplateTest {
	public static void main(String[] args) {
		
		SubTemplate t = new SubTemplate();
		
		t.spendTime();
	}
}

abstract class Template{
	
	//计算某段代码执行所需要花费的时间
	public void spendTime(){
		
		long start = System.currentTimeMillis();
		
		this.code();//不确定的部分、易变的部分
		
		long end = System.currentTimeMillis();
		
		System.out.println("花费的时间为:" + (end - start));
		
	}
	
	public abstract void code();
}

class SubTemplate extends Template{

	@Override
	public void code() {
		
		for(int i = 2;i <= 1000;i++){
			boolean isFlag = true;
			for(int j = 2;j <= Math.sqrt(i);j++){
				if(i % j == 0){
					isFlag = false;
					break;
				}
			}
			if(isFlag){
				System.out.println(i);
			}
        }
	}	
}
  1. 常用场景:
    • 数据库访问的封装
    • Junit 单元测试
    • JavaWeb 的 Servlet 中关于 doGet/doPost 方法调用
    • Hibernate 中模板程序
    • Spring 中 JDBCTemlate、HibernateTemplate 等

二十六、接口(interface)

26.1 接口简介:

  1. 接口使用关键字 interface 来定义

  2. Java中,接口和类是并列的两个结构。主要解决 Java 中不能实现多重继承的(即一个子类不能同时继承于多个父类)问题。

26.2 接口的定义和实现:

  1. JDK7 及以前:只能定义全局常量和抽象方法。

    •   	全局常量:public static final 。但是书写时,可以省略不写。
      
    •   	抽象方法:public abstract 。也可省略不写。
      
  2. JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法

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

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

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

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

interface i {
    // 全局变量
    public static final int MAX_NUM = 100;	//完整写法
	int MIN_NUM = 1; //省略了public static final
    
    // 抽象方法
    public abstract void show();
	//省略了public abstract
	void change();
    
}

class I implements i {

	@Override
	public void show() {
		System.out.println("调用i接口的show方法");
	}

	@Override
	public void change() {
		System.out.println("调用i接口的change方法");
	}
	
}
// 实现类没有覆盖接口中所有的抽象方法
abstract class I2 implements i{

	@Override
	public void show() {
		
	}
	
}

public class InterfaceTest {
	public static void main(String[] args) {
		System.out.println(i.MAX_NUM);
		System.out.println(i.MIN_NUM);
//		i.MIN_NUM = 2;
		
		I i2 = new I();
		i2.show();
	}
}
  1. Java类可以实现多个接口 —> 弥补了Java单继承性的局限性

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

    格式:class AA extends BB implements CC,DD,EE

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

class Bullet extends Object implements CC,DD{
    
	@Override
	public void method1() {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void method2() {
		// TODO Auto-generated method stub
		
	}

interface AA{
	void method1();
}
interface BB{	
	void method2();
}
interface CC extends AA,BB{
}
interface DD{
}

26.3 接口的使用:

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

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

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

  4. 举例:JDBC

public class USBTest {
	public static void main(String[] args) {
		
		Computer com = new Computer();
		
		Flash flash = new Flash();
		com.transferData(flash);
		
	}
}

class Computer{
	
	public void transferData(USB usb){	// USB usb = new Flash();
		usb.start();
		
		System.out.println("具体传输数据的细节");
		
		usb.stop();
	}
	
	
}

interface USB{
	//常量:定义了长、宽、最大最小的传输速度等
	
	void start();
	
	void stop();
	
}

class Flash implements USB{

	@Override
	public void start() {
		System.out.println("U盘开启工作");
	}

	@Override
	public void stop() {
		System.out.println("U盘结束工作");
	}
	
}

class Printer implements USB{
	@Override
	public void start() {
		System.out.println("打印机开启工作");
	}

	@Override
	public void stop() {
		System.out.println("打印机结束工作");
	}
	
}

26.4 接口的实现类的对象创建(例子)

public class USBTest {
	public static void main(String[] args) {
		
		Computer com = new Computer();
		//1.创建了接口的非匿名实现类的非匿名对象
		Flash flash = new Flash();
		com.transferData(flash);
		
		//2. 创建了接口的非匿名实现类的匿名对象
		com.transferData(new Printer());
		
		//3. 创建了接口的匿名实现类的非匿名对象
		USB phone = new USB(){

			@Override
			public void start() {
				System.out.println("手机开始工作");
			}

			@Override
			public void stop() {
				System.out.println("手机结束工作");
			}
			
		};
		com.transferData(phone);
		
		
		//4. 创建了接口的匿名实现类的匿名对象		
		com.transferData(new USB(){
			@Override
			public void start() {
				System.out.println("mp3开始工作");
			}

			@Override
			public void stop() {
				System.out.println("mp3结束工作");
			}
		});
	}
}

class Computer{
	
	public void transferData(USB usb){//USB usb = new Flash();
		usb.start();
		
		System.out.println("具体传输数据的细节");
		
		usb.stop();
	}
	
	
}

interface USB{
	//常量:定义了长、宽、最大最小的传输速度等
	void start();	
	void stop();
	
}

class Flash implements USB{

	@Override
	public void start() {
		System.out.println("U盘开启工作");
	}

	@Override
	public void stop() {
		System.out.println("U盘结束工作");
	}
	
}

class Printer implements USB{
	@Override
	public void start() {
		System.out.println("打印机开启工作");
	}

	@Override
	public void stop() {
		System.out.println("打印机结束工作");
	}
	
}

26.5 接口应用:代理模式(Proxy)

  1. 概述: 代理模式是Java开发中使用较多的一种设计模式。

    代理设计就是为其他对象提供一种代理以控制对这个对象的访问。

public class StaticProxyTest {

	public static void main(String[] args) {
		Star s = new Proxy(new RealStar());
		s.confer();
		s.signContract();
		s.bookTicket();
		s.sing();
		s.collectMoney();
	}
}

interface Star {
	void confer();// 面谈

	void signContract();// 签合同

	void bookTicket();// 订票

	void sing();// 唱歌

	void collectMoney();// 收钱
}

class RealStar implements Star {	// 被代理类

	public void confer() {
	}

	public void signContract() {
	}

	public void bookTicket() {
	}

	public void sing() {
		System.out.println("明星:歌唱~~~");
	}

	public void collectMoney() {
	}
}

class Proxy implements Star {	// 代理类
	private Star real;

	public Proxy(Star real) {
		this.real = real;
	}

	public void confer() {
		System.out.println("经纪人面谈");
	}

	public void signContract() {
		System.out.println("经纪人签合同");
	}

	public void bookTicket() {
		System.out.println("经纪人订票");
	}

	public void sing() {
		real.sing();
	}

	public void collectMoney() {
		System.out.println("经纪人收钱");
	}
}
  1. 应用场景:

    • 安全代理:屏蔽对真实角色的直接访问。

    • 远程代理:通过代理类处理远程方法调用(RMI)

    • 延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象

      比如你要开发一个大文档查看软件,大文档中有大的图片,有可能一个图片有100MB,在打开文件时,不可能将所有的图片都显示出来,这样就可以使用代理模式,当需要查看图片时,用proxy来进行大图片的打开。

  2. 分类 :

  • 静态代理(静态定义代理类)
  • 动态代理(动态生成代理类):JDK自带的动态代理,需要反射等知识

26.6 特殊例子:

example one:

interface A {
	int x = 0;
}

class B {
	int x = 1;
}

class C extends B implements A {
	public void pX() {
		// The field x is ambiguous(编译不通过。因为x是不明确的)
		// System.out.println(x);
		System.out.println(super.x);// 1
		System.out.println(A.x);// 0,全局常量
		
	}

	public static void main(String[] args) {
		new C().pX();
	}
}

example two:

interface Playable {
	void play();
}
interface Bounceable {
	void play();
}
interface Rollable extends Playable, 
	Bounceable {
	Ball ball = new Ball("PingPang");
}
class Ball implements Rollable {
	private String name;
	public String getName() {
		return name;
	}
	public Ball(String name) {
		this.name = name;
	}
	public void play() {
		ball = new Ball("Football");	// 编译报错,在接口中已经定义了ball对象,所以该对象为 public static final,此处违反 final 不能重新赋值
		System.out.println(ball.getName());
	}
}

26.7 Java8 接口新特性

  1. JDK8:除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法

    • 静态方法:使用 static 关键字修饰。可以通过接口直接调用静态方法,并执行其方法体。我们经常在相互一起使用的类中使用静态方法。
    • 默认方法:默认方法使用 default 关键字修饰。可以通过实现类对象来调用。 我们在已有的接口中提供新方法的同时,还保持了与旧版本代码的兼容性。
  2. 注意点:
    (1)接口中定义的静态方法,只能通过接口来调用。

    (2)通过实现类的对象,可以调用接口中的默认方法。如果实现类重写了接口中的默认方法,调用时,仍然调用的是重写以后的方法

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

    (4)如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,那么在实现类没有重写此方法的情况下,报错。–>接口冲突。需要在实现类重写该方法。

    (5)在子类(或实现类)的方法中调用父类、接口中被重写的方法。

    • 调用自己重写的方法:方法名();
    • 调用父类的方法:super.方法名();
    • 调用接口的默认方法:接口名.super.方法名();

例子:

public interface CompareA {
	
	//静态方法
	public static void method1(){
		System.out.println("CompareA:北京");
	}
	//默认方法
	public default void method2(){
		System.out.println("CompareA:上海");
	}
	
	default void method3(){
		System.out.println("CompareA:上海");
	}
}


public interface CompareB {
	
	default void method3(){
		System.out.println("CompareB:上海");
	}
	
}

public class SuperClass {
	
	public void method3(){
		System.out.println("SuperClass:北京");
	}	
}

public class SubClassTest {
	
	public static void main(String[] args) {
		SubClass s = new SubClass();
		
//		s.method1();
//		SubClass.method1();
		//知识点1:接口中定义的静态方法,只能通过接口来调用。
		CompareA.method1();
		//知识点2:通过实现类的对象,可以调用接口中的默认方法。
		//如果实现类重写了接口中的默认方法,调用时,仍然调用的是重写以后的方法
		s.method2();
		//知识点3:如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的默认方法,
		//那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法。-->类优先原则
		//知识点4:如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,
		//那么在实现类没有重写此方法的情况下,报错。-->接口冲突。
		//这就需要我们必须在实现类中重写此方法
		s.method3();
		
	}
	
}

class SubClass extends SuperClass implements CompareA,CompareB{
	
	public void method2(){
		System.out.println("SubClass:上海");
	}
	
	public void method3(){
		System.out.println("SubClass:深圳");
	}
	
	//知识点5:如何在子类(或实现类)的方法中调用父类、接口中被重写的方法
	public void myMethod(){
		method3();//调用自己定义的重写的方法
		super.method3();//调用的是父类中声明的
		//调用接口中的默认方法
		CompareA.super.method3();
		CompareB.super.method3();
	}
}

26.8 接口与抽象类的区别

NO. 区别点 抽象类 接口
1 定义 包含抽象方法的类 主要是抽象方法和全局常量的集合
2 组成 构造方法、抽象方法、普通方法、 常量、变量 常量、抽象方法、(jdk8.0:默认方法、静态方法)
3 使用 子类继承抽象类(extends) 子类实现接口(implements)
4 关系 抽象类可以实现多个接口 接口不能继承抽象类,但允许继承多个接口
5 常见设计模式 模板方法 简单工厂、工厂方法、代理模式
6 对象 都通过对象的多态性产生实例化对象
7 局限 抽象类有单继承的局限 接口没有此局限
8 实际 作为一个模板 是作为一个标准或是表示一种能力
9 选择 如果抽象类和接口都可以使用的话,优先使用接口,因为避免单继承的局
  • 在开发中,常看到一个类不是去继承一个已经实现好的类,而是要么继承抽象类, 要么实现接口。