带你轻松搞懂Java的多态

多态(Polymorphism)属于面向对象三大特征之一,它的前提是封装形成独立体,独立体之间存在继承关系,从而产生多态机制。多态是同一个行为具有多个不同表现形式或形态的能力。现实中,比如我们按下 F1 键这个动作:

● 如果当前在 Flash 界面下弹出的就是 AS 3 的帮助文档;

● 如果当前在 Word 下弹出的就是 Word 帮助;

● 如果当前在 Windows 下弹出的就是 Windows 帮助和支持。

多态就是“同一个行为”发生在“不同的对象上”会产生不同的效果。

那么在java中多态是如何体现的呢?

在java中允许这样的两种语法出现,一种是 向上转型(Upcasting),一种是 向下转型(Downcasting),向上转型是指子类型转换为父类型,又被称为自动类型转换,向下转型是指父类型转换为子类型,又被称为强制类型转换。请看下图:

在java语言中有这样的一个规定,无论是向上转型还是向下转型,两种类型之间必须要有继承关系,没有继承关系情况下进行向上转型或向下转型的时候编译器都会报错, 这一点要死记硬背哦!

接下来我们来看一段代码:

运行结果如下图所示:

其实在java中还允许这样写代码,请看:

运行结果如下图所示:

以上程序演示的就是多态,多态就是“ 同一个行为(move)”作用在“不同的对象上”会有不同的表现结果。

java中之所以有多态机制,是因为java允许一个父类型的引用指向一个子类型的对象。也就是说允许这种写法:Animal a2 = new Bird(),因为Bird is a Animal是能够说通的。

其中Animal a1 = new Cat()或者Animal a2 = new Bird()都是父类型引用指向了子类型对象,都属于向上转型(Upcasting),或者叫做自动类型转换。

我来解释一下这段代码片段【Animal a1 = ****new**** Cat();a1.move(); 】:

java程序包括编译和运行两个阶段,分析java程序一定要先分析编译阶段,然后再分析运行阶段,在编译阶段编译器只知道a1变量的数据类型是Animal,那么此时编译器会去Animal.class字节码中查找move()方法,发现Animal.class字节码中存在move()方法,然后将该move()方法绑定到a1引用上,编译通过了,这个过程我们可以理解为“静态绑定”阶段完成了。

紧接着程序开始运行,进入运行阶段,在运行的时候实际上在堆内存中new的对象是Cat类型,也就是说真正在move移动的时候,是Cat猫对象在移动,所以运行的时候就会自动执行Cat类当中的move()方法,这个过程可以称为“动态绑定”。但无论是什么时候,必须先“静态绑定”成功之后才能进入“动态绑定”阶段。

来看以下的一段代码以及编译结果:

编译结果:

有人认为Cat猫是可以抓老鼠的呀,为什么会编译报错呢?

那是因为“Animal a = ****new**** Cat();”在编译的时候,编译器只知道a变量的数据类型是Animal,也就是说它只会去Animal.class字节码中查找catchMouse()方法,结果没找到,自然“静态绑定”就失败了,编译没有通过。就像以上描述的错误信息一样:在类型为Animal的变量a中找不到方法catchMouse()。

那么,假如说我就是想让这只猫去抓老鼠,以上代码应该如何修改呢?

请看以下代码:

运行结果如下图所示:

我们可以看到直接使用a引用是无法调用catchMouse()方法的,因为这个方法属于子类Cat中特有的行为,不是所有Animal动物都可以抓老鼠的,要想让它去抓老鼠,就必须做向下转型(Downcasting),也就是使用强制类型转换将Animal类型的a引用转换成Cat类型的引用c(Cat c = (Cat)a;),使用Cat类型的c引用调用catchMouse()方法。

通过这个案例,可以得出:只有在访问子类型中特有数据的时候,需要先进行向下转型。其实向下转型就是用在这种情形之下。

那么向下转型会存在什么风险吗?请看以下代码:

以上代码可以编译通过吗?答案是可以的,为什么呢?

那是因为编译器只知道a变量是Animal类型,Animal类和Cat类之间存在继承关系,所以可以进行向下转型(前面提到过,只要两种类型之间存在继承关系,就可以进行向上或向下转型),语法上没有错误,所以编译通过了。但是运行的时候会出问题吗,因为毕竟a引用指向的真实对象是一只小鸟。来看运行结果:

