我这里以 30 道 Java 基础知识题目,带着大家回顾一下 Java 基础知识。
1. 面向对象和面向过程的区别
面向过程
长处: 性能比面向对象高,因为类调用时须要实例化,开销比拟大,比拟耗费资源; 比方单片机、嵌入式开发、Linux/Unix 等个别采纳面向过程开发,性能是最重要的因素。
毛病: 没有面向对象易保护、易复用、易扩大
面向对象
长处: 易保护、易复用、易扩大,因为面向对象有封装、继承、多态性的个性,能够设计出低耦合的零碎,使零碎更加灵便、更加易于保护
毛病: 性能比面向过程低
2. Java 语言有哪些特点?
- 简略易学;
- 面向对象(封装,继承,多态);
- 平台无关性(Java 虚拟机实现平台无关性);
- 可靠性;
- 安全性;
- 反对多线程(C++ 语言没有内置的多线程机制,因而必须调用操作系统的多线程性能来进行多线程程序设计,而 Java 语言却提供了多线程反对);
- 反对网络编程并且很不便(Java 语言诞生自身就是为简化网络编程设计的,因而 Java 语言不仅反对网络编程而且很不便);
- 编译与解释并存;
3. 什么是 JDK? 什么是 JRE?什么是 JVM?三者之间的分割与区别
这几个是 Java 中很根本很根本的货色,然而我置信肯定还有很多人搞不清楚!为什么呢?因为咱们大多数时候在应用现成的编译工具以及环境的时候,并没有去思考这些货色。
JDK: 顾名思义它是给开发者提供的开发工具箱, 是给程序开发者用的。它除了包含残缺的 JRE(Java Runtime Environment),Java 运行环境,还蕴含了其余供开发者应用的工具包。
JRE: 普通用户而只须要装置 JRE(Java Runtime Environment)来运行 Java 程序。而程序开发者必须装置 JDK 来编译、调试程序。
JVM: 当咱们运行一个程序时,JVM 负责将字节码转换为特定机器代码,JVM 提供了内存治理 / 垃圾回收和平安机制等。这种独立于硬件和操作系统,正是 java 程序能够一次编写多处执行的起因。
区别与分割:
- JDK 用于开发,JRE 用于运行 java 程序;
- JDK 和 JRE 中都蕴含 JVM;
- JVM 是 java 编程语言的外围并且具备平台独立性。
4. 什么是字节码?采纳字节码的最大益处是什么?
先看下 java 中的编译器和解释器:
Java 中引入了虚拟机的概念,即在机器和编译程序之间退出了一层形象的虚构的机器。这台虚构的机器在任何平台上都提供给编译程序一个的独特的接口。
编译程序只须要面向虚拟机,生成虚拟机可能了解的代码,而后由解释器来将虚拟机代码转换为特定零碎的机器码执行。在 Java 中,这种供虚拟机了解的代码叫做 字节码
(即扩大名为 .class
的文件),它不面向任何特定的处理器,只面向虚拟机。
每一种平台的解释器是不同的,然而实现的虚拟机是雷同的。Java 源程序通过编译器编译后变成字节码,字节码由虚拟机解释执行,虚拟机将每一条要执行的字节码送给解释器,解释器将其翻译成特定机器上的机器码,而后在特定的机器上运行。这也就是解释了 Java 的编译与解释并存的特点。
Java 源代码 —-> 编译器 —->jvm 可执行的 Java 字节码(即虚构指令)—->jvm—->jvm 中解释器 —–> 机器可执行的二进制机器码 —-> 程序运行。
采纳字节码的益处:
Java 语言通过字节码的形式,在肯定水平上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点。所以 Java 程序运行时比拟高效,而且,因为字节码并不专对一种特定的机器,因而,Java 程序毋庸从新编译便可在多种不同的计算机上运行。
5. Java 和 C ++ 的区别
我晓得很多人没学过 C ++,然而面试官就是没事喜爱拿咱们 Java 和 C ++ 比呀!没方法!!!就算没学过 C ++,也要记下来!
- 都是面向对象的语言,都反对封装、继承和多态
- Java 不提供指针来间接拜访内存,程序内存更加平安
- Java 的类是单继承的,C++ 反对多重继承;尽管 Java 的类不能够多继承,然而接口能够多继承。
- Java 有主动内存管理机制,不须要程序员手动开释无用内存
6. 什么是 Java 程序的主类?应用程序和小程序的主类有何不同?
一个程序中能够有多个类,但只能有一个类是主类。在 Java 应用程序中,这个主类是指蕴含 main()办法的类。而在 Java 小程序中,这个主类是一个继承自零碎类 JApplet 或 Applet 的子类。应用程序的主类不肯定要求是 public 类,但小程序的主类要求必须是 public 类。主类是 Java 程序执行的入口点。
7. Java 应用程序与小程序之间有那些差异?
简略说应用程序是从主线程启动 (也就是 main() 办法)。applet 小程序没有 main 办法,次要是嵌在浏览器页面上运行 (调用 init() 线程或者 run()来启动),嵌入浏览器这点跟 flash 的小游戏相似。
8. 字符型常量和字符串常量的区别
- 模式上: 字符常量是单引号引起的一个字符 字符串常量是双引号引起的若干个字符
- 含意上: 字符常量相当于一个整形值(ASCII 值), 能够加入表达式运算 字符串常量代表一个地址值(该字符串在内存中寄存地位)
- 占内存大小上: 字符常量只占一个字节 字符串常量占若干个字节(至多一个字符完结标记)
9. 结构器 Constructor 是否可被 override
在讲继承的时候咱们就晓得父类的公有属性和构造方法并不能被继承,所以 Constructor 也就不能被 override, 然而能够 overload, 所以你能够看到一个类中有多个构造函数的状况。
10. 重载和重写的区别
重载: 产生在同一个类中,办法名必须雷同,参数类型不同、个数不同、程序不同,办法返回值和拜访修饰符能够不同,产生在编译时。
重写: 产生在父子类中,办法名、参数列表必须雷同,返回值范畴小于等于父类,抛出的异样范畴小于等于父类,拜访修饰符范畴大于等于父类;如果父类办法拜访修饰符为 private 则子类就不能重写该办法。
11. Java 面向对象编程三大个性: 封装、继承、多态
封装
封装把一个对象的属性私有化,同时提供一些能够被外界拜访的属性的办法,如果不想被外界办法,咱们大可不必提供办法给外界拜访。然而如果一个类没有提供给外界拜访的办法,那么这个类也没有什么意义了。
继承
继承是应用已存在的类的定义作为根底建设新类的技术,新类的定义能够减少新的数据或新的性能,也能够用父类的性能,但不能选择性地继承父类。通过应用继承咱们可能十分不便地复用以前的代码。
对于继承如下 3 点请记住:
- 子类领有父类非 private 的属性和办法。
- 子类能够领有本人属性和办法,即子类能够对父类进行扩大。
- 子类能够用本人的形式实现父类的办法。(当前介绍)。
多态
所谓多态就是指程序中定义的援用变量所指向的具体类型和通过该援用变量收回的办法调用在编程时并不确定,而是在程序运行期间才确定,即一个援用变量倒底会指向哪个类的实例对象,该援用变量收回的办法调用到底是哪个类中实现的办法,必须在由程序运行期间能力决定。
在 Java 中有两种模式能够实现多态:继承(多个子类对同一办法的重写)和接口(实现接口并笼罩接口中同一办法)。
12. String 和 StringBuffer、StringBuilder 的区别是什么?String 为什么是不可变的?
可变性
String 类中应用字符数组保留字符串,private final char value[],所以 string 对象是不可变的。StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder 类,在 AbstractStringBuilder 中也是应用字符数组保留字符串,char[]value,这两种对象都是可变的。
线程安全性
String 中的对象是不可变的,也就能够了解为常量,线程平安。AbstractStringBuilder 是 StringBuilder 与 StringBuffer 的公共父类,定义了一些字符串的基本操作,如 expandCapacity、append、insert、indexOf 等公共办法。StringBuffer 对办法加了同步锁或者对调用的办法加了同步锁,所以是线程平安的。StringBuilder 并没有对办法进行加同步锁,所以是非线程平安的。
性能
每次对 String 类型进行扭转的时候,都会生成一个新的 String 对象,而后将指针指向新的 String 对象。StringBuffer 每次都会对 StringBuffer 对象自身进行操作,而不是生成新的对象并扭转对象援用。雷同状况下应用 StirngBuilder 相比应用 StringBuffer 仅能取得 10%~15% 左右的性能晋升,但却要冒多线程不平安的危险。
对于三者应用的总结:
如果要操作大量的数据用 = String 单线程操作字符串缓冲区 下操作大量数据 = StringBuilder 多线程操作字符串缓冲区 下操作大量数据 = StringBuffer
13. 主动装箱与拆箱
装箱:将根本类型用它们对应的援用类型包装起来;
拆箱:将包装类型转换为根本数据类型;
14. 在一个静态方法内调用一个非动态成员为什么是非法的?
因为静态方法能够不通过对象进行调用,因而在静态方法里,不能调用其余非动态变量,也不能够拜访非动态变量成员。
15. 在 Java 中定义一个不做事且没有参数的构造方法的作用
Java 程序在执行子类的构造方法之前,如果没有用 super()来调用父类特定的构造方法,则会调用父类中“没有参数的构造方法”。因而,如果父类中只定义了有参数的构造方法,而在子类的构造方法中又没有用 super()来调用父类中特定的构造方法,则编译时将产生谬误,因为 Java 程序在父类中找不到没有参数的构造方法可供执行。解决办法是在父类里加上一个不做事且没有参数的构造方法。
16. import java 和 javax 有什么区别
刚开始的时候 JavaAPI 所必须的包是 java 结尾的包,javax 过后只是扩大 API 包来说应用。然而随着工夫的推移,javax 逐步的扩大成为 Java API 的组成部分。然而,将扩大从 javax 包挪动到 java 包将是太麻烦了,最终会毁坏一堆现有的代码。因而,最终决定 javax 包将成为规范 API 的一部分。
所以,实际上 java 和 javax 没有区别。这都是一个名字。
17. 接口和抽象类的区别是什么?
- 接口的办法默认是 public,所有办法在接口中不能有实现,抽象类能够有非形象的办法
- 接口中的实例变量默认是 final 类型的,而抽象类中则不肯定
- 一个类能够实现多个接口,但最多只能实现一个抽象类
- 一个类实现接口的话要实现接口的所有办法,而抽象类不肯定
- 接口不能用 new 实例化,但能够申明,然而必须援用一个实现该接口的对象 从设计层面来说,形象是对类的形象,是一种模板设计,接口是行为的形象,是一种行为的标准。
18. 成员变量与局部变量的区别有那些?
- 从语法模式上,看成员变量是属于类的,而局部变量是在办法中定义的变量或是办法的参数;成员变量能够被 public,private,static 等修饰符所润饰,而局部变量不能被访问控制修饰符及 static 所润饰;然而,成员变量和局部变量都能被 final 所润饰;
- 从变量在内存中的存储形式来看,成员变量是对象的一部分,而对象存在于堆内存,局部变量存在于栈内存
- 从变量在内存中的生存工夫上看,成员变量是对象的一部分,它随着对象的创立而存在,而局部变量随着办法的调用而主动隐没。
- 成员变量如果没有被赋初值,则会主动以类型的默认值而赋值(一种状况例外被 final 润饰但没有被 static 润饰的成员变量必须显示地赋值);而局部变量则不会主动赋值。
19. 创立一个对象用什么运算符?对象实体与对象援用有何不同?
new 运算符,new 创建对象实例(对象实例在堆内存中),对象援用指向对象实例(对象援用寄存在栈内存中)。一个对象援用能够指向 0 个或 1 个对象(一根绳子能够不系气球,也能够系一个气球); 一个对象能够有 n 个援用指向它(能够用 n 条绳子系住一个气球)。
20. 什么是办法的返回值?返回值在类的办法里的作用是什么?
办法的返回值是指咱们获取到的某个办法体中的代码执行后产生的后果!(前提是该办法可能产生后果)。返回值的作用: 接管出后果,使得它能够用于其余的操作!
21. 一个类的构造方法的作用是什么?若一个类没有申明构造方法,改程序能正确执行吗?为什么?
次要作用是实现对类对象的初始化工作。能够执行。因为一个类即便没有申明构造方法也会有默认的不带参数的构造方法。
22. 构造方法有哪些个性?
- 名字与类名雷同;
- 没有返回值,但不能用 void 申明构造函数;
- 生成类的对象时主动执行,无需调用。
23. 静态方法和实例办法有何不同?
- 在内部调用静态方法时,能够应用 ” 类名. 办法名 ” 的形式,也能够应用 ” 对象名. 办法名 ” 的形式。而实例办法只有前面这种形式。也就是说,调用静态方法能够无需创建对象。
- 静态方法在拜访本类的成员时,只容许拜访动态成员(即动态成员变量和静态方法),而不容许拜访实例成员变量和实例办法;实例办法则无此限度.
24. 对象的相等与指向他们的援用相等,两者有什么不同?
对象的相等 比的是内存中寄存的内容是否相等而援用相等 比拟的是他们指向的内存地址是否相等。
25. 在调用子类构造方法之前会先调用父类没有参数的构造方法,其目标是?
帮忙子类做初始化工作。
26. == 与 equals(重要)
== : 它的作用是判断两个对象的地址是不是相等。即,判断两个对象是不是同一个对象。(根本数据类型 == 比拟的是值,援用数据类型 == 比拟的是内存地址)
equals() : 它的作用也是判断两个对象是否相等。但它个别有两种应用状况:
- 状况 1:类没有笼罩 equals()办法。则通过 equals()比拟该类的两个对象时,等价于通过“==”比拟这两个对象。
- 状况 2:类笼罩了 equals()办法。个别,咱们都笼罩 equals()办法来两个对象的内容相等;若它们的内容相等,则返回 true(即,认为这两个对象相等)。
举个例子:
public class test1 {public static void main(String[] args) {String a = new String("ab"); // a 为一个援用
String b = new String("ab"); // b 为另一个援用, 对象的内容一样
String aa = "ab"; // 放在常量池中
String bb = "ab"; // 从常量池中查找
if (aa == bb) // true
System.out.println("aa==bb");
if (a == b) // false,非同一对象
System.out.println("a==b");
if (a.equals(b)) // true
System.out.println("aEQb");
if (42 == 42.0) { // true
System.out.println("true");
}
}}
阐明:
- String 中的 equals 办法是被重写过的,因为 object 的 equals 办法是比拟的对象的内存地址,而 String 的 equals 办法比拟的是对象的值。
- 当创立 String 类型的对象时,虚构机会在常量池中查找有没有曾经存在的值和要创立的值雷同的对象,如果有就把它赋给以后援用。如果没有就在常量池中从新创立一个 String 对象。
27. hashCode 与 equals(重要)
面试官可能会问你:“你重写过 hashcode 和 equals 么,为什么重写 equals 时必须重写 hashCode 办法?”
hashCode()介绍
hashCode() 的作用是获取哈希码,也称为散列码;它实际上是返回一个 int 整数。这个哈希码的作用是确定该对象在哈希表中的索引地位。hashCode() 定义在 JDK 的 Object.java 中,这就意味着 Java 中的任何类都蕴含有 hashCode() 函数。
散列表存储的是键值对(key-value),它的特点是:能依据“键”疾速的检索出对应的“值”。这其中就利用到了散列码!(能够疾速找到所须要的对象)
为什么要有 hashCode
咱们以“HashSet 如何查看反复”为例子来阐明为什么要有 hashCode:
当你把对象退出 HashSet 时,HashSet 会先计算对象的 hashcode 值来判断对象退出的地位,同时也会与其余曾经退出的对象的 hashcode 值作比拟,如果没有相符的 hashcode,HashSet 会假如对象没有反复呈现。然而如果发现有雷同 hashcode 值的对象,这时会调用 equals()办法来查看 hashcode 相等的对象是否真的雷同。如果两者雷同,HashSet 就不会让其退出操作胜利。如果不同的话,就会从新散列到其余地位。(摘自我的 Java 启蒙书《Head fist java》第二版)。这样咱们就大大减少了 equals 的次数,相应就大大提高了执行速度。
hashCode()与 equals()的相干规定
- 如果两个对象相等,则 hashcode 肯定也是雷同的
- 两个对象相等, 对两个对象别离调用 equals 办法都返回 true
- 两个对象有雷同的 hashcode 值,它们也不肯定是相等的
- 因而,equals 办法被笼罩过,则 hashCode 办法也必须被笼罩
- hashCode()的默认行为是对堆上的对象产生独特值。如果没有重写 hashCode(),则该 class 的两个对象无论如何都不会相等(即便这两个对象指向雷同的数据)
28. Java 中的值传递和援用传递
值传递 是指对象被值传递,意味着传递了对象的一个正本,即便正本被扭转,也不会影响源对象。(因为值传递的时候,实际上是将实参的值复制一份给形参。)
援用传递 是指对象被援用传递,意味着传递的并不是理论的对象,而是对象的援用。因而,内部对援用对象的扭转会反映到所有的对象上。(因为援用传递的时候,实际上是将实参的地址值复制一份给形参。)
29. 简述线程,程序、过程的基本概念。以及他们之间关系是什么?
线程 与过程类似,但线程是一个比过程更小的执行单位。一个过程在其执行的过程中能够产生多个线程。与过程不同的是同类的多个线程共享同一块内存空间和一组系统资源,所以零碎在产生一个线程,或是在各个线程之间作切换工作时,累赘要比过程小得多,也正因为如此,线程也被称为轻量级过程。
程序 是含有指令和数据的文件,被存储在磁盘或其余的数据存储设备中,也就是说程序是动态的代码。
过程 是程序的一次执行过程,是零碎运行程序的根本单位,因而过程是动静的。零碎运行一个程序即是一个过程从创立,运行到沦亡的过程。简略来说,一个过程就是一个执行中的程序,它在计算机中一个指令接着一个指令地执行着,同时,每个过程还占有某些系统资源如 CPU 工夫,内存空间,文件,文件,输入输出设施的使用权等等。换句话说,当程序在执行时,将会被操作系统载入内存中。线程是过程划分成的更小的运行单位。线程和过程最大的不同在于基本上各过程是独立的,而各线程则不肯定,因为同一过程中的线程极有可能会相互影响。从另一角度来说,过程属于操作系统的领域,次要是同一段时间内,能够同时执行一个以上的程序,而线程则是在同一程序内简直同时执行一个以上的程序段。
30. 线程有哪些根本状态?这些状态是如何定义的?
- 新建(new):新创建了一个线程对象。
- 可运行 (runnable):线程对象创立后,其余线程(比方 main 线程)调用了该对象的 start() 办法。该状态的线程位于可运行线程池中,期待被线程调度选中,获 取 cpu 的使用权。
- 运行 (running):可运行状态(runnable) 的线程取得了 cpu 工夫片(timeslice),执行程序代码。
- 阻塞 (block):阻塞状态是指线程因为某种原因放弃了 cpu 使用权,也即让出了 cpu timeslice,临时进行运行。直到线程进入可运行(runnable) 状态,才有 机会再次取得 cpu timeslice 转到运行 (running) 状态。阻塞的状况分三种:(一). 期待阻塞:运行 (running) 的线程执行 o.wait()办法,JVM 会把该线程放 入期待队列 (waitting queue) 中。(二). 同步阻塞:运行 (running) 的线程在获取对象的同步锁时,若该同步锁 被别的线程占用,则 JVM 会把该线程放入锁池 (lock pool) 中。(三). 其余阻塞: 运行 (running) 的线程执行 Thread.sleep(long ms)或 t.join()办法,或者收回了 I / O 申请时,JVM 会把该线程置为阻塞状态。当 sleep()状态超时 join()期待线程终止或者超时、或者 I / O 处理完毕时,线程从新转入可运行 (runnable) 状态。
- 死亡 (dead):线程 run()、main() 办法执行完结,或者因异样退出了 run()办法,则该线程完结生命周期。死亡的线程不可再次复活。
备注:能够用早起坐地铁来比喻这个过程:
还没起床:sleeping
起床拾掇好了,随时能够坐地铁登程:Runnable
等地铁来:Waiting
地铁来了,但要排队上地铁:I/ O 阻塞
上了地铁,发现临时没座位:synchronized 阻塞
地铁上找到座位:Running
达到目的地:Dead
对于 Java 根底面试题,你学废了么?