Java基础(面试题)

时间:2022-05-30 01:07:47

1:面向对象编程有很多重要的特性:

  封装,继承,多态和抽象。

2:什么是Java虚拟机?为什么Java被称作是“平台无关的编程语言”?

  (1)Java虚拟机是一个可以执行Java字节码的虚拟机进程。Java源文件被编译成能被Java虚拟机执行的字节码文件。

  (2)Java被设计成允许应用程序可以运行在任意的平台,而不需要程序员为每一个平台单独重写或者是重新编译。

   Java虚拟机让这个变为可能,因为它知道底层硬件平台的指令长度和其他特性。

3:JDK和JRE的区别是什么?

  Java运行时环境(JRE)是将要执行Java程序的Java虚拟机。它同时也包含了执行applet需要的浏览器插件。Java开

  发工具包(JDK)是完整的Java软件开发包,包含了JRE,编译器和其他的工具(比如:JavaDoc,Java调试器),可以

  让开发者开发、编译、执行Java应用程序。

4:”static”关键字是什么意思?Java中是否可以覆盖(override)一个private或者是static的方法?

  static”关键字表明一个成员变量或者是成员方法可以在没有所属的类的实例变量的情况下被访问。

  Java中static方法不能被覆盖,因为方法覆盖是基于运行时动态绑定的,而static方法是编译时静态绑定的。

5:是否可以在static环境中访问非static变量?

  static变量在Java中是属于类的,它在所有的实例中的值是一样的。当类被Java虚拟机载入的时候,会对static变量进

  行初始化。如果你的代码尝试不用实例来访问非static的变量,编译器会报错,因为这些变量还没有被创建出来,还没

  有跟任何实例关联上。

6:Java支持的数据类型有哪些?什么是自动拆装箱?

  Java语言支持的8中基本数据类型是:

  • byte
  • short
  • int
  • long
  • float
  • double
  • boolean
  • char

  自动装箱是Java编译器在基本数据类型和对应的对象包装类型之间做的一个转化。比如:把int转化成Integer,double转化

  成double,等等。反之就是自动拆箱。

7:Java中的方法覆盖(Overriding)和方法重载(Overloading)是什么意思?

  Java中的方法重载发生在同一个类里面两个或者是多个方法的方法名相同但是参数不同的情况。与此相对,方法覆盖是说子类

  重新定义了父类的方法。方法覆盖必须有相同的方法名,参数列表和返回类型。覆盖者可能不会限制它所覆盖的方法的访问。

8:Java中,什么是构造函数?什么是构造函数重载?什么是复制构造函数?

  当新对象被创建的时候,构造函数会被调用。每一个类都有构造函数。在程序员没有给类提供构造函数的情况下,Java编译器会为这个类创建一个默认的构造函数。

  Java中构造函数重载和方法重载很相似。可以为一个类创建多个构造函数。每一个构造函数必须有它自己唯一的参数列表。

  Java不支持像C++中那样的复制构造函数,这个不同点是因为如果你不自己写构造函数的情况下,Java不会创建默认的复制构造函数。

9:Java支持多继承么?

  不支持,Java不支持多继承。每个类都只能继承一个类,但是可以实现多个接口。

10:接口和抽象类的区别是什么?

  • 接口中所有的方法隐含的都是抽象的。而抽象类则可以同时包含抽象和非抽象的方法。
  • 类可以实现很多个接口,但是只能继承一个抽象类
  • 类如果要实现一个接口,它必须要实现接口声明的所有方法。但是,类可以不实现抽象类声明的所有方法,当然,在这种情况下,类也必须得声明成是抽象的。
  • 抽象类可以在不提供接口方法实现的情况下实现接口。
  • Java接口中声明的变量默认都是final的。抽象类可以包含非final的变量。
  • Java接口中的成员函数默认是public的。抽象类的成员函数可以是private,protected或者是public。
  • 接口是绝对抽象的,不可以被实例化。抽象类也不可以被实例化,但是,如果它包含main方法的话是可以被调用的。

