Java面试题——继承,多态

来源:互联网 发布:本地ip和网络ip 编辑:程序博客网 时间:2024/05/24 23:15

一、面向对象的思想
Java是一门纯粹的面向对象的语言。面向对象这种程序设计模式它将现实世界中的一切事物都看作是对象,例如,一个人是一个对象,汽车、飞机、小鸟等等,都是对象;它强调从对象出发,以对象为中心用人类的思维方式来认识和思考问题。每个对象都具有各自的状态特征(也可以称为属性)及行为特征(方法),java就是通过对象之间行为的交互来解决问题的。
类是面向对象中一个重要的概念。类是具有相同属性和行为特征的对象的抽象,类是对象的概念模型,对象是类的一个实例,通过类来创建对象,同一类的所有对象具有相同的属性和行为特征。类具有三个基本特征:封装、继承、多态。
封装就是将对象的属性和行为特征包装到一个程序单元(即类)中,把实现细节隐藏起来,通过公用的方法来展现类对外提供的功能,提高了类的内聚性,降低了对象之间的耦合性。
继承是对原有类的拓展,举例说明:我现在有一个Person类,但是我想要一个学生对象,他拥有Person类的所有属性和方法,此外他还有学号属性,及上课、写作业等一些方法,我可以创建一个Student类,但是我不想重复写Person类中已经有了的属性和方法,那么,此时我就可以用Student类继承Person类,Student类就拥有了Person类里的属性和方法了,我只需要在Student类里添加另外的新的属性和方法就可以了。Person类就成为父类,Student类就称为子类。父类和子类之间是一般和特殊的关系,子类是一种特殊的父类。此外,子类还可以通过重写来改变父类中的方法,重写可以改变方法的返回类型和访问权限,不能改变方法名称。

多态是建立在继承的基础上的,是指子类类型的对象可以赋值给父类类型的引用变量,但运行时仍表现子类的行为特征。也就是说,同一种类型的对象执行同一个方法时可以表现出不同的行为特征。

java中的访问修饰符:
(1)public: 用public修饰的类、类属变量及方法,包内及包外的任何类(包括子类和普通类)均可以访问;
  (2)protected: 用protected修饰的类、类属变量及方法,包内的任何类及包外那些继承了该类的子类才能访问(此处稍后解释),protected重点突出继承;
  (3)default: 如果一个类、类属变量及方法没有用任何修饰符(即没有用public、protected及private中任何一种修饰),则其访问权限为default(默认访问权限)。默认访问权限的类、类属变量及方法,包内的任何类(包括继承了此类的子类)都可以访问它,而对于包外的任何类都不能访问它(包括包外继承了此类的子类)。default重点突出包;
  (4)private: 用private修饰的类、类属变量及方法,只有本类可以访问,而包内包外的任何类均不能访问它。

1. public、private和protected对我们来说没有任何异议。

   2. 顶层类只能用public访问修饰符和default(默认)访问修饰符修饰,其中用默认修饰符修饰的类(及没有任何修饰符的类,如class B{})不能被其他包中的类继承,这也说明了default(默认)访问修饰符突出的是包权限

   3. protected:本人做了一次实验,发现在不同包的子类中,new一个父类对象,并用该父类对象去访问父类中的用protected修饰的类属变量和方法时不能访问,而new一个子类对象时,子类对象可以访问(说明protected修饰的类可以被其他包中的类继承)。也可以在子类重写父类的方法中使用super关键字调用。这岂不是和上面表格中的总结(红色对勾)冲突了?本人也是百思不得其解。最后在网上找到了一个相对比较认可的解释,如下:
