Java语言基础(7)面向对象——抽象类和接口的概念和使用详细介绍(一文看懂接口和抽象类的使用以及区别)

3.1、 抽象类

看我们以前示例中的Person、Student和Employee,从我们使用的角度来看主要对Student和Employee进行实例化,Person中主要包含了一些公共的属性和方法,而Person我们通常不会实例化,所以我们可以把它定义成抽象的:
● 在java中采用abstract关键字定义的类就是抽象类,采用abstract关键字定义的方法就是抽象方法
● 抽象的方法只需在抽象类中,提供声明,不需要实现
● 如果一个类中含有抽象方法,那么这个类必须定义成抽象类
● 如果这个类是抽象的,那么这个类被子类继承,抽象方法必须被重写。如果在子类中不复写该抽象方法,那么必须将此类再次声明为抽象类
● 抽象的类是不能实例化的,就像现实世界中人其实是抽象的,张三、李四才是具体的
● 抽象类不能被final修饰(它是需要被继承的)
● 抽象方法不能被final修饰,因为抽象方法就是被子类实现的
抽象类中可以包含方法实现,可以将一些公共的代码放到抽象类中,另外在抽象类中可以定义一些抽象的方法,这样就会存在一个约束,而子类必须实现我们定义的方法,如:teacher必须实现printInfo方法,Student也必须实现printInfo方法,方法名称不能修改,必须为printInfo,这样就能实现多态的机制,有了多态的机制,我们在运行期就可以动态的调用子类的方法。所以在运行期可以灵活的互换实现。
在这里插入图片描述

3.1.1、采用abstract声明抽象类

public class AbstractTest01 {
	
	public static void main(String[] args) {
		
		
		//不能实例化抽象类
		//抽象类是不存在,抽象类必须有子类继承		
		Person p = new Person();
		
		//以下使用是正确的,因为我们new的是具体类					
		Person p1 = new Employee();
		p1.setName("张三");
		System.out.println(p1.getName());
		
	}	
}

//采用abstract定义抽象类
//在抽象类中可以定义一些子类公共的方法或属性
//这样子类就可以直接继承下来使用了,而不需要每个
//子类重复定义
abstract class Person {
	
	private String name;
	
	public void setName(String name) {
		this.name = name;
	}	
	
	public String getName() {
		return name;	
	}
	
	//此方法各个子类都可以使用	
	public void commonMethod1() {
		System.out.println("---------commonMethod1-------");
	}
}

class Employee extends Person {
	
}

class Student extends Person {
	
}

3.1.2、抽象的方法只需在抽象类中,提供声明,不需要实现,起到了一个强制的约束作用,要求子类必须实现

public class AbstractTest02 {
	
	public static void main(String[] args) {
		//Person p = new Employee();
		//Person p = new Student();
		//Person p = new Person();
		p.setName("张三");
		p.printInfo();
	}	
}

abstract class Person {
	
	private String name;
	
	public void setName(String name) {
		this.name = name;
	}	
	
	public String getName() {
		return name;	
	}
	
	//此方法各个子类都可以使用	
	public void commonMethod1() {
		System.out.println("---------commonMethod1-------");
	}
	
	//public void printInfo() {
	//	System.out.println("------Person.printInfo()--------");	
	//}
	
	//采用abstract定义抽象方法
	//如果有一个方法为抽象的,那么此类必须为抽象的
	//如果一个类是抽象的,并不要求具有抽象的方法
	public abstract void printInfo();
}

class Employee extends Person {
	
	//必须实现抽象的方法
	public void printInfo() {
		System.out.println("Employee.printInfo()");	
	}
}

class Student extends Person {
	
	//必须实现抽象的方法
	public void printInfo() {
		System.out.println("Student.printInfo()");	
	}
}

3.1.3、如果这个类是抽象的,那么这个类被子类继承,抽象方法必须被覆盖。如果在子类中不覆盖该抽象方法,那么必须将此方法再次声明为抽象方法

public class AbstractTest03 {
	
	public static void main(String[] args) {
		//此时不能再new Employee了
		Person p = new Employee();
	}	
}

abstract class Person {
	
	private String name;
	
	public void setName(String name) {
		this.name = name;
	}	
	
	public String getName() {
		return name;	
	}
	
	//此方法各个子类都可以使用	
	public void commonMethod1() {
		System.out.println("---------commonMethod1-------");
	}

	//采用abstract定义抽象方法
	public abstract void printInfo();
}

abstract class Employee extends Person {
	
	//再次声明该方法为抽象的
	public abstract void printInfo();
}

