【Java基础教程】(十四)面向对象篇 · 第八讲:多态性详解——向上及向下转型、关键字 final与 instanceof的作用~

这篇具有很好参考价值的文章主要介绍了【Java基础教程】(十四)面向对象篇 · 第八讲:多态性详解——向上及向下转型、关键字 final与 instanceof的作用~。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

【Java基础教程】(十四)面向对象篇 · 第八讲:多态性详解——向上及向下转型、关键字 final与 instanceof的作用~,# Java基础教程,java,开发语言,jvm,经验分享,java-ee,后端

本节学习目标

  • 掌握final 关键字的主要作用及使用;
  • 掌握对象多态性的概念以及对象转型的操作;
  • 掌握instanceof 关键字的主要作用及使用;

1️⃣ final 关键字

在Java 中 final称为终结器,在Java 里面可以使用 final定义类、方法和属性,用于表示不可变性

  • final类:当一个类被声明为final时,意味着该类不能被继承。换句话说,不能创建该类的子类。通常将类声明为final的主要原因是防止其他类修改或扩展该类的行为。例如,java.lang.String就是一个被声明为final的类;
  • final方法:当一个方法被声明为final时,意味着该方法不能被子类重写或覆盖。这种限制可以保护方法的实现,确保不会被修改。通常情况下,一个方法被声明为final是因为它的实现在父类中已经足够完善,不希望子类对其进行修改;
  • final变量:当一个变量被声明为final时,意味着该变量的值不能被更改。一旦给final变量赋值,就不能再修改它的值。通常将变量声明为final是为了防止其被重新分配或重新赋值,保持其不可变性。final变量可以是基本类型(如intdouble等)或引用类型(如对象、数组引用等),但引用类型的final变量指的是不能被重新分配,即不能再指向其他对象,但仍然可以修改对象的状态。

使用final关键字可以带来以下好处:

  • 提高性能:final关键字可以使编译器进行优化,因为它知道这些元素不会被改变,所以可以进行一些优化处理。
  • 增加安全性:final关键字可以防止意外修改或覆盖。当某个类、方法或变量对于某种场景下应该保持固定不变时,使用final可以提供更强的安全性和可靠性。
  • 支持设计决策:通过将类、方法或变量声明为final,可以明确表达出它们在设计中的意图和限制。

1.1 final类

使用 final 定义的类不能再有子类,即:任何类都不能继承以 final 声明的父类。

//	范例 1: 观察 final 定义的类
final class A{}       // 此类不能够有子类

class B extends A{}  	// 错误的继承

此程序中由于 A 类在定义时使用了 final关键字,这样 A 就不能再有子类了,所以当 B 类继承 A 类时会在编译时出现语法错误。

需要注意的是,只是进行应用开发的话,那么大部分情况下不需要使用 final 来定义类。而如果是一些系统架构的代码开发时,才有可能会使用到这样的代码。同时要注意一点:String也是使用 final 定义的类,所以String类不允许被继承。

1.2 final方法

使用 final 定义的方法不能被子类所覆写。 在一些时候由于父类中的某些方法具备一些重要的特征,并且这些特征不希望被子类破坏(不能够覆写), 就可以在方法的声明处加上final, 意思是子类不要去破坏这个方法的原本作用。

//	范例 2: 观察 final 定义方法
class A{
	public final void fun(){}  // 此方法不允许子类覆写
}

class B extends A{
	public void fun(){}			//错误:此处不允许覆写
}

此程序在 A 类中定义的 fun()方法上使用了final进行定义,这就意味着子类在继承 A 类后将不允许覆写 fun()方法。

1.3 final属性

使用 final定义的变量就成为了常量,常量必须在定义的时候设置好内容,并且不能修改。

//	范例 3: 定义常量
class A{
	final double GOOD = 100.0;	//GOOD级别就是100.0

	public final void fun(){
		GOOD = 1.1;                          //错误:不能够修改常量
	}
}

此程序使用 final 定义了一个常量 “GOOD”, 这就相当于利用 “GOOD” 这个名称代表了“100.0” 这个数据。所以代码中定义常量的最大意义在于:使用常量可以利用字符串(常量名称)来更直观地描述数据。

大家可以发现本处定义的常量名称使用了全部字母大写的形式(final double GOOD =100.0;),这是Java中的命名规范要求,这样做的好处是可以明确地与变量名称进行区分,开发中也应该遵守。

而在定义常量中还有一个更为重要的概念 — — 全局常量,所谓全局常量指的就是使用了"public"、“static”、“final”3个关键字联合定义的常量,例如:

public static final String MSG = "小山";

