草庐IT

多态与抽象

道长爱睡懒觉 2023-08-04 原文

文章目录


认识多态

  1. 多态一词的通常含义是指能够呈现出多种不同的形式或形态。

  2. 在程序设计的术语中,它意味着一个特定类型的变量可以引用不同类型的对象,并且自动地调用引用的对象的方法,也就是根据作用到的不同对象类型,响应不同的操作。

  3. 方法重写是实现多态的基础。

  4. 多态意味着在一次方法调用中根据包含的对象的实际类型(即实际的子类对象)来决定应该调用哪个方法,而不是由用来存储对象引用的变量的类型决定的。

  5. 当调用一个方法时,为了实现多态的操作,这个方法既是在父类中声明过的,也必须是在子类中重写过的方法。

向上转型

  1. 向上转型:子类向父类的转换称为向上转型。

  2. 向上转型的语法格式如下:

<父类型> <引用变量> = new <子类型>();

  • 将一个父类的引用指向一个子类对象称为向上转型,系统会自动进行类型转换。
  • 此时通过父类引用变量调用的方法是子类覆盖或继承了父类的方法,不是父类的方法。
  • 此时通过父类引用变量无法调用子类特有的方法。

向下转型

  1. 向上转型中,父类引用变量无法调用子类特有的方法,如果需要调用子类特有的方法,可以通过把父类转换为子类来实现。

  2. 向下转型:将一个指向子类对象的父类引用赋给一个子类的引用,即将父类类型转换为子类类型,称为向下转型,此时必须进行强制类型转换。

  3. 向下转型的语法格式如下:

<子类型> <引用变量名> = (<子类型>)<父类型的引用变量>;

  1. 上述这种向下转型的操作对接口和抽象(普通)父类同样适用。

instanceof运算符

  1. 在向下转型的过程中,如果不是转换为真实子类类型,会出现类型转换异常(ClassCastException)。

  2. 在Java中提供了instanceof运算符类进行类型的判断。

  3. 使用instanceof时,对象的类型必须和instanceof后面的参数所指定的类有继承关系,否则会出现编译错误。

  4. instanceof通常和强制类型转换结合使用。

多态的优势

1)可替换性:多态对已存在的代码具有可替换性。

2)可扩充性:多态对代码具有可扩充性。增加新的子类不影响已存在类的多态性、继承性,以及其他特征的运行和操作。实际上新加子类更容易获得多态功能。

3)接口性:多态是父类向子类提供了一个共同接口,由子类来具体实现。

4)灵活性:多态在应用中体现了灵活多样的操作,提高了使用效率。

5)简化性:多态简化了应用软件的代码编写和修改过程,尤其在处理大量对象的运算和操作时,这个特点尤为突出和重要。

多态的两种主要应用形式

1、 使用父类作为方法的形参

2、 使用父类作为方法的返回值

多态和继承章节总结

1、 继承是Java中实现代码重用的重要手段之一。Java中只支持单根继承,即一个类只能有一个直接父类。Object类是所有Java类的祖先。

2、 在子类中可以根据实际需要对从父类继承的方法进行重新编写,称为方法的重写或覆盖。

3、 子类中重写的方法和父类中被重写的方法必须具有相同的方法名、参数列表,返回值类型必须和被重写方法的返回值类型相同。

4、 在实例化子类是,会首先执行其父类的构造方法,然后在执行子类的构造方法。

5、 通过super关键字可以访问父类的成员。

6、 通过多态可以减少类中的代码量,可以提高代码的可扩展性和可维护性。继承是多态的基础,没有继承就没有多态。

7、 在多态的应用中,可以使用父类作为方法的形参,还可以作为方法的返回值。

8、 把子类转换为父类称为向上转型,系统自动进行类型转换。把父类转换为子类,称为向下转型,必须进行强制类型转换。

