[Java程序员面试笔记] 面试笔试部分 --面向对象

1 基本概念

知识点梳理

在面向对象程序设计中,类和对象是两个最基本的概念。类是一批对象的抽象,是各个对象共性的总结和提炼。对象则是某个类的具体实例化,因此对象才是程序中参与各种事物处理的主体。
Java语言提供了对创建类和创建对象的语法支持。

  • 下面是Java 语言定义类的语法格式。
[类修饰符]  class 类名
{
	构造方法
	属性
	方法
	......
}

1)类修饰符:对于外部类可以使用public, final或者省略修饰符修饰,内部类还可以使用protected和private 修饰。
2)类名:描述一个类具体特征的名字,只要是合法的标识符即可,但是为了程序具有更好的可读性和可维护性,建议按照标准的类命名规则给类起名字。
3)构造方法:用于创建一个类的实例的方法,构造方法必须与类名相同,不能有返回值,可以重载。
4)属性:定义该类或该类实例中所包含的数据。
5)方法:用于定义该类或该类实例中的行为。

  • 定义属性的格式为:
			[修饰符]  属性类型  属性名 [=初始值]

1)修饰符:修饰符可以省略,也可以是public, protected, private,static, final。
其中public, protected, private为该属性的访问权限,static, final组合起来修饰属性。
修饰符static修饰的成员属性表示它属于这个类共有,而不属于这个类的某个对象。
修饰符final修饰的成员属性为常量属性,其值不可以改变。
2)属性类型:可以是Java支持的任意基本数据类型或者引用类型。
3)属性名:描述一个属性特征的名字,只要是合法的标识符即可。

  • 定义方法的语法格式为:
		[修饰符] 方法返回值类型  方法名 (形参列表)

1)修饰符:修饰符可以省略,也可以是public, protected, private,static, final,abatract。
其中public, protected, private为该方法的访问权限,最多只能出现其中的一个。
abstract表示方法为抽象方法,在该类中没有实现,需要在其子类中实现,而final表示该方法为最终方法,不能在子类中覆盖,所以abstract和final 只能出现其中之一,不能同时出现。
修饰符static修饰的方法表示它属于这个类共有,而不属于这个类的某个对象。所以static修饰的方法只能调用static的方法和static的成员,而不能调用非static的方法或非static的成员。
2)方法返回值类型:可以是Java支持的任意基本数据类型或者引用类型。
3)方法名: 描述该方法的功能特征的名字。

面试题1 简述面向对象和面向过程的区别

面向过程的程序设计方法是一种结构化的程序设计方法。它是以过程为中心,按照一定的顺序和步骤,自顶向下逐步求精的一种程序设计方法。 在进行面向过程的程序设计时,通常会将问题按照功能划分为一个一个的子模块,一个模块可以独立的完成一个功能,模块之间是调用和被调用的关系。每个字模块还可以根据其功能和问题的规模继续划分为更小的子模块。这样程序一级一级地自上而下地顺序执行,最终解决整个问题。
面向过程的程序设计方法更适合于功能相对简单、处理流程相对清晰的软件程序开发。

面向对象的编程思想是一种以事物为中心的编程思想。这种编程思想模拟了客观世界事务的真实存在形式,用更加符合常规的思维方式来处理客观世界的问题。 在面向对象的程序设计中,将事物的共性抽象为类,并在类中封装了该类自身的属性,定义了该类自身行为的方法。而在运行的程序中,则要通过类的具体实例化实例,即对象,来实现某个具体的功能。同时,对象与对象之间可以通过消息进行通信,这样可以利用多个对象共同完成一件事情。
所以面向对象的程序设计方法更适合于一些功能强大、结构和流程相对复杂的软件系统开发。

面试题2 简述面向对象的基本特征

