继承,抽象类,接口

继承

继承

多个事物发现有共同的属性和行为,那么要提高代码的复用性,将相同的代码抽取,抽取出来放在一个单独的类中

class Student 
{
	String name;
	int age;
	void study()
	{
		System.out.println("study");
	}
}
class Worker
{
	String name;
	int age;
	void work()
	{
		System.out.println("work");
	}
}

然后抽取

class Student 
{
	void study()
	{
		System.out.println("study");
	}
}
class Worker
{
	void work()
	{
		System.out.println("work");
	}
}
class Person
{
	String name;
	int age;
}

代码抽取到了Person类,但是现在的三个类之间没有一点关系,我们现在要搭建一个桥梁,让它们之间产生关系,就需要继承,它是让类鱼类之间产生关系的桥梁,继承使用的关键字是extends

//将学生和工人的共性抽取到Person这个类中
class Person {
	String name;
	int age;
}
//学生通过extends继承Person,即Person为Student的父类
class Student extends Person{
	//学生类中的自己特有方法
	public void study(){
		System.out.println(name+"同学正在学习。。。。");
	}
}
//工人通过extends继承Person,即Person为工人的父类
class Worker extends Person{
	//工人类中的自己特有方法
	public void work(){
		System.out.println(name+"工人正在工作。。。。");
	}
}
//测试类
public  class Test{
	public static void main(String[] args) {
		Student s = new Student();
		s.name = "小明";
		s.study();
		
		Worker w = new Worker();
		w.name = "张三";
		w.work();
	}
}

继承的好处:
1、继承的出现提高了代码的复用性,提高软件开发效率。
2、继承的出现让类与类之间产生了关系,提供了多态的前提。

单继承&多继承&多重继承

Java只支持单继承,不支持多继承。一个类只能有一个父类,不可以有多个父类。

Java支持多层继承(继承体系)

class A{}
class B extends A{}
class C extends B{}

定义继承不要为了获取类中的某一个功能而去继承。

日常开发中小技巧:

多层次继承出现的继承体系中,通常看父类中的功能,了解该体系的基本功能,建立子类对象即可使用该体系功能。

多继承虽然能使子类同时拥有多个父类的特征,但是其缺点也是很显著的,主要有两方面:

  1. 如果在一个子类继承的多个父类中拥有相同名字的实例变量,子类在引用该变量时将产生歧义,无法判断应该使用哪个父类的变量。
  2. 如果在一个子类继承的多个父类中拥有相同方法,子类中有没有覆盖该方法,那么调用该方法时将产生歧义,无法判断应该调用哪个父类的方法。

子类中成员变量的特点

成员变量:如果子类和父类出现不同名的成员变量,这使得访问是没有一点问题的。

class Fu
{
	//Fu中的成员变量。
	int num = 5;
}
class Zi extends Fu
{
	//Zi中的成员变量
	int num2 = 6;
	void show()
	{
		//访问父类中的num
		System.out.println("Fu num="+num);
		//访问子类中的num2
		System.out.println("Zi num2="+num2);
	}
}
class Demo 
{
	public static void main(String[] args) 
	{
		Zi z = new Zi(); //创建子类对象
		z.show(); //调用子类中的show方法
	}
}

Fu类中的成员变量是非私有的,子类中可以直接访问,若Fu类中的成员变量私有了,子类是不能直接访问的。
当子类和父类中的成员变量相同的时候,在子类中若要访问父类中的成员变量,必须使用super关键字

class Fu
{
	//Fu中的成员变量。
	int num = 5;
}
class Zi extends Fu
{
	//Zi中的成员变量
	int num = 6;
	void show()
	{
		//子父类中出现了同名的成员变量时
		//在子类中需要访问父类中非私有成员变量时,需要使用super关键字
		//访问父类中的num
		System.out.println("Fu num="+super.num);
		//访问子类中的num2
		System.out.println("Zi num2="+this.num);
	}
}
class Demo5 
{
	public static void main(String[] args) 
	{
		Zi z = new Zi(); //创建子类对象
		z.show(); //调用子类中的show方法
	}
}

流程解析:

  • 当程序执行new Zi();这时会加载Zi类字节码文件,但由于Zi类继承了Fu类,因此需要将父类字节码文件加载进方法区。
  • ZiFu的字节码加载完毕后。会执行new Zi();即在堆内存中创建Zi类对象,并为其分配内存地址0x99;对象的内存空间分配结束之后,开始成员变量默认初始化,此时需要注意Funum同样也会在此对象中。
  • 紧接着开始zi构造函数压栈,在zi的构造函数中有隐式的super()语句,此时Fu的构造函数也会压栈,Fu的构造函数压栈后会将Funum显示初始化。
  • 接着Fu构造函数弹栈,执行Zi的构造函数,Zi的num显示初始化。接着Zi构造函数弹栈。此时Zi对象在堆中创建完成,并将内存地址0x99赋值给main方法中的z引用。接着调用zishow()方法,show方法压栈。

注意:super和this的用法相似,this:代表本类的对象引用。super:代表的父类内存空间,而不是父类的引用。

