抽象类可以创建对象吗_【Java视频教程】day16-抽象类

2b077a63b9dc953fd6e498dfec9caccc.png

抽象类的概念

  • 抽象的产生

需求:创建类描述猫和狗;

猫:颜色;名字;品种; 吃;叫;抓老鼠;
狗:颜色;名字;品种; 吃;叫;看家;
  • 老的实现方式:
//创建类描述狗
class Dog {
	private String color;// 颜色
	private String name;// name
	private String pinZhong;// 品种

	public Dog() {
	}

	public Dog(String color, String name, String pinZhong) {
		this.color = color;
		this.name = name;
		this.pinZhong = pinZhong;
	}

	public String getColor() {
		return color;
	}

	public void setColor(String color) {
		this.color = color;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getPinZhong() {
		return pinZhong;
	}

	public void setPinZhong(String pinZhong) {
		this.pinZhong = pinZhong;
	}

	// 吃
	public void eat() {
		System.out.println("吃骨头!");
	}

	// 叫
	public void say() {
		System.out.println("汪汪");
	}

	// 看家
	public void lookHome() {
		System.out.println("看家");
	}
}
  • 创建类描述猫
class Cat {
	private String color;// 颜色
	private String name;// 名字
	private String pinZhong;// 品种

	public Cat() {
	}

	public Cat(String color, String name, String pinZhong) {
		this.color = color;
		this.name = name;
		this.pinZhong = pinZhong;
	}

	public String getColor() {
		return color;
	}

	public void setColor(String color) {
		this.color = color;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getPinZhong() {
		return pinZhong;
	}

	public void setPinZhong(String pinZhong) {
		this.pinZhong = pinZhong;
	}

	// 猫的吃
	public void eat() {
		System.out.println("吃小鱼!");
	}

	// 猫的叫
	public void say() {
		System.out.println("喵喵喵");
	}

	// 抓老鼠
	public void catchMouse() {
		System.out.println("抓老鼠");
	}
}
思考:这两个类中存在大量相同代码,每个类都要重新书写一遍,太麻烦。可以考虑使用继承技术来简化代码;

因为猫和狗之间没有 是 的关系,所以不能相互继承;

所以要找一个共同的父类,将猫和狗的共同代码都抽取到父类中;

因为猫和狗都 是 动物,所以可以定义一个动物类,将相同代码抽取到动物类中,然后让猫和狗都继承这个动物类;

/*
* 需求:创建类描述猫和狗;
猫:颜色;名字;品种; 吃;叫;抓老鼠;
狗:颜色;名字;品种; 吃;叫;看家;
*/

//创建一个共同的父类动物

abstract class Animal {
	private String color;// 颜色
	private String name;// name
	private String pinZhong;// 品种

	public Animal() {
	}

	public Animal(String color, String name, String pinZhong) {
		this.color = color;
		this.name = name;
		this.pinZhong = pinZhong;
	}

	public String getColor() {
		return color;
	}

	public void setColor(String color) {
		this.color = color;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getPinZhong() {
		return pinZhong;
	}

	public void setPinZhong(String pinZhong) {
		this.pinZhong = pinZhong;
	}

	// 吃
	public abstract void eat();

	// 叫
	public abstract void say();
}

创建类描述狗

class Dog extends Animal {
	public Dog() {
	}

	public Dog(String color, String name, String pinZhong) {
		super(color, name, pinZhong);
	}

	// 吃
	public void eat() {
		System.out.println("吃骨头!");
	}

	// 叫
	public void say() {
		System.out.println("汪汪");
	}

	// 看家
	public void lookHome() {
		System.out.println("看家");
	}
}

//创建类描述猫

class Cat extends Animal {
	public Cat() {
	}

	public Cat(String color, String name, String pinZhong) {
		super(color, name, pinZhong);
	}

	// 猫的吃
	public void eat() {
		System.out.println("吃小鱼!");
	}

	// 猫的叫
	public void say() {
		System.out.println("喵喵喵");
	}

	// 抓老鼠
	public void catchMouse() {
		System.out.println("抓老鼠");
	}
}

新的问题:

在不同的子类中有相同的方法,但是方法的具体实现不同;因为是相同的方法,是共有的功能,所以应该抽取到共同的父类中;

但是又因为不同的子类具体实现不同,所以再父类中没办法给这些共同的方法提供一个具体的实现;

像这种方法,就应该使用新的技术:抽象函数来描述;

抽象函数与抽象类

