java接口(十)

本文深入探讨Java中接口与抽象类的区别,包括它们的定义、使用场景及设计模式应用,如工厂模式和代理模式,帮助读者理解两者在实际开发中的角色。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

知识点:

   所有的开发里面可以不实用抽象类,但是没有不使用接口的。

   1.接口的基本概念与主要特点;

   2.接口的各个实用;

   3.工厂设计模式、代理 模式;

具体内容: 

    嘿嘿:如果不会接口,那你别说你会java,如果不会接口,其他任何语言都难以理解。

接口的基本定义:

      如果一个类之中只是由抽象方法和全局常量所组成,那么在这种情况下不会将将其定义为一个抽象类,而只会将其定义为接口,所以所谓的接口严格来讲就属于一个特殊的抽象类,而这个类里面只有抽象方法与全局变量。

    要定义一个接口实用interface关键字完成。

范例:定义接口

interface A{
	public static final String MSG="hello";
	public abstract void perint(); //抽象方法
}

由于接口里面存在有抽象方法,所以接口对像不可能直接使用关键字NEW进行实例化的操作,所以接口的使用原则如下;

  • 接口必须要有子类,但是此时一个子类可以使用implements关键字实现多个接口。
  • 接口的子类(如果不是抽象方法),那么必须覆写接口中的全部抽象方法;
  • 接口对象可以利用子类向上转型进行实例化操作;换言之只能实例化其子类,然后向上转型得到抽象类的实例化对象;

范例:实现接口

package cn.mldn.po;

interface A{
	public static final String MSG="hello";
	public abstract void perint(); //抽象方法
}
 interface B{
	 public abstract void get();
 }
 
 class X implements A,B{

	@Override
	public void get() {
		System.out.println("A接口的抽象方法!");
	}

	@Override
	public void perint() {
		System.out.println("B接口的抽象方法!");
		
	}
	 
 }
public class TestDemo {

	public static void main(String[] args) {
		X x = new X();   //实例化子类对象
		A a = x;     //向上转型
		B b = x;     //向上转型
        a.perint();
        b.get();
	}

}

    以上的代码实例化勒X类对象。现在由于X是A和B的子类,那么X类对象可以变为A接口类或B接口类对象。

package cn.mldn.po;

interface A{
    public static final String MSG="hello";
    public abstract void perint(); //抽象方法
}
 interface B{
     public abstract void get();
 }
 
 class X implements A,B{

    @Override
    public void get() {
        System.out.println("B接口的抽象方法!");
    }

    @Override
    public void perint() {
        System.out.println("A接口的抽象方法!");
        
    }
     
 }
public class TestDemo {

    public static void main(String[] args) {
        A a = new X();   //实例化子类对象
        B b = (B)a;     //向上转型
        b.get();
        System.out.println(a instanceof A);
        System.out.println(a instanceof B);
    }

}

    在定义结构上来讲A和B接口没有任何的直接联系,但是这两个接口却同时拥有同一个子类:X子类,千万不要被它的类型和名称所迷惑,因为最终实例化的是X子类,而这子类属于B类的对象,所以以上的代码就可以行得通,所以以上的代码就可以行得通,只不过从代码的编写上,并不是很好。

     但是要知道一点,对于子类而言,除了接口之外,还有可能会取继承抽象类,所以说如果一个子类同时要继承抽象类和实现接口,那么请先使用extends继承,而后使用implements实现。

package cn.mldn.po;

interface A{
	public static final String MSG="hello";
	public abstract void perint(); //抽象方法
}
 interface B{
	 public abstract void get();
 }
 abstract class C{
	 public abstract void change();
 }
 
 class X extends C implements A,B{

	@Override
	public void get() {
		System.out.println("B接口的抽象方法!");
	}

	@Override
	public void perint() {
		System.out.println("A接口的抽象方法!");
		
	}

	@Override
	public void change() {
		System.out.println("C类的抽象方法!");
		
	}
	 
 }
public class TestDemo {

	public static void main(String[] args) {
		A a = new X();   //实例化子类对象
		B b = (B)a;     //向上转型
        b.get();
        System.out.println(a instanceof A);
        System.out.println(a instanceof B);
	}

}

  对于接口而言,里面的组成就是抽象方法和全局常量,所以很多的时候也有一些人为了省略编写,可以不用写上abstract或public static final,并且在方法上是否编写public结果都是一样的,因为在接口里面只有一种访问权限就是public。以下两个接口的定义最终效果是完全相同的:

interface A{
    public static final String MSG = "HELLO";
    public abstract void fun();
}
interface A{
     String MSG = "HELLO";
     void fun();
}

在接口里面没有写上public,其最终的访问权限也是public,绝对不是defualt. 不过为了方便,强烈建议在接口定义的方法一定写上public。

interface A{
	 String MSG = "HELLO";
	 public void fun();
}

  对于接口的组成。99%的情况下都是以抽象方法为主,很少有接口只是单纯的去定义全局常量。