protected修饰符的修饰的成员变量和方法也称为受保护的成员变量和方法, 受保护的成员变量和方法可以在本类或同一个包中的其它类(包括子类)中通过类的实例进行访问,也可以被同一个包中的类或不同包中的类继承,但是不能在不同包中的其它类(包括子类)中通过类的实例进行访问。
  4. 如果一个类使用public修饰,那该类的类名必须与他所在的源文件名相同。一个.java源文件中有且只有一个public类,顶层类只能用public和默认修饰符(即无修饰符)修饰;
  5. final修饰的类不能被继承,没有子类。
  6. abstract修饰的类不能被实例化,必须被子类继承。类只要有一个抽象方法就必定是抽象类,但抽象类不一定要有抽象方法。
最终总结,就一句话:protected修饰符所修饰的类(这句话中指父类)属成员变量和方法,只可以被子类访问,而不管子类是不是和父类位于同一个包中。default修饰符所修饰的类属成员变量和方法,只可被同一个包中的其他类访问,而不管其他类是不是该类的子类。protected属于子类限制修饰符,而default属于包限制修饰符。

二、继承,多态,向上转型
由于Cat是继承自它的父类Animal,所以Animal类型的引用是可以指向Cat类型的对象的。这就是“向上转型”。

因为子类是对父类的一个改进和扩充,所以一般子类在功能上较父类更强大,属性较父类更独特, 定义一个父类类型的引用指向一个子类的对象既可以使用子类强大的功能,又可以抽取父类的共性。 所以,父类类型的引用可以调用父类中定义的所有属性和方法,而对于子类中定义而父类中没有的方法,父类引用是无法调用的;

那什么是动态链接呢?当父类中的一个方法只有在父类中定义而在子类中没有重写的情况下,才可以被父类类型的引用调用; 对于父类中定义的方法,如果子类中重写了该方法,那么父类类型的引用将会调用子类中的这个方法,这就是动态连接。