11:什么是值传递和引用传递?

  对象被值传递,意味着传递了对象的一个副本。因此,就算是改变了对象副本,也不会影响源对象的值。

  对象被引用传递,意味着传递的并不是实际的对象,而是对象的引用。因此,外部对引用对象所做的改变会反映到所有的对象上。

12:进程和线程的区别是什么?

  进程是执行着的应用程序,而线程是进程内部的一个执行序列。一个进程可以有多个线程。线程又叫做轻量级进程。

13:创建线程有几种不同的方式?你喜欢哪一种?为什么?

  有三种方式可以用来创建线程:

  

  • 继承Thread类
  • 实现Runnable接口
  • 应用程序可以使用Executor框架来创建线程池

  实现Runnable接口这种方式更受欢迎,因为这不需要继承Thread类。在应用设计中已经继承了别的对象的情况下,这需要多继承

  (而Java不支持多继承),只能实现接口。同时,线程池也是非常高效的,很容易实现和使用。

14:概括的解释下线程的几种可用状态。

  

  • 就绪(Runnable):线程准备运行,不一定立马就能开始执行。
  • 运行中(Running):进程正在执行线程的代码。
  • 等待中(Waiting):线程处于阻塞的状态,等待外部的处理结束。
  • 睡眠中(Sleeping):线程被强制睡眠。
  • I/O阻塞(Blocked on I/O):等待I/O操作完成。
  • 同步阻塞(Blocked on Synchronization):等待获取锁。
  • 死亡(Dead):线程完成了执行。

15:同步方法和同步代码块的区别是什么?

  在Java语言中,每一个对象有一把锁。线程可以使用synchronized关键字来获取对象上的锁。synchronized关键字可应用在方法级别(粗粒度锁)或者是代码块级别(细粒度锁)。

16:在监视器(Monitor)内部,是如何做线程同步的?程序应该做哪种级别的同步?

  监视器和锁在Java虚拟机中是一块使用的。监视器监视一块同步代码块,确保一次只有一个线程执行同步代码块。每一个监视器都和

  一个对象引用相关联。线程在获取锁之前不允许执行同步代码。

17:什么是死锁(deadlock)?

  两个进程都在等待对方执行完毕才能继续往下执行的时候就发生了死锁。结果就是两个进程都陷入了无限的等待中。

18:如何确保N个线程可以访问N个资源同时又不导致死锁?

  使用多线程的时候,一种非常简单的避免死锁的方式就是:指定获取锁的顺序,并强制线程按照指定的顺序获取锁。因此,如果所有的

  线程都是以同样的顺序加锁和释放锁,就不会出现死锁了。

19:Java集合类框架的基本接口有哪些?

  Java集合类提供了一套设计良好的支持对一组对象进行操作的接口和类。Java集合类里面最基本的接口有:

  

  • Collection:代表一组对象,每一个对象都是它的子元素。
  • Set:不包含重复元素的Collection。
  • List:有顺序的collection,并且可以包含重复元素。
  • Map:可以把键(key)映射到值(value)的对象,键不能重复。

20:为什么集合类没有实现Cloneable和Serializable接口?

  集合类接口指定了一组叫做元素的对象。集合类接口的每一种具体的实现类都可以选择以它自己的方式对元素进行保存和排序。

  有的集合类允许重复的键,有些不允许。

21:什么是迭代器(Iterator)?

  Iterator接口提供了很多对集合元素进行迭代的方法。每一个集合类都包含了可以返回迭代器实例的
  迭代方法。迭代器可以在迭代的过程中删除底层集合的元素。