面向对象的基本特征包括:抽象、继承、封装和多态。

  • 1)抽象
    抽象是面向对象的重要特征之一。所谓抽象就是将某类事物的共性抽离出来,构成这类事物共有特性的集合,即类。
    抽象包括两个方面, 一是过程抽象,二是数据抽象。
  • 2)继承
    继承是指一个新类继承了原始类的特性。
    在这对继承关系中,新类称为原始类的派生类,或子类,原始类称为新类的基类,或父类。
    在类的继承关系中,子类可以从其父类中继承方法和属性以实现代码的复用。
    同时子类也可以修改继承来的方法或者增加新的方法,以便更加符合子类的需求。
  • 3)封装
    封装是指一个类将自己的数据和一些方法的实现细节进行隐藏,只暴露出一些开放的接口供外界访问。
    封装是类对自身数据和内部行为的一种保护措施,增加了代码的安全性和可靠性。
  • 4)多态
    多态是指允许不同类的对象对同一消息作出自己不同的回应。
    多态语言具有灵活、抽象、行为共享、代码共享等优势,在程序的设计开发及设计模式中应用非常广泛。
    一般认为,面向对象的多态存在两种形式,一是运行时多态,二是编译时多态,但有些书认为编译时多态(函数重载或运算符重载)不属于多态范畴,因为它不存在动态绑定。

2 继承

知识点梳理

继承是面向对象的四大特征之一,之所以要支持类的继承,主要是为了实现软件的复用,子类可以继承并服用父类的属性和方法,从而节约软件的开发成本。
Java 中的继承是通过extends 关键字实现的。被继承的类称为父类,实现继承的类称为子类。在Java中每个子类只能有一个直接父类,所以Java不支持多继承。

在Java继承中,需要注意以下问题:

  • 1)访问控制符
    Java提供了三种访问控制符,private、protected、public 分别代表三种访问控制级别。
    如果不佳任何访问控制符,则表示默认的访问控制级别defalut。所以Java的访问控制级别共有四种。
    在这里插入图片描述
    对于继承关系来说, 如果子类和父类处于同一个包内,则子类可继承父类中的public、protected和defalut访问权限的成员和方法;如果子类和父类不在同一个包内,则子类只能继承父类中的public、protected访问权限的成员和方法。

  • 2)成员和方法的覆盖
    虽然子类继承父类多是为了复用父类的属性和方法,但有时父类的某个方法的实现不符合子类的要求,这时就需要子类覆盖父类的方法。
    Java中子类包含与父类同名方法的现象叫做方法的重写,也称为方法的覆盖,即子类中的方法覆盖了其父类中的同名方法。
    覆盖了父类中同名方法的子类的对象在调用这个同名的方法时,一定是调用的子类中的那个方法,而不是父类中的方法。方法的覆盖是面向对象中多态的实现基础。
    同样,成员变量也存在这种覆盖的关系。当子类中的成员变量与父类中定义的成员变量同名时,子类的成员变量也会覆盖父类的成员变量。覆盖了父类中的同名成员变量的子类对象在调用这个成员变量时,一定调用的是子类的那个成员变量,而不是父类的同名成员变量。

  • 3)通过super引用访问父类的属性和方法
    在Java中,super是一个关键字,表示该类的直接父类对象的引用。

super只能在类的对象中被引用,所以它不能出现在static修饰的方法中。

面试题1 什么是继承?Java继承有哪些特性?

分析

继承是面向对象的一个非常重要的特性,是面向对象程序设计中实现代码复用的一个重要手段。在继承过程中,子类可以继承并复用父类的属性和方法,从而可以节约软件的开发成本,提高开发效率。
Java 语言是一种典型的面向对象程序设计语言,因此Java 也支持类的继承。Java继承有以下几个特征:

  • 1)不支持多重继承。 与C++不同,Java语言不支持多重继承,在Java 中一个子类至多有一个父类,但是Java 可以通过实现多个接口来达到多重继承的目的。

  • 2)**继承的权限问题。**如果子类和父类处于同一个包内,则子类可继承父类中的public、protected和defalut访问权限的成员和方法;如果子类和父类不在同一个包内,则子类只能继承父类中的public、protected访问权限的成员和方法。

  • 3)成员变量和方法存在覆盖关系。 子类中定义的成员变量或方法与父类中的成员变量或方法同名时,子类中的成员变量或方法将覆盖父类中的成员变量或方法,此时不会发生继承。