9、向上转型后,通过父类引用变量调用的方法是子类覆盖或继承自父类的方法,通过父类引用变量无法调用子类特有的方法。

10、 向下转型后可以访问子类特有的方法。向下转型必须转换为父类指向的真实子类类型,否则将出现类型转换异常ClassCastException。

11、 instanceof运算符用于判断一个对象是否属于一个类。

抽象方法和抽象类

区分普通方法和抽象方法

(1)在Java中,当一个类的方法被abstract关键字修饰时,该方法称为抽象方法。
(2)抽象方法所在的类必须定义为抽象类。
(3)当一个方法被定义为抽象方法后,意味着该方法不会有具体的实现(没有方法体),而是在抽象类的子类中通过方法重写进行实现。
(4)定义抽象方法的语法格式如下:

[访问修饰符] abstract <返回类型> <方法名>([参数列表]);

  • 抽象方法需要使用abstract修饰,普通方法不需要。
  • 抽象方法没有方法体,普通方法没有。
  • 抽象方法所在的类必须被定义为抽象类。
  • 抽象方法在子类中必须被实现(子类要重写父类中的抽象方法),如果子类不实现,则子类要定位为抽象类。
  • private关键字不能用来修饰抽象方法。
  • abstract修饰符不能和final修饰符一起使用。

区分普通类和抽象类

(1)在Java中,当一个类被abstract关键字修饰时,该类称为抽象类。
(2)定义抽象类的语法格式如下:

abstract class <类名>{
				//代码
		}
  • 抽象类需要用修饰符abstract修饰,普通类不需要。
  • 普通类可以实例化,抽象类不能被实例化。
  • 抽象类中可以有抽象方法也可以没有抽象方法,可以有普通方法也可以没有普通方法。
  • 抽象类中可以包含普通类包含的一切成员。

定义一个抽象类

当一个类被定义为抽象类时,它可以包含各种类型的成员,包括属性、方法等。其中方法又可以分为普通方法和抽象方法。

public abstract class 类名称{
	修饰符 abstract 返回类型 方法名();
	修饰符 返回类型 方法名(){
		//方法体
	}
}

使用抽象类描述抽象的事物

当一个类实例化没有意义时,可以将该类定义为抽象类

抽象类和抽象方法的优势

(1)抽象类中已经实现的方法可以被子类使用,使代码可以被复用。
(2)抽象类中的抽象方法,子类需要进行重写,保证了子类具有自身的独特性。

抽象类的局限性

(1)抽象类有时候会出现代码冗余的问题。
(2)类的继承是单根继承,一个类只能直接继承一个类。

案例

package cn.bdqn.demo03;

public class Animal {

	private String name;
	private int health;
	private int love;

	public Animal() {
		super();// 调用Object类里无参构造方法
	}

	public Animal(String name, int health, int love) {
		super();
		this.name = name;
		this.health = health;
		this.love = love;
	}

	public String getName() {
		return name;
	}

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

	public int getHealth() {
		return health;
	}

	public void setHealth(int health) {
		this.health = health;
	}

	public int getLove() {
		return love;
	}

	public void setLove(int love) {
		this.love = love;
	}

	public void print() {
		System.out.println("Animal:宠物信息:昵称:" + this.getName() + ",健康值:"
				+ this.getHealth() + ",亲密度:" + this.getLove());
	}
	
	public void toHospital(){
		System.out.println("宠物看病......");
	}

}

package cn.bdqn.demo03;

public class Cat extends Animal {

	private String color;

	public Cat() {
		super();
	}

	public Cat(String name, int health, int love, String color) {
		super(name, health, love);
		this.color = color;
	}

	public String getColor() {
		return color;
	}

	public void setColor(String color) {
		this.color = color;
	}
	
	@Override
	public void print() {
		super.print();
		System.out.println("猫的颜色:"+this.getColor());
	}
	
	@Override
	public void toHospital() {
		System.out.println("打针,吃药......");
		this.setHealth(90);
	}
}