以上的异常是很常见的ClassCastException,翻译为类型转换异常,这种异常通常出现在向下转型的操作过程当中,当类型不兼容的情况下进行转型出现的异常,之所以出现此异常是因为在程序运行阶段a引用指向的对象是一只小鸟,然后我们要将一只小鸟转换成一只猫,这显然是不合理的,因为小鸟和猫之间是没有继承关系的。为了避免这种异常的发生,建议在进行向下转型之前进行运行期类型判断,这就需要我们学习一个运算符了,它就是instanceof。

instanceof运算符的语法格式是这样的:

(引用 instanceof 类型)

instanceof运算符的运算结果是布尔类型,可能是true,也可能是false,假设(c instanceof Cat)结果是true则表示在运行阶段c引用指向的对象是Cat类型,如果结果是false则表示在运行阶段c引用指向的对象不是Cat类型。有了instanceof运算符,向下转型就可以这样写了:

以上程序运行之后不再发生异常,并且什么也没有输出,那是因为if语句的条件并没有成立,因为在运行阶段a引用指向的对象不是Cat类型,所以(a instanceof Cat)是false,自然就不会进行向下转型了,也不会出现ClassCastException异常了。

在实际开发中,java中有这样一条默认的规范需要大家记住:在进行任何向下转型的操作之前,要使用instanceof进行判断,这是一个很好的编程习惯。就像下面的代码:

运行结果如下图所示:

图示:向下转型前判断

到这里大家理解什么是多态了吗?其实多态存在的三个必要条件分别是:

● 继承

● 方法覆盖

● 父类型引用指向子类型对象

多态显然是离不开方法覆盖机制的,多态就是因为编译阶段绑定父类当中的方法,程序运行阶段自动调用子类对象上的方法,如果子类对象上的方法没有进行重写,这个时候创建子类对象就没有意义了,自然多态也就没有意义了,只有子类将方法重写之后调用到子类对象上的方法产生不同效果时,多态就形成了。实际上方法覆盖机制和多态机制是捆绑的,谁也离不开谁,多态离不开方法覆盖,方法覆盖离开了多态也就没有意义了。

接下里就来看看之前没有解决的问题:方法覆盖主要是说实例方法,静态方法为什么不谈方法覆盖?

运行结果如下图所示:

图示:尝试覆盖静态方法

我们发现貌似也发生了覆盖,在程序运行的时候确实也调用了“子类MathSubClass”的sum方法,但这种“覆盖”有意义吗?其实上面的课程我们已经说过了,方法覆盖和多态机制联合起来才有意义,我们来看看这种“覆盖”是否能够达到“多态”的效果,请看代码:

运行结果如下图所示:

通过以上的代码,我们发现虽然创建了子类型对象“****new**** MathSubClass()”,但是程序在运行的时候仍然调用的是Math类当中的sum方法,甚至m = null的时候再去调用m.sum()也没有出现空指针异常,这说明静态方法的执行压根和对象无关,既然和对象无关那就表示和多态无关,既然和多态无关,也就是说静态方法的“覆盖”是没有意义的,所以通常我们不谈静态方法的覆盖。

2021年最新华为java面试题及答案

大公司的面试题,总是能代表着行业对从业者的要求,也代表着技术的发展趋势和方向,动力节点java学院小编专门整理了2019年最新华为java面试题及答案,供java从业者参考。

1.Java是从( )语言改进重新设计。

A.Ada

B.C++

C.Pasacal

D.BASIC

答案:B

2.下列语句哪一个正确( )

A. Java程序经编译后会产生machine code

B. Java程序经编译后会产生byte code

C. Java程序经编译后会产生DLL

D. 以上都不正确  

答案:B

3.下列说法正确的有( )

A. class中的constructor不可省略

B. constructor必须与class同名,但方法不能与class同名

C. constructor在一个对象被new时执行

D. 一个class只能定义一个constructor  

答案:C

4.提供Java存取数据库能力的包是( )

A.javasql

B.javaawt

C.javalang

D.javaswing  

答案:A

5.下列运算符合法的是( )

A.&&

B.<>

C.if

D.:=

答案:A

6.执行如下程序代码

a=0;c=0;

do{

–c;

a=a-1;

}while(a>0);

后,C的值是( )

A.0

B.1

C.-1

D.死循环  

答案:C

7.下列哪一种叙述是正确的( )