一个抽象类可以取继承一个抽象类,但是反过来,一个接口却可以使用extends关键字同时继承多个接口(但是接口不能继承抽象类)。

观察:观察接口的多继承


interface A{
	 public void funA();
}

interface B{
	public void funB();
}

//c类接口同时继承A和B两个父亲接口
interface C extends A,B{    //此处使用的是extends
	public void funC();
}
 class X implements C {
	 public void funA(){};
	 public void funB(){};
   	public void funC(){};
 }

从继承关系上讲抽象的限制要比接口多了太多:

  • 一个抽象类只能够继承一个抽象的父类,而接口没有这个限制;
  • 一个子类只能继承一个抽象类,而却可以实现多个接口;

在整个java里面,接口的主要功能是解决但集成局限问题。

  虽然从接口本身的概念上来讲只能够由抽象 方法和全局常量所组成,但是所有的内部结构不受这些要求限制的,也就是说在我们的接口里面可以定义普通内部类、抽象内部类、内部接口。

范例:在接口定义抽象类

interface A{
	public void funA();
	abstract class B{
		public abstract void funB();
	}
}

class X implements A{  //x实现勒接口A
	public void funA(){
		System.out.println("Hello World");
	}
	class Y extends B{   //内部抽象类的子类
		public void funB(){
			
		}
	}
}

范例:在一个接口内部如果使用了static去定义一个内部接口表示的是一个外部接口。

interface A{
	public void funA();
	static interface B{   //外部接口
		public void funB();
	}
}

class X implements A.B{  //x实现勒接口A
	public void funB(){}
}

   大部分情况,只要求清楚内部接口的定义即可。

先期总结:接口在实际的开发之中有三大核心作用:

  • 定义不同层之间的操作标准
  • 表示一种操作能力
  • 表示服务器端的远程方法试图暴露给客户端。

接口的实际应用——标准定义

   电脑上可以使用u盘、Mp3、打印机,这些设别都是连接到usb设备上的。

   所有的代码如果要进行开发,一定首先开发出USB标准接口,因为有了这些标准后,电脑才可以使用这些标准,设备厂商才可以设计出USB设备。

范例:定义USB标准

//标准可以连接不同的操作类
interface USB{    //定义标准一定就是接口
	public void start();
	public void stop();
}

范例:定义电脑

class Computer{
	public void plugin(USB usb){  //插入
		usb.start();     //固定操作
		usb.stop();
	}
}

不管以后会有多少设备,只要它是USB标准的实现子类,就都可以在电脑上使用。

范例:定义U盘

class Flash implements USB{

	@Override
	public void start() {
		System.out.println("u盘开始使用!");
	}

	@Override
	public void stop() {
		System.out.println("u盘停止使用!");
		
	}
	
}

范例:定义打印机

class Print implements USB{
	public void start(){
		System.out.println("打印机开始工作!");
	}

	@Override
	public void stop() {
		System.out.println("打印机停止工作!");
		
	}
}
public class TestDemo {

	public static void main(String[] args) {
		Computer com = new Computer();
		com.plugin(new Flash());
		com.plugin(new Print());
	}

}

此时可以很好的描述出现实的关系。 

在显示生活中,标准概念随处可见,而在程序里面标准就是用来定义接口的。

接口的应用——工厂设计模式(Factory)

   下面首先来观察一段程序代码:

 

package cn.mldn.po;

interface Fruit{
	public void eat();
}

class Apple implements Fruit{
	@Override
	public void eat() {
		System.out.println("吃苹果!");	
	}	
}

public class TestDemo {

	public static void main(String[] args) {
		Fruit f = new Apple();
		f.eat();
	}

}

   以上的程序可以通过主方法得到Fruit接口对象,但是有没有问题呢?

 如果想要确认一个代码是否真的好,有这么几个标准?

  • 客户端调用简单,不需要关注具体的细节;
  • 客户端之外的代码修改,不影响用户的使用,即:用户可以不用去关心代码是否变更。

   本次的程序没有任何的语法错误,但是关键的问题就出现在了关键字“new”上 。一个接口不可能只有一个子类,所以对于Fruit也有可能产生多个子类对象。

class Orange implements Fruit{

	@Override
	public void eat() {
		System.out.println("**吃橘子!");
	}
	
}

  现在的客户端上想要得到这新的子类对象,需要修改代码,修改为:

public class TestDemo {

	public static void main(String[] args) {
		Fruit f = new Orange();
		f.eat();
	}

}

   发现如果现在直接在客户端上产生实例化对象,那么每一次要想更换对象,那么都需要修改客户端上的执行代码。这样的做法明显是不科学的。

   在整个代码过程中,我们最需要关心的就是如何取得一个Fruit接口对象,而后进行方法的调用,至于说这个接口对象是被谁实例化的,不是客户端的工作。

   所以经过分析发现,最大的问题就在与关键字NEW,而这一问题就可以理解为耦合度太高。耦合度太高的直接问题就是代码不方便维护,就相当于A一直要与B绑定在一起。可以完全参考java虚拟机设计的思想:

  • 程序—>JVM—>适应不同的操作系统(A->C->B);

