Java面试基础
1.面向对象和面向过程的区别?
-
面向过程:面向过程性能比面向对象高
-
面向对象:面向对象易维护、易复用、易扩展
2.Java 语言有哪些特点?
简单易学、面向对象(封装,继承,多态)、平台无关性( Java 虚拟机实现平台无关性)、可靠性、安全性、支持多线程、支持网络编程并且很方便、编译与解释并存。
3.JDK和JRE?
-
JDK:Java Development Kit,它是功能齐全的Java SDK。它拥有JRE所拥有的一切,还有编译器(javac)和工具(如javadoc和jdb)。它能够创建和编译程序。
-
JRE 是 Java运行时环境。它是运行已编译 Java 程序所需的所有内容的集合,包括 Java虚拟机(JVM),Java类库,java命令和其他的一些基础构件。但是,它不能用于创建新程序。
4.Java和C++的区别?
-
都是面向对象的语言,都支持封装、继承和多态
-
Java 不提供指针来直接访问内存,程序内存更加安全
-
Java 的类是单继承的,C++ 支持多重继承;虽然 Java 的类不可以多继承,但是接口可以多继承。
-
Java 有自动内存管理机制,不需要程序员手动释放无用内存。
5.Java 面向对象三大特性
封装 继承 多态
-
封装:利用抽象数据类型将数据和操作数据的方法绑定起来,数据被保护在抽象数据类型的内部,尽可能地隐藏内部的细节,对数据的访问只能通过已定义的接口。
-
继承:使用已存在的类的定义作为基础建立新类,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过使用继承我们能够非常方便地复用以前的代码。关于继承需要注意的点:子类拥有父类对象所有的属性和方法,但是父类中的私有属性和方法子类是无法访问,只是拥有。子类可以拥有自己属性和方法,即子类可以对父类进行扩展。子类可以拥有自己属性和方法,即子类可以对父类进行扩展。
-
多态:允许不同子类型的对象对同一消息作出不同响应。也就是,用同样的对象引用调用同样的方法但是做了不同的事情。多态有两种实现方式:继承,即多个子类对同一方法的重写;接口,即实现接口并覆盖接口中同一方法;
6.抽象类和接口的区别?
相同点:
-
都不能直接实例化对象;
-
都包含抽象方法,其子类都必须覆写这些抽象方法;
不同点:
-
抽象类为部分方法提供实现,避免子类重复实现这些方法,提高代码重用性;接口只能包含抽象方法;
-
一个类只能继承一个直接父类(可能是抽象类),却可以实现多个接口;(接口弥补了Java的单继承);
-
接口是这个事物中的额外内容,继承体系是一种 like..a关系
-
抽象类是这个事物中应该具备的你内容, 继承体系是一种 is..a关系
7.抽象类可以用final修饰吗?可以有static方法吗?可以创建抽象类的实例吗?抽象类必须有抽象方法吗?
(1)抽象类不能是final的,将它们声明为final的将会阻止它们被继承,而这正是使用抽象类唯一的方法,它们也是彼此相反的,关键字abstract强制继承类,而关键字final阻止类被扩张。Java类不能既是final又是abstract的,编译时会报错。
(2)抽象类可以定义static方法,但此static方法无法没继承。
(3)不能创建抽象类的实例,即使抽象类不包含抽象方法,也不能实例化抽象类,否则编译时会报错。
(4)抽象类不强制性有抽象方法。只要使用关键字abstract就可以将类声明为抽象类。
8.重载和重写的区别
- 重载: 发生在同一个类中,方法名必须相同,参数类型不同、个数不同、顺序不同,方法返回值和访问修饰符可以不同,发生在编译时。
- 重写: 发生在父子类中,方法名、参数列表必须相同,返回值范围小于等于父类,抛出的异常范围小于等于父类,访问修饰符范围大于等于父类;如果父类方法访问修饰符为 private 则子类就不能重写该方法。
9.在 Java 中定义一个不做事且没有参数的构造方法的作用?
Java 程序在执行子类的构造方法之前,如果没有用
super()
来调用父类特定的构造方法,则会调用父类中“没有参数的构造方法”。因此,如果父类中只定义了有参数的构造方法,而在子类的构造方法中又没有用 super()
来调用父类中特定的构造方法,则编译时将发生错误,因为 Java 程序在父类中找不到没有参数的构造方法可供执行。解决办法是在父类里加上一个不做事且没有参数的构造方法。10.String StringBuffer 和 StringBuilder 的区别是什么? String 为什么是不可变的?
- 线程安全性:String 中的对象是不可变的,也就可以理解为常量,线程安全。AbstractStringBuilder 是 StringBuilder 与 StringBuffer 的公共父类,定义了一些字符串的基本操作,如 expandCapacity、append、insert、indexOf 等公共方法。StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的。
- 性能:每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的 String 对象。StringBuffer 每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用 StringBuilder 相比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。
- 可变性:String 类中使用 final 关键字修饰字符数组来保存字符串,
private final char value[]
,所以 String 对象是不可变的。而StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder 类,在 AbstractStringBuilder 中也是使用字符数组保存字符串char[]value
但是没有用 final 关键字修饰,所以这两种对象都是可变的。
总结:
(1)操作少量的数据: 适用String
(2)单线程操作字符串缓冲区下操作大量数据: 适用StringBuilder
(3)多线程操作字符串缓冲区下操作大量数据: 适用StringBuffer
11.成员变量与局部变量的区别有哪些?
- 从语法形式上看:成员变量是属于类的,而局部变量是在方法中定义的变量或是方法的参数;成员变量可以被 public,private,static 等修饰符所修饰,而局部变量不能被访问控制修饰符及 static 所修饰;但是,成员变量和局部变量都能被 final 所修饰。
- 从变量在内存中的存储方式来看:如果成员变量是使用
static
修饰的,那么这个成员变量是属于类的,如果没有使用static
修饰,这个成员变量是属于实例的。而对象存在于堆内存,局部变量则存在于栈内存。 - 从变量在内存中的生存时间上看:成员变量是对象的一部分,它随着对象的创建而存在,而局部变量随着方法的调用而自动消失。
- 成员变量如果没有被赋初值:则会自动以类型的默认值而赋值(一种情况例外:被 final 修饰的成员变量也必须显式地赋值),而局部变量则不会自动赋值。
12.一个类的构造方法的作用是什么? 若一个类没有声明构造方法,该程序能正确执行吗? 为什么? 构造方法有哪些特性?
(1)主要作用是完成对类对象的初始化工作。可以执行。因为一个类即使没有声明构造方法也会有默认的不带参数的构造方法。
(2)构造方法的特性:
- 名字和类名相同;
- 没有返回值,但不能用void声明构造函数。
- 生成类的对象时自动执行,无需调用。
13.静态方法和实例方法有何不同?
- 在外部调用静态方法时,可以使用"类名.方法名"的方式,也可以使用"对象名.方法名"的方式。而实例方法只有后面这种方式。也就是说,调用静态方法可以无需创建对象。
- 静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),而不允许访问实例成员变量和实例方法;实例方法则无此限制。
13. == 与equals的区别?
- == : 它的作用是判断两个对象的地址是不是相等。即,判断两个对象是不是同一个对象(基本数据类型==比较的是值,引用数据类型==比较的是内存地址)。
- equals() : 它的作用也是判断两个对象是否相等。但它一般有两种使用情况:
(1)情况1:类没有覆盖 equals() 方法。则通过 equals() 比较该类的两个对象时,等价于通过“==”比较这两个对象。
(2)情况2:类覆盖了 equals() 方法。一般我们都覆盖 equals() 方法来比较两个对象的内容是否相等;若它们的内容相等,则返回 true (即,认为这两个对象相等)。
14.hashCode()与equals()的区别?
从性能和可靠性进行分析:
- 重写equals()方法时性能比较低,而利用hashCode()进行对比,则只要生成一个hash值进行比较就可以了,效率很高。
- hashCode()并不是完全可靠,有时候不同的对象生成的hashcode也会一样,所以hashCode()只能说是大部分时候可靠,并不是绝对可靠。所以有以下结论:
(1)equals()相等的两个对象他们的hashCode()肯定相等,也就是用equals()对比是绝对可靠的;
(2)hashCode()相等的两个对象他们的equals()不一定相等,也就是hashCode()不是绝对可靠的。
总结:
- 若重写了equals(Object obj)方法,则有必要重写hashCode()方法。
- 若两个对象equals(Object obj)返回true,则hashCode()有必要也返回相同的int数。
- 若两个对象equals(Object obj)返回false,则hashCode()不一定返回不同的int数。
- 若两个对象hashCode()返回相同int数,则equals(Object obj)不一定返回true。
- 若两个对象hashCode()返回不同int数,则equals(Object obj)一定返回false。
- 同一对象在执行期间若已经存储在集合中,则不能修改影响hashCode值的相关信息,否则会导致内存泄露问题。
15.hashCode()的作用?
hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。hashCode() 定义在JDK的Object.java中,这就意味着Java中的任何类都包含有hashCode() 函数。hashCode() 在散列表中才有用,在其它情况下没用。在散列表中hashCode() 的作用是获取对象的散列码,进而确定该对象在散列表中的位置。
16.final关键字的作用?
- 对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。
- 当用final修饰一个类时,表明这个类不能被继承。final类中的所有成员方法都会被隐式地指定为final方法。
- 使用final方法的原因有两个。第一个原因是把方法锁定,以防任何继承类修改它的含义;第二个原因是效率。在早期的Java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升(现在的Java版本已经不需要使用final方法进行这些优化了)。类中所有的private方法都隐式地指定为final。
17.Java中的异常处理?
![](https://app.yinxiang.com/shard/s45/res/2b4dd515-c884-4429-ba2f-7429fa92a629/687474707331392d322f457863657074696f6e2e706e67.png)
所有的异常是Throwable类。Throwable: 有两个重要的子类:Exception(异常) 和 Error(错误) ,二者都是 Java 异常处理的重要子类,各自都包含大量子类。
- Error:程序无法处理的错误。表示运行应用程序中较严重问题,大多数错误与代码编写者执行的操作无关,而表示代码运行时 JVM(Java 虚拟机)出现的问题。
- Exception:程序本身可以处理的异常。xception 类有一个重要的子类 RuntimeException。RuntimeException 异常由Java虚拟机抛出。NullPointerException(要访问的变量没有引用任何对象时,抛出该异常)、ArithmeticException(算术运算异常,一个整数除以0时,抛出该异常)和 ArrayIndexOutOfBoundsException (下标越界异常)。
finally块不会被执行的四种情况:
- 在finally语句块第一行发生了异常。 因为在其他行,finally块还是会得到执行;
- 在前面的代码中用了System.exit(int)已退出程序。 若该语句在异常语句之后,finally会执行;
- 程序所在的线程死亡;
- 关闭CPU;
18.BIO、NIO、AIO有什么区别?
- BIO (Blocking I/O): 同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待其完成。在活动连接数不是特别高(小于单机1000)的情况下,这种模型是比较不错的,可以让每一个连接专注于自己的 I/O 并且编程模型简单,也不用过多考虑系统的过载、限流等问题。线程池本身就是一个天然的漏斗,可以缓冲一些系统处理不了的连接或请求。但是,当面对十万甚至百万级连接的时候,传统的 BIO 模型是无能为力的。因此,我们需要一种更高效的 I/O 处理模型来应对更高的并发量。
- NIO (New I/O): NIO是一种同步非阻塞的I/O模型,在Java 1.4 中引入了NIO框架,对应 java.nio 包,提供了 Channel , Selector,Buffer等抽象。NIO中的N可以理解为Non-blocking,不单纯是New。它支持面向缓冲的,基于通道的I/O操作方法。 NIO提供了与传统BIO模型中的
Socket
和ServerSocket
相对应的SocketChannel
和ServerSocketChannel
两种不同的套接字通道实现,两种通道都支持阻塞和非阻塞两种模式。阻塞模式使用就像传统中的支持一样,比较简单,但是性能和可靠性都不好;非阻塞模式正好与之相反。对于低负载、低并发的应用程序,可以使用同步阻塞I/O来提升开发速率和更好的维护性;对于高负载、高并发的(网络)应用,应使用 NIO 的非阻塞模式来开发。 - AIO (Asynchronous I/O): AIO 也就是 NIO 2。在 Java 7 中引入了 NIO 的改进版 NIO 2,它是异步非阻塞的IO模型。异步 IO 是基于事件和回调机制实现的,也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。AIO 是异步IO的缩写,虽然 NIO 在网络操作中,提供了非阻塞的方法,但是 NIO 的 IO 行为还是同步的。对于 NIO 来说,我们的业务线程是在 IO 操作准备好时,得到通知,接着就由这个线程自行进行 IO 操作,IO操作本身是同步的。