A. abstract修饰符可修饰字段、方法和类

B. 抽象方法的body部分必须用一对大括号{ }包住

C. 声明抽象方法,大括号可有可无

D. 声明抽象方法不可写出大括号  

答案:D

8.下列语句正确的是( )

A. 形式参数可被视为local variable

B. 形式参数可被字段修饰符修饰

C. 形式参数为方法被调用时,真正被传递的参数

D. 形式参数不可以是对象  

答案:A

9.下列哪种说法是正确的( )

A. 实例方法可直接调用超类的实例方法

B. 实例方法可直接调用超类的类方法

C. 实例方法可直接调用其他类的实例方法

D. 实例方法可直接调用本类的类方法  

答案:D

二、 多项选择题

1.Java程序的种类有( )

A.类(Class)

B.Applet

C.Application

D.Servlet

答案:BCD

2.下列说法正确的有( )

A. 环境变量可在编译source code时指定

B. 在编译程序时,所能指定的环境变量不包括class path

C. javac一次可同时编译数个Java源文件

D. javac.exe能指定编译结果要置于哪个目录(directory) 

答案:BCD

3.下列标识符不合法的有( )

A.new

B$Usdollars

C.1234

D.cartaxi  

答案:ACD

4.下列说法错误的有( )

A. 数组是一种对象

B. 数组属于一种原生类

C. int number=[]={31,23,33,43,35,63}

D. 数组的大小可以任意改变  

答案:BCD

5.不能用来修饰interface的有( )

A.private

B.public

C.protected

D.static

答案:ACD

6.下列正确的有( )

A. call by value不会改变实际参数的数值

B. call by reference能改变实际参数的参考地址

C. call by reference不能改变实际参数的参考地址

D. call by reference能改变实际参数的内容  

答案:ACD

7.下列说法错误的有( )

A. 在类方法中可用this来调用本类的类方法

B. 在类方法中调用本类的类方法时可直接调用

C. 在类方法中只能调用本类中的类方法

D. 在类方法中绝对不能调用实例方法  

答案:ACD

8.下列说法错误的有( )

A. Java面向对象语言容许单独的过程与函数存在

B. Java面向对象语言容许单独的方法存在

C. Java语言中的方法属于类中的成员(member)

D. Java语言中的方法必定隶属于某一类(对象),调用方法与过程或函数相同  

答案:ABC

9.下列说法错误的有( )

A. 能被java.exe成功运行的java class文件必须有main()方法

B. J2SDK就是Java API

C. Appletviewer.exe可利用jar选项运行.jar文件

D. 能被Appletviewer成功运行的java class文件必须有main()方法  

答案:BCD

更新Java面试题请关注动力节点java学院官网java面试题教程:http://www.bjpowernode.com/tutorial_baseinterviewquestions/

Java程序设计试卷

JAVA程序设计试卷库(第5套)

一、单选题(每小题 2 分,共 20 分)

1、Java Application源程序的主类是指包含有( A )方法的类。

A. main方法 B. toString方法

C. init方法 D. actionPerfromed方法

2、分析下面的程序段,下面的哪个描述是正确的。( B )

char mychar=’c’;

switch(mychar){

default:

case ‘a’: System.out.println(“a”);break;

case ‘b’: System.out.println(“b”);break;

}

A. switch语句块是错误的,因为switch后面的表达式值的类型不是整数;

B. switch语句块是正确的;

C. switch语句块是错误的,因为default没有放在语句块的最后面;

D. 代码运行时,没有任何输出结果。

3、编译并运行下面的Java程序,将产生(  B  )结果。

class A{

    int var1=1;

     int var2;

     public static void main(String[] args){

    int var3=3;

   A a=new A();

System.out.println(a.var1+a.var2+var3);

}

}

A. 0 B. 4

C. 3 D. 代码无法编译,因为var2根本没有被初始化

4、在Java中,下面关于包的陈述中正确的是( D )。

A. 包的声明必须是源文件的任意位置;

B. 包的声明必须紧跟在import语句的后面;

C. 只有公共类才能放在包中;

D. 可以将多个源文件中的类放在同一个包中

5、 在Java语言中,当一个类的某个变量声明为protected时下列说法正确的是( C )。

A. 只有同一类中的成员才能访问它;

B. 不同包中的任何其他类都能够访问它;

C. 同包中的任何其他类能够访问它;