答案:
继承是面向对象的一个重要特性,通过继承实现代码的复用。Java继承的重要特征包括:一是不支持多重继承可实现多个接口;二是子类只能继承父类的非私有成员变量和方法;三是子类可覆盖父类的成员变量和方法。

面试题2 简述继承与组合的区别

  • 分析
    继承和组合都是实现代码复用的重要手段,但是两者存在着本质的区别。

对于继承来说,父类是其所有子类共性的抽象,而子类则是其父类的某种特例,因此也有人说在继承中子类和父类的关系是一种“is - a”的关系,就像在知识点中列出的“Chinese is People”。 另外,在继承关系中,虽然一个子类的对象中包含其父类的对象,但这种包含是隐式的。

组合则是另一种形式的代码复用。所谓组合是指在一个新类中嵌入一个旧类的对象,从而重复利用已有类的功能。通常情况下,新类作为整体类而存在,旧类作为局部类存在,新类和旧类之间存在整体和部分的关系。 所以也有人说在组合中新类和旧类的关系是一种“has - a” 的关系。另外,在新类的对象中包含旧类的对象,这种包含是显式的。

**虽然继承可以很好的实现代码的复用,但是继承本身存在着先天的不足,其最大的缺点就是破坏了类的封装特性。**由于在访问权限允许的前提下,子类可以直接访问父类的属性和方法,这样就造成了子类和父类之间的深度耦合,这种情况并不利于代码的维护和拓展。所以除非两个类之间存在明确的“has - a”关系,否则不应轻易使用继承,通过实现接口和实现组合的方式同样可以达到实现多态和代码复用的目的。

  • 答案
    继承和组合都是实现代码复用的方式,继承的子类和父类之间是一种“is - a” 的关系,组合的新类和旧类之间是一种“has - a”的关系。
    在实际应用中尽量少用继承,因为它可能造成子类和父类之间的深度耦合,从而使代码变得臃肿而不利于维护,建议更多的使用组合的方式实现代码复用。

面试题3 简述overload和override的区别

  • 考察的知识点
    1)方法的覆盖
    2)方法的重载
  • 问题分析
    overload和override 是两个不同的概念。overload 的意思是重载,是指一个类中定义了多个同名的函数,并依靠函数的参数个数或参数的类型进行区分。由于在程序的编译过程中,编译器会根据调用的函数的参数类型以及参数个数匹配到具体调用的是哪一个函数,所以函数的重载技术也被称为编译时多态(但也有人认为函数的重载不应属于多态范畴)。

在使用函数重载时请牢记,同名的函数只能通过参数的不同加以区分,即通过不同的参数个数,不同的参数类型或者参数序列来区分不同的函数,不能通过函数的访问权限、函数的返回值类型以及函数抛出的异常类型等信息来区分同名函数。

override 的意思是覆盖,子类包含与父类同名方法的现象叫做方法的覆盖。覆盖了父类中同名方法的子类的对象在调用这个同名的方法时,一定是调用的子类中的那个方法,而不是父类中的方法,因此方法的覆盖是实现多态的基础。

在子类覆盖父类的方法时,应注意以下几点:
1)子类的方法名、形参列表要与父类中被覆盖的方法一致
2)子类方法返回值类型英语被覆盖的父类方法返回值类型相同
3)子类方法声明抛出的异常类应当与父类方法声明抛出的异常类相同
4)子类方法的访问权限应该比父类中被覆盖方法的访问权限更大或相等
5)父类中的private 访问权限的方法在子类中不能被覆盖,因为该方法在子类中是不可见的
6)父类中的private访问权限的方法要么都是类方法(static方法),要么都是实例方法(非static方法),不能一个是类方法,一个是实例方法

如果违背了以上的覆盖原则,则程序编译时会报错。

  • 答案
    overload 的意思是重载,是指在同一类中定义了多个同名的函数,依靠函数的参数个数或者参数类型进行区分的一项技术。
    override 的意思是覆盖,是指子类通过重写父类中的同名方法覆盖度类中的方法,覆盖是实现多态的基础。

