面试准备系列——Java基础技术篇(2)/面向对象

1.面向对象与面向过程有什么区别?

  1. 出发点不同。面向对象方法是用符合常规思维的方式来处理客观世界的问题,强调把问题域的要领直接映射到对象及对象之间的接口上。而面向过程方法强调的则是过程的抽象化与模块化,它是以过程为中心构造或处理客观世界问题的。
    2)层次逻辑关系不同。面向对象方法则是用计算机逻辑来模拟客观世界中的物理存在,以对象的集合类作为处理问题的基本单位,尽可能地使计算机世界向客观世界靠拢,以使问题的处理更清晰直接,面向对象方法是用类的层次结构来体现类之间的继承和发展。而面向过程方法处理问题的基本单位是能清晰准确地表达过程的模块,用模块的层次结构概括模块或模块间的关系与功能,把客观世界的问题抽象成计算机可以处理的过程。
    3)数据处理方式与控制程序方式不同。面向对象方法将数据与对应的代码封装成一个整体,原则上其他对象不能直接修改其数据,即对象的修改只能由自身的成员函数完成,控制程序方式上是通过“事件驱动”来激活和运行程序。而面向过程方法是直接通过程序来处理数据,处理完毕后即可显示处理结果,在控制程序方式上是按照设计调用或返回程序,不能自由导航,各模块之间存在着控制与被控制、调用与被调用的关系。
    4)分析设计与编码转换方式不同。面向对象方法贯穿于软件生命周期的分析、设计及编码中,是一种平滑过程,从分析到设计再到编码是采用一致性的模型表示,即实现的是一种无缝连接。而面向过程方法强调分析、设计及编码之间按规则进行转换,贯穿于软件生命周期的分析、设计及编码中,实现的是-一种有缝的连接。

2.面向对象可以解释下吗?都有哪些特性?

面向对象是一种思想,可以将复杂问题简单化,让我们从执行者变为了指挥者。面向对象的三大特性为:封装,继承与多态

  • 封装:将事物封装成一个类,减少耦合,隐藏细节。保留特定的接口与外界联系,当接口内部发生改变时,不会影响外部调用方。
  • 继承:从一个已知的类中派生出一个新的类,新类可以拥有已知类的行为和属性,并且可以通过覆盖/重写来增强已知类的能力。
  • 多态:多态的本质就是一个程序中存在多个同名的不同方法,主要通过三种方式来实现:
    • 通过子类对父类的覆盖来实现
    • 通过在一个类中对方法的重载来实现
    • 通过将子类对象作为父类对象使用来实现

3.什么是继承

继承是面向对象中的一个非常重要的特性。通过继承,子类可以使用父类中的一些成员变量与方法,从而能够提高代码的复用性,提高开发效率。在Java 语言中,被继承的类叫基类( superclass).或父类,继承基类或父类的类叫派生类或子类( subclass)。继承是通过extends关键字来实现的,使用格式为: class 子类名extends父类名。
继承主要有如下几个特性:

  • 1)Java 语言不支持多重继承,也就是说,子类至多只能有一个父类,但是可以通过实现多个接口来达到多重继承的目的。
  • 2)子类只能继承父类的非私有( public与protected)成员变量与方法
  • 3)当子类中定义的成员变量和父类中定义的成员变量同名时,子类中的成员变量会覆盖父类的成员变量,而不会继承。
  • 4)当子类中的方法与父类中的方法有相同的函数签名(相同的方法名,相同的参数个数与类型)时,子类将会覆盖父类的方法,而不会继承。
    常见笔试题目:
    在这里插入图片描述
    答案:B

4.组合和继承有什么区别(is-a/has-a)