static 修饰的数据保存在公共数据区,所以此处的常量就是一个公共常量。同时一定要记住,在定义常量时必须对其进行初始化赋值,否则将出现语法错误。

2️⃣ 多态性

前面文章中已经详细解析了面向对象的封装性、继承性两大特征,而多态是面向对象的最后一个主要特征,也是一个非常重要的特性,掌握了多态性才可以编写出更加合理的面向对象程序。而多态性在开发中可以体现在以下两个方面:

  • 方法的多态性:重载与覆写;

    • 方法重载:同一个方法名称,根据不同的参数类型及个数可以完成不同的功能;
    • 方法覆写:同一个方法,根据实例化的子类对象不同,所完成的功能也不同。
  • 对象的多态性:父子类对象的转换。

    • 向上转型:子类对象变为父类对象,格式:父类 父类对象 = 子类实例,自动类型转换;
    • 向下转型:父类对象变为子类对象,格式:子类 子类对象 = (子类) 父类实例,强制类型转换。

对于方法的多态性在之前已经有了详细地阐述,所以本节主要介绍对象多态性,有一点需要注意的是,对象多态性和方法覆写是紧密联系在一起的。

当然,要想真正理解多态性是如何去应用的,多态性更合理的解释需要结合抽象类与接口来一起讲解,下一篇文章将会为大家介绍抽象类与接口等知识,而要想充分理解这一概念也需要更多时间的了解与沉淀。

//	范例 4: 观察如下程序
class A{
	public void print(){
		System.out.println("A 、public void print(){}");
	}
}

class B extends A{
	public void print(){  //  此时子类覆写了父类中的print()方法
		System.out.println("B 、public void print(){}");
	}
}

public class TestDemo {
	public static void main(String args[]){
		B b = new B();                       //实例化的是子类对象
		b.print();                                  //调用被子类覆写过的方法
	}
}

程序执行结果:

B 、public void print(){}

此程序在方法覆写中已经讲解过了,由于现在子类B 覆写了A 类中的 print(), 并且在主方法中实例化的是子类对象,这样当调用 print()方法时调用的一定是已经被覆写过的方法。也就是说在本程序中需要观察以下两点。
(1)观察实例化的是哪个类的对象:“new B()”;
(2)观察调用的方法是否被子类所覆写,如果覆写了,则调用被覆写过的方法,否则调用父类方法。

2.1 向上转型

那么这样的概念与对象的多态性有什么关联呢?下面对上边案例的主方法进行一些变更,以观察对象的向上转型操作。

//	范例 5: 对象向上转型(自动完成)
public class TestDemo {
	public static void main(String args[]){
		A a = new B();  // 实例化的是子类对象,对象向上转型
		a.print();     	//调用被子类覆写过的方法
	}
}

程序执行结果:

B 、public void print(){}

可以看到此程序的执行结果与范例4 的程序执行结果没有任何区别,然而在本程序中发生了对象的向上转型操作 (A a = new B();),并且最终由父类对象调用了 print() 方法,但是最终的执行结果却是被子类所覆写过的方法的执行结果。

而产生这样结果的原因也很好理解,在范例4 中已经重点强调过:根据实例化对象所在类是否覆写了父类中的方法来决定最终执行,此程序实例化的是子类对象 (new B()),并且 print() 方法又被子类覆写了,那么最终所调用的一定是被覆写过的方法。

实际上通过此程序大家可以发现对象向上转型的特点,整个操作中根本不需要关心对象的声明类型,关键在于实例化新对象时所调用的是哪个子类的构造,如果方法被子类所覆写,调用的就是被覆写过的方法,否则就调用父类中定义的方法。这一点与方法覆写的执行原则是完全一样的。

2.2 向下转型

在清楚了对象的向上转型操作及特点后,下面再来观察对象的向下转型操作。

//	范例 6: 对象向下转型
public class TestDemo {
	public static void main(String args[]){
		A a = new B();		//实例化的是子类对象,对象向上转型 
		B b = (B) a;		//对象需要强制性地向下转型
		b.print();			//调用被子类覆写过的方法
	}
}

程序执行结果:

B 、public void print(){}

此程序首先利用对象的向上转型实例化了父类A 的对象,然后将此对象进行向下转型为子类 B 的对象,由于整个代码中关键字 new 调用的是子类 B 的构造 (new B()),所以调用的是被子类 B 所覆写的 print() 方法。

因为有强制性转换的操作,所以向下转型操作本身是有前提条件的,即必须发生向上转型后才可以发生向下转型。如果是两个没有关系的类对象发生强制转换,就会出现 “ClassCastException” 异常。