子父类中的成员函数特点–重写&应用

当程序通过对象来调用方法的时候,会先在子类中寻找对应的方法如果没有,就回去父类寻找,就近原则,所以,如果子类和父类中的方法重名了,一摸一样的,那肯定会调用子类的方法。
这也叫覆盖(重写方法)

class Fu
{
	void show()
	{
		System.out.println("Fu show");
	}
}
class Zi extends Fu
{
	//子类复写了父类的show方法
	void show()
	{
		System.out.println("Zi show");
	}
}

重写的注意事项

  1. 子类方法覆盖父类方法,必须要保证权限大于等于父类的权限。
class Fu()
{	void show(){}
    public void method(){}
}
class Zi() extends Fu
{	public void show(){}  //编译运行没问题
    void method(){}      //编译错误
}
public >缺省 > protected > private

2.静态只能覆盖静态,或者被静态覆盖
3.必须一模一样,函数的返回值,函数名,还有参数列表都要一样

总结:当一个类是另一个类中的一种时,可以通过继承,来扩展功能。如果父类具备的功能内容需要子类特殊定义时,使用重写

子父类中构造函数特点&super

在子类创建对象的时候,父类的构造函数会先执行,因为子类的第一行会有默认的隐式super()语句,调用父类的构造函数。
因为继承了父类,所以创建对象的时候会先看父类是如何对内容进行初始化的。才可以方便使用父类的内容。

当父类中没有空参数构造函数是,子类的构造函数是必须有显示super语句指定要访问父类中的构造函数。
看个程序就懂了

class Fu extends Object
{
	Fu()
	{
		//super();
		//显示初始化。
		System.out.println("fu constructor run..A..");
	}
	Fu(int x)
	{
		//显示初始化。
		System.out.println("fu constructor run..B.."+x);
	}
}
class Zi extends Fu
{
	Zi()
	{
		//super();
		System.out.println("zi constructor run..C..");
	}
	Zi(int x)
	{
		super();
		System.out.println("zi constructor run..D.."+x);
	}
}
class Demo
{
	public static void main(String[] args)
	{
		new Zi();
		new Zi(3);
	}
}

就是子类的构造函数如果没有参数可以不书写super语句,但是如果有参数的话,就必须书写super语句来访问父类的构造函数。

子类实例化的细节

如果子类的构造函数第一行写了this调用了本类其他构造函数,这时是没有super调用父类的语句的,因为this()或者super(),只能定义在构造函数的第一行,因为初始化动作要先执行。

父类构造函数中也有隐式的super。记住:只要是构造函数默认第一行都是super(); Java体系在设计,定义了一个所有对象的父类Object 。

类中的构造函数默认第一行都有隐式的super()语句,在访问父类中的构造函数。所以父类的构造函数既可以给自己的对象初始化,也可以给自己的子类对象初始化。如果默认的隐式super语句没有对应的构造函数,必须在构造函数中通过this或者super的形式明确调用的构造函数

final关键字

继承有很多的好处,提高了代码的复用性等,但随之也会有很多的问题,我们现在父类不想被继承,方法不i想被重写,但是当子类继承了之后,他就可以重写了,所以为了针对这种情况,Java里面有一个关键字final,意思是最终的不可变的,他是一个修饰符,可以修饰类和方法还有局部变量

final修饰类不可以被继承,但是可以继承其他类。
final修饰的方法不可以被覆盖,但父类中没有被final修饰方法,子类覆盖后可以加final。
final修饰的变量称为常量,这些变量只能赋值一次,定义的时候必须有初始值,不能变。
final修饰的引用类型变量,表示该引用变量的引用不能变,而不是引用所指的对象中的数据还是可以变化的;

抽象类

产生&特点&细节

当编写一个类的时候,我们往往会为该类定义一些方法,这些方法是用来描述一些类的行为方式,这些方法都有具体的方法体。
会有一种特殊的情况,就是功能声明是相同的,但是功能主体不同,这是可以进行抽取,但只是抽取方法声明,不抽取方法主体。那么这种方法就是抽象方法。
个人理解就是抽像方法是定义了一个方法框架,没有{}的方法。

关键字abstrac是修饰抽象方法的,他也是可以修饰类的,就叫抽象类

abstract class 犬科 
{
	abstract void 吼叫();//抽象函数。需要abstract修饰,并分号;结束
}
class Dog extends 犬科
{
	void 吼叫()
	{
		System.out.println("汪汪汪汪");
	}
}
class Wolf extends 犬科
{
	void 吼叫()
	{
		System.out.println("嗷嗷嗷嗷");
	}
}

抽象类的特点

  1. 抽象类和抽象方法都需要被abstract来修饰的,抽象方法一定要定义在抽象类中。
  2. 抽象类可以不创建实例,是因为调用抽象方法没有一点意义
  3. 只有覆盖了抽象类中所有的抽象方法之后,其子类才可以实例化,否则子类还是一个抽象类

抽象关键字abstract不可以和哪些关键字共存?