D. 不同包中的子类不可以访问该变量。

6、在Java中,执行下面的语句后,c的值为(  D  )。

String s= \”Jessica \”;

char c=s.charAt(6);

A. \”c \”   B. \”a \”

C. \’c \’ D. \’a \’

7、设有下面两个赋值语句:

a = Integer.parseInt(“1024”);

b = Integer.valueOf(“1024”).intValue();

下述说法正确的是( D )。

A.a是整数类型变量,b是整数类对象。

B.a是整数类对象,b是整数类型变量。

C.a和b都是整数类对象并且它们的值相等。

D.a和b都是整数类型变量并且它们的值相等。

8、事件剪裁类如WindowAdapter(它实现了WindowListener接口)的优点是什么? ( C )。

A. 继承了那个类的所有行为 ;

B. 子类自动成为监听器;

C. 没有必要实现不使用的任何WindowListener接口中定义的方法;

D.可以自动充当事件的监听者。

9、在用Java构建动画时,(C )方法创建新线程并启动它运行。

A. init() B. start()

C. run() D. paint()

10、下面哪一个选项能正确地创建一个InputStreamReader对象( A )。

A.new InputStreamReader(new FileInputStream(“data”))

B.new InputStreamReader(new FileReader(“data”))

C.new InputStreamReader(new BufferedReader(“data”))

D.new InputStreamReader(“data”)

二、填空题(每空格1分,共15分)

1、阅读下面的程序片段,在①中,parent类与child类之间是继承关系;在②中,parent类与child类之间包含。

① class parent{

int fields;

}

class child extends parent{

int fieldChild;

}

② class child{

int fieldsChild;

}

class parent {

child myChild;

}

2、Java中的多态有两种方式:子类对父类方法的覆盖、同类中方法的重载。

3、下面程的输出结果是:?吗对,上海自来水来自海上

public class turnString {

public static void main(String args[]) {

String s=“上海自来水来自海上,对吗?” ;

     for(int i=s.length( )-1;i>=0;i++)

System.out.println(s.charAt(i));

System.out.println():

}

}

4、Applet的init( )方法仅在开始时执行一次?start( )方法在用户每次访问包含Applet的HTML文件时都被调用?

5、创建一个FlowLayout的对象fd,使应用该布局管理器的各个组件都向右对齐排列。该对象的创建语句为:FlowLayout fd=new FlowLayout(FlowLayout.RIGHT);。

6、java.net包中定义的两个类Socket、ServerSocket;分别用来实现双向连接的Client和Server端。

7、巳知a=14,b=3,则表达式a<<b运算结果为:112;。

8、写出运行下面程序片段后的屏幕输出:0 1 2 i=3。

int i;

for(i=0;i<5;i++)

{

if(i==3)

break;

System.out.print(i+” ”);

}

System.out.println(“i=”+i)

9、在Java Application程序中,一个类是主类的标志是包含main( )方法,在Java Applet程序中,一个类是主类的标志是该类是Applet类的子类;。

10、多态的概念是一个程序中同名的不同方法共存的情况。。

三、判断改错题(每题2分,共20分)(正确的打√,错误的打×并说明原因)

1、 Java系统包提供了很多预定义类,我们可以直接引用它们而不必从头开始编写程序。 (√ )

2、 程序可以用字符‘*’替代一个TextField中的每个字符的显示,使别人看不出其中的具体内容。( √)

3、 Class1类的属性被private修饰符修饰为私有变量,所以方法不能访问它。(×,Class1类自身的方法可以访问这个私有变量。 )

4、 如果p是父类Parent的对象,而c是子类Child的对象,则语句c=p是正确的。(×,如果p实际代表的是一个子类对象的引用,可以用强制类型转换成c=(Child)p;否则不能把父类对象赋值给子类对象。 )

5、 如果希望程序能够处理所有的异常,包括未知的异常,可以在所有的catch块后面加下catch(Exception e){}。(√ )

6、 TextComponent是一个抽象类,它的两个子类TextField和TextArea才拥有具体的对象。( √)

7、 一个Applet程序在运行过程中,创建的Frame窗框,当Applet或浏览器关闭时也会自动跟着关闭。(×,Frame与Applet平级,不受Applet的关闭的影响。 )

8、 判断下面程序片段的正确性。(×,Vector对象的addElement( )方法的参数类型为对象,所以myv.addElement(i)应改为所以myv.addElement(new Integer(i)); )