class Student extends Person {
	
	//实现抽象的方法
	public void printInfo() {
		System.out.println("Student.printInfo()");	
	}
}

3.1.4、抽象类不能被final修饰

public class AbstractTest04 {
	
	public static void main(String[] args) {
	}	
}

//不能采用final修改抽象类
//两个关键字是矛盾的
final abstract class Person {
	
	private String name;
	
	public void setName(String name) {
		this.name = name;
	}	
	
	public String getName() {
		return name;	
	}
	
	//此方法各个子类都可以使用	
	public void commonMethod1() {
		System.out.println("---------commonMethod1-------");
	}

	//采用abstract定义抽象方法
	public abstract void printInfo();
}

class Employee extends Person {
	
	//实现抽象的方法
	public void printInfo() {
		System.out.println("Student.printInfo()");	
	}
}

class Student extends Person {
	
	//实现抽象的方法
	public void printInfo() {
		System.out.println("Student.printInfo()");	
	}
}

3.1.5、抽象方法不能被final修饰

public class AbstractTest05 {
	
	public static void main(String[] args) {
	}	
}

abstract class Person {
	
	private String name;
	
	public void setName(String name) {
		this.name = name;
	}	
	
	public String getName() {
		return name;	
	}
	
	//此方法各个子类都可以使用	
	public void commonMethod1() {
		System.out.println("---------commonMethod1-------");
	}

	//不能采用final修饰抽象的方法
	//这两个关键字存在矛盾
	public final abstract void printInfo();
}

class Employee extends Person {
	
	//实现抽象的方法
	public void printInfo() {
		System.out.println("Student.printInfo()");	
	}
}

class Student extends Person {
	
	//实现抽象的方法
	public void printInfo() {
		System.out.println("Student.printInfo()");	
	}
}

3.1.6、抽象类中可以没有抽象方法(参见3.1.1)

4.1、 接口(行为)

接口我们可以看作是抽象类的一种特殊情况,在接口中只能定义抽象的方法和常量

  1. 在java中接口采用interface声明
  2. 接口中的方法默认都是public abstract的,不能更改
  3. 接口中的变量默认都是public static final类型的,不能更改,所以必须显示的初始化
  4. 接口不能被实例化,接口中没有构造函数的概念
  5. 接口之间可以继承,但接口之间不能实现
  6. 接口中的方法只能通过类来实现,通过implements关键字
  7. 如果一个类实现了接口,那么接口中所有的方法必须实现
  8. 一类可以实现多个接口
  9. 接口可以继承多个接口

4.1.1、接口中的方法默认都是public abstract的,不能更改

public class InterfaceTest01 {
	
	public static void main(String[] args) {
	}	
}

//采用interface定义接口
//定义功能,没有实现
//实现委托给类实现
interface StudentManager {
	
	//正确,默认public abstract 等同public abstract void addStudent(int id, String name);
	public void addStudent(int id, String name);	
	
	//正确
	//public abstract void addStudent(int id, String name);
	
	//正确,可以加入public修饰符,此种写法较多
	public void delStudent(int id);
	
	//正确,可以加入abstract,这种写法比较少
	public abstract void modifyStudent(int id, String name);
	
	//编译错误,因为接口就是让其他人实现
	//采用private就和接口原本的定义产生矛盾了
	private String findStudentById(int id);

}

4.1.2、接口中的变量是public static final类型的,不能更改,所以必须显示的初始化

public class InterfaceTest02 {
	
	public static void main(String[] args) {
		
		//不能修改,因为final的
		//StudentManager.YES = "abc";
		
		System.out.println(StudentManager.YES);
	}	
}

interface StudentManager {
	
	//正确,默认加入public static final 
	String YES = "yes";
	
	//正确, 开发中一般就按照下面的方式进行声明
	public static final String NO = "no";	
	
	//错误,必须赋值,因为是final的
	//int ON;
	
	//错误,不能采用private声明
	private static final int OFF = -1;
}

4.1.3、接口不能被实例化,接口中没有构造函数的概念

public class InterfaceTest03 {
	
	public static void main(String[] args) {
		
		//接口是抽象类的一种特例,只能定义方法和变量,没有实现
		//所以不能实例化
		StudentManager studentManager = new StudentManager();
	}	
}

interface StudentManager {
	
	public void addStudent(int id, String name);	
}

4.1.4、接口之间可以[多]继承,但接口之间不能实现

public class InterfaceTest04 {
	
	public static void main(String[] args) {
	}	
}

interface inter1 {
	public void method1();	
	
	public void method2();	
}