面试题4 如何获取父类的类名?如何获取当前运行类的类名?

  • 分析
    Java 中任何类都继承自Object类,而在Object类中定义类一个方法getclass(),该方法的功能是返回该对象运行时所属类对应的Class对象,然后通过调用Class对象的getName()方法获取该类的类名。

当企图在Child类中的方法企图用super引用调用父类对象的getClass().getName()方法来获取其父类的名字,但是返回的运行结果仍为“Child”,而不是“Father”。究竟原因是什么呢?

这是因为虽然Object类中定义了getClass() 方法,并且其子类都可以继承该方法,但是这个方法在Object类中被定义为final,所以子类不能覆盖该方法。因为无论是this.getClass() 还是 super.getClass() 最终调用的都是Object类中的getClass()方法,而Object类中的getClass()方法的作用就是获取当前运行时类, 所以上面两种方法都只能输出当前运行类的类名,并不能得到其父类的类名。

  • 答案
    通过this.getClass().getName()获取当前运行类的名字。
    通过this.getClass().getSuperClass().getName()获取当前运行类的父类的名字。

final关键字

接上面这道题,涉及到一个重要的知识点:final修饰的方法不能被其子类覆盖。
关于final关键字,请牢记以下三点:

  • 1)final修饰属性时表示该属性不可变
  • 2)final修饰方法时表示该方法不可被覆盖
  • 3)final修饰类时表示该类不可被继承

3 构造方法

知识点梳理

在Java中构造方法也叫做构造函数或构造器,它是一种用于创建实例的特殊方法,构造方法的用途就是在创建对象时执行对象的初始化操作。
当对一个对象进行初始化时,系统会默认地为对象中的属性进行初始化,初始化的原则是:数值类型的属性默认初始化为0,boolean类型的属性初始化为false,引用类型的属性初始化为null。 但是如果要将这些属性初始化为其他的值,就需要构造函数来实现。

在Java中一个类里面至少包含一个构造方法。
所以如果程序中没有显式的为一个类定义一个构造方法,则系统会为这个类提供一个无参的构造方法,这个构造方法函数体为空,不做任何操作。但是一旦程序中显式的定义了一个构造方法(无论是带参数的还是不带参数的),那么系统就不再提供默认的构造方法。
定义构造方法时需要注意以下几点:
1)构造方法必须与类名一致。
2)构造方法不能有返回类型,void也不行。
3)构造方法可以重载,即在同一个类中可以定义多个构造方法,通过参数列表的不同来区别不同的构造函数。
4)在一个构造方法中可以通过this(X,X) 的形式调用另一个重载的构造方法,使用this调用另一个重载的构造函数只能在构造方法中使用,而且必须作为构造方法来执行体的第一条语句。
5)在子类的构造方法中可以通过super(X, X)的形式调用父类的构造方法,使用super调用父类的构造方法也必须出现在子类构造方法执行体的第一行,因此在构造方法中this调用和super调用不能同时出现。

面试题1 构造函数能否被继承?能否被重载?

  • 分析
    构造函数时不能被继承的,也就是说,子类不能获得父类的构造函数。 一个类里面至少包含一个构造方法,即使不显式的定义一个构造函数,系统也会为该类生成一个默认的无参构造函数。这就说明,子类时无法继承父类的构造函数的,必须在自身的类中单独定义。

其次,**构造函数是可以被重载的。**同一个类中可以定义多个构造方法,通过参数列表的不同来区分不同的构造函数。同时也可以通过t his(X, X) 的 形式调用另一个重载的构造方法。

  • 答案
    1)子类不能继承父类的构造方法,但是可以通过super引用父类的构造方法。
    2)构造方法可以被重载,同时在构造方法中可以通过this调用其它构造方法。

面试题2 简述静态块、非静态块和构造函数的初始化顺序

执行顺序是:父类的静态初始化块 -> 子类的静态初始化块 -> 父类的初始化块 -> 父类的构造函数 -> 子类的初始化块 -> 子类的构造函数