package cn.bdqn.demo03;

public class Dog extends Animal {
	//在这个Dog类中只定义Dog类中特有的属性和方法,原来和Penguin类中相同的代码在Animal类中,通过继承获取,使用extends关键字来获取
	
	private String strain;

	public Dog() {
		super();//表示使用Animal类中的无参构造方法
	}

	public Dog(String name, int health, int love, String strain) {
		super(name, health, love);//表示使用Animal类中的有参构造方法
		this.strain = strain;
	}

	public String getStrain() {
		return strain;
	}

	public void setStrain(String strain) {
		this.strain = strain;
	}
	
	
	
	public void print(){
		super.print();
		System.out.println("Dog:品种:"+this.getStrain());
	}
	
	@Override
	public void toHospital() {
		System.out.println("打针......");
		this.setHealth(80);
	}
	
	//定义Dog类中特头的方法
	public void eat(){
		System.out.println("狗吃骨头......");
	}

}

package cn.bdqn.demo03;

public class Penguin extends Animal {
	//定义企鹅类中特有的属性
	private String sex;

	public Penguin() {
		super();//表示使用Animal类中的无参构造方法
	}

	public Penguin(String name, int health, int love, String sex) {
		super(name, health, love);
		this.sex = sex;
	}

	public String getSex() {
		return sex;
	}

	public void setSex(String sex) {
		this.sex = sex;
	}
	
	@Override
	public void print() {
		
		super.print();
		System.out.println("Penguin:性别:"+this.getSex());
	}

	@Override
	public void toHospital() {
		System.out.println("吃药......");
		this.setHealth(75);
	}
	
	//定义Penguin类中特头的方法
	public void swimming(){
		System.out.println("企鹅会仰泳");
	}
	
}

package cn.bdqn.demo03;

public class Tiger extends Animal {
	
	private String weight;

	public Tiger() {
		super();
	}

	public Tiger(String name, int health, int love, String weight) {
		super(name, health, love);
		this.weight = weight;
	}

	public String getWeight() {
		return weight;
	}

	public void setWeight(String weight) {
		this.weight = weight;
	}
	
	@Override
	public void toHospital() {
		System.out.println("吃一只鸡......");
		this.setHealth(99);
	}
	
	//定义Tiger类中特有的方法
	public void sleep(){
		System.out.println("老虎打盹");
	}

	
}

package cn.bdqn.demo03;

public class Master {
	//定义给宠物Animal看病的方法
	public void cure(Animal animal){
		if(animal.getHealth()<60){
			//这里animal调用的方法在形式上看是调用Animal类中的toHospital()方法,实际上调用的方法是animal对象指向的子类中重写后的toHospital()方法
			animal.toHospital();
		}
	}

}

package cn.bdqn.demo03;

public class Test {