范例:增加一个过度

class Factory{
	public static Fruit getInstance(String className){
		if("apple".equals(className)){
			return new Apple();
		}else if("orange".equals(className)){
			return new Orange();
		}else{
			return null;
		}
	}
}

public class TestDemo {

	public static void main(String[] args) {
		Fruit f = Factory.getInstance("orange");
		f.eat();
	}

}

    现在的客户端不会看见具体的子类,因为所有的接口对象都是通过Factory类取得得,如果日后要扩充新的Furit子类对象,则只需要修改Factory类即可,但是客户端的调用不会发生变化。

面试题:请编写一个Factory程序  

package cn.mldn.po;

interface Fruit{
	public void eat();
}

class Apple implements Fruit{
	@Override
	public void eat() {
		System.out.println("吃苹果!");	
	}	
}

class Orange implements Fruit{

	@Override
	public void eat() {
		System.out.println("**吃橘子!");
	}
	
}

class Factory{
	public static Fruit getInstance(String className){
		if("apple".equals(className)){
			return new Apple();
		}else if("orange".equals(className)){
			return new Orange();
		}else{
			return null;
		}
	}
}

public class TestDemo {

	public static void main(String[] args) {
		Fruit f = Factory.getInstance(args[0]);
		f.eat();
	}

}

   接口的应用——代理设计模式(Proxy)

 

范例:转化为程序

package cn.mldn.po;

interface Subject{   //整个操作的核心主题
	public void make();  //整个临幸的核心功能
}

class RealSubject implements Subject{

	@Override
	public void make() {
	    System.out.println("皇帝正在XXX!");
	}	
}

class ProxySubject implements Subject{
	private Subject subject;
	//要接收一个真实主题的操作对象
	public ProxySubject(Subject subject){
		this.subject=subject;
	}
	public void prepare(){
		System.out.println("为临幸做准备!");
	}
	@Override
	public void make() {
		this.prepare();
		this.subject.make();  //告诉皇帝可以开始了
		this.destory();
	}
	public void destory(){
		System.out.println("把娘娘搬走了,皇帝伺候睡觉了!");
	}
	
}
public class TestDemo {

	public static void main(String[] args) {
		Subject sub = new ProxySubject(new RealSubject());
		sub.make();   //调用的是代理主题操作
	}

}

  代理模式有一个核心精髓在于有一个主题操作接口(可能有多种方法),核心的业务主题值完成核心功能,例如:吃饭。而代理主题负责完成与核心主题有关的辅助性操作。

面试题:请编写Proxy程序

package cn.mldn.po;

interface Subject{   //整个操作的核心主题
	public void make();  //整个临幸的核心功能
}

class RealSubject implements Subject{

	@Override
	public void make() {
	    System.out.println("核心主题!");
	}	
}

class ProxySubject implements Subject{
	private Subject subject;
	//要接收一个真实主题的操作对象
	public ProxySubject(Subject subject){
		this.subject=subject;
	}
	public void prepare(){
		System.out.println("核心主题前的准备!");
	}
	@Override
	public void make() {
		this.prepare();
		this.subject.make();  //告诉皇帝可以开始了
		this.destory();
	}
	public void destory(){
		System.out.println("核心主题后的收尾!");
	}
	
}
public class TestDemo {

	public static void main(String[] args) {
		Subject sub = new ProxySubject(new RealSubject());
		sub.make();   //调用的是代理主题操作
	}

}

抽象类与接口的区别(面试题)

抽象类和接口在使用的形式上是非常相似的,所以很多人也很乐意去解释两者的区别.

No区别抽象类接口
1关键字abstract classinterface
2组成构造方法、普通方法、抽象方法、static方法、常量、变量抽象方法、全局常量
3子类使用class 子类 extends 抽象类class 子类 implements 接口,接口,,,
4关系抽象类可以实现多个接口接口不能够继承抽象类,但是可以继承多个父接口
5权限可以使用各种权限只有public 
6限制单继承局限无但继承局限
7子类抽象类和接口都必须有子类,子类必须覆写全部的抽象方法
8实例化对象依靠子类的向上转型进行对象的实例化

经过比较发现,抽象类中支持的功能绝对要比接口更多,但是只有一点不好,那就是单继承局限,所以这一重要的一点都掩盖了抽象类所有的优点,即:抽象类和接口都可以使用的时候,优先考虑接口。

  一个不成文的参考(50%):

  • 在进行某些公共操作的时候一定要定义出接口;
  • 有了接口就需要利用子类完善方法;
  • 如果是你们自己写的接口,那么绝对不要去使用关键字new直接实例化接口子类,用工厂类完成。

总结:

1.接口与抽象类定义的不同。

2.接口做为标准用于解耦合以及不同层之间的连接桥梁。

3.一定要将工厂设计模式与代理模式的机构记下来。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值