1、final:fianl修饰的类是无法被继承的,而abstract修饰的类一定要有子类。
final修饰的方法无法被覆盖,但是abstract修饰的方法必须要被子类去实现的。

2、static:静态修饰的方法属于类的,它存在与静态区中,和对象就没关系了。而抽象方法没有方法体,使用类名调用它没有任何意义。

3、private:私有的方法子类是无法继承到的,也不存在覆盖,而abstract和private一起使用修饰方法,abstract既要子类去实现这个方法,而private修饰子类根本无法得到父类这个方法。互相矛盾。

接口

实现&定义

当抽象类中的方法全部都是抽象方法的时候,那这个抽象类就可以叫做接口了,接口就是极度的抽象类。
接口的定义必须使用关键字interface接口中只能定义常量

interface class Demo
{
	abstract void show1();
	abstract void show2();
}

接口中成员的特点:

1、接口中可以定义变量,但是变量必须有固定的修饰符修饰,public static final 所以接口中的变量也称之为常量。

2、接口中可以定义方法,方法也有固定的修饰符,public abstract

3、接口中的成员都是公共的。

4、接口不可以创建对象。

5、子类必须覆盖掉接口中所有的抽象方法后,子类才可以实例化。否则子类是一个抽象类。

接口(全部是抽象方法)–抽象类(部分抽象方法)–类(没有抽象方法)

interface Demo//定义一个名称为Demo的接口。
{
	public static final int NUM = 3;
	public abstract void show1();
	public abstract void show2();
}
//定义子类去覆盖接口中的方法。子类必须和接口产生关系,类与类的关系是继承,类与接口之间的关系是 实现。通过 关键字 implements
class DemoImpl implements Demo//子类实现Demo接口。
{
	//重写接口中的方法。
	public void show1(){}
	public void show2(){}
}

接口多实现

接口最重要的体现就是解决了继承无法多实现的问题,将多继承通过多实现完成了

interface A
{
	void abstract show1();
}
interface B
{
	void abstract show2();
}
class C implements A,B// 多实现。同时实现多个接口。
{
	public void show1(){}
	public void show2(){}
}

其实核心原因就是在于多继承父类中功能有主体,而导致调用运行时,不确定运行哪个主体内容。
而在多实现里 因为接口中的功能都没有方法体,由子类来明确,来源确定就是实现他们的实现类。

类继承类同时实现接口

子类继承父类拓展自己的功能,如果父类没有的功能,子类想要拓展其他的功能,这就可以通过接口来实现

class Fu
{
	public void show(){}
}
interface Inter
{
	public abstract void show1();
}
class Zi extends Fu implements Inter
{
	public void show1()
	{}
}

接口的多实现

多个接口之间可以使用extends进行继承。

interface A{
	 abstract void show();
}
interface B{
	abstract void show1();
}
interface C{
	abstract void show2();
}
interface D extends A,B,C{
	 abstract void show3();
}

继承了多个接口,可能这几个接口里面有同样的方法,但是子类需要来实现这些接口才可以使用,所以子类实现这些方法后调用的话不会出现调用的不确定性。

没有抽象方法的抽象类的由来

当我们需要继承一个接口,我们想要实现接口的一部分方法,但是我们话是需要全部实现了才可以使用,有一种方法就是,用一个抽象类,来继承这个接口,把接口里面的方法全都空实现,然后再有一个正常的类来继承这个没有抽象方法的抽象类,然后重写自己需要使用的方法即可。

//拥有多个方法的接口
interface Inter{
	abstract void show();
	abstract void show1();
	abstract void show2();
	abstract void show3();
}
//作为过度的抽象类,此类将接口的所有方法都实现,这里的实现是空实现
abstract class AbsInter implements Inter{
	public void show(){}
	public void show1(){}
	public void show2(){}
	public void show3(){}
}
/*
//此类直接实现Inter,但只使用其他show和show2方法,这样导致其他两个方法也要实现
//不符合我们的要求
class SubInter2 implements Inter{

	public void show() {
		System.out.println("show");
	}
	public void show1() {
	}
	public void show2() {
		System.out.println("show2");
		
	}
	public void show3() {
	}
}
*/
//此类只使用接口中的show和show2方法,只要覆盖抽象类中的show和show2即可
class SubInter extends AbsInter{
	public void show(){
		System.out.println("show");
	}
	public void show2(){
		System.out.println("show2");
	}
}

接口和抽象类的区别

相同点:

  • 都位于继承的顶端,用于被其他实现或继承;
  • 都不能实例化;
  • 都包含抽象方法,其子类都必须覆写这些抽象方法;

区别:

  • 抽象类为部分方法提供实现,避免子类重复实现这些方法,提供代码重用性;接口只能包含抽象方法,极度的抽象类;
  • 一个类只能继承一个直接父类(可能是抽象类),却可以实现多个接口;(接口弥补了Java的单继承)

二者的选用:

  • 优先选用接口,尽量少用抽象类
  • 需要定义子类的行为,又要为子类提供共性功能时才选用抽象类
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值