interface inter2 {
	public void method3();	
}

//接口可以继承
interface inter3 extends inter1 {

	public void method4();
}

//接口不能实现接口
//接口只能被类实现
interface inter4 implements inter2 {
	public void method3();
}

4.1.5、如果一个类实现了接口,那么接口中所有的方法必须实现

public class InterfaceTest05 {
	
	public static void main(String[] args) {
		//Iter1Impl实现了Inter1接口
		//所以它是Inter1类型的产品
		//所以可以赋值
		Inter1 iter1 = new Iter1Impl();
		iter1.method1();
		
		//Iter1Impl123实现了Inter1接口
		//所以它是Inter1类型的产品
		//所以可以赋值
		iter1 = new Iter1Impl123();
		iter1.method1();
		
		//可以直接采用Iter1Impl来声明类型
		//这种方式存在问题
		//不利于互换,因为面向具体编程了
		Iter1Impl iter1Impl = new Iter1Impl();
		iter1Impl.method1();
		
		//不能直接赋值给iter1Impl
		//因为Iter1Impl123不是Iter1Impl类型
		//iter1Impl = new Iter1Impl123();
		//iter1Impl.method1();
		
	}	
}

//接口中的方法必须全部实现
class Iter1Impl implements Inter1 {
	
	public void method1() {
		System.out.println("method1");	
	}
	
	public void method2() {
		System.out.println("method2");	
	}
	
	public void method3() {
		System.out.println("method3");	
	}
}

class Iter1Impl123 implements Inter1 {
	
	public void method1() {
		System.out.println("method1_123");	
	}
	
	public void method2() {
		System.out.println("method2_123");	
	}
	
	public void method3() {
		System.out.println("method3_123");	
	}
}

abstract class Iter1Impl456 implements Inter1 {
	
	public void method1() {
		System.out.println("method1_123");	
	}
	
	public void method2() {
		System.out.println("method2_123");	
	}
	
	//再次声明成抽象方法
	public abstract void method3();
}


//定义接口
interface Inter1 {
	
	public void method1();
	
	public void method2();
	
	public void method3();	
}

4.1.6、一类可以实现多个接口

public class InterfaceTest06 {

public static void main(String[] args) {
	
	//可以采用Inter1定义
	Inter1 inter1 = new InterImpl();
	inter1.method1();
	
	//可以采用Inter2定义
	Inter2 inter2 = new InterImpl();
	inter2.method2();
	
	//可以采用Inter3定义
	Inter3 inter3 = new InterImpl();
	inter3.method3();
}	

}

//实现多个接口,采用逗号隔开
//这样这个类就拥有了多种类型
//等同于现实中的多继承
//所以采用java中的接口可以实现多继承
//把接口粒度划分细了,主要使功能定义的含义更明确
//可以采用一个大的接口定义所有功能,替代多个小的接口,
//但这样定义功能不明确,粒度太粗了
class InterImpl implements Inter1, Inter2, Inter3 {

public void method1() {
	System.out.println("----method1-------");
}	

public void method2() {
	System.out.println("----method2-------");
}

public void method3() {
	System.out.println("----method3-------");
}

}

interface Inter1 {

public void method1();	

}

interface Inter2 {

public void method2();	

}

interface Inter3 {

public void method3();	

}

/*
interface Inter {

public void method1();	

public void method2();	

public void method3();

}
*/

4.1.7、一个接口可以继承多个接口

//声明接口:interface
interface A{
	public static final int CONST = 1;
	void a();//接口中的方法默认都是public abstract的
}
interface K{
	void k();
}
//接口可以继承多个接口
interface B extends A,K{
	
	void b();
	
	int d();
	
}

4.1.8、 接口的进一步应用

在java中接口其实描述了类需要做的事情,类要遵循接口的定义来做事,使用接口到底有什么本质的好处?可以归纳为两点:(提供了标准、规范)
● 采用接口明确的声明了它所能提供的服务
● 解决了Java单继承的问题(一个类可以实现多个接口,一个接口可以继承多个接口)
● 实现了可接插性(重要)
示例:完成学生信息的增删改操作,系统要求适用于多个数据库,如:适用于Oracle和MySQL;
第一种方案,不使用接口,每个数据库实现一个类:
在这里插入图片描述

//Oracle的实现
public class StudentOracleImpl {
	
	public void add(int id, String name) {
		System.out.println("StudentOracleImpl.add()");	
	}
	
	public void del(int id) {
		System.out.println("StudentOracleImpl.del()");	
	}
	
	public void modify(int id, String name) {
		System.out.println("StudentOracleImpl.modify()");	
	}
}	