	public static void main(String[] args) {
		// 创建Master类对象
		Master master = new Master();
		
		//创建Dog类对象
//		Dog dog = new Dog("宝马", 30, 90, "泰迪");
//		master.cure(dog);
		
		//向上转型:父类的引用(对象名)指向子类的实例(对象)
		Animal animal = new Dog("奥迪", 30, 90, "泰迪");
		System.out.println("看病前的健康值:"+animal.getHealth());
		master.cure(animal);
		System.out.println("看病后的健康值:"+animal.getHealth());
		//父类引用无法调用子类中特有的方法
		//animal.eat();
		//向下转型:子类的引用(对象名)指向父类的引用(对象名)
		Dog dog =(Dog)animal;
		dog.eat();
		
		
		animal = new Penguin("精灵", 20, 88, "母");
		System.out.println("看病前的健康值:"+animal.getHealth());
		master.cure(animal);
		System.out.println("看病后的健康值:"+animal.getHealth());
		//animal是父类引用,无法调用子类中特有的方法
//		animal.swimming();
		Penguin penguin = (Penguin)animal;
		penguin.swimming();
		
		animal = new Tiger("东北虎", 10, 99, "500公斤");
		System.out.println("看病前的健康值:"+animal.getHealth());
		master.cure(animal);
		System.out.println("看病后的健康值:"+animal.getHealth());
		//父类引用无法调用子类中特有的方法
//		animal.sleep();
//		Tiger tiger =(Tiger)animal;
//		tiger.sleep();
//		Penguin penguin2 = (Penguin)animal;//ClassCastException 类型转换异常,父类没有转换成其指向的子类
//		penguin2.swimming();
		/*
		 * 在向下转型的时候,有可能转换错误,没有转换成其指向的子类,这时候会报ClassCastException异常
		 * 我们可以在转型之前使用instanceof关键字进行判断父类引用指向了哪个子类对象
		 * 
		 * 
		 */
		
		if(animal instanceof Dog){
			Dog dog2 =(Dog)animal;
			dog2.eat();
		}else if(animal instanceof Penguin){
			Penguin penguin2 = (Penguin)animal;
			penguin2.swimming();
		}else if(animal instanceof Tiger){
			Tiger tiger2 =(Tiger)animal;
			tiger2.sleep();
		}
		

	}

}

总结

  1. 方法重写是实现多态的基础。
  2. 向上转型:子类向父类的转换称为向上转型。
  3. 向下转型:将一个指向子类对象的父类引用赋给一个子类的引用,即将父类类型转换为子类类型,称为向下转型,此时必须进行强制类型转换。
  4. 多态的优势:可替换性,可扩充性,接口性,灵活性,简化性。
  5. 多态的两种主要应用形式:
    1) 使用父类作为方法的形参
    2) 使用父类作为方法的返回值
  6. 抽象类需要用修饰符abstract修饰,普通类不需要。
  7. 普通类可以实例化,抽象类不能被实例化。
  8. 抽象类中可以有抽象方法也可以没有抽象方法,可以有普通方法也可以没有普通方法。
  9. 抽象类中可以包含普通类包含的一切成员。
  10. 抽象类和抽象方法的优势
    (1)抽象类中已经实现的方法可以被子类使用,使代码可以被复用。
    (2)抽象类中的抽象方法,子类需要进行重写,保证了子类具有自身的独特性。
  11. 抽象类的局限性
    (1)抽象类有时候会出现代码冗余的问题。
    (2)类的继承是单根继承,一个类只能直接继承一个类。