对于这个执行顺序的理解其实也很容易。
因为静态初始化块是在类加载时就执行的,主要是为了初始化类中的static成员,所以肯定会最早得到执行。
而对于类的加载,都要先加载父类再加载子类,因为子类要从父类中派生 并复用父类的代码,所以父类的静态初始化块一定早于子类。
在构造对象时,如果对象的类存在继承关系,一定先构造父类的对象再构造子类的对象,因此总是先执行父类的构造方法,再执行子类的构造方法。

另外,对于非静态的初始化块,它也是咋初始化类对象时才会被调用的,而且它会优于该类的构造方法执行。

4 抽象类和接口

知识点梳理

使用关键字abstract修饰的类称为抽象类,使用关键字abstract修饰的方法称为抽象方法。
与一般的类不同,抽象类不能被实例化,也就是说,不能使用new关键字调用一个抽象类的构造方法来实例化一个对象。
抽象类中的构造方法主要用于被其子类调用。
与一般的方法不同,抽象方法不能有方法体,它只是一个方法的定义,该方法要在该抽象类的子类中实现。
需要注意的一点是,含有抽象方法的类只能被定义为抽象类。

面试题1 简述抽象类和接口的相同点与差别

使用关键字abstract修饰的类称为抽象类,使用关键字abstract修饰的方法称为抽象方法。抽象方法不能有方法体,只能有方法的定义。而抽象类不能被实例化,抽象类中的构造方法主要用于被其子类调用。如果一个类中包含了抽象方法,那么这个类必须被定义为抽象类。

接口本质上也是一种抽象类,只不过接口的定义不是通过关键字class,而是通过关键字interface。另外,接口中不能包含构造方法和初始化块,包含的属性只能是常量,同时定义的方法也必须是抽象方法(在接口中定义的抽象方法系统自动为其增加abstract修饰符,所以不必显式添加)。同时在接口中也可以包含内部类、内部接口和枚举类的定义。

  • 总结起来,接口和抽象类有以下相同点:
    1)抽象类和接口都不能实例化,它们都位于继承树的顶端,都是用于被其他类实现和继承的。
    2)抽象类和接口都可以包含抽象方法,而实现接口或继承抽象类的普通子类都必须实现这些抽象方法

  • 与此同时,接口和抽象类也存在着很大的差别,具体表现为:

  • (1)语法层面上
    1)接口的定义中只能包含抽象方法,而不能包含已经提供实现的方法;抽象类中既可以包含抽象方法,也可以包含普通方法,甚至抽象类完全可以只包含普通的方法。
    2)接口里不能定义静态方法,而抽象类中可以定义静态方法。
    3)接口里只能定义静态常量属性,不能定义普通属性,而抽象类中既可以定义普通属性也可以定义静态常量属性。
    4)接口里不能定义构造方法,而抽象类中可以定义构造方法。但是抽象类中的构造方法不是用于创建对象的,而是为了让其子类调用这些构造方法来完成属于抽象类的初始化工作。
    5)接口中不能包含初始化块,而抽象类中可以包含初始化块。
    6)抽象类中的抽象方法的访问类型可以是public, protected,但接口中的抽象方法只能是public类型的,并且默认为public abstract 类型。
    7)抽象类和接口中都可以包含静态成员变量,抽象类中的静态成员变量的访问类型可以任意,但接口中定义的变量只能是public static final 类型,并且默认为public static final 类型.
    8)一个类最多只能包含一个直接父类,包括抽象类,但是一个类可以实现多个接口。

  • 2)设计层面上
    接口是一种规范或者叫做契约,规定了实现者必须向外提供哪些服务。而抽象类则是系统中多个子类的公共父类,它更多的体现的是一种设计模板,未实现的抽象方法需要其子类进一步完善。

面试题2 Java 抽象类可以实现接口吗?它们需要实现所有的方法吗?

  • 分析
    与普通的类一样,抽象类也可以实现接口。实现接口的方法同样是使用implements 关键字。 对于普通类来说,如果一个类实现了一个接口,那么该类必须实现该接口所定义的全部方法,而抽象类则不然。因为抽象类不能被实例化,所以抽象类不需要实现接口的所有方法。