//	范例 7: 错误的转型操作
public class TestDemo {
	public static void main(String args[]){
		A a = new A(); 		//直接实例化对象, 此时并没有发生子类对象向上转型的操作,所以强制转型会带来安全隐患
		B b = (B)a;      	//强制向下转型,此处产生“ClassCastException”异常
		b.print();          //此语句无法执行
	}
}

程序执行结果:

Exception in thread "main" java.lang.ClassCastException: A cannot be cast to B at TestDemo.main(TestDemo.java:29)

本程序出现的异常表示的是类转换异常,指的是两个没有关系的类对象强制发生转型时所带来的异常。因为此时并没有发生向上转型,所以向下转型是会存在安全隐患的,开发中应该尽量避免此类操作。

对象多态性的本质是根据实例化对象所在的类是否覆写了父类中的指定方法来决定最终执行的方法体,那么这种向上或向下的对象转型有什么意义呢?

在实际开发中,对象向上转型的主要意义在于参数的统一,也是最为重要的用法,而对象的向下转型指的是调用子类的个性化操作方法。

//	范例 8: 对象向上转型作用分析
class A {
	public void print(){
		System.out.println("A、public void print(){}");
	}
}

class B extends A{    //定义A的子类
	public void print(){	//此时子类覆写了父类中的print()方法
		System.out.println("B、public void print(){}");
	}
}

class C extends A {    //定义A的子类
	public void print(){	//此时子类覆写了父类中的print()方法
		System.out.println("C、public void print(){}");
	}
}

public class TestDemo {
	public static void main(String args[]){
		fun(new B());    //对象向上转型,等价于:A a=new B();
		fun(new C());    //对象向上转型,等价于:A a=new C();
	}
	
	/**
	* 接收A类或其子类对象,不管传递哪个对象都要求调用print()方法
	* @param a A类实例化对象
	*/
	public static void fun(A a)(
		a.print();
	}
}

程序执行结果:

B、public void print(){}
C、public void print(){}

在此程序的 fun() 方法上只是接收了一个 A 类的实例化对象,按照对象的向上转型原则,此时的 fun() 方法可以接收 A类对象或所有A类的子类对象,这样只需要一个A类的参数类型,此方法就可以处理一切 A的子类对象 (即便A类有几百万个子类,fun() 方法依然可以接收及处理 )。 而在 fun() 方法中将统一调用 print() 方法,如果此时传递的是子类对象,并且覆写过 print()方法,就表示执行被子类所覆写过的方法。

如果说向上转型是统一调用的参数类型,那么向下转型就表示要执行子类的个性化操作方法。实际上当发生继承关系后,父类对象可以使用的方法必须在父类中明确定义,例如:此时在父类中存在一个 print() 方法,哪怕这时此方法被子类覆写过,父类对象依然可以调用。但是如果子类要扩充一个 funB()方法,这个方法父类对象并不知道, 一旦发生向 上转型,那么 funB()方法父类对象肯定无法使用。

//	范例 9: 子类扩充父类方法
class A{
	public void print(){
		System.out.println("A、public void print(){}");
	}
}

class B extends A{  //定义A的子类
	public void print(){	//此时子类覆写了父类中的print()方法
		System.out.println("B、public void print(){}");
	}
	
	/**
	* 在子类中扩充一个新的方法,但是此方法只能由子类对象调用,父类对象不能调用 
	*/
	public void funB(){
		System.out.println("B、扩充的funB()方法");
	}
}

此程序在子类B中定义的 funB() 方法在子类对象发生向上转型时( A a = new B();),父类对象将无法调用,也就是说这个方法是子类自己的特殊功能,并没有在父类中定义,如果此时要想调用子类中的方法,就必须将父类对象向下转型。

//	范例 10: 向下转型,调用子类中的特殊功能
public class TestDemo {
	public static void main(String args[]){
		fun(new B());//向上转型,只能调用父类中定义的方法
	}
	public static void fun(A a){
		B b = (B)a;  //要调用子类的特殊操作,需要向下转型
		b.funB();    //调用子类的扩充方法
	}
}

程序执行结果:

B、扩充的funB()方法

此程序如果要调用 fun()方法,则子类B的实例化对象一定要发生向上转型操作,但是这个时候父类对象无法调用子类BfunB()方法,所以需要进行向下转型才能正常调用 funB()方法。但是如果每一个子类都去大量扩充自己的新功能,这样就会严重破环开发的参数统一性,所以方法应该以父类为主,子类可以覆写父类方法,但尽量不要扩充新的方法。