22:Java中的HashMap的工作原理是什么?

  Java中的HashMap是以键值对(key-value)的形式存储元素的。HashMap需要一个hash函数,它使用hashCode()和equals()

  方法来向集合/从集合添加和检索元素。当调用put()方法的时候,HashMap会计算key的hash值,然后把键值对存储在集合中合

  适的索引上。如果key已经存在了,value会被更新成新值。HashMap的一些重要的特性是它的容量(capacity),负载因子(load factor)

  和扩容极限(threshold resizing)。

23:hashCode()和equals()方法的重要性体现在什么地方?

  Java中的HashMap使用hashCode()和equals()方法来确定键值对的索引,当根据键获取值的时候也会用到这两个方法。如果没有

  正确的实现这两个方法,两个不同的键可能会有相同的hash值,因此,可能会被集合认为是相等的。而且,这两个方法也用来发现

  重复元素。所以这两个方法的实现对HashMap的精确性和正确性是至关重要的。

24:HashMap和Hashtable有什么区别?

  

  • HashMap和Hashtable都实现了Map接口,因此很多特性非常相似。但是,他们有以下不同点:
  • HashMap允许键和值是null,而Hashtable不允许键或者值是null。
  • Hashtable是同步的,而HashMap不是。因此,HashMap更适合于单线程环境,而Hashtable适合于多线程环境。
  • HashMap提供了可供应用迭代的键的集合,因此,HashMap是快速失败的。

25:数组(Array)和列表(ArrayList)有什么区别?什么时候应该使用Array而不是ArrayList?

  下面列出了Array和ArrayList的不同点:

  

  • Array可以包含基本类型和对象类型,ArrayList只能包含对象类型。
  • Array大小是固定的,ArrayList的大小是动态变化的。
  • ArrayList提供了更多的方法和特性,比如:addAll(),removeAll(),iterator()等等。
  • 对于基本类型数据,集合使用自动装箱来减少编码工作量。但是,当处理固定大小的基本数据类型的时候,这种方式相对比较慢。

26:ArrayList和LinkedList有什么区别?

  ArrayList和LinkedList都实现了List接口,他们有以下的不同点:

  (1)ArrayList是基于索引的数据接口,它的底层是数组。它可以以O(1)时间复杂度对元素进行随机访问。与此对应,LinkedList是以元

  素列表的形式存储它的数据,每一个元素都和它的前一个和后一个元素链接在一起,在这种情况下,查找某个元素的时间复杂度是O(n)。

  (2)相对于ArrayList,LinkedList的插入,添加,删除操作速度更快,因为当元素被添加到集合任意位置的时候,不需要像数组那样重新计算大小或者是更新索引。

  (3)LinkedList比ArrayList更占内存,因为LinkedList为每一个节点存储了两个引用,一个指向前一个元素,一个指向下一个元素。

27:如何权衡是使用无序的数组还是有序的数组?

  有序数组最大的好处在于查找的时间复杂度是O(log n),而无序数组是O(n)。有序数组的缺点是插入操作的时间复杂度是O(n),因为值大

  的元素需要往后移动来给新元素腾位置。相反,无序数组的插入时间复杂度是常量O(1)。

28:HashSet和TreeSet有什么区别?

  HashSet是由一个hash表来实现的,因此,它的元素是无序的。add(),remove(),contains()方法的时间复杂度是O(1)。

  另一方面,TreeSet是由一个树形的结构来实现的,它里面的元素是有序的。因此,add(),remove(),contains()方法的时间复杂度是O(logn)。

29:Java中垃圾回收有什么目的?什么时候进行垃圾回收?

  垃圾回收的目的是识别并且丢弃应用不再使用的对象来释放和重用资源。

30:System.gc()和Runtime.gc()会做什么事情?

  这两个方法用来提示JVM要进行垃圾回收。但是,立即开始还是延迟进行垃圾回收是取决于JVM的。

31:finalize()方法什么时候被调用?

  在释放对象占用的内存之前,垃圾收集器会调用对象的finalize()方法。一般建议在该方法中释放对象持有的资源。