  • 抽象函数:

当一个类中,知道有某个功能,但是不确定这个功能该如何实现,就应该将这个方法定义为抽象函数;表示描述不清的功能;

  • 书写格式:

抽象函数使用abstract关键字描述,直接写在函数的返回值类型前面;而且抽象函数没有函数体代码,连大括号都不能写;

88e736958034217a6db7032e82c8038a.png
  • 抽象类:

当一个类中存在抽象函数时,就表示这个类描述不清楚,这个类也应该定义为抽象类;

  • 书写格式:

抽象类也是用abstract关键字描述,直接写在class关键字前面;

17a003e957c08fbfcfc78de04da1058b.png

结论:

当多个不能相互继承的类具有相同的功能时,就需要将共同的信息向上抽取,放到公共的父类中;如果公共的父类只能描述所有子类都具有的功能,但描述不清功能的具体实现,就需要将该函数定义为抽象的,使用关键字abstract修饰;

如果一个类中出现了抽象的函数,说明这个类也是不具体的,应该定义为抽象类,使用abstract修饰;

dca50312481241511ceabecbffe0c679.png
https://www.zhihu.com/video/1066470716991582208

抽象类的使用

抽象类不能实例化,只能由子类继承

抽象类不能创建对象,因为抽象类中有抽象的函数;如果可以创建对象,就可以通过对象调用抽象的函数;而调用抽象的函数是没有意义的;

7ead7d4fe6d700fb1a372cbadaa3303b.png

子类继承抽象父类,必须实现父类的所有抽象函数,否则子类也是抽象的

子类继承抽象父类,子类中就把父类的抽象函数也继承下来了;如果不重写这些抽象函数,就说明子类中也有抽象函数,所以子类就要定义为抽象类;

如果子类需要被实例化,就不能定义为抽象类,就不能有抽象函数,就必须在子类中将从父类继承下来的抽象函数重写一下;

a3219e58e95a0bbfdace4d1f34351844.png

抽象类的使用方法

一般开发中,会先定义共同的父类,然后根据具体需求,去书写子类;

如果有些功能在开始不确定如何书写,那么就应该定义为抽象函数,交给子类去实现;

需求:编写程序,模拟手机使用网络信号(NetworkSignal)打电话的功能;;

注意:联通手机(UnicomMobile)和移动手机(MobilePhone)使用的网络不同,所以具体实现也会有不同;

public class AbstractTest {
	public static void main(String[] args) {
		new UnicomMobile().call("18888888888");

		new MobilePhone().call("18666886688");
	}
}

/*
 * 需求:编写程序,模拟手机使用网络信号(NetworkSignal)打电话的功能;;
 * 注意:联通手机(UnicomMobile)和移动手机(MobilePhone)使用的网络不同,所以具体实现也会有不同;
 */
abstract class Phone {
	// 定义一个获取网络的方法
	public abstract String getNet();

	public void call(String num) {
		String net = getNet();
		System.out.println("使用" + net + "网络,给" + num + "打电话!");
	}
}

// 定义一个移动手机,可以使用移动网络
class MobilePhone extends Phone {
	public String getNet() {
		return "移动";
	}
}

// 定义一个联通手机,可以使用联通网络
class UnicomMobile extends Phone {
	public String getNet() {
		return "联通";
	}
}
3ef51f9a3d2a13a0014a96923fef7782.png
https://www.zhihu.com/video/1066471231426985984

抽象类的细节问题

1、一个类是抽象类,那么这个类不能创建对象。

因为如果类是抽象类,类中就有可能存在抽象方法,由于抽象方法是没有方法体。那么如果这个类可以创建对象,

那么就可能会调用到这个类中的抽象方法,那么抽象方法没有方法体,调用这样的方法根本就没有任何的意义。

2、抽象类中有没有构造函数?

有,虽然抽象类不能创建对象,但是它一定会子类,而子类中的构造函数中的隐式super会找父类的构造函数。

所以抽象类中的构造函数不是给自己用的,是给子类创建对象时候使用的。

3、抽象类一定是父类吗?

抽象肯定是父类,因为它必须有子类去复写其中的抽象函数。但不一定是最顶层的父类。

4、抽象类中可以没有抽象方法吗?

可以。如果一个类中一个抽象方法都没有,但是这个类却是抽象类,目的只有一个,就是不让创建这个类的对象。

这种用法主要用在适配器设计模式中。

5、abstract关键字不能和哪些关键字共存?

final:被final修饰的类不能被继承,被final修饰的方法不能被复写。但是abstract修饰的必须要求继承或复写。

private:被private修饰的函数,子类是根本继承不到的。但是abstract修饰的函数要求子类必须复写。

static:静态修饰的方法可以直接通过类名调用。但是abstract修饰的方法根本就没有方法体。调用就没有任何的意义。