组合和继承是面向对象中两种代码复用的方式。组合是指在新类里面创建原有类的对象,重复利用已有类的功能。继承是面向对象的主要特性之一,它允许设计人员根据其他类的实现来定义一个类的实现。组合和继承都允许在新的类中设置子对象(subobject),只是组合是显式的,而继承则是隐式的。组合和继承存在着对应关系:组合中的整体类和继承中的子类对应,组合中的局部类和继承中的父类对应。
二者的区别在哪里呢?首先分析一个实例。Car 表示汽车对象,Vehicle 表示交通工具对象,Tire 表示轮胎对象。三者的关系如下:
在这里插入图片描述
从图可以看出,Car是Vehicle的t种,因此是一-种继承关系(又被称为“is-a”关系);而Car包含了多个Tire ,因此是一种组合关系(又被称为“has-a"关系)。

既然继承和组合都可以实现代码的重用,那么在实际使用时又该如何选择呢?一般情况下,遵循以下两点原则。

  • 1) 除非两个类之间是“is-a” 的关系,否则不要轻易地使用继承,不要单纯地为了实现,代码的重用而使用继承,因为过多地使用继承会破坏代码的可维护性,当父类被修改时,会影响到所有继承自它的子类,从而增加程序的维护难度与成本。
  • 2)不要仅仅为了实现多态而使用继承,如果类之间没有“is-a” 的关系,可以通过实现接口与组合的方式来达到相同的目的。设计模式中的策略模式可以很好地说明这一一点, 采用接口与组合的方式比采用继承的方式具有更好的可扩展性。

由于Java语言只支持单继承,如果想同时继承两个类或多个类,在Java中是无法直接实现的。同时,在Java语言中,如果继承使用太多,也会让-一个class里面的内容变得臃肿不堪。所以,在Java语言中,能使用组合就尽量不要使用继承。

5.(重点)多态的实现机制是什么

多态是面向对象程序设计中代码重用的一个重要机制,它表示当同一个操作作用在不同对象时,会有不同的语义,从而会产生不同的结果,例如,同样是执行“+”操作,“3+4"用来实现整数相加,而“3”+“4”却实现了字符串的连接。在Java语言中,多态主要有以下两种表现方式:

  • 1)方法的重载( overload)。重载是指同一个类中有多个同名的方法,但这些方法有着不同的参数,因此在编译时就可以确定到底调用哪个方法,它是一种编译时多态。重载可以被看作一个类中的方法多态性。
  • 2)方法的覆盖( override)。子类可以覆盖父类的方法,因此同样的方法会在父类与子类中有着不同的表现形式。在Java语言中,基类的引用变量不仅可以指向基类的实例对象,也可以指向其子类的实例对象。同样,接口的引用变量也可以指向其实现类的实例对象。而程序调用的方法在运行期才动态绑定(绑定指的是将-一个 方法调用和一一个方法主体连接到一起),就是引用变量所指向的具体实例对象的方法,也就是内存里正在运行的那个对象的方法,而不是引用变量的类型中定义的方法。通过这种动态绑定的方法实现了多态。由于只有在运行时才能确定调用哪个方法,因此通过方法覆盖实现的多态也可以被称为运行时多态,示例如下。
class Derived extends Base {
    @Override
    public void f(){
        System.out.println("Derived f()");
    }
    @Override
    public void g(){
        System.out.println("Derived g()");
    }

    public static void main(String[] args) {
        Base b = new Derived();
        b.f();
        b.g();
    }
}

输出:
在这里插入图片描述
上例中,由于子类Derived的f( )方法和g()方法与父类Base的方法同名,因此Derived的方法会覆盖Base的方法。在执行Baseb=newDerived()语句时,会调用Base类的构造函数,而在Base的构造函数中,执行了g()方法,由于Java语言的多态特性,此时会调用子类Derived的g()方法,而非父类Base的g()方法,因此会输出Derived g()。由于实际创建的是Derived类的对象,后面的方法调用都会调用子类Derived的方法。
此外,只有类中的方法才有多态的概念,类中成员变量没有多态的概念,示例如下:

class MyBase{
    public int i = 1;

    public void getI(){
        System.out.println(i);
    }
}

class MyDerived extends MyBase{
    public int i = 2;

    @Override
    public void getI(){
        System.out.println(i);
    }

    public static void main(String[] args) {
        MyBase b = new MyDerived();
        //成员变量没有改变
        System.out.println(b.i);
        //改变了方法
        b.getI();
    }
}

输出:
在这里插入图片描述
由此可见,成员变量是无法实现多态的,成员变量的值取父类还是子类并不取决于创建对象的类型,而是取决于所定义变量的类型,这是在编译期间确定的。在上例中,由于b所属的类型为Base, b. i指的是Base类中定义的i,因此程序输出结果为1。