32:如果对象的引用被置为null,垃圾收集器是否会立即释放对象占用的内存?

  不会,在下一个垃圾回收周期中,这个对象将是可被回收的。

33:Java堆的结构是什么样子的?什么是堆中的永久代(Perm Gen space)?

  JVM的堆是运行时数据区,所有类的实例和数组都是在堆上分配内存。它在JVM启动的时候被创建。对象所占的堆内存是由自动内存管理系统也就是垃圾收集器回收。

  堆内存是由存活和死亡的对象组成的。存活的对象是应用可以访问的,不会被垃圾回收。死亡的对象是应用不可访问尚且还没有被垃圾收集器回收掉的对象。一直到

  垃圾收集器把这些对象回收掉之前,他们会一直占据堆内存空间。

34:在Java中,对象什么时候可以被垃圾回收?

  当对象对当前使用这个对象的应用程序变得不可触及的时候,这个对象就可以被回收了。

35:JVM的永久代中会发生垃圾回收么?

  垃圾回收不会发生在永久代,如果永久代满了或者是超过了临界值,会触发完全垃圾回收(Full GC)。如果你仔细查看垃圾收集器的输出信息,就会发现永久代也是被回

  收的。这就是为什么正确的永久代大小对避免Full GC是非常重要的原因。

36:String是最基本的数据类型么?

  不是。Java中的基本数据类型只有8个:byte、short、int、long、float、double、char、boolean;除了基本类型(primitive type)

  和枚举类型(enumeration type),剩下的都是引用类型(reference type)。

37:float f=3.4;是否正确?

  不正确。3.4是双精度数,将双精度型(double)赋值给浮点型(float)属于下转型(down-casting,也称为窄化)会造成精度损失,

  因此需要强制类型转换float f =(float)3.4; 或者写成float f =3.4F;。

38:short s1=1;s1=s1+1;有错么?s1=1;s1+=1;有错么?

  对于short s1 = 1; s1 = s1 + 1;由于1是int类型,因此s1+1运算结果也是int 型,需要强制转换类型才能赋值给short型。

  而short s1 = 1; s1 += 1;可以正确编译,因为s1+= 1;相当于s1 = (short)(s1 + 1);其中有隐含的强制类型转换。

39:&和&&的区别?

  &运算符有两种用法:(1)按位与;(2)逻辑与。&&运算符是短路与运算。逻辑与跟短路与的差别是非常巨大的,虽然二者都要求运算符左右两端的

  布尔值都是true整个表达式的值才是true。&&之所以称为短路运算是因为,如果&&左边的表达式的值是false,右边的表达式会被直接短路掉,不

  会进行运算。很多时候我们可能都需要用&&而不是&,例如在验证用户登录时判定用户名不是null而且不是空字符串,应当写为:username !=

  null &&!username.equals(""),二者的顺序不能交换,更不能用&运算符,因为第一个条件如果不成立,根本不能进行字符串的equals比较,否则

  会产生NullPointerException异常。注意:逻辑或运算符(|)和短路或运算符(||)的差别也是如此。

40:解释内存中的栈(stack),堆(heap),静态区(static area)的用法?

  通常我们定义一个基本数据类型的变量,一个对象的引用,还有就是函数调用的现场保存都使用内存中的栈空间;而通过new关键字和构造器创建的

  对象放在堆空间;程序中的字面量(literal)如直接书写的100、"hello"和常量都是放在静态区中。栈空间操作起来最快但是栈很小,通常大量的对象

  都是放在堆空间,理论上整个内存没有被其他进程使用的空间甚至硬盘上的虚拟内存都可以被当成堆空间来使用。

  String str=new String("hello");

  上面的语句中变量str放在栈上,用new创建出来的字符串对象放在堆上,而"hello"这个字面量放在静态区。

41:数组有没有length()方法?String有没有length()方法?

  数组没有length()方法,有length 的属性。String 有length()方法。JavaScript中,获得字符串的长度是通过length属性得到的。