/** * * 当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法, * 但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法, * 但是它仍然要根据继承链中方法调用的优先级来确认方法 * * 其实在继承链中对象方法的调用存在一个优先级:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。 * @author Administrator * */public class Polymorphic {            public static void main(String[] args) {                        A a1 = new A();                        A a2 = new B();                        B b = new B();                        C c = new C();                        D d = new D();                        System.out.println("1--" + a1.show(b));//B继承了A,相当于传进去的是A,  A and A  第三级。。。                        System.out.println("2--" + a1.show(c));//C继承了B。B继承了A      A and A   第三级。。                        System.out.println("3--" + a1.show(d)); ///  A and D   第一级。。                        System.out.println("4--" + a2.show(b));//引用是A,所以this代表A对象。  B and A                          System.out.println("5--" + a2.show(c));//  B and A   在第三级时,确定了要调用A中的show(A obj)的方法,但是,由于动态连接的问题,最终却调用了子类重写的方法                        System.out.println("6--" + a2.show(d));// A and D                        System.out.println("7--" + b.show(b)); //B and B                        System.out.println("8--" + b.show(c)); //  B and B                        System.out.println("9--" + b.show(d)); // A and D            }}class A {            public String show(D obj) {                        return ("A and D");            }            public String show(A obj) {                        return ("A and A");            }}class B extends A {            public String show(B obj) {                        return ("B and B");            }            public String show(A obj) {                        return ("B and A");            }/*         public String show(D obj) {                        return ("B and D");            }*/}class C extends B {}class D extends B {}

我们分析5,a2.show(c),a2是A类型的引用变量,所以this就代表了A,a2.show(c),它在A类中找发现没有找到,于是到A的超类中找(super),由于A没有超类(Object除外),所以跳到第三级,也就是this.show((super)O),C的超类有B、A,所以(super)O为B、A,this同样是A,这里在A中找到了show(A obj),同时由于a2是B类的一个引用且B类重写了show(A obj),因此最终会调用子类B类的show(A obj)方法,结果也就是B and A。

方法已经找到了但是我们这里还是存在一点疑问,我们还是来看这句话:当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。这我们用一个例子来说明这句话所代表的含义:a2.show(b);

对于多态,可以总结以下几点:

一、使用父类类型的引用指向子类的对象;
二、该引用只能调用父类中定义的方法和变量;
三、如果子类中重写了父类中的一个方法,那么在调用这个方法的时候,将会调用子类中的这个方法;(动态连接、动态调用)
四、变量不能被重写(覆盖),”重写“的概念只针对方法,如果在子类中”重写“了父类中的变量,那么在编译时会报错

1、抽象类:
(1)、抽象类不可以被实例化;
(2)、抽象类中可以没有抽象方法;可以拥有非抽象方法或者属性;
(3)、接口与抽象类中的抽象方法不能具体实现;
(4)、抽象类可以有构造函数;
(5)、抽象类中的成员变量可以被不同的修饰符来修饰;
(6)、
2、接口:
(1)、接口中不能有普通数据成员,只能够有静态的不能被修改的数据成员,final表示全局,static 表示不可修改,可以不用static final 修饰,会隐式的声明为static和final ;
(2)、接口中的方法一定是抽象方法,所以不用abstract修饰;
(3)、接口不能且它里面的方法只是一个声明必须用public来修饰没有具体实现的方法。

三、java中静态属性和和静态方法的继承问题 以及多态的实质
首先结论是:java中静态属性和和静态方法可以被继承,但是没有被重写(overwrite)而是被隐藏。

静态方法和属性是属于类的,调用的时候直接通过类名.方法名完成的,不需继承机制就可以调用如果子类里面定义了静态方法和属性,那么这时候父类的静态方法 或属性称之为“隐藏”,你如果想要调用父类的静态方法和属性,直接通过父类名.方法名或变量名完成,至于是否继承一说,子类是有继承静态方法和属性,但是 跟实例方法和属性不太一样,存在“隐藏”的这种情况。

多态之所以能够实现是依赖于 继承 接口和 重写 、重载(继承和重写最为关键)。有了继承和重写就可以 实现父类的引用可以指向不同子类的对象。重写的功能是:“重写”后子类的优先级要高于父类的优先级,但是“隐藏”是没有这个优先级之分的。

静态属性、静态方法和非静态的属性都可以被 继承 和 隐藏 而不能够被重写,因此不能实现多态,不能实现父类的引用可以指向不同子类的对象。
非静态的方法可以被继承和重写,因此可以实现多态。

接口中的实现和类中的继承是两个不同的概念,因此不可以说实现接口的子类从接口那里继承了常量和方法

public class MyStatic {             public static void main(String[] args)                {                    J j = new J();                    System.out.println(j.name);                    System.out.println(j.str);                    j.sing();//输出的结果都是父类中的非静态属性、静态属性和静态方法,推出静态属性和静态方法可以被继承                    G j1 = new J();                    System.out.println(j1.name);                    System.out.println(j1.str);                    j1.sing();//结果同上,输出的结果都是父类中的非静态属性、静态属性和静态方法,推出静态属性和静态方法可以被继承                    H h = new H();                    System.out.println(h.name);                    System.out.println(h.str);                    h.sing();//结果都是子类的非静态属性,静态属性和静态方法,这里和非静态属性和非静态类的继承相同                    h.run();//子类的改写后非静态方法                    G h1 = new H();                    System.out.println(h1.str);//结果是父类的静态属性,说明静态属性不可以被重写,不能实现多态                    System.out.println(h1.name);//结果是父类的非静态属性,说明非静态属性不可以被重写,不能实现多态                    h1.sing();//结果都是父类的静态方法,说明静态方法不可以被重写,不能实现多态                 h1.run();//结果是子类的改写后的非静态方法                }}class G{            public static String str = "静态属性";    public String name = "非静态属性";    public static void sing()    {        System.out.println("静态方法");    }    public void run()    {        System.out.println("非静态方法");    }}class H extends G{             public static String str = "H该改写后的静态属性";                public String name ="H改写后的非静态属性";                public static void sing()                {                    System.out.println("H改写后的静态方法");                }                public void run()                {                    System.out.println("H改写后的非静态方法");                }}class J extends G{}
1 0
原创粉丝点击