常见的笔试题:Java中提供了那两种用于多态的机制?
答案:编译时多态和运行时多态。编译时多态是通过方法的重载实现的,运行时多态是通过方法的覆盖(子类覆盖父类方法)实现的。

6.(重点)重载和覆盖有什么区别

重载( overload)和覆盖( override)是Java多态性的不同表现方式。
其中,重载是在一个类中多态性的一种表现,是指在一个类中定义了多个同名的方法,它们或有不同的参数个数或有不同的参数类型。在使用重载时,需要注意以下几点:

  • 1)重载是通过不同的方法参数来区分的,例如不同的参数个数、不同的参数类型或不同的参数顺序。
  • 2)不能通过方法的访问权限、返回值类型和抛出的异常类型来进行重载。
  • 3)对于继承来说,如果基类方法的访问权限为private,那么就不能在派生类对其重载;如果派生类也定义了一个同名的函数,这只是一个新的方法,不会达到重载的效果。

覆盖是指派生类函数覆盖基类函数。覆盖一个方法并对其重写,以达到不同的作用。在使用覆盖时需要注意以下几点:

  • 1)派生类中的覆盖方法必须要和基类中被覆盖的方法有相同的函数名和参数。
  • 2)派生类中的覆盖方法的返回值必须和基类中被覆盖白方法的返回值相同。
  • 3)派生类中的覆盖方法所抛出的异常必须和基类(或是其子类):中被覆盖的方法所抛出的异常一致。
  • 4)基类中被覆盖的方法不能private, 否则其子类只是定义了一个方法,并没有对其覆盖。

重载与覆盖的区别主要有以下几个方面:

  • 1)覆盖是子类和父类之间的关系,是垂直关系;重载是同一个类中方法之间的关系,是水平关系。
  • 2)覆盖只能由一个方法或只能由一对方法产生关系;重载是多个方法之间的关系。
  • 3)覆盖要求参数列表相同;重载要求参数列表不同。
  • 4)覆盖关系中,调用方法体是根据对象的类型(对象对应存储空间类型)来决定;而重载关系是根据调用时的实参表与形参表来选择方法体的。
    常见笔试题:
    如下的代码输出的结果是什么?
    在这里插入图片描述
    答案:
    编译错误。因为函数是不能以返回值来区分的,虽然父类与子类中的函数有着不同的返回值,但是它们有着相同的函数名,因此,编译器无法区分。

7.抽象类与接口的区别

如果一个类中包含抽象方法,那么这个类就是抽象类。在Java语言中,可以通过把类或者类中的某些方法声明为abstract (abstract只能用来修饰类或者方法,不能用来修饰属性)来表示一个类是抽象类。接口就是指-一个方法的集合,接口中的所有方法都没有方法体,在Java语言中,接口是通过关键字interface来实现的。

抽象类( abstract class);和接口( interface)都是支持抽象类定义的两种机制(注意:此句中的前后两个抽象类的意义不一样,前者表示的是一个实体,后者表示的是一个概念)。二者具有很大的相似性,甚至有时候是可以互换的。但同时,二者也存在很大的区别。

只要包含一个抽象方法的类就必须被声明为抽象类,抽象类可以声明方法的存在而不去实现它,被声明为抽象的方法不能包含方法体。在实现时,必须包含相同的或者更低的访问级别( public-→protected→private)。抽象类在使用的过程中不能被实例化,但是可以创建一个对象使其指向具体子类的一个实例。抽象类的子类为父类中的所有抽象方法提供具体的实现,否则它们也是抽象类。接口可以被看作抽象类的变体。接口中的所有方法都是抽象的,可以通过接口来间接地实现多重继承。接口中的成员变量都是static final 类型。由于抽象类可以包含部分方法的实现,因此,在一些场合下抽象类比接口存在更多的优势。
接口与抽象类的相同点如下:

  • 1)都不能被实例化。
  • 2):接口的实现类或抽象类的子类都只有实现了接口或抽象类中的方法后才能被实例化。