42:在Java中如何跳出当前的多重嵌套循环?

  在最外层循环前加一个标记如A,然后用break A;可以跳出多重循环。

43:构造器(constructor)是否可被重写(override)?

  构造器不能被继承,因此不能被重写,但可以被重载。

44:俩个对象值相同(x.equals(y)==true),但却可有不同的hash code,这句话对不对?

  不对,如果两个对象x和y满足x.equals(y) == true,它们的哈希码(hash code)应当相同。Java对于eqauls方法和hashCode方法是这样

  规定的:(1)如果两个对象相同(equals方法返回true),那么它们的hashCode值一定要相同;(2)如果两个对象的hashCode相同,它们并不一定相同。

  使用==操作符检查"参数是否为这个对象的引用"。

45:是否可以继承String类?

  String 类是final类,不可以被继承。

46:当一个对象被作为参数传入一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里是值传递还是引用传递?

  是值传递。Java语言的方法调用只支持参数的值传递。当一个对象实例作为一个参数被传递到方法中时,参数的值就是对该对象的引用。

  对象的属性可以在被调用过程中被改变,但对对象引用的改变是不会影响到调用者的。

47:String,StringBuilder,Stringbuffer的区别?

  Java平台提供了两种类型的字符串:String和StringBuffer/StringBuilder,它们可以储存和操作字符串。其中String是只读字符串,

  也就意味着String引用的字符串内容是不能被改变的。而StringBuffer/StringBuilder类表示的字符串对象可以直接进行修改。

48:抽象类和接口有什么异同?

  抽象类和接口都不能够实例化,但可以定义抽象类和接口类型的引用。一个类如果继承了某个抽象类或者实现了某个接口都需要对其中的抽象方法全部进行实现,

  否则该类仍然需要被声明为抽象类。接口比抽象类更加抽象,因为抽象类中可以定义构造器,可以有抽象方法和具体方法,而接口中不能定义构造器而且其中的

  方法全部都是抽象方法。抽象类中的成员可以是private、默认、protected、public的,而接口中的成员全都是public的。抽象类中可以定义成员变量,而接口中

  定义的成员变量实际上都是常量。有抽象方法的类必须被声明为抽象类,而抽象类未必要有抽象方法。

49:java中会存在内存泄露吗,请简要叙述一下?

  理论上Java因为有垃圾回收机制(GC)不会存在内存泄露问题(这也是Java被广泛使用于服务器端编程的一个重要原因);然而在实际开发中,可能会存在无用

  但可达的对象,这些对象不能被GC回收,因此也会导致内存泄露的发生。例如hibernate的Session(一级缓存)中的对象属于持久态,垃圾回收器是不会回收这

  些对象的,然而这些对象中可能存在无用的垃圾对象,如果不及时关闭(close)或清空(flush)一级缓存就可能导致内存泄露。下面例子中的代码也会导致内存泄露。

50:抽象方法(abstract)是否可同时是静态(static)的?是否可同时是本地的?是否可同时被syhcronized修饰?

  都不能。抽象方法需要子类重写,而静态的方法是无法被重写的,因此二者是矛盾的。本地方法是由本地代码(如C代码)实现的方法,而抽象方法是没有实现的,也是

  矛盾的。synchronized和方法的实现细节有关,抽象方法不涉及实现细节,因此也是相互矛盾的。

51:阐述静态变量和实例变量的区别?

  静态变量是被static修饰符修饰的变量,也称为类变量,它属于类,不属于类的任何一个对象,一个类不管创建多少个对象,静态变量在内存中有且仅有一个拷贝;实例

  变量必须依存于某一实例,需要先创建对象然后通过对象才能访问到它。静态变量可以实现让多个对象共享内存。

52:是否可以从静态方法的内部发出对非静态方法的调用?

  不可以,静态方法只能访问静态成员,因为非静态方法的调用要先创建对象,在调用静态方法时可能对象并没有被初始化。