那么用抽象类实现接口有什么用处呢?
一个很重要的用途就是在抽象类中只能实现接口中的通用方法,而对于接口中的一些特殊方法可交由抽象类的子类具体表现实现。这样抽象类在接口和子类之间充当了一个中间层,这样可以避免子类实现接口而必须实现每一个方法所产生的负担。

  • 答案
    抽象类可以实现接口,但不需要实现所有的方法。

面试题3 Java抽象类可以是final吗?

  • 分析
    Java的抽象类中是不能被实例化的,只有继承了该抽象类的普通类才能够被实例化,所以抽象类的存在意义就在于被继承。 但是如果一个类被声明为final,则这个类将不能被继承。因此Java中的抽象类不能是final的,如果将一个抽象类声明为final,则会阻止该类被继承,这与“抽象类的存在意义就在于被继承”是相互矛盾的。
    其实在java 中abstract和final 本身就是互斥的,两者同时作用于一个类将会产生编译的错误。

这个题的核心就是考查“final 和abstract 这两个关键字 是属于互斥的,不能同时存在”这个知识点 还可以进行延伸

接口中的方法能否被声明为final
  • 答案是不可以,因为接口中的方法默认是public abstract 类型,所以不可以声明为final。
  • 但是接口中的属性默认是public static final 类型,属性不存在是不是abstract类型。

5 内部类

知识点梳理

顾名思义,所谓内部类就是定义一个类内部的类,所以内部类有时也被称为嵌套类。相应的,包含内部类的类也被称为外部类。
根据内部类定义的位置不同划分,又可将内部类分为成员内部类、局部内部类和匿名内部类。

  • 1 成员内部类
    成员内部类是一种与类的属性和方法相似的类成员。成员内部类分为两种,一种是静态成员内部类,另一种是非静态成员内部类。

非静态成员内部类是一种最为常见的内部类。它是一种不使用static关键字修饰的内部类,并且具有以下特性:
1)就像类中的方法一样,在非静态成员内部类里可以直接访问其外部类的private成员。
2)包含非静态成员内部类的类编译后生成两个class文件,其文件名的命名规则是

OuterClassName java
OuterClassName $ InnerClassName.java

3)在非静态成员内部类的方法中访问某个变量,该变量的查找顺序是<1> 该方法的局部变量-> <2>该方法所在的内部类变量 -> <3> 该内部类所在外部类中的变量。

静态成员内部类是使用static修饰的成员内部类。静态成员内部类是类相关的内部类,它属于整个外部类,而不属于外部类的某个对象。 所以有时静态成员内部类也被称为类内部类。 静态成员内部类具有以下特征:
1)静态成员内部类不能访问其外部类的实例成员(非static 成员),只能访问外部类的类成员(static成员)。即使是静态成员内部类的实例方法(非static方法)也不能访问其外部类的实例成员。
2)外部类不能直接访问静态成员内部类的成员,但可以通过静态成员内部类的类名进行访问,也可以通过静态成员内部类的对象进行访问。
3)接口里定义的内部类只能是静态成员内部类。

  • 2 局部内部类
    如果把一个内部类定义在方法中,则这个内部类就是一个局部内部类。局部内部类仅在该方法中有效,而不能在外部类以外的地方使用。因此局部内部类不能使用访问控制符(public, protected, private )修饰,也不能使用static修饰。

  • 3 匿名内部类
    顾名思义,匿名内部类就是一种没有显式定义名字的内部类。匿名内部类适用于那种只需要使用一次的类。 在创建匿名内部类时会同时创建一个该类的对象,并且该匿名内部类不能重复使用。定义匿名内部类的格式如下:

new 父类构造方法(参数列表) | 实现接口(){
		//内部类的实现
}

从匿名内部类的定义语法格式可以看出,由于匿名内部类本身无类名,所以它必须继承一个父类或者实现一个接口,并且最多只能继承一个父类或实现一个接口。 同时,在定义匿名内部类应该注意以下两点:
1) 由于匿名内部类无类名,所以匿名内部类中不能定义构造方法,但是可以定义实例初始化以完成一些初始化的动作。
2)由于在定义匿名内部类时会同时创建该类的一个对象,所以匿名内部类不能是抽象类。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值