面试:Java 面向对象

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

面向对象把数据以及对数据的操作放在一起,作为相互依存的整体(即对象)。

对同类对象抽象出共性,即类。

类通过简单的外部接口与外界发生关系,对象与对象之间通过消息进行通信。


面向过程是一种一是 事件 为中心的的开发方法,自顶向下顺序执行,逐步求精。程序结构按功能划分为若干个基本模块,这些模块形成树状接口,各模块之间的关系也比较简单,在功能上相对独立,每一个模块内部一般都是由顺序、选择、循环 3 种基本结构组成。其模块化实现的具体方法是使用子程序,而程序流程在写程序时就已经决定。

2. 面向对象有哪些特征

  1. 抽象

  2. 继承

  3. 封装(信息隐藏)

  4. 多态(允许不同类的对象对同意消息作出相应),包括参数化多态、包含多态。具有灵活、抽象、行为共享、代码共享等优势,很好地解决了函数同名问题。

3. 面向对象的开发方式有什么优点

  1. 较高的开发效率

  2. 保证软件的鲁棒性

  3. 保证软件的高可维护性

4.什么是继承

子类可以通过父类的一些成员变量与方法,提高代码的复用性,提高开发效率。

5. 组合和继承有什么区别

组合是在新类里面创建原有类的对象,重复利用已有类的功能。

组合和继承存在着对应关系:组合中的整体类和继承中的子类对应,组合中的局部类和继承中的父类对应。

如何选择?

除非两个类是 is-a 关系,否则不要轻易用继承。

如果类之间没有 is-a 关系,可以通过实现接口与组合的方式来达到相同的目的,比如策略模式。

采用接口与组合的方式,比采用继承的方式具有更好的可扩展性。

能使用组合就尽量不用继承。

6. 多态的实现机制

多态主要有 2 种表现方式:

  1. 方法的重载(overload):同一个类中又多个同名方法(不同参数),到编译时就可以确定到底调用哪个方法,是一种 编译时多态。重载可以被看作一个类中方法的多态性。

  2. 方法的覆盖(override):子类可以覆盖父类的方法,同样的方法在子类和父类中又不同的表现形式。父类的引用变量可以指向子类实例对象,调用的方法在运行期间才动态绑定。因此方法覆盖实现的多态是运行时多态。

7. 重载和覆盖有什么区别

使用重载时要注意:

  1. 重载是通过不同的方法参数来区分的,例如不同的参数个数、不同的参数类型、不同的参数顺序

  2. 不能通过方法的访问权限、返回类型、以及抛出的异常类型来进行重载

  3. 对于继承来说,如果基类方法的权限为 private,那么不能在派生类对其重载;否则只是一个新的方法(同名函数而已)

使用覆盖时要注意:

  1. 子类的覆盖方法必须要和父类中被覆盖的方法有相同的函数名和参数

  2. 子类…返回类型必须和父类…返回类型相同

  3. 子类…抛出的异常必须和父类…抛出的异常一致

  4. 父类宏被覆盖的方法不能是 private

常见笔试题:下面代码运行结果


class Super{

public int f(){ return 1;}

}



public class SubClass extends Super{

public float f(){ return 2f;}

}

编译错误:函数不能以返回值来区分。虽然父类和子类中的函数有不同的返回值,但有相同的函数名,编译器无法区分。

8. 抽象类和接口的异同

相同点

  1. 都不能被实例化

  2. 接口的实现类或抽象类的子类只有实现了接口或抽象类中的方法才能被实例化

不同点

  1. 接口只有定义,方法不能在接口中实现。抽象类可以有定义与实现,其方法可以在抽象类中被实现。

  2. 接口需要实现 implements,抽象类只能被继承 extends,一个类可以实现多个接口,但只能继承一个抽象类

  3. 接口强调特定功能的实现,接口强调所属关系

  4. 接口中定义的成员变量默认为 public static final,只能是静态的不能被修改的数据成员,而且必须赋值。而抽象类可以有自己的数据成员,也可以有非抽象方法,而且抽象类的成员默认为 default(本包可见)。抽象类的抽象方法(其前有 abstract 修饰不能用 private, static, synchronized, native 等访问修饰符溪水,同时方法必须要以分号结尾,并且不带花括号。

  5. 接口被用于实现比较常常用的功能,便于日后维护或添加删除方法;而抽象类更倾向于当公共类的角色,不适用于日后重新修改代码。

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

接口不能有静态方法(因为接口是不能实现方法的,但静态方法又是必须要实现的,两者矛盾)。

9. 内部类有哪些

内部类: 即定义在一个类内部的类。

所谓的嵌套类就是内部类,只是嵌套类是 C++ 的说法。

内部类分为:

  1. 静态内部类

  2. 成员内部类

  3. 局部内部类

  4. 匿名内部类


class outerClass{

static class innerClass(){} // 静态内部类

}



class outerClass{

class innerClass{} // 成员内部类(普通内部类)

}

class outerClass{

public void memberFunction(){

class innerClass{} // 局部内部类

}

}



public class MyFrame extends Frame{

public MyFrame(){

addWindowListener(new WindowAdapter(){ // 匿名内部类

public void windowClosing(WindowEvent e){

dispose();

System.exit(0);

}

}

}

}

静态内部类不能与外部类同名,不能访问外部类普通的成员变量,只能访问外部类静态成员和静态变量(包括私有类型)

成员内部类可以自由使用外部类的属性和方法(无论是否静态)

非静态内部类不能定义静态成员。

局部内部类是定义在一个代码块里面的类,作用范围为所在代码块,是内部类中最少见的一种,局部内部类像局部变量一样,不能被public, protected, private, static修饰,只能访问方法中定义为 final 类型的局部变量

10. 如何获取父类的类名

getClass().getName()可以获取类名


class A{}



public class Test extends A{

public void test(){

System.out.println(super.getClass().getName());

}



public static void main(String[] args){

new Test().test();

}

}

// 输出是 Test

为什么输出不是 “A” 而是 Test,因为 Java 中任何类都继承自 Object,getClass()Object 中被定义为 finalnative,子类不能覆盖改方法。this.getClass()super.getClass() 最终调用的都是 Object 中的 getClass(),而 ObjectgetClass() 方法释义为:返回此 Object 运行时的类。所以说白了就是运行时是 Test 类而不是 A 类。

我们可以通过 Java 反射机制(类.getSuperClass().getClass())来获得 父类的名字


class A{}



public class Test extends A{

public void test(){

System.out.println(this.getClass().getSuperClass().getName());

}



public static void main(String[] args){

new Test().test();

}

}



// 输出 A

11. this 和 super 的区别


class People{

String name;

public People(String name){

// 正确的写法

this.name = name;

// 错误的写法

// name = name (都是形参)

// 必须要用this才能访问成员变量

}

}

当子类方法或成员变量与父类有相同名字时,也会覆盖父类的方法或成员变量,想要访问父类的成员变量只能通过 super 关键字。

当子类构造函数需要显示父类构造函数时候,super()必须为构造函数第一句,否则编译出错。