53:String str=new String("xyz");创建了几个字符串对象?

  两个对象,一个是静态区的"xyz",一个是用new创建在堆上的对象。

54:java中的final关键字有哪些用法?

  (1)修饰类:表示该类不能被继承;(2)修饰方法:表示方法不能被重写;(3)修饰变量:表示变量只能一次赋值以后值不能被修改(常量)。

55:如何将基本数据类型转换成字符串?如何将字符串转换成基本数据类型?

  调用基本数据类型对应的包装类中的方法parseXXX(String)或valueOf(String)即可返回相应基本类型; 
  一种方法是将基本数据类型与空字符串("")连接(+)即可获得其所对应的字符串;另一种方法是调用String 类中的valueOf()方法返回相应字符串。

56:error和exception的区别?

  Error表示系统级的错误和程序不必处理的异常,是恢复不是不可能但很困难的情况下的一种严重问题;比如内存溢出,不可能指望程序能处理这样的情况;

  Exception表示需要捕捉或者需要程序进行处理的异常,是一种设计或实现问题;也就是说,它表示如果程序运行正常,从不会发生的情况。

57:列出你常见一些运行时异常?

  ArithmeticException(算术异常) 
  ClassCastException (类转换异常) 
  IllegalArgumentException (非法参数异常) 
  IndexOutOfBoundsException (下标越界异常) 
  NullPointerException (空指针异常) 
  SecurityException (安全异常)

58:阐述final,finally,finalize的区别?

  (1)final:修饰符(关键字)有三种用法:如果一个类被声明为final,意味着它不能再派生出新的子类,即不能被继承,因此它和abstract是反义词。将变量声明为final,

  可以保证它们在使用中不被改变,被声明为final的变量必须在声明时给定初值,而在以后的引用中只能读取不可修改。被声明为final的方法也同样只能使用,不能在子类中被重写。

  (2)finally:通常放在try…catch…的后面构造总是执行代码块,这就意味着程序无论正常执行还是发生异常,这里的代码只要JVM不关闭都能执行,可以将释放外部资源的代码写在finally块中。

  (3)finalize:Object类中定义的方法,Java中允许使用finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。这个方法是由垃圾收集器在销毁对象时调用的,

  通过重写finalize()方法可以整理系统资源或者执行其他清理工作。

59:Thread类的sleep()方法和对象的wait()方法都可以让线程暂停执行,他们有什么区别?

  sleep()方法(休眠)是线程类(Thread)的静态方法,调用此方法会让当前线程暂停执行指定的时间,将执行机会(CPU)让给其他线程,但是对象的锁依然保持,

  因此休眠时间结束后会自动恢复(线程回到就绪状态,请参考第66题中的线程状态转换图)。wait()是Object类的方法,调用对象的wait()方法导致当前线程放弃对象

  的锁(线程暂停执行),进入对象的等待池(wait pool),只有调用对象的notify()方法(或notifyAll()方法)时才能唤醒等待池中的线程进入等锁池(lock pool),

  如果线程重新获得对象的锁就可以进入就绪状态。

  补充:可能不少人对什么是进程,什么是线程还比较模糊,对于为什么需要多线程编程也不是特别理解。简单的说:进程是具有一定独立功能的程序关于某个数据集合上的

  一次运行活动,是操作系统进行资源分配和调度的一个独立单位;线程是进程的一个实体,是CPU调度和分派的基本单位,是比进程更小的能独立运行的基本单位。线程的划

  分尺度小于进程,这使得多线程程序的并发性高;进程在执行时通常拥有独立的内存单元,而线程之间可以共享内存。使用多线程的编程通常能够带来更好的性能和用户体验,

  但是多线程的程序对于其他程序是不友好的,因为它可能占用了更多的CPU资源。当然,也不是线程越多,程序的性能就越好,因为线程之间的调度和切换也会浪费CPU时间。