接口与抽象类的不同点如下:

  • 1)接口只有定义,其方法不能在接口中实现,只有实现接口的类才能实现接口中定义的方法,而抽象类可以有定义与实现,即其方法可以在抽象类中被实现。(JDK8和后边版本可以在接口中实现default类型的方法)
  • 2)接口需要实现(用implements),但抽象类只能被继承(用extends)。一个类可以实现多个接口,但一个类只能继承一个抽象类,因此使用接口可以间接地达到多重继承的目的
  • 3)接口强调特定功能的实现,其设计理念是“has-a” 关系;而抽象类强调所属关系,其设计理念为“is-a” 关系。
  • 4)接口中定义的成员变量默认public static final,只能够有静态的不能被修改的数据成员,而且,必须给其赋初值,其所有成员方法都是默认public、abstract 的,而且只能被这两个关键字修饰。 而抽象类可以有自己的数据成员变量,也可以有非抽象的成员方法,而且,抽象类中的成员变量默认为default(本包可见),当然也可以被定义为private、protected和public,这些成员变量可以在子类中被重新定义,也可以被重新赋值,抽象类中的抽象方法(其前有abstract 修饰)不能private、static 、synchronized 、native 等访问修饰符修饰,同时方法必须以分号结尾,并且不带花括号。所以,当功能需要累积时,用抽象类做不到累积时,用接口。
  • 5)接口被运用于实现比较常用的功能,便于日后维护或者添加删除方法;而抽象类更倾向于充当公共类的角色不适用于日后重新对里面的代码进行修改。

简单点说,接口是一种特殊形式的抽象类,使用接口完全有可能实现与抽象类相同的操作,但一般而言,抽象类多用于在同类事物中有无法具体描述的方法的场景,所以当子类和父类之间存在有逻辑上的层次结构时,推荐使用抽象类;而接口多用于不同类之间,定义不同类之间的通信规则,所以当希望支持差别较大的两个或者更多对象之间的特定交互行为时,应该使用接口。

此外,接口可以继承接口,抽象类可以实现接口,抽象类也可以继承具体类。抽象类也可以有静态的main方法。

重点记住:接口和抽象类中的方法默认都用public abstract修饰,但是接口中的成员变量默认用public static final修饰,而抽象类中的的成员变量默认使用的是default。

常考题型:
在这里插入图片描述
解释:
在这里插入图片描述
在这里插入图片描述
解释:
在这里插入图片描述

为什么设计了接口还需要抽象类

首先,假如有一个类实现了多个接口,而这些接口中其实有很多的功能方法是重复的,那么这个时候如果有个中间件解决这些重复的方法就可以减少很多的冗余代码,这就诞生了抽象类。然后,在面试中,可以通过举例集合类中的List集合这个例子去解析,比如说,List是一个接口,继承了Collection接口,AbstractList是一个抽象类,实现了AbstractCollection类,实现了List接口,并在其中实现了接口中的多个方法(没有全部实现),并提供了一些接口中没写的常用功能方法,而ArrayList则是直接继承了AbstractList抽象类,对抽象类中抽象方法进行实现(AbstractList中的抽象方法就只有get方法一个),并实现抽象类中还没实现的List接口中的方法。另外ArrayList也会对抽象类中的部分方法进行覆盖重写,以达到自己想要的功能目标。

为什么设计了抽象类还需要接口?

抽象类是单继承的,接口是多继承的,抽象类中除了定义一个(也可能多个,但至少一个)抽象方法之外,还会有许多通用的方法,接口中只有抽象方法。只有抽象类的时候,一个类就只能继承一个抽象类,需要再继承或者使用其他的抽象类的功能都受限制了,直接在类中写的话,那么你又为什么要写个抽象类呢?其实抽象类就是简化了当多个类需要继承一个抽象类的时候,一些功能都可以帮你封装起来,这样就不需要每个实现类都具体地再实现一遍了。

8.内部类有哪些

嵌套类( Nested Class) 与内部类( Inner Class) 类似,只是嵌套类是C ++的说法,而内部类是Java的说法而已。内部类可以分为很多种,主要有以下4种:静态内部类( static inner class) 、成员内部类( member inner class) 、局部内部类( local inner class )和匿名内部类( anonymous inner class)。它们的定义方法如下。
在这里插入图片描述
在这里插入图片描述
静态内部类是指被声明为static的内部类,它可以不依赖于外部类实例而被实例化,而通常的内部类需要在外部类实例化后才能实例化。静态内部类不能与外部类有相同的名字,不能访问外部类的普通成员变量,只能访问外部类中的静态成员和静态方法(包括私有类型)。