import java.util.*;

public class UseVector{

public static void main(String args[])

{

Vector myv=new Vector( );

for(int i=0;i<10;i++)

myv.addElement(i);

}

}

9、 在Applet中显示一幅图像,首先要把它调入内存,保存在一个Image对象中,然后调用drawImage()方法画出这个图像。(√)

10、挂起、阻塞或等待的线程都能够恢复运行,但是停止运行的线程将不可能再运行(√)。

四、问答题(每小题10分,共30分)

1、 阅读下面的程序片段,并回答问题。

① Class1被称为是Class2的什么?Class2被称为是Class1的什么?

② Class1有哪些属性和方法?Class2有哪些属性和方法?其中哪些与Class1的属性和方法有关?

class Class1

{

 int field1;

private int field2;

void method1( )

 {

  System.out.println(“我是Class1的第一个方法”);

 }

  void method2( )

 {

  System.out.println(“我是Class1的第二个方法”);

 }

  }

class Class2 extends Class1{

int fieldChild1;

void methodchild1()

{

System.out.println(“我是Class2的第一个方法”);

 }

}

1、 阅读下面的程序片段,并回答问题。

① Class1被称为是Class2的什么?Class2被称为是Class1的什么?

答:Class1被称为Class2的父类,Class2被称为是Class1的子类。

② Class1有哪些属性和方法?Class2有哪些属性和方法?其中哪些与Class1的属性和方法有关?

答:Class1包括属性field1和field2,方法method1和method2;Class2包括自身定义的属性fieldChild1和自身定义的方法methodChild1,以及它从父类继承来的属性field1和方法method1、method2。

2、写出下面程序的功能

import java.applet.Applet;

import java.applet.Applet;

import java.awt.*;

import java.awt.event.*;

public class DataType extends Applet implements ActionListener

{ Label prompt=new Label(\”请分别输入整数和浮点数:\”);

TextField input_int=new TextField(6);

TextField input_double=new TextField(6);

TextField output=new TextField(35);

int getInt; double getDouble;

public void init() {

add(prompt); add(input_int); add(input_double);

add(output); output.setEditable(false); input_double.addActionListener(this);

}

public void actionPerformed(ActionEvent e) {

getInt=Integer.parseInt(input_int.getText());

getDouble=Double.parseDouble(input_double.getText());

output.setText(\”您输入了整数: \”+getInt+\”和浮点数:

\”+getDouble);

}

}

2、写出下列程序完成的功能

答:功能:从图形界面输入一个整数和一个浮点数,然后将其输出来。

3、 说明下面程序段是否有误,如有误,请将错误的地方指出来,并改正

Interface MyInterface

{

void method1();

}

abstract class Parent implements MyInterface

{ }

class Child extends Parend

{

void method1()

{

System.out.println(“I am implemented now!”);

}

}

3、说明下面程序是否有误,如有误,请将错误的地方指出来,并改正

答:接口中的方法都是public ,所以重载方法也必须声明为public.

class Child extends Parend

{

{

System.out.println(“I am implemented now!”);

}

}

五、用Java语言编写程序。(共15分)

假设用户输入一组整数被保存在Vector对象中,请定义一个类vecSort,用来求Vector对象中整数中的最大和最小数据。

要求:

1、 根据题目给出vecSort类应包含的属性和方法(5分)

2、 用Java语言定义类vecSort(10分)

五、用Java语言编写程序。(共15分)

参考代码:

class vecSort{

int max,min;

Vector x=new Vector();

vecSort(Vector y)

{

for(int i=0;i<y.size() ;i++)

x.addElement(y.elementAt(i) );

}

int getmax()

{ max=((Integer)(x.elementAt(0))).intValue() ;

for(int i=1;i<x.size();i++)

if(max<((Integer)(x.elementAt(i))).intValue())

max=((Integer)(x.elementAt(i))).intValue();

return max;

}

int getmin()

{ min=((Integer)(x.elementAt(0))).intValue() ;

for(int i=1;i<x.size();i++)

if(min>((Integer)(x.elementAt(i))).intValue())

min=((Integer)(x.elementAt(i))).intValue();

return min;

}

}

本文作者及来源:Renderbus瑞云渲染农场https://www.renderbus.com

点赞 0
收藏 0

文章为作者独立观点不代本网立场,未经允许不得转载。