有关多态与抽象的更多相关文章

  1. ruby-on-rails - rails 多态关联(遗留数据库) - 2

    我使用的是遗留数据库,所以我无法控制数据模型。他们使用了很多多态链接/连接表,就像这样createtableperson(per_ident,name,...)createtableperson_links(per_ident,obj_name,obj_r_ident)createtablereport(rep_ident,name,...)其中obj_name是表名,obj_r_ident是标识符。因此链接的报告将按如下方式插入:insertintoperson(1,...)insertintoreport(1,...)insertintoreport(2,...)insertint

  2. ruby - ActiveRecord,通过多态属性查找 - 2

    有这个:classEventtrueenduser=User.create!我可以:Event.create!(:historizable=>user)但我不能:Event.where(:historizable=>user)#Mysql2::Error:Unknowncolumn'events.historizable'in'whereclause'我必须改为这样做:Event.where(:historizable_id=>user.id,:historizable_type=>user.class.name)更新重现问题的代码:https://gist.github.com/fg

  3. ruby-on-rails - 如何使用 graphql-ruby 指定多态类型? - 2

    我有一个UserType和一个可以是Writer或Account的userable。对于GraphQL,我想也许我可以像这样使用UserableUnion:UserableUnion=GraphQL::UnionType.definedoname"Userable"description"AccountorWriterobject"possible_types[WriterType,AccountType]end然后像这样定义我的用户类型:UserType=GraphQL::ObjectType.definedoname"User"description"Auserobject"fie

  4. python - Ruby 是否有相当于 Python 的扭曲框架作为网络抽象层? - 2

    据我了解,Python的扭曲框架为网络通信提供了更高级别的抽象(?)。我正在寻找在Rails应用程序中使用与twisted等效的Ruby。 最佳答案 看看EventMachine.它不像Twisted那样广泛,但它是围绕事件驱动网络编程的相同概念构建的。 关于python-Ruby是否有相当于Python的扭曲框架作为网络抽象层?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/9

  5. ruby - ruby 中的过程和数据抽象 - 2

    我是Ruby新手。我正在学习ruby​​中的抽象原则。据我了解,过程抽象是对用户隐藏实现细节,或者只是专注于要点而忽略细节。我关心的是如何实现它1)是不是一个简单的函数调用就这样#functiontosortarray#@paramsarray[Array]tobesortdefmy_sort(array)returnarrayifarray.sizearray[i+1]array[i],array[i+1]=array[i+1],array[i]swapped=trueendendendarrayend然后这样调用sorted_array=my_sort([12,34,123,43,

  6. sql - 如何获得不同的多态关联 - 2

    我试图在没有任何重复的情况下显示多态关系列表。我有一个StoreViews表,其中包含一个名为viewable的多态字段(因此我的表中有一个viewable_id和viewable_type列)。现在我想显示View,每个多态关系只显示一次,没有重复。@views=StoreView..distinct(:viewable_id).distinct(:viewable_type).order("created_atDESC").limit(10)因此,如果StoreViews中有两条记录,并且都具有相同的可见关系,@views应该只返回最近的一条。然而,事实并非如此。

  7. ruby-on-rails - 在 Rails 3 中将现有的 has_many 关系更改为多态 - 2

    我在两个模型之间有一个现有的has_many关系-称它们为“汽车”和“乘客”-在我的生产环境中有几千个“乘客”属于几百个“卡片”。我正在添加另一个模型,称之为“火车”,我想将汽车和乘客之间现有的has_many关系更改为多态关系,将每位乘客与汽车或火车相关联。我的迁移应该是什么样的?我想在迁移时保留数据库中的现有关系,所以我宁愿进行一些表重命名,而不是删除一列以将其替换为另一列。此外,我希望能够在不打开服务器控制台并手动编辑所有记录的情况下执行此操作,这样当我将更改推送到生产环境时,我可以一次性迁移整个数据库。有什么建议吗?TL;DR:如何使用新模型将现有的has_many关系更改为多

  8. ruby-on-rails - Ruby:Declarative_authorization 多态关联 - 2

    我有两个模型(项目和主题)。它们都属于具有has_many关联的第三个模型用户(用户有很多主题和项目)。Item和Theme都有_many:images.图像模型是一个多态关联,因此该表具有列imageable_id和imageable_type。如果我同时拥有一个ID为1的项目和一个ID为1的主题,那么该表将如下所示idimageable_idimageable_type------------------------------------11Item21Theme我正在使用declarative_authorization重写我的数据库的SQL查询,以防止用户访问他们帐户之外的项

  9. ruby-on-rails - 多态关联 - 2

    如果您有多态的belongs_to关联,那么引用将添加所需的两个列:create_table:productsdo|t|t.references:attachment,:polymorphic=>{:default=>'Photo'}end将添加一个attachment_id列和一个默认值为“Photo”的字符串attachment_type列。这到底是什么意思? 最佳答案 这里是引用方法的文档:http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/Ta

  10. ruby-on-rails - Ruby on Rails 多态关系的优点和缺点 - 2

    关闭。这个问题需要更多focused.它目前不接受答案。想改进这个问题吗?更新问题,使其只关注一个问题editingthispost.关闭9年前。Improvethisquestion您知道RubyonRails多态关系的优点和缺点。

随机推荐