一个静态内部类,如果去掉“static"关键字,就成为成员内部类。成员内部类为非静态内部类,它可以自由地引用外部类的属性和方法,无论这些属性和方法是静态的还是非静态的。但是它与一个实例绑定在了一-起,不可以定义静态的属性和方法。只有在外部的类被实例化后,这个内部类才能被实例化。需要注意的是,非静态内部类中不能有静态成员

局部内部类指的是定义在-一个代码块内的类,它的作用范围为其所在的代码块,是内部类中最少使用到的一种类型。局部内部类像局部变量- -样,不能被public、protected、 private 以及static修饰,只能访问方法中定义为final类型的局部变量。对- -个静态内部类,去掉其声明中的“static”关键字,将其定义移人其外部类的静态方法或静态初始化代码段中就成为了局部静态内部类。对一一个成员类,将其定义移人其外部类的实例方法或实例初始化代码中就成为了局部内部类。局部静态内部类与静态内部类的基本特性相同。局部内部类与内部类的基本特性相同。

匿名内部类是一种没有类名的内部类,不使用关键字class、extends、implements,没有构造函数,它必须继承(extends) 其他类或实现其他接口。匿名内部类的好处是代码更加简洁、紧凑,但带来的问题是易读性下降。它一般应用于GUI ( Graphical User Interface,图形用户界面)编程中实现事件处理等。在使用匿名内部类时,需要牢记以下几个原则:

  • 1)匿名内部类不能有构造函数。
  • 2)匿名内部类不能定义静态成员、方法和类。
  • 3)匿名内部类不能是public、protected、private、static
  • 4)只能创建匿名内部类的-一个实例。
  • 5)一个匿名内部类一-定是在new的后面,这个匿名类必须继承-一个父类或实现一个接口。
  • 6)因为匿名内部类为局部内部类,所以局部内部类的所有限制都对其生效。

常见题型:
在这里插入图片描述
解释:
在这里插入图片描述

9.如何获取父类的类名

Java语言提供了获取类名的方法: getClass( ). getName(),开发人员可以调用这个方法来获取类名,代码如下:

在这里插入图片描述
但是想直接通过此方法获取父类的类名是不可行的!代码:
在这里插入图片描述
运行结果:
为什么输出的结果不是“A”而是“Test"呢?主要原因在于Java语言中任何类都继承自Object类,getClass()方法在Object类中被定义为final与native,子类不能覆盖该方法。因此this. getClass( )和super. getClass( )最终都调用的是Object 中的getClass( )方法。而Object的getClass( )方法的释义是:返回此Object 的运行时类。由于在示例2中实际运行的类是Test而不是A,因此程序输出结果为Test。那么如何才能在子类中得到父类的名字呢?可以通过Java的反射机制,使用getClass ( ). getSuperclass( ). getName(),代码如下所示:
在这里插入图片描述

10.this和super有什么区别

在Java语言中,this 用来指向当前实例对象,它的一个非常重要的作用就是用来区分对象的成员变量与方法的形参(当一个方法的形参与成员变量的名字相同时,就会覆盖成员变量)。为了能够对this有一个更好的认识,首先创建一个类People, 示例如下:
在这里插入图片描述
上例中,第一个构造函数使用this. name来表示左边的值为成员变量,而不是这个构造函数的形式参数。对于第二个构造函数,由于在这个函数中形参与成员变量有着相同的名字,因此对于语句name = name,等号左边和右边的两个name都代表的是形式参数。在这种情况下,只有通过this才能访问到成员变量

super可以用来访问父类的方法或成员变量。当子类的方法或成员变量与父类有相同名字时也会覆盖父类的方法或成员变量,要想访问父类的方法或成员变量只能通过super关键字来访问,示例:
在这里插入图片描述
常见题型:
下面的代码输出结果是什么?
在这里插入图片描述
解释:
在这里插入图片描述

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值