  1. 一个类什么时候需要定义为抽象类?
  2. 类中存在抽象函数;
  3. 如果一个类中没有抽象函数,但是不希望被实例化,也需要定义为抽象类;
265cd19ce80bfb06572510303ab42fa6.png
https://www.zhihu.com/video/1066471472343683072

抽象类总结

  • 抽象类的概念:

是使用关键字abstract修饰的类就是抽象类;

  • 抽象类的产生:

当多个不能相互继承的类具有相同的功能时,就需要将共同的信息向上抽取,放到公共的父类中;如果公共的父类只能描述所有子类都具有的功能,但描述不清功能的具体实现,就需要将该函数定义为抽象的,使用关键字abstract修饰;如果一个类中出现了抽象的函数,说明这个类也是不具体的,应该定义为抽象类,使用abstract修饰;

  • 抽象类的特点:

抽象类不能实例化;

抽象类中的抽象函数必须由子类去实现,否则子类也是抽象类;

抽象类有构造函数,是供子类创建对象时使用的;

因为抽象函数必须由子类实现,所以不参与继承的(private)和不能被子类重写的(final)关键字不能和abstract共存;

因为静态函数不需要类的对象就可以直接使用,所以static关键字也不能和abstract共存;

抽象类中可以书写所有成员,也可以没有抽象函数;

如果一个类不希望被实例化,但又希望被子类继承,就可以定义为抽象的,即使类中没有抽象函数;

265cd19ce80bfb06572510303ab42fa6.png
https://www.zhihu.com/video/1066472605619593216

多态

  • 问题的引入

多态:就是同一个事物具有多种不同的表示方式;

ec891748ba672e86ffb7efcc3d957e7e.png

狗;旺财;哈士奇;……

在现实生活中,我们一般更倾向于使用表示范围更广的概念描述这个具体的事物;

衣服:棉衣;羽绒服;毛衣……

水果:苹果、桔子、梨子、香蕉……

131cdd64585ef720d46bc198efa45dbe.png
https://www.zhihu.com/video/1066473618237804544

多态的写法

c394b7e7728de12b9bf7323b72d7d646.png

Java中,多态指的是:父类型引用指向子类对象;

18e2d1a500f6a878cc24a1397077f7eb.png

(接口类型引用指向实现类对象)

aa62032c3336b483ee6e8d8dcdcb120a.png
https://www.zhihu.com/video/1066473707098275840
  • 多态的好处

生活中使用多态,可以指代更广泛的范围;

在Java中,使用多态,也可以使变量的表示范围更广泛,可以提高代码的复用性和程序的扩展性(降低耦合性);

需求:描述并测试猫和狗的行为;

猫具有吃、叫和抓老鼠的功能;

狗具有吃、叫和看家的功能;

过去的做法:
public class Demo2 {
	public static void main(String[] args) {
		// 创建一只猫
		Cat c = new Cat();
		// 调用函数测试猫的行为
		test(c);

		// 创建一条狗
		Dog d = new Dog();
		// 调用函数测试狗的行为
		test(d);
	}

	private static void test(Dog d) {
		// 测试狗的吃的行为
		d.eat();
		// 测试狗叫的行为
		d.say();
		// 测试狗看家的行为
		d.lookHome();
	}

	private static void test(Cat c) {
		// 测试猫吃的行为
		c.eat();
		// 测试猫叫的行为
		c.say();
		// 测试猫抓老鼠的行为
		c.catchMouse();
	}
}

/*
 * 需求:描述并测试猫和狗的行为; 猫具有吃、叫和抓老鼠的功能; 狗具有吃、叫和看家的功能;
 */
// 定义一个类表示狗
class Dog {
	// 吃
	public void eat() {
		System.out.println("吃骨头");
	}

	// 叫
	public void say() {
		System.out.println("汪汪……");
	}

	// 看家
	public void lookHome() {
		System.out.println("看家");
	}
}

// 定义一个类表示猫
class Cat {
	// 吃
	public void eat() {
		System.out.println("吃小鱼");
	}

	// 叫
	public void say() {
		System.out.println("喵喵……");
	}