通过以上所有分析可以发现如下特点:
向上转型:其目的是参数的统一,但是向上转型中,通过子类实例化后的父类对象所能调用的方法只能是父类中定义过的方法;
向下转型:其目的是父类对象要调用实例化它的子类中的特殊方法,但是向下转型是需要强制转换的,这样的操作容易带来安全隐患。

以个人经验来说,对于对象的转型,实际上 80%的情况下都只会使用向上转型,因为可以得到参数类型的统一,方便于程序设计;并且子类定义的方法大部分情况下应该以父类的方法名称为标准进行覆写,而不要过多地扩充方法。5% 的情况下会使用向下转型,目的是调用子类的特殊方法。还有15%的情况下是不转型,例如:String

2.3 关键字 instanceof

为了保证转型的顺利进行,Java 提供了一个关键字:instanceof,利用此关键字可以判断某一个对象是否是指定类的实例,使用格式如下。

//	返回boolean型
对象 instanceof

如果某个对象是某个类的实例,就返回 true, 否则返回 false

//	范例 11: 使用instanceof 判断
public class TestDemo  {
	public  static void main(String  args[]){
		A a = new B();               //对象向上转型
		System.out.println(a instanceof A);
		System.out.println(a instanceof B);
		System.out.println(null instanceof A);
	}
}

程序执行结果:

true
true
false

此程序利用 instanceof 判断了某个对象是否是指定类的实例,通过程序的执行结果可以发现 a 对象由于采用了向上转型进行实例化操作,所以 aA 类或 B类的实例化对象,而 null 在使用 instanceof 判断时返回的结果为 false

既然 instanceof 关键字可以准确地判断出实例化对象与类的关系,那么就可以在进行对象强制转换前进行判断,以保证安全可靠的向下转型操作。

从实际的开发来讲,向下转型的操作几乎是很少见到的,但是如果真的出现了,并且开发者不确定此操作是否安全时,一定要先使用instanceof 关键字判断。

//	范例 12: 使用 instanceof  判断
class A{
	public void print(){
		System.out.println("A、public void print(){}");
	}
}

class B extends A{                         //定义A 的子类
	public void print(){               //此时子类覆写了父类中的print()方法
		System.out.println("B、public void print(){}");
	}
	public void funB(){
		System.out.println("B、扩充的funB()方法");
	}
}

public class TestDemo {
	public static void main(String args[]){
		fun(new B());
	}
	public static void fun(A a){ //对象向上转型
		a.print();
		if (a instanceof B){ //如果 a 对象是B 类的实例
			B b= (B)a;		//向下转型
			b.funB();		//调用子类扩充的方法
		}
	}
}

程序执行结果:

B、public void print(){}
B、扩充的funB()方法

在此程序中为了保证安全的向下转型操作,在将父类转换为子类对象时首先使用了 instanceof 进行判断,如果当前对象是子类实例,则进行强制转换,以调用子类的扩充方法。

🌾 总结

在本文中我们学习了Java中两个重要的概念:final关键字和多态性。

首先,我们了解到final关键字可以应用于类、方法和属性。final类表示不可继承,final方法表示不可覆盖,而final属性表示不可修改。使用final关键字可以提高代码的安全性和性能,并明确表达设计意图。

其次,我们探讨了多态性的概念。多态性是面向对象编程的重要特性,其中包括向上转型、向下转型和关键字instanceof。通过向上转型,我们可以实现参数的统一和代码的灵活性。而向下转型则允许调用子类的个性化操作方法。关键字instanceof则可用于检查对象是否属于某个类或其子类的实例。

理解final关键字和多态性对于Java开发至关重要。它们能帮助我们设计更安全、灵活且易于维护的代码。合理运用final关键字,可以防止不必要的修改和继承;深入理解多态性,可以提高代码的可扩展性和可复用性。

最后,我们需要注意在使用final关键字时要谨慎,并根据具体需求进行选择。同时,熟练掌握多态性的概念和技巧,有助于编写更具有弹性和适应性的代码。


温习回顾上一篇(点击跳转)《【Java基础教程】(十三)面向对象篇 · 第七讲:继承性详解——继承概念及其限制,方法覆写和属性覆盖,关键字super的魔力~》

继续阅读下一篇(点击跳转)《【Java基础教程】(十五)面向对象篇 · 第九讲:抽象类和接口——定义、限制与应用的细节,初窥模板设计模式、工厂设计模式与代理设计模式~》
文章来源地址https://www.toymoban.com/news/detail-568674.html

【Java基础教程】(十四)面向对象篇 · 第八讲:多态性详解——向上及向下转型、关键字 final与 instanceof的作用~,# Java基础教程,java,开发语言,jvm,经验分享,java-ee,后端