需求发生变化了,客户需要将数据移植Mysql上

Mysql的实现
public class StudentMysqlImpl {
	
	public void addStudent(int id, String name) {
		System.out.println("StudentMysqlImpl.addStudent()");	
	}
	
	public void deleteStudent(int id) {
		System.out.println("StudentMysqlImpl.deleteStudent()");	
	}
	
	public void udpateStudent(int id, String name) {
		System.out.println("StudentMysqlImpl.udpateStudent()");	
	}
}	

调用以上两个类完成向Oracle数据库和Mysql数据存储数据

public class StudentManager {
	
	public static void main(String[] args) {
		//对Oracle数据库的支持
		/*
		StudentOracleImpl studentOracleImpl = new StudentOracleImpl();
		studentOracleImpl.add(1, "张三");
		studentOracleImpl.del(1);
		studentOracleImpl.modify(1, "张三");
		*/
		
		//需要支持Mysql数据库
		StudentMysqlImpl studentMysqlImpl = new StudentMysqlImpl();
		studentMysqlImpl.addStudent(1, "张三");
		studentMysqlImpl.deleteStudent(1);
		studentMysqlImpl.udpateStudent(1, "张三");
	}
}	

以上代码不能灵活的适应需求,当需求发生改变需要改动的代码量太大,这样可能会导致代码的冗余,另外可能会导致项目的失败,为什么会导致这个问题,在开发中没有考虑到程序的扩展性,就是一味的实现,这样做程序是不行的,所以大的项目比较追求程序扩展性,有了扩展性才可以更好的适应需求。

第二种方案,使用接口
UML,统一建模语言
在这里插入图片描述

public class Student4OracleImpl implements IStudent {
	
	public void add(int id, String name) {
		System.out.println("Student4OracleImpl.add()");	
	}
	
	public void del(int id) {
		System.out.println("Student4OracleImpl.del()");	
	}
	
	public void modify(int id, String name) {
		System.out.println("Student4OracleImpl.modify()");	
	}

}
public class Student4MysqlImpl implements IStudent {

	public void add(int id, String name) {
		System.out.println("Student4MysqlImpl.add()");	
	}
	
	public void del(int id) {
		System.out.println("Student4MysqlImpl.del()");	
	}
	
	public void modify(int id, String name) {
		System.out.println("Student4MysqlImpl.modify()");	
	}
}
public class StudentService {
	
	public static void main(String[] args) {
		/*
		IStudent istudent = new Student4OracleImpl();
		IStudent istudent = new Student4MysqlImpl();
		istudent.add(1, "张三");
		istudent.del(1);
		istudent.modify(1, "张三");
		*/
		//IStudent istudent = new Student4OracleImpl();
		//IStudent istudent = new Student4MysqlImpl();
		//doCrud(istudent);
		//doCrud(new Student4OracleImpl());
		//doCrud(new Student4MysqlImpl());
		
		//doCrud(new Student4OracleImpl());
		doCrud(new Student4MysqlImpl());
		
	}
	
	//此种写法没有依赖具体的实现
	//而只依赖的抽象,就像你的手机电池一样:你的手机只依赖电池(电池是一个抽象的事物),
	//而不依赖某个厂家的电池(某个厂家的电池就是具体的事物了)
	//因为你依赖了抽象的事物,每个抽象的事物都有不同的实现
	//这样你就可以利用多态的机制完成动态绑定,进行互换,是程序具有较高的灵活
	//我们尽量遵循面向接口(抽象)编程,而不要面向实现编程
	public static void doCrud(IStudent istudent) {
		istudent.add(1, "张三");
		istudent.del(1);
		istudent.modify(1, "张三");
	}
	
	
	//以下写法不具有扩展性
	//因为它依赖了具体的实现
	//建议不要采用此种方法,此种方案是面向实现编程,就依赖于具体的东西了
	/*
	public static void doCrud(Student4OracleImpl istudent) {
		istudent.add(1, "张三");
		istudent.del(1);
		istudent.modify(1, "张三");
	}
	*/
	
}

接口和抽象类的区别?

  1. 接口描述了方法的特征,不给出实现,一方面解决java的单继承问题,实现了强大的可接插性
  2. 抽象类提供了部分实现,抽象类是不能实例化的,抽象类的存在主要是可以把公共的代码移植到抽象类中
  3. 面向接口编程,而不要面向具体编程(面向抽象编程,而不要面向具体编程)
  4. 优先选择接口(因为继承抽象类后,此类将无法再继承,所以会丧失此类的灵活性)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值