	// 抓老鼠
	public void catchMouse() {
		System.out.println("抓老鼠");
	}
}

问题:

首先,猫和狗之间存在相同功能;

其次:测试的功能函数,一个函数只能测试一种动物,而且每个测试函数的具体实现基本类似;那么每添加一个新的动物,就必须新写一个测试函数,挺麻烦;

解决办法:使用继承和多态技术:

cbd2f882ccbd9c2a85c11c781da57062.png
public class Demo2 {
	public static void main(String[] args) {
		// 创建一只猫
		Animal c = new Cat();
		// 调用函数测试猫的行为
		test(c);
		// 创建一条狗
		Dog d = new Dog();
		// 调用函数测试狗的行为
		test(d);
	}

	private static void test(Animal c) {
		// 测试动物吃的行为
		c.eat();
		// 测试动物叫的行为
		c.say();
		// 测试动物抓老鼠的行为
		// c.catchMouse();
	}
}

需求:描述并测试猫和狗的行为;

猫具有吃、叫和抓老鼠的功能;

狗具有吃、叫和看家的功能;

//为了提高代码的复用性,可以定义一个猫和狗的共同的父类动物类,然后让猫和狗继承动物类
abstract class Animal {
	public abstract void eat();

	public abstract void say();
}

// 定义一个类表示狗
class Dog extends Animal {
	// 吃
	public void eat() {
		System.out.println("吃骨头");
	}

	// 叫
	public void say() {
		System.out.println("汪汪……");
	}

	// 看家
	public void lookHome() {
		System.out.println("看家");
	}
}

// 定义一个类表示猫
class Cat extends Animal {
	// 吃
	public void eat() {
		System.out.println("吃小鱼");
	}

	// 叫
	public void say() {
		System.out.println("喵喵……");
	}

	// 抓老鼠
	public void catchMouse() {
		System.out.println("抓老鼠");
	}
}

d1353c686067461fbb1cb790b41c8b1d.png
0de9b472420ac207b47d4359025eb025.png
https://www.zhihu.com/video/1066674362819739648

多态的弊端

6afde4e14d490b8294db8e5ebd568882.png

结论:

如果程序中使用了多态,那么在编译期,编译器回去父类型中找需要使用的成员,如果找不到,就会报错;

多态的弊端:

只能使用父类中定义好的成员,不能使用子类特有的成员;

b7b48658f5e1490b8bac925b25812d34.png
https://www.zhihu.com/video/1066674447179771904

java的类型转换

自动向上转型: 可以直接将子类型引用赋值给父类型变量,可以自动进行,叫做自动向上转型;

例如:

class  Fu{}
class Zi extends Fu{}
Zi zi = new Zi();

Fu f = zi;//这行代码就发生了自动向上转型

强制向下转型: java中允许将父类型引用赋值给子类型变量,叫做向下转型;但是这种转型不能自动实现,需要强制进行,所以叫做强制向下转型;

例如:

class  Fu{}
class Zi extends Fu{}
Fu  f  = new Zi();//这行使用了多态,发生了向上转型;

Zi z = (Zi)f; //这行发生了强制向下转型

使用强制向下转型解决多态的弊端:

001bcab2c8a3c2d4336fb0e8e93d98fc.png
18193dc4ba721669e6a8a719e00171d3.png
https://www.zhihu.com/video/1066674688524206080

强制向下转型的问题

Exception in thread "main" java.lang.ClassCastException: com.kuaixueit.duotai.test.Dog cannot be cast to com.kuaixueit.duotai.test.Cat
	at com.kuaixueit.duotai.test.Test.testAnimal(Test.java:9)
	at com.kuaixueit.duotai.test.Test.main(Test.java:23)

6374912ada15c04f3dcc426f01a68265.png

类型转换异常,也是一个运行时异常。一般发生在强制向下转型时,当将一个不属于某个类型的对象强转为这个类型时,就会出现这个问题。

结论:

在使用强制向下转型时,如果将不属于某个类型的对象强制转换为这个类型,就会出现类型转换异常;

71bc6ed8184cc45e38f673da3a289802.png
https://www.zhihu.com/video/1066674971165839360

instanceof关键字

使用格式:

引用类型的变量名 instanceof 类名

作用:

表示判断左边这个变量指向的对象,是否是右边这个类的对象;

这个表达式返回的结果一定是一个布尔值;

2cc6f77fbff9b28d352fdfc65a6aefaf.png

多态中成员使用的特点

编译期:

1963b8d0ca3b1a69a8c78e2eb0ea298c.png

运行期:

b739bbf92fa0dc4a7ceabff802a98881.png

结论:

多态中,编译期,所有成员都要看父类有没有;

运行期,只有非静态函数使用子类中定义的,其他所有成员,都看父类;

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值