到了这里,关于【Java基础教程】(十四)面向对象篇 · 第八讲:多态性详解——向上及向下转型、关键字 final与 instanceof的作用~的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Java基础教程】(七)面向对象篇 · 第一讲:上干货!面向对象的特性、类与对象、内存结构引用分析、垃圾收集器 GC处理、封装性详解、构造方法、匿名对象、简单 Java 类~

    程序是将数据和逻辑封装在一起的代码段。在Java中,方法是常用的代码段封装方式。然而,在Java中,方法必须存在于一个类中才能使用。因此,我们将进入本章的核心内容——面向对象编程。 利用面向对象设计的程序可以实现代码的重用,并方便开发者进行项目维护。面向

    2024年02月13日
    浏览(44)
  • 前端技术学习第八讲:VUE基础语法---初识VUE

    Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时

    2023年04月27日
    浏览(47)
  • 【Java基础教程】(八)面向对象篇 · 第二讲:Java 数组全面解析——动态与静态初始化、二维数组、方法参数传递、排序与转置、对象数组、操作API~

    掌握数组的动态及静态创建方式、使用及特征; 掌握引用类型数据的特征; 掌握数组的排序、转置操作; 数组可以将多个变量进行统一的命名,这样相同类型的元素就可以按照一定的顺序进行组合排列 。在 Java中,数组属于引用类型数据,所以在数组的操作过程中,也一定

    2024年02月13日
    浏览(53)
  • 第八站:C++面向对象(继承和派生)

     派生:由父类派生出子类 继承:子类继承父类(继承不会继承 析构函数和构造函数 : 父类的所有成员函数,以及数据成员,都会被子类继承! ) \\\"子类派生出的类\\\"会指向\\\"父类被继承的类\\\", 父类就是基类 实例1: 先创建一个父类,有私有成员数据(name,和age),成员函数,描述信息,有参的

    2024年01月19日
    浏览(44)
  • Java面向对象程序设计实验报告(实验二 面向对象基础练习)

     ✨ 作者: 命运之光  ✨  专栏:Java面向对象程序设计实验报告 目录 ✨一、需求设计 ✨二、概要设计 ✨三、详细设计 ✨四、调试结果 ✨五、测试结果 ✨附录:源程序代码(带注释) 测试类demo2 Address类 Employee类 实验二 面向对象基础练习 实验环境: Eclipse+JDK 实验目的:

    2024年02月06日
    浏览(75)
  • Java基础 --- 面向对象

    面向:拿、找 对象:能干活的东西 面向对象编程:拿东西过来坐对应的事情 类(设计图):是对象共同特征的描述; 对象:是真实存在的具体东西 在Java中,必须先设计类,才能获得对象。 如何定义类 如何得到类的对象 如何使用对象 访问属性:对象名.成员变量 访问行为

    2024年03月17日
    浏览(44)
  • [Java基础]面向对象

           目录 1、对象和类 2、类之间的关系 3、引用 4、对象的创建和使用 5、构造函数/构造方法 6、内存解析        在这篇文章中,我们将学习面向对象的思想,并学习如何使用面向对象思想编程。在学习面向对象编程之前,我们先了解一下编程语言的发展:机器语言,汇

    2024年02月08日
    浏览(42)
  • Android java基础_多态性

    向上转换:只能定义被子类覆写的方法,不能调用在子类中定义的方法。 运行结果: JAVA向下转换的例子,在进行对象的向下转换前,必须首先发生对象的向上转换.否则会编译不过 运行结果: 看一下,下面的例子,假如有一千个类继承了father这个类,如果我们要打印他们的信

    2024年02月22日
    浏览(41)
  • 【Java基础教程】(十五)面向对象篇 · 第九讲:抽象类和接口——定义、限制与应用的细节,初窥模板设计模式、工厂设计模式与代理设计模式~

    掌握 抽象类和接口的定义、使用、区别、常见设计模式; 抽象类是代码开发中的重要组成部分,利用抽象类可以明确地定义子类需要覆写的方法,这样相当于在语法程度上对子类进行了严格的定义限制,代码的开发也就更加标准。下面具体介绍抽象类的概念。 普通类可以直

    2024年02月16日
    浏览(48)
  • Java基础之基础语法与面向对象

    小知识 Java由Sun公司于1995年推出,2009年Sun公司被Oracle公司收购,取得Java的版权 Java之父:James Gosling(詹姆斯·高斯林)   专业术语 JDK:java development kit(java开发工具包) JRE:java runtime environment(java运行环境) JVM: java virual machine(java虚拟机:跨平台的核心)   java实现跨平